feat: improve nursing article module with rich content and view tracking
- Add content field (LONGTEXT) with Quill rich text editor in admin UI
- Add view_count field with auto-increment API endpoint
- Replace manual image URL input with image-upload component
- Add /system/nursing/published and /view/{id} public endpoints
- Improve Android error handling with toast and cache fallback
- Optimize ViewHolderFive adapter to reuse instead of recreating
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -145,7 +145,7 @@ public class HomeFragment extends BaseFragment {
|
||||
private void initNursingInfo() {
|
||||
OkGo.<String>get(HttpConstants.URi_system_getAppIndexInfo)
|
||||
.converter(new StringConvert())
|
||||
.cacheMode(CacheMode.NO_CACHE)
|
||||
.cacheMode(CacheMode.REQUEST_FAILED_READ_CACHE)
|
||||
.adapt(new ObservableResponse<String>())
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
@@ -181,6 +181,7 @@ public class HomeFragment extends BaseFragment {
|
||||
@Override
|
||||
public void onError(@NonNull Throwable e) {
|
||||
e.printStackTrace();
|
||||
toast("加载护理资讯失败");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -32,10 +32,12 @@ import de.greenrobot.event.EventBus;
|
||||
public class ViewHolderFive extends AbstractViewTypeHolder {
|
||||
|
||||
private final RecyclerView style_recyleview;
|
||||
private BaseQuickAdapter<homeListBean, BaseViewHolder> mAdapter;
|
||||
|
||||
public ViewHolderFive(View itemView) {
|
||||
super(itemView);
|
||||
style_recyleview = (RecyclerView) itemView.findViewById(R.id.reply_rcey);
|
||||
style_recyleview.setLayoutManager(new LinearLayoutManager(itemView.getContext()));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -43,36 +45,40 @@ public class ViewHolderFive extends AbstractViewTypeHolder {
|
||||
super.bindHolder(dataBean, position, mContext, mviewListener);
|
||||
|
||||
//护理资讯
|
||||
LinearLayoutManager manager = new LinearLayoutManager(mContext);
|
||||
style_recyleview.setLayoutManager(manager);
|
||||
List<homeListBean> data = dataBean.getNursingInfoList();
|
||||
if (data == null) {
|
||||
data = new ArrayList<>();
|
||||
}
|
||||
final List<homeListBean> finalData = data;
|
||||
style_recyleview.setAdapter(new BaseQuickAdapter<homeListBean, BaseViewHolder>(R.layout.home_list_item, data) {
|
||||
@Override
|
||||
protected void convert(final BaseViewHolder helper, final homeListBean item) {
|
||||
WidgetTools.setTextfive((TextView) helper.getView(R.id.img_modify), "", helper.getAdapterPosition() + 1 + "." + item.getContent());
|
||||
GlideTools.init(mContext).displaypic((ImageView) helper.getView(R.id.home_img_01), item.getPic(), R.mipmap.home_bj01);
|
||||
RelativeLayout rl_style = (RelativeLayout)helper.getView(R.id.reply_dialog_rl);
|
||||
rl_style.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
if(!ProfileSpUtils.getInstance().isLogin()){
|
||||
Intent intent =new Intent(mContext, NewCodeLoginActivity.class);
|
||||
mContext.startActivity(intent);
|
||||
return;
|
||||
|
||||
if (mAdapter == null) {
|
||||
mAdapter = new BaseQuickAdapter<homeListBean, BaseViewHolder>(R.layout.home_list_item, data) {
|
||||
@Override
|
||||
protected void convert(final BaseViewHolder helper, final homeListBean item) {
|
||||
WidgetTools.setTextfive((TextView) helper.getView(R.id.img_modify), "", helper.getAdapterPosition() + 1 + "." + item.getContent());
|
||||
GlideTools.init(mContext).displaypic((ImageView) helper.getView(R.id.home_img_01), item.getPic(), R.mipmap.home_bj01);
|
||||
RelativeLayout rl_style = (RelativeLayout) helper.getView(R.id.reply_dialog_rl);
|
||||
rl_style.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
if (!ProfileSpUtils.getInstance().isLogin()) {
|
||||
Intent intent = new Intent(mContext, NewCodeLoginActivity.class);
|
||||
mContext.startActivity(intent);
|
||||
return;
|
||||
}
|
||||
String url = item.getUrl();
|
||||
if (url != null && !url.isEmpty()) {
|
||||
XfiveWebActivity.runActivity(mContext, "了解陪护", url);
|
||||
} else {
|
||||
XfiveWebActivity.runActivity(mContext, "了解陪护", "file:///android_asset/privacy.html");
|
||||
}
|
||||
}
|
||||
String url = item.getUrl();
|
||||
if (url != null && !url.isEmpty()) {
|
||||
XfiveWebActivity.runActivity(mContext, "了解陪护", url);
|
||||
} else {
|
||||
XfiveWebActivity.runActivity(mContext, "了解陪护", "file:///android_asset/privacy.html");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
style_recyleview.setAdapter(mAdapter);
|
||||
} else {
|
||||
mAdapter.setNewData(finalData);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -73,11 +73,12 @@
|
||||
<el-table-column label="标题" align="center" prop="title" :show-overflow-tooltip="true" />
|
||||
<el-table-column label="封面图片" align="center" prop="imageUrl" width="100">
|
||||
<template slot-scope="scope">
|
||||
<el-image v-if="scope.row.imageUrl" :src="scope.row.imageUrl" style="width:50px;height:50px;" fit="cover" />
|
||||
<image-preview v-if="scope.row.imageUrl" :src="scope.row.imageUrl" :width="50" :height="50" />
|
||||
<span v-else>-</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="文章链接" align="center" prop="articleUrl" :show-overflow-tooltip="true" />
|
||||
<el-table-column label="阅读量" align="center" prop="viewCount" width="80" />
|
||||
<el-table-column label="状态" align="center" prop="status" width="80">
|
||||
<template slot-scope="scope">
|
||||
<el-tag :type="scope.row.status == '0' ? 'success' : 'danger'" size="small">
|
||||
@@ -120,10 +121,13 @@
|
||||
<el-input v-model="form.title" placeholder="请输入标题" />
|
||||
</el-form-item>
|
||||
<el-form-item label="封面图片" prop="imageUrl">
|
||||
<el-input v-model="form.imageUrl" placeholder="请输入图片URL地址" />
|
||||
<image-upload v-model="form.imageUrl" :limit="1" />
|
||||
</el-form-item>
|
||||
<el-form-item label="文章链接" prop="articleUrl">
|
||||
<el-input v-model="form.articleUrl" placeholder="请输入文章链接URL地址" />
|
||||
<el-input v-model="form.articleUrl" placeholder="请输入文章链接URL地址(可选)" />
|
||||
</el-form-item>
|
||||
<el-form-item label="文章正文" prop="content">
|
||||
<editor v-model="form.content" :min-height="192"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="排序" prop="sortOrder">
|
||||
<el-input-number v-model="form.sortOrder" :min="0" style="width: 100%" placeholder="请输入排序号" />
|
||||
@@ -174,12 +178,6 @@ export default {
|
||||
{ required: true, message: "标题不能为空", trigger: "blur" }
|
||||
],
|
||||
imageUrl: [
|
||||
{ required: true, message: "封面图片不能为空", trigger: "blur" }
|
||||
],
|
||||
articleUrl: [
|
||||
{ required: true, message: "文章链接不能为空", trigger: "blur" }
|
||||
],
|
||||
status: [
|
||||
{ required: true, message: "请选择状态", trigger: "change" }
|
||||
],
|
||||
}
|
||||
@@ -207,6 +205,7 @@ export default {
|
||||
title: null,
|
||||
imageUrl: null,
|
||||
articleUrl: null,
|
||||
content: null,
|
||||
sortOrder: 0,
|
||||
status: '0',
|
||||
remark: null,
|
||||
|
||||
@@ -109,7 +109,7 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter
|
||||
// 过滤请求
|
||||
.authorizeRequests()
|
||||
// 对于登录login 注册register 验证码captchaImage 允许匿名访问
|
||||
.antMatchers("/login","/appLogin","/system/msm","/smsLogin", "/register", "/captchaImage","/weixinLogin","/getUserToken","/system/weixin/wxPayNotify","/system/weixin/wxRefundNotify","/system/user/getAppIndexInfo").anonymous()
|
||||
.antMatchers("/login","/appLogin","/system/msm","/smsLogin", "/register", "/captchaImage","/weixinLogin","/getUserToken","/system/weixin/wxPayNotify","/system/weixin/wxRefundNotify","/system/user/getAppIndexInfo","/system/nursing/published","/system/nursing/view/**").anonymous()
|
||||
// 静态资源,可匿名访问
|
||||
.antMatchers(HttpMethod.GET, "/", "/*.html", "/**/*.html", "/**/*.css", "/**/*.js", "/profile/**").permitAll()
|
||||
.antMatchers("/swagger-ui.html", "/swagger-resources/**", "/webjars/**", "/*/api-docs", "/druid/**").permitAll()
|
||||
|
||||
@@ -20,6 +20,8 @@ import com.ruoyi.system.domain.RlzNursingArticle;
|
||||
import com.ruoyi.system.service.IRlzNursingArticleService;
|
||||
import com.ruoyi.common.utils.poi.ExcelUtil;
|
||||
import com.ruoyi.common.core.page.TableDataInfo;
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
|
||||
/**
|
||||
* 护理资讯Controller
|
||||
@@ -91,6 +93,39 @@ public class RlzNursingArticleController extends BaseController
|
||||
return toAjax(rlzNursingArticleService.updateRlzNursingArticle(rlzNursingArticle));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取已发布的护理资讯列表(无需认证,供App使用)
|
||||
*/
|
||||
@GetMapping("/published")
|
||||
public AjaxResult publishedList()
|
||||
{
|
||||
List<RlzNursingArticle> list = rlzNursingArticleService.selectPublishedList();
|
||||
JSONArray arr = new JSONArray();
|
||||
if (list != null) {
|
||||
for (RlzNursingArticle a : list) {
|
||||
JSONObject item = new JSONObject();
|
||||
item.put("id", a.getId());
|
||||
item.put("title", a.getTitle());
|
||||
item.put("realTimeInfoImage", a.getImageUrl());
|
||||
item.put("realTimeInfoUrl", a.getArticleUrl());
|
||||
item.put("content", a.getContent());
|
||||
item.put("viewCount", a.getViewCount());
|
||||
arr.add(item);
|
||||
}
|
||||
}
|
||||
return AjaxResult.success(arr);
|
||||
}
|
||||
|
||||
/**
|
||||
* 阅读量+1(无需认证,App点击文章时调用)
|
||||
*/
|
||||
@PutMapping("/view/{id}")
|
||||
public AjaxResult incrementView(@PathVariable("id") Long id)
|
||||
{
|
||||
rlzNursingArticleService.incrementViewCount(id);
|
||||
return AjaxResult.success();
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除护理资讯
|
||||
*/
|
||||
|
||||
@@ -30,6 +30,9 @@ public class RlzNursingArticle extends BaseEntity
|
||||
@Excel(name = "文章链接")
|
||||
private String articleUrl;
|
||||
|
||||
/** 文章正文(HTML) */
|
||||
private String content;
|
||||
|
||||
/** 排序 */
|
||||
private Integer sortOrder;
|
||||
|
||||
@@ -37,6 +40,9 @@ public class RlzNursingArticle extends BaseEntity
|
||||
@Excel(name = "状态", readConverterExp = "0=发布,1=隐藏")
|
||||
private String status;
|
||||
|
||||
/** 阅读次数 */
|
||||
private Long viewCount;
|
||||
|
||||
public void setId(Long id)
|
||||
{
|
||||
this.id = id;
|
||||
@@ -77,6 +83,16 @@ public class RlzNursingArticle extends BaseEntity
|
||||
return articleUrl;
|
||||
}
|
||||
|
||||
public void setContent(String content)
|
||||
{
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
public String getContent()
|
||||
{
|
||||
return content;
|
||||
}
|
||||
|
||||
public void setSortOrder(Integer sortOrder)
|
||||
{
|
||||
this.sortOrder = sortOrder;
|
||||
@@ -97,6 +113,16 @@ public class RlzNursingArticle extends BaseEntity
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setViewCount(Long viewCount)
|
||||
{
|
||||
this.viewCount = viewCount;
|
||||
}
|
||||
|
||||
public Long getViewCount()
|
||||
{
|
||||
return viewCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
|
||||
@@ -104,8 +130,10 @@ public class RlzNursingArticle extends BaseEntity
|
||||
.append("title", getTitle())
|
||||
.append("imageUrl", getImageUrl())
|
||||
.append("articleUrl", getArticleUrl())
|
||||
.append("content", getContent())
|
||||
.append("sortOrder", getSortOrder())
|
||||
.append("status", getStatus())
|
||||
.append("viewCount", getViewCount())
|
||||
.append("createBy", getCreateBy())
|
||||
.append("createTime", getCreateTime())
|
||||
.append("updateBy", getUpdateBy())
|
||||
|
||||
@@ -24,4 +24,6 @@ public interface RlzNursingArticleMapper
|
||||
public int deleteRlzNursingArticleById(Long id);
|
||||
|
||||
public int deleteRlzNursingArticleByIds(Long[] ids);
|
||||
|
||||
public int incrementViewCount(Long id);
|
||||
}
|
||||
|
||||
@@ -24,4 +24,6 @@ public interface IRlzNursingArticleService
|
||||
public int deleteRlzNursingArticleByIds(Long[] ids);
|
||||
|
||||
public int deleteRlzNursingArticleById(Long id);
|
||||
|
||||
public int incrementViewCount(Long id);
|
||||
}
|
||||
|
||||
@@ -63,4 +63,10 @@ public class RlzNursingArticleServiceImpl implements IRlzNursingArticleService
|
||||
{
|
||||
return rlzNursingArticleMapper.deleteRlzNursingArticleById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int incrementViewCount(Long id)
|
||||
{
|
||||
return rlzNursingArticleMapper.incrementViewCount(id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,8 +9,10 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<result property="title" column="title" />
|
||||
<result property="imageUrl" column="image_url" />
|
||||
<result property="articleUrl" column="article_url" />
|
||||
<result property="content" column="content" />
|
||||
<result property="sortOrder" column="sort_order" />
|
||||
<result property="status" column="status" />
|
||||
<result property="viewCount" column="view_count" />
|
||||
<result property="createBy" column="create_by" />
|
||||
<result property="createTime" column="create_time" />
|
||||
<result property="updateBy" column="update_by" />
|
||||
@@ -19,7 +21,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
</resultMap>
|
||||
|
||||
<sql id="selectRlzNursingArticleVo">
|
||||
select id, title, image_url, article_url, sort_order, status,
|
||||
select id, title, image_url, article_url, cast(content as char) as content, sort_order, status, view_count,
|
||||
create_by, create_time, update_by, update_time, remark
|
||||
from rlz_nursing_article
|
||||
</sql>
|
||||
@@ -50,6 +52,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<if test="title != null">title,</if>
|
||||
<if test="imageUrl != null">image_url,</if>
|
||||
<if test="articleUrl != null">article_url,</if>
|
||||
<if test="content != null">content,</if>
|
||||
<if test="sortOrder != null">sort_order,</if>
|
||||
<if test="status != null">status,</if>
|
||||
<if test="createBy != null">create_by,</if>
|
||||
@@ -60,6 +63,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<if test="title != null">#{title},</if>
|
||||
<if test="imageUrl != null">#{imageUrl},</if>
|
||||
<if test="articleUrl != null">#{articleUrl},</if>
|
||||
<if test="content != null">#{content},</if>
|
||||
<if test="sortOrder != null">#{sortOrder},</if>
|
||||
<if test="status != null">#{status},</if>
|
||||
<if test="createBy != null">#{createBy},</if>
|
||||
@@ -74,6 +78,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<if test="title != null">title = #{title},</if>
|
||||
<if test="imageUrl != null">image_url = #{imageUrl},</if>
|
||||
<if test="articleUrl != null">article_url = #{articleUrl},</if>
|
||||
<if test="content != null">content = #{content},</if>
|
||||
<if test="sortOrder != null">sort_order = #{sortOrder},</if>
|
||||
<if test="status != null">status = #{status},</if>
|
||||
<if test="updateBy != null">update_by = #{updateBy},</if>
|
||||
@@ -87,6 +92,10 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
delete from rlz_nursing_article where id = #{id}
|
||||
</delete>
|
||||
|
||||
<update id="incrementViewCount" parameterType="Long">
|
||||
update rlz_nursing_article set view_count = view_count + 1 where id = #{id}
|
||||
</update>
|
||||
|
||||
<delete id="deleteRlzNursingArticleByIds" parameterType="String">
|
||||
delete from rlz_nursing_article where id in
|
||||
<foreach item="id" collection="array" open="(" separator="," close=")">
|
||||
|
||||
Reference in New Issue
Block a user