建设银行的官方网站高铁纪念币,全球建筑设计公司排名,p2p贷款网站建设,七台河新闻哪里看回放文章目录 界面效果界面实现工具js页面日期格式化 后端收藏ControllerServicemapper 评论ControllerServiceMapper 商品Controller 阅读Service 界面效果
【说明】
界面中商品的图片来源于闲鱼#xff0c;若侵权请联系删除
【商品详情】
【评论】
界面实现
工具js
该工… 文章目录 界面效果界面实现工具js页面日期格式化 后端收藏ControllerServicemapper 评论ControllerServiceMapper 商品Controller 阅读Service 界面效果
【说明】
界面中商品的图片来源于闲鱼若侵权请联系删除
【商品详情】
【评论】
界面实现
工具js
该工具类的作用是给定一个图片的url地址计算出图片的高宽比计算高宽比的作用是让图片可以按照正常比例显示
/*** 获取uuid*/
export default {/*** 获取高宽比 乘以 100%*/getAspectRatio(url) {uni.getImageInfo({src: url,success: function(res) {let aspectRatio res.height * 100.0 / res.width;// console.log(aspectRatio: aspectRatio);return aspectRatio %;}});},
}export default {/*** 日期格式化*/formatDateToString(date) {return new Date(date).toLocaleString();},
}页面
templateview classcontaineru-toast refuToast/u-toastview classuserItemview classuserProfileu--image :srcproductVo.avatar width35 height35 shapecircle/u--imageview stylewidth: 10px;/viewviewview classnickname{{productVo.nickname}}/viewview classother10分钟前来过 广东工业大学大学城校区/view/view/viewview classfollow clickfollow v-ifhadFollowfalseviewu-icon nameplus color#ffffff stylefont-weight: bold; size15/u-icon/viewview stylemargin-left: 10rpx;font-size: 15px;关 注/view/viewview classfollowed clickcancelFollow v-elseview stylefont-size: 15px;color: #C2C2C2;已 关 注/view/view/viewview classproductItemview classtopview classprice¥text classnumber{{productVo.price}}/text/{{productVo.unit}}/viewview classbrowseInformation{{product.starNum}}人想要 | {{product.readNum}}个浏览/view/viewview classproductDetail{{productVo.description}}/viewu--image :showLoadingtrue v-for(pic,index) in productVo.picList :srcpic width100%:heightgetAspectRatio(pic) radius10 modewidthFix/u--image/viewview classcommentViewview stylecolor: #3D3D3D;{{commentNum}}条评论/viewview v-for(commentItem,index) in commentVoListview classcommentItemview styledisplay: flex;u--image :srccommentItem.userAvatar width30 height30 shapecircle/u--imageview stylewidth: 10px;/viewview clickclickShowBottomPopup(1, commentItem.id,commentItem.userNickName)view classnickname{{commentItem.userNickName}}/viewview classcontent{{commentItem.content}}/viewview classdateAndPosition{{formatDateToString(commentItem.createTime)}}/view/view/viewview styledisplay: inline-block;text-align: center;u-icon namethumb-up size28 clicklikeComment(commentItem.id,commentItem)v-ifcommentItem.isLike0/u-iconu-icon namethumb-up-fill color#2B92FF size28clickcancelLikeComment(commentItem.id,commentItem) v-else/u-iconview stylefont-size: 12px;color: #B9B9B9;{{commentItem.likeNum}}/view/view/viewview classsonCommentItem v-for(commentItem1,index1) in commentItem.childrenview styledisplay: flex;u--image :srccommentItem1.userAvatar width30 height30 shapecircle/u--imageview stylewidth: 10px;/viewview clickclickShowBottomPopup(1, commentItem1.id,commentItem1.userNickName)view classnickname{{commentItem1.userNickName}}/viewview classcontenttext stylefont-size: 14px;回复了text stylecolor:#B9B9B9 ;{{commentItem1.toUserNickName}}/text/texttext{{ commentItem1.content }}/text/viewview classdateAndPosition{{formatDateToString(commentItem1.createTime)}}/view/view/viewview styledisplay: inline-block;text-align: center;u-icon namethumb-up size28 clicklikeComment(commentItem1.id,commentItem1)v-ifcommentItem1.isLike0/u-iconu-icon namethumb-up-fill color#2B92FF size28clickcancelLikeComment(commentItem1.id, commentItem1) v-else/u-iconview stylefont-size: 12px;color: #B9B9B9;{{commentItem1.likeNum}}/view/view/view/view/viewview classfooterviewview classitem clickclickShowBottomPopup(0, productVo.id,)u-icon namechat size28/u-iconview classcomment评论/view/viewview classitem clickstarProduct() v-ifhadStarfalseu-icon namestar size28/u-iconview classcomment我想要/view/viewview classitem clickcancelStar() v-ifhadStartrueu-icon namestar-fill color#2B92FF size28/u-iconview classcomment stylecolor: #2B92FF已收藏/view/view/viewview classchatu-icon namechat color#ffffff size18/u-iconview stylewidth: 5px;/view私 聊/view/view!-- 底部弹出框用于输入评论 --!-- closethis.showBottomPopupfalse 点击遮罩层关闭弹框 --u-popup :showshowBottomPopup modebottom :round10 closethis.showBottomPopupfalseview classcommentPopupu--textarea v-modelcomment.content :placeholdercommentPlaceHolder autoHeight height200bordersurround/u--textareaview classcommentButton clickcommitComment()u-icon namechat color#ffffff size18/u-iconview stylewidth: 5px;/view评 论/view/view/u-popup/view
/templatescriptimport pictureApi from /utils/picture.js;import {addFollow,hadFollowSomeone,cancelFollowSomeone} from /api/market/follow.js;import {starProduct,cancelStar,hadStar} from /api/market/star.js;import {addComment,listCommentVoOfProduct} from /api/market/comment.js;import dateUtil from /utils/date.js;import {likeComment,cancelLikeComment} from /api/market/commentLike.jsimport {getProduct} from /api/market/prodct.jsexport default {data() {return {productVo: {},product: {},// 是否已经关注商品主人hadFollow: false,// 是否已经收藏商品hadStar: false,// 是否显示底部弹出框showBottomPopup: false,// 评论comment: {itemId: undefined,type: undefined,content: ,isTop: 0},// 存储商品对应的评论集合commentVoList: [],// 评论数量commentNum: undefined,commentPlaceHolder: ,}},methods: {/*** 获取高宽比 乘以 100%*/getAspectRatio(url) {// uni.getImageInfo({// src: url,// success: function(res) {// let aspectRatio res.height * 100.0 / res.width;// // console.log(aspectRatio: aspectRatio);// return aspectRatio %;// }// });return pictureApi.getAspectRatio(url);},/*** 关注用户*/follow() {let data {followedId: this.productVo.userId}addFollow(data).then(res {this.hadFollow true;this.$refs.uToast.show({type: success,message: 关注成功,duration: 300})}).catch(err {this.$refs.uToast.show({type: error,message: err.msg,duration: 300})})},/*** 取消关注*/cancelFollow() {cancelFollowSomeone(this.productVo.userId).then(res {this.hadFollow false;this.$refs.uToast.show({type: success,message: 取消关注成功,duration: 300})})},/*** 查询是否已经关注了用户*/searchWhetherFollow() {hadFollowSomeone(this.productVo.userId).then(res {// console.log(res: JSON.stringify(res));this.hadFollow res.hadFollow;// console.log(this.hadFollow : this.hadFollow);})},/*** 收藏商品*/starProduct() {starProduct(this.productVo.id).then(res {this.hadStar true;this.getProduct();this.$refs.uToast.show({type: success,message: 收藏成功,duration: 300})})},/*** 取消收藏*/cancelStar() {cancelStar(this.productVo.id).then(res {this.hadStar false;this.getProduct();this.$refs.uToast.show({type: success,message: 取消收藏成功,duration: 300})})},/*** 点赞评论*/likeComment(commentId, comment) {// console.log(comment JSON.stringify(comment))likeComment(commentId).then(res {comment.isLike 1;comment.likeNum 1;this.$refs.uToast.show({type: success,message: 点赞成功,duration: 300})})},/*** 取消点赞评论*/cancelLikeComment(commentId, comment) {cancelLikeComment(commentId).then(res {comment.isLike 0;comment.likeNum - 1;this.$refs.uToast.show({type: success,message: 取消点赞成功,duration: 300})})},/*** 查询是否已经关注了用户*/searchWhetherStar() {hadStar(this.productVo.id).then(res {// console.log(res: JSON.stringify(res));this.hadStar res.hadStar;// console.log(this.hadFollow : this.hadFollow);})},/*** 显示底部弹出框*/clickShowBottomPopup(type, itemId, username undefined) {this.showBottomPopup true;this.comment.type type;this.comment.itemId itemId;if (type 0) {this.commentPlaceHolder 想要了解更多信息,可以评论让商品主人看见哟;} else {this.commentPlaceHolder 正在回复 username ;}},/*** 发表评论*/commitComment() {// console.log(发送评论comment JSON.stringify(this.comment))addComment(this.comment).then(res {this.showBottomPopup false;this.comment.content ;this.listCommentVoOfProduct();this.$refs.uToast.show({type: success,message: 评论发送成功,duration: 300})})},/*** 获取商品对应的所有评论*/listCommentVoOfProduct() {listCommentVoOfProduct(this.productVo.id).then(res {// console.log(listCommentVoOfProduct: JSON.stringify(res));this.commentVoList res.tree;this.commentNum res.commentNum;})},/*** 格式化日期* param {Object} date*/formatDateToString(dateStr) {let date new Date(dateStr);// 月份需要加一return date.getFullYear() - (date.getMonth() 1) - date.getDate();},/*** 获取商品详细信息同时增加阅读量*/getProduct() {getProduct(this.productVo.id).then(res {console.log(product: JSON.stringify(res.data));this.product res.data;})}},onLoad(e) {this.productVo JSON.parse(decodeURIComponent(e.productVo));this.searchWhetherFollow();this.searchWhetherStar();this.listCommentVoOfProduct();this.getProduct();// console.log(productVo: JSON.stringify(productVo));}}
/scriptstyle langscss.container {// padding: 20rpx;background: #F7F7F7;.userItem {display: flex;align-items: center;justify-content: space-between;background: #ffffff;padding: 20rpx;.userProfile {display: flex;.nickname {color: #202020;font-weight: bold;font-size: 14px;}.other {color: #A6A4A5;font-size: 11px;}}.follow {display: flex;align-items: center;font-weight: bold;color: #ffffff;background: #2B92FF;border-radius: 20px;padding: 4px 8px;}.followed {background: #F6F6F6;border-radius: 20px;padding: 4px 8px;}}.productItem {background: #ffffff;padding: 20rpx;.top {display: flex;align-items: center;justify-content: space-between;.price {color: #F84442;font-weight: bold;.number {font-size: 30px;}}.browseInformation {color: #A6A4A5;font-size: 14px;}}.productDetail {margin-top: 20rpx;margin-bottom: 10rpx;color: #4C4C4C;font-size: 15px;line-height: 30px;font-weight: bold;}}.commentView {margin-top: 10px;// 用来预留展示 footer 的高度不然footer会挡住评论margin-bottom: calc(60px 10rpx);background: #ffffff;padding: 30rpx 30rpx;.nickname {font-size: 14px;color: #B9B9B9;}.content {margin: 5px;// 解决英文字符串、数字不换行的问题word-break: break-all;word-wrap: break-word;}.dateAndPosition {font-size: 11px;color: #B9B9B9;}.commentItem {display: flex;margin: 10px;justify-content: space-between;}.sonCommentItem {display: flex;margin: 10px 10px 10px 50px;justify-content: space-between;}}.footer {padding: 20rpx;position: fixed;// right: 20rpx;bottom: 0rpx;background: #ffffff;height: 60px;width: 710rpx;padding-top: 2px;display: flex;align-items: center;justify-content: space-between;.item {display: inline-block;text-align: center;margin-right: 10px;.comment {font-size: 10px;}}.chat {display: flex;align-items: center;background-color: #2B92FF;border-radius: 20px;padding: 7px;color: #ffffff;// margin-right: 20px;font-size: 12px;}}.commentPopup {display: flex;padding: 10px;min-height: 200rpx;.commentButton {background-color: #2B92FF;border-radius: 5px;padding: 7px;color: #ffffff;font-size: 12px;height: 20px;display: flex;align-items: center;}}}
/style日期格式化
有时候后端传递过来的日期格式直接在前端页面中展示不太美观或简洁那就可以自己写一个日期格式化方法将日期转化为我们需要的格式来显示
/*** 格式化日期* param {Object} date*/
formatDateToString(dateStr) {let date new Date(dateStr);// 月份需要加一return date.getFullYear() - (date.getMonth() 1) - date.getDate();
},后端
收藏
Controller
为了便于商品数据的查询我在数据库设计的时候给商品表增加了收藏数的冗余字段因此每次收藏商品或者取消商品的收藏的同时需要更新商品表的收藏数 /*** 收藏商品*/
PreAuthorize(ss.hasPermi(market:star:star))
GetMapping(/starProduct/{productId})
public AjaxResult starProduct(PathVariable(productId) Long productId) {Star star new Star();star.setUserId(getLoginUser().getUserId());star.setProductId(productId);boolean isStar starService.addStar(star);if (isStar){// 需要将商品的收藏量1productService.starNumPlusOne(productId);}return AjaxResult.success();
}Service
package com.shm.service.impl;import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.common.core.domain.entity.Star;
import com.shm.mapper.StarMapper;
import com.shm.service.IStarService;
import org.springframework.stereotype.Service;/**
* author dam
* description 针对表【collection(收藏表)】的数据库操作Service实现
* createDate 2023-08-09 19:41:23
*/
Service
public class IStarServiceImpl extends ServiceImplStarMapper, Starimplements IStarService {Overridepublic boolean addStar(Star star) {return baseMapper.addStar(star);}
}mapper
public interface StarMapper extends BaseMapperStar {boolean addStar(Param(star) Star star);
}将商品添加收藏的时候需要先判断同样的收藏数据不存在于数据库中才执行插入操作否则如果用户网络卡顿并多次发送收藏请求数据库会出现冗余的脏数据
insert idaddStarINSERT INTO star (user_id, product_id)SELECT #{star.userId},#{star.productId} FROM DUALWHERE NOT EXISTS (SELECT 1 FROM starWHERE user_id #{star.productId} AND product_id #{star.productId} limit 1);
/insert评论
Controller
/*** 获取商品对应的所有评论** param productId* return*/
PreAuthorize(ss.hasPermi(market:comment:list))
GetMapping(/listCommentVoOfProduct/{productId})
public AjaxResult listCommentVoOfProduct(PathVariable(productId) Long productId) {// 查询出商品对应的所有评论数据ListCommentVo commentVoList commentService.listCommentVoOfProduct(productId, getLoginUser().getUserId());int commentNum commentVoList.size();// 将评论数据封装成树形结构ListCommentVo tree commentService.buildTree(commentVoList);return AjaxResult.success().put(tree, tree).put(commentNum, commentNum);
}Service
需要注意的是这里的树形结构只有两层数据针对商品的评论为一层针对评论的所有评论为一层因为小程序不方便显示太多层数据否则宽度会非常大用户需要反复滑动来查看完整的评论 Override
public ListCommentVo listCommentVoOfProduct(Long productId, Long userId) {return commentMapper.listCommentVoOfProduct(productId, userId);
}/*** 将评论数据封装成树形结构** param commentVoList* return*/
Override
public ListCommentVo buildTree(ListCommentVo commentVoList) {// 将所有父级评论过滤出来ListCommentVo fatherList commentVoList.stream().filter((item) - {return item.getType() 0;}).collect(Collectors.toList());commentVoList.removeAll(fatherList);// 为所有父级评论寻找孩子for (CommentVo father : fatherList) {father.setChildren(new ArrayList());this.searchSon(father.getId(), father.getUserNickName(), father.getChildren(), commentVoList);}return fatherList;
}/*** 寻找孩子** param fatherId* param children* param commentVoList*/
private void searchSon(Long fatherId, String fatherNickName, ListCommentVo children, ListCommentVo commentVoList) {for (CommentVo commentVo : commentVoList) {if (commentVo.getItemId().equals(fatherId)) {commentVo.setToUserNickName(fatherNickName);children.add(commentVo);this.searchSon(commentVo.getId(), commentVo.getUserNickName(), children, commentVoList);}}
}Mapper
这段sql非常复杂一次性将评论的主人昵称、头像、评论的点赞数量查出来了同时还使用递归查询来不断查询出评论的子评论。我目前不能保证这段sql的效率只是实现了功能后面如果性能不足我再想办法优化
select idlistCommentVoOfProduct resultTypecom.ruoyi.common.core.domain.vo.CommentVoSELECTct.id,ct.user_id,ct.item_id,ct.type,ct.content,ct.create_time,u.nick_name AS userNickName,u.avatar AS userAvatar,CASEWHEN cl.user_id IS NULL THEN0 ELSE 1END AS isLike,ct.LEVEL,COALESCE ( likeNum, 0 ) AS likeNumFROM(WITH RECURSIVE comment_tree AS (SELECTid,user_id,item_id,type,content,create_time,0 AS LEVELFROMCOMMENTWHEREitem_id #{productId} and type0UNION ALLSELECTc.id,c.user_id,c.item_id,c.type,c.content,c.create_time,ct.LEVEL 1 AS LEVELFROMCOMMENT cINNER JOIN comment_tree ct ON c.item_id ct.idWHEREc.type 1) SELECT*FROMcomment_tree) ctLEFT JOIN ( SELECT comment_id, COUNT(*) AS likeNum FROM comment_like WHERE is_deleted 0 GROUP BY comment_id ) pc ON ct.id pc.comment_idLEFT JOIN sys_user AS u ON ct.user_id u.user_idLEFT JOIN comment_like cl ON ct.id cl.comment_idAND cl.user_id #{userId} and cl.is_deleted 0/select商品
Controller
/*** 获取商品详细信息*/
PreAuthorize(ss.hasPermi(market:product:query))
GetMapping(value /{id})
Transactional // 同时处理多个表添加事务
public AjaxResult getInfo(PathVariable(id) Long id) {// 首先判断用户有没有阅读该商品boolean isAdd productReadService.addRead(new ProductRead(getLoginUser().getUserId(), id));if (isAdd) {// 需要将商品的阅读量1productService.readNumPlusOne(id);}return success(productService.getById(id));
}阅读
Service
insert idaddReadINSERT INTO product_read (user_id, product_id)SELECT #{productRead.userId},#{productRead.productId} FROM DUALWHERE NOT EXISTS (SELECT 1 FROM product_readWHERE user_id #{productRead.userId} AND product_id #{productRead.productId} limit 1);/insert