赞
踩
帖子详情页主要由以下五部分组成:
然后帖子主要包含四个主要部分:
详情页是通过点击帖子列表页中的单个卡片中的链接跳转而来的,详情页的路径可以定义为 /detail/{postId},用帖子id来确定具体的帖子。在首页帖子列表渲染时已经做了处理,跳转路径为:
<a th:href="@{'/detail/'+${bbsEntity.postId}}" th:text="${bbsEntity.postTitle}">
添加链接后,在点击后就会跳转到详情页面。
首先需要实现数据查询的功能,详情页中不止会展示帖子详情数据,还会展示该发帖人信息和评论数据,发帖人信息和帖子详情内容可以根据 postId 和 publicUserId 通过一定的逻辑查询到,因此会涉及到帖子表、用户表两张表的查询操作。
这里先需要将数据获取并转发到对应的模板页面中,需要在帖子请求的 Controller 方法中将查询到的数据放入 request 域中,在 BBSPostController 类中新增 postDetail() 方法。
/** * 跳转帖子详情页 * @param request * @param postId * @param commentPage * @return */ @GetMapping("detail/{postId}") public String postDetail(HttpServletRequest request, @PathVariable(value = "postId") Long postId, @RequestParam(value = "commentPage", required = false, defaultValue = "1") Integer commentPage) { List<BBSPostCategory> bbsPostCategories = bbsPostCategoryService.getBBSPostCategories(); if (CollectionUtils.isEmpty(bbsPostCategories)) { return "error/error_404"; } //将分类数据封装到request域中 request.setAttribute("bbsPostCategories", bbsPostCategories); // 帖子内容 BBSPost bbsPost = bbsPostService.getBBSPostForDetail(postId); if (bbsPost == null) { return "error/error_404"; } request.setAttribute("bbsPost", bbsPost); // 发帖用户信息 BBSUser bbsUser = bbsUserService.getUserById(bbsPost.getPublishUserId()); if (bbsUser == null) { return "error/error_404"; } request.setAttribute("bbsUser", bbsUser); // todo 是否收藏了本贴 // 本周热议的帖子 request.setAttribute("hotTopicBBSPostList", bbsPostService.getHotTopicBBSPostList()); // todo 评论数据 return "jie/detail"; }
首先通过BBSPostService类查询出帖子的详细信息。
BBSPostService
/**
* 获取详情&浏览数加1
*
* @param bbsPostId
* @return
*/
BBSPost getBBSPostForDetail(Long bbsPostId);
@Override
public BBSPost getBBSPostForDetail(Long bbsPostId) {
BBSPost bbsPost = bbsPostMapper.selectByPrimaryKey(bbsPostId);
if (bbsPost != null) {
bbsPost.setPostViews(bbsPost.getPostViews() + 1);
bbsPostMapper.updateByPrimaryKeySelective(bbsPost);
}
return bbsPost;
}
BBSPostMapper
BBSPost selectByPrimaryKey(Long postId);
int updateByPrimaryKeySelective(BBSPost record);
<select id="selectByPrimaryKey" parameterType="java.lang.Long" resultMap="ResultMapWithBLOBs"> select <include refid="Base_Column_List"/> , <include refid="Blob_Column_List"/> from tb_bbs_post where post_id = #{postId,jdbcType=BIGINT} and post_status=1 </select> <update id="updateByPrimaryKeySelective" parameterType="top.picacho.bbs.entity.BBSPost"> update tb_bbs_post <set> <if test="publishUserId != null"> publish_user_id = #{publishUserId,jdbcType=BIGINT}, </if> <if test="postTitle != null"> post_title = #{postTitle,jdbcType=VARCHAR}, </if> <if test="postCategoryId != null"> post_category_id = #{postCategoryId,jdbcType=INTEGER}, </if> <if test="postCategoryName != null"> post_category_name = #{postCategoryName,jdbcType=VARCHAR}, </if> <if test="postStatus != null"> post_status = #{postStatus,jdbcType=TINYINT}, </if> <if test="postViews != null"> post_views = #{postViews,jdbcType=BIGINT}, </if> <if test="postComments != null"> post_comments = #{postComments,jdbcType=BIGINT}, </if> <if test="postCollects != null"> post_collects = #{postCollects,jdbcType=BIGINT}, </if> <if test="lastUpdateTime != null"> last_update_time = #{lastUpdateTime,jdbcType=TIMESTAMP}, </if> <if test="createTime != null"> create_time = #{createTime,jdbcType=TIMESTAMP}, </if> <if test="postContent != null"> post_content = #{postContent,jdbcType=LONGVARCHAR}, </if> </set> where post_id = #{postId,jdbcType=BIGINT} </update>
BBSPostService
/**
* 获取用户详情
*
* @param userId
* @return
*/
BBSUser getUserById(Long userId);
@Override
public BBSUser getUserById(Long userId) {
return bbsUserMapper.selectByPrimaryKey(userId);
}
BBSUserMapper数据持久层前面实现过,这里不再阐述了。
在 templates/jie/ 目录下新增详情页 detail.html。
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <head th:replace="header::head-fragment('帖子详情')"> </head> <body> <div th:replace="header::header-fragment"></div> <div class="fly-panel fly-column"> <div class="layui-container"> <ul class="layui-clear"> <li th:class="${null ==categoryId} ?'layui-hide-xs layui-this':''"> <a href="/">首页</a> </li> <th:block th:unless="${null == bbsPostCategories}"> <th:block th:each="c : ${bbsPostCategories}"> <li th:class="${null !=categoryId and categoryId==c.categoryId} ?'layui-hide-xs layui-this':''" > <a th:href="@{${'/index?categoryId='+c.categoryId}}" th:text="${c.categoryName}" >分享</a > </li> </th:block> </th:block> <li class="layui-hide-xs layui-hide-sm layui-show-md-inline-block"> <span class="fly-mid"></span> </li> </ul> <div class="fly-column-right layui-hide-xs"> <a th:href="@{/addPostPage}" class="layui-btn">发布新帖</a> </div> </div> </div> <div class="layui-container"> <div class="layui-row layui-col-space15"> <div class="layui-col-md8 content detail"> <div class="fly-panel detail-box"> <h1 th:text="${bbsPost.postTitle}">My-BBs</h1> <div class="fly-detail-info"> <div> <a class="layui-btn layui-btn-xs jie-admin" th:href="@{'/editPostPage/'+${bbsPost.postId}}" >编辑</a > </div> <span class="fly-list-nums"> <a href="#comments" ><i class="iconfont" title="评论"></i> <th:block th:text="${bbsPost.postComments}"></th:block ></a> <i class="iconfont" title="人气"></i> <th:block th:text="${bbsPost.postViews}"></th:block> </span> </div> <div class="detail-about"> <a class="fly-avatar" th:href="@{${'/userCenter/'+bbsUser.userId}}" > <img th:src="@{${bbsUser.headImgUrl}}" /> </a> <div class="fly-detail-user"> <a th:href="@{${'/userCenter/'+bbsUser.userId}}" class="fly-link" > <cite th:text="${bbsUser.nickName}">picacho</cite> <span> <th:block th:if="${bbsUser.userStatus==0}" >账号正常 </th:block> <th:block th:if="${bbsUser.userStatus==1}" >账号已被封 </th:block> </span> </a> </div> <div class="detail-hits" id="LAY_jieAdmin" data-id="123"> <span th:text="${#dates.format(bbsPost.createTime, 'yyyy-MM-dd')}" >2021-08-01</span > <span style="margin-left: 6px; padding-right: 10px; color: #FF7200" th:text="${'最新修改时间:'+#dates.format(bbsPost.lastUpdateTime, 'yyyy-MM-dd HH:mm:ss')}" >2021-08-01</span > </div> </div> <div class="detail-body photos" th:utext="${bbsPost.postContent}" ></div> </div> <div class="fly-panel detail-box" id="comments"> <fieldset class="layui-elem-field layui-field-title" style="text-align: center;" > <legend>回帖</legend> </fieldset> <ul class="jieda" id="jieda"> <li class="fly-none">消灭零回复</li> </ul> </div> </div> <div class="layui-col-md4"> <dl class="fly-panel fly-list-one"> <dt class="fly-panel-title">本周热议</dt> <th:block th:if="${#lists.isEmpty(hotTopicBBSPostList)}"> <!-- 无数据时 --> <div class="fly-none">没有相关数据</div> </th:block> <th:block th:unless="${#lists.isEmpty(hotTopicBBSPostList)}"> <th:block th:each="bbsEntity : ${hotTopicBBSPostList}"> <dd> <a th:href="@{'/detail/'+${bbsEntity.postId}}" th:text="${bbsEntity.postTitle}" >My-BBS</a > <span ><i class="iconfont icon-pinglun1"></i> <th:block th:text="${bbsEntity.postComments}"></th:block ></span> </dd> </th:block> </th:block> </dl> </div> </div> </div> <div class="fly-footer"> <p> My-BBS社区 2021 © <a href="#" target="_blank">picacho</a> </p> </div> <script th:src="@{/layui/layui.js}"></script> </body> </html>
根据返回的帖子信息、帖子作者信息依次将数据渲染到作者信息展示区域、帖子标题区域、帖子的基础信息区域和帖子详情区域。有一点需要注意,读取帖子详情时,使用的 th 标签为 th:utext,并不是 th:text。
将请求转发到编辑页,因为要获取帖子详情所以需要根据一个字段来查询,这里就选择 id 作为传参了。在访问 /editPostPage/{postId} 时,会把帖子编辑页所需的帖子详情内容查询出来并转发到 edit 页面。
BBSPostController
/** * 跳转至编辑页 * @param request * @param postId * @return */ @GetMapping("editPostPage/{postId}") public String editPostPage(HttpServletRequest request, @PathVariable(value = "postId") Long postId) { BBSUser bbsUser = (BBSUser) request.getSession().getAttribute(Constants.USER_SESSION_KEY); List<BBSPostCategory> bbsPostCategories = bbsPostCategoryService.getBBSPostCategories(); if (CollectionUtils.isEmpty(bbsPostCategories)) { return "error/error_404"; } //将分类数据封装到request域中 request.setAttribute("bbsPostCategories", bbsPostCategories); if (null == postId || postId < 0) { return "error/error_404"; } BBSPost bbsPost = bbsPostService.getBBSPostById(postId); if (bbsPost == null) { return "error/error_404"; } if (!bbsUser.getUserId().equals(bbsPost.getPublishUserId())) { request.setAttribute("message", "非本人发帖,无权限修改"); return "error/error"; } request.setAttribute("bbsPost", bbsPost); request.setAttribute("postId", postId); return "jie/edit"; }
BBSPostService
/**
* 获取详情
*
* @param bbsPostId
* @return
*/
BBSPost getBBSPostById(Long bbsPostId);
@Override
public BBSPost getBBSPostById(Long bbsPostId) {
return bbsPostMapper.selectByPrimaryKey(bbsPostId);
}
BBSPostMapper层前面已经实现,这里就不再阐述了。
在 templates/jie目录下新增 edit.html 的代码,该页面与新增帖子的页面 add.html 基本一致,唯一的区别就是会回显当前帖子的数据,将前一个请求携带的 bbsPost 对象进行读取并显示在编辑页面对应的 DOM 中即可。
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <head th:replace="header::head-fragment('编辑帖子')"> </head> <body> <div th:replace="header::header-fragment"></div> <div class="layui-container fly-marginTop"> <div class="fly-panel" pad20 style="padding-top: 5px;"> <!--<div class="fly-none">没有权限</div>--> <div class="layui-form layui-form-pane"> <div class="layui-tab layui-tab-brief" lay-filter="user"> <ul class="layui-tab-title"> <li class="layui-this">编辑帖子</li> </ul> <div class="layui-form layui-tab-content" id="LAY_ucm" style="padding: 20px 0;" > <div class="layui-tab-item layui-show"> <form method="post" id="postForm" onsubmit="return false;" action="##" > <div class="layui-row layui-col-space15 layui-form-item"> <input type="hidden" id="postId" th:value="${bbsPost.postId}" /> <div class="layui-col-md6"> <label for="postTitle" class="layui-form-label" >标题</label > <div class="layui-input-block"> <input type="text" id="postTitle" name="postTitle" required autocomplete="off" class="layui-input" th:value="${bbsPost.postTitle}" /> </div> </div> <div class="layui-col-md6"> <label class="layui-form-label">所在专栏</label> <div class="layui-input-block"> <select name="class" lay-filter="column" id="postCategoryId" > <option value="0"></option> <th:block th:unless="${null == bbsPostCategories}"> <th:block th:each="c : ${bbsPostCategories}"> <option th:value="${c.categoryId}" th:text="${c.categoryName}" th:selected="${null !=c.categoryId and bbsPost.postCategoryId==c.categoryId} ?true:false" > 提问 </option> </th:block> </th:block> </select> </div> </div> </div> <div class="layui-form-item layui-form-text"> <div class="layui-input-block"> <div id="wangEditor" name="postContent" required placeholder="详细描述" style="height: 260px;" th:utext="${bbsPost.postContent}" ></div> </div> </div> <div class="layui-form-item" style="margin-top: 56px;"> <label for="verifyCode" class="layui-form-label" >验证码</label > <div class="layui-input-inline"> <input type="text" id="verifyCode" name="verifyCode" required placeholder="验证码" autocomplete="off" class="layui-input" /> </div> <div class="layui-form-mid"> <span style="color: #c00;" ><img data-tooltip="看不清楚?换一张" th:src="@{/common/captcha}" onclick="this.src='/common/captcha?d='+new Date()*1" alt="单击图片刷新!" /></span> </div> </div> <div class="layui-form-item"> <button class="layui-btn" lay-filter="*" lay-submit onclick="editBBSPost()" > 立即发布 </button> </div> </form> </div> </div> </div> </div> </div> </div> <div class="fly-footer"> <p> My-BBS社区 2021 © <a href="#" target="_blank">picacho</a> </p> </div> <script th:src="@{/js/public.js}"></script> <script th:src="@{/layui/layui.js}"></script> <!-- wangEditor --> <script type="text/javascript" src="//unpkg.com/wangeditor/dist/wangEditor.min.js" ></script> <script type="text/javascript"> layui.use(["layer", "element", "jquery", "form"], function () { var layer = layui.layer, $ = layui.$, element = layui.element, form = layui.form; var editorD; //富文本编辑器 用于商品详情编辑 const E = window.wangEditor; editorD = new E("#wangEditor"); // 设置编辑区域高度为 260px editorD.config.height = 260; editorD.config.zIndex = 1; //配置服务端图片上传地址 editorD.config.uploadImgServer = "/uploadFiles"; editorD.config.uploadFileName = "files"; //限制图片大小 2M editorD.config.uploadImgMaxSize = 2 * 1024 * 1024; //限制一次最多能传几张图片 一次最多上传 5 个图片 editorD.config.uploadImgMaxLength = 5; //隐藏插入网络图片的功能 editorD.config.showLinkImg = false; editorD.config.uploadImgHooks = { // 图片上传并返回了结果,图片插入已成功 success: function (xhr) { console.log("success", xhr); }, // 图片上传并返回了结果,但图片插入时出错了 fail: function (xhr, editor, resData) { console.log("fail", resData); }, // 上传图片出错,一般为 http 请求的错误 error: function (xhr, editor, resData) { console.log("error", xhr, resData); }, // 上传图片超时 timeout: function (xhr) { console.log("timeout"); }, customInsert: function (insertImgFn, result) { if (result != null && result.resultCode == 200) { // insertImgFn 可把图片插入到编辑器,传入图片 src ,执行函数即可 result.data.forEach((img) => { insertImgFn(img); }); } else { alert("error"); } }, }; editorD.create(); window.editBBSPost = function () { var postId = $("#postId").val(); var postTitle = $("#postTitle").val(); if (isNull(postTitle)) { layer.alert('请输入标题!', {title: '提醒', skin: 'layui-layer-molv', icon: 2}); return; } var verifyCode = $("#verifyCode").val(); if (!validLength(verifyCode, 5)) { layer.alert('请输入正确的验证码!', {title: '提醒', skin: 'layui-layer-molv', icon: 2}); return; } var postCategoryId = $('#postCategoryId option:selected').val(); if (isNull(postCategoryId)) { layer.alert('请选择分类!', {title: '提醒', skin: 'layui-layer-molv', icon: 2}); return; } var postContent = editorD.txt.html(); if (!validLength(postContent, 100000)) { layer.alert('内容超出长度!', {title: '提醒', skin: 'layui-layer-molv', icon: 2}); return; } var url = '/editPost'; var data = { "postId" : postId, "postTitle": postTitle, "verifyCode": verifyCode, "postCategoryId": postCategoryId, "postContent": postContent }; $.ajax({ type: 'POST',//方法类型 url: url, data: data, success: function (result) { if (result.resultCode == 200) { window.location.href = '/detail/'+postId; } else { layer.msg(result.message); } ; }, error: function () { layer.alert('操作失败!', {title: '提醒', skin: 'layui-layer-molv', icon: 2}); } }); } }); </script> </body> </html>
在 BBSPostController 中新增 editPost() 方法,接口的映射地址为 /editPost,请求方法为 POST。
首先会对所有的参数进行基本的校验,之后交给业务层代码进行操作,与添加接口不同的是传参,多了帖子的主键 id,我们需要知道要修改的哪一条数据。
/** * 修改帖子 * @param postId * @param postTitle * @param postCategoryId * @param postContent * @param verifyCode * @param httpSession * @return */ @PostMapping("/editPost") @ResponseBody public Result editPost(@RequestParam("postId") Long postId, @RequestParam("postTitle") String postTitle, @RequestParam("postCategoryId") Integer postCategoryId, @RequestParam("postContent") String postContent, @RequestParam("verifyCode") String verifyCode, HttpSession httpSession) { BBSUser bbsUser = (BBSUser) httpSession.getAttribute(Constants.USER_SESSION_KEY); if (null == postId || postId < 0) { return ResultGenerator.genFailResult("postId参数错误"); } BBSPost temp = bbsPostService.getBBSPostById(postId); if (temp == null) { return ResultGenerator.genFailResult("postId参数错误"); } if (!bbsUser.getUserId().equals(temp.getPublishUserId())) { return ResultGenerator.genFailResult("非本人发帖,无权限修改"); } if (!StringUtils.hasLength(postTitle)) { return ResultGenerator.genFailResult("postTitle参数错误"); } if (null == postCategoryId || postCategoryId < 0) { return ResultGenerator.genFailResult("postCategoryId参数错误"); } BBSPostCategory bbsPostCategory = bbsPostCategoryService.selectById(postCategoryId); if (null == bbsPostCategory) { return ResultGenerator.genFailResult("postCategoryId参数错误"); } if (!StringUtils.hasLength(postContent)) { return ResultGenerator.genFailResult("postContent参数错误"); } if (postTitle.trim().length() > 32) { return ResultGenerator.genFailResult("标题过长"); } if (postContent.trim().length() > 100000) { return ResultGenerator.genFailResult("内容过长"); } String kaptchaCode = httpSession.getAttribute(Constants.VERIFY_CODE_KEY) + ""; if (!StringUtils.hasLength(kaptchaCode) || !verifyCode.equals(kaptchaCode)) { return ResultGenerator.genFailResult(ServiceResultEnum.LOGIN_VERIFY_CODE_ERROR.getResult()); } temp.setPostTitle(postTitle); temp.setPostContent(postContent); temp.setPostCategoryId(postCategoryId); temp.setPostCategoryName(bbsPostCategory.getCategoryName()); temp.setLastUpdateTime(new Date()); if (bbsPostService.updateBBSPost(temp) > 0) { httpSession.removeAttribute(Constants.VERIFY_CODE_KEY);//清空session中的验证码信息 return ResultGenerator.genSuccessResult(); } else { return ResultGenerator.genFailResult("请求失败,请检查参数及账号是否有操作权限"); } }
BBSPostService
/**
* 修改帖子
*
* @param bbsPost
* @return
*/
int updateBBSPost(BBSPost bbsPost);
@Override public int updateBBSPost(BBSPost bbsPost) { BBSUser bbsUser = bbsUserMapper.selectByPrimaryKey(bbsPost.getPublishUserId()); if (bbsUser == null || bbsUser.getUserStatus().intValue() == 1) { //账号已被封禁 return 0; } BBSPostCategory bbsPostCategory = bbsPostCategoryMapper.selectByPrimaryKey(bbsPost.getPostCategoryId()); if (bbsPostCategory == null) { //分类数据错误 return 0; } return bbsPostMapper.updateByPrimaryKeySelective(bbsPost); }
BBSPostMapper数据持久层这里就不再阐述了。
帖子修改接口的实现方法实现的步骤如下:
项目源码下载地址:项目源码
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。