vue+springboot实现主流软件都在使用的二级评论的详细思路及效果展示

17 篇文章 0 订阅
13 篇文章 0 订阅

效果展示

comment2.0

步骤

前端

思路

评论输入的显示与隐藏

点击dom时记录该评论的id,v-show控制展示那条评论的 回复输入框界面 ,第一次点为回复,第二次点则显示取消回复,点击后currReply置空

由于store没有响应式,这里使用计算属性检测变化

一,二级评论实现
一级正常显示,二级复制一级的dom,然后加个padding-left 实现缩进的区别,层次区别

二级评论遍历一级评论的子评论数组并展示

DOM结构

     <div>
        <!--一级评论-->
        <div class="commentBox">
            <div class="commentAvatar">
                <a :href="comment.url" target="_blank" v-show="comment.url&&comment.email">
                    <img :src="`http://q1.qlogo.cn/g?b=qq&nk=${comment.email.split(`@`)[0]}&s=100`" loading="lazy">
                </a>
                <img src="../../assets/logo.png" v-show="!comment.email" loading="lazy">
            </div>
            <div class="commentBody">
                <a class="showNickName" :href="comment.url" target="_blank">{{ comment.nickname }}</a>
                <div class="showComment themeText">{{ comment.commentContent }}</div>
                <div class="commentFooter">
                    <div class="commentTime">{{ getTime(comment.createTime) }}</div>
                    <div class="thumbUp"><i class="fa fa-thumbs-o-up"></i> 666</div>
                    <i class="fa fa-thumbs-o-down thumbDown"></i>
                    <!--当用户点击回复时,如果是第一次点,则显示,如果是第二次,则隐藏-->
                    <button @click="showReplyView(comment.id,comment.nickname)">
                        {{ this.$store.state.currReply === comment.id ? '取消回复' : '回复' }}
                    </button>
                </div>
            </div>
        </div>

        <!--评论回复输入界面-->
        <!--点击回复时记录了当前回复的id,评论比对是否等于自身id,是的话则显示-->
        <div class="replyBox" v-show="this.$store.state.currReply===comment.id">
            <!--<div class="replyAvatar"><img src="../assets/logo.png" alt=""></div>-->
            <div class="replyMain">
                <CommentInfoInput/>
            </div>
        </div>

        <!--二级评论-->
        <div v-show="comment.children&&comment.children.length>0">
            <div class="commentBox" v-for="subComment in comment.children" :key="subComment.id"
                 style="padding-left: 50px">
                <div class="commentAvatar">
                    <a :href="subComment.url" target="_blank" v-show="subComment.url&&subComment.email">
                        <!--<img :src="`http://q1.qlogo.cn/g?b=qq&nk=${subComment.email.split(`@`)[0]}&s=100`" loading="lazy">-->
                    </a>
                    <img src="../../assets/logo.png" v-show="!subComment.email" loading="lazy">
                </div>
                <div class="commentBody">

                    <div class="showComment themeText" style="margin-top: 0">
                        <a
                            class="showNickName" :href="subComment.url" target="_blank" style="margin-right: 5px">{{
                                subComment.nickname
                            }}
                        </a>
                        <a style="color: #00b4d8 ;cursor: pointer" v-if="subComment.replyname!==subComment.nickname">
                               <!--当回复人为一级评论人的名字时不需要显示,自己脑部b站评论效果吧,或者调一下看下不解释了-->
                                <span v-show="subComment.replyname!==comment.nickname">回复@{{ subComment.replyname }}</span>
                        </a>
                        <!--文章内容区域-->
                        {{ subComment.commentContent }}
                    </div>
                    <div class="commentFooter">
                        <div class="commentTime">{{ getTime(subComment.createTime) }}</div>
                        <div class="thumbUp"><i class="fa fa-thumbs-o-up"></i> 666</div>
                        <i class="fa fa-thumbs-o-down thumbDown"></i>
                        <!--当用户点击回复时,如果针对当前回复对象显示 取消回复选项-->
                        <button @click="showReplyView(subComment.id,subComment.nickname)">
                            {{ currReply === subComment.id ? '取消回复' : '回复' }}
                        </button>
                    </div>
                </div>
                <!--子评论回复输入界面-->
                <div class="replyBox" v-show="currReply===subComment.id">
                    <!--<div class="replyAvatar"><img src="../assets/logo.png" alt=""></div>-->
                    <div class="replyMain">
                        <CommentInfoInput/>
                    </div>
                </div>

            </div>
        </div>


    </div>

Method方法 computed数据

 name: "Comment",
    components: {CommentInfoInput},
    props: ['comment'],
    // 计算属性设置值时改变,且获取值时获取,避免v-for里读取不到this.$store问题
    // 动态变化
    computed: {
        currReply: {
            get() {
                return this.$store.state.currReply
            },
            set(commentId) {
                this.$store.commit('changeCurrReply', commentId)
            }
        }
    },
    methods: {
        showReplyView(commentId, nickName) {
            if (commentId === this.$store.state.currReply) {
                this.$store.state.currReply = null
                return
            }
            this.currReply = commentId
            this.$store.state.currReplyName = nickName
        },
    }

后端

思路

前面的分页查询不多概述

由于list列表只能通过下标index,0,1,2,3获取对应的评论,我们这里用map给index映射一个id值,就可以根据评论id来获取list对应的评论

遍历所有评论,如果没有父评论说明是一级节点,直接return出去

如果有父评论说明是二级节点,找到其父节点,添加到其children数组

但是如果是这样的话,那我们给二级评论回复时,二级评论的父评论还是二级评论,这样就变成了3级节点

因此我们需要使用递归找到子评论的顶级父节点,添加进去children数组,这样一来,我们就可以保持只有父亲-儿子的关系,而不是爷爷-父亲-孙子-曾孙的关系

由于子评论通常以最早发送的在最上面,也就是升序排列,这里可以使用list身上的api,sort传入Comparator的静态方法comparing根据时间对比如果大的则交换位置,类似于冒泡排序,好像这里还可以传入一个匿名内部类自定义排列规则

最后我们只需要返回过滤出所有的一级评论返回即可,二级的已经被添加到一级评论里去了

@Slf4j
@RestController
@RequestMapping("/comment")
public class CommentController {
    @Autowired
    private CommentService commentService;


    /**
     * 查询评论列表
     */	
    @GetMapping("/selectList/{articleId}/{limit}")
    public Result getPageList(
            @PathVariable int articleId,
            @PathVariable int limit
    		) {
        System.out.println("页数" + limit);
        Page<Comment> pageOne = new Page<Comment>(1, 999);
            	LambdaQueryWrapper<Comment> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Comment::getArticleId, articleId);
        queryWrapper.orderByDesc(Comment::getCreateTime);
        commentService.page(pageOne, queryWrapper);
        // 获取所有评论,如果有遍历放到一个map,id=>index,
        // 后续遍历map里的评论如果有父节点则判断父节点有无子数组  ‘
       		 //  无-》开辟一个数组,存放所有子评论,有=》直接add
        List<Comment> records = pageOne.getRecords();
        HashMap<Integer, Integer> hm = new HashMap<>();
        for (int index = 0; index < records.size(); index++) {
            hm.put(records.get(index).getId(), index);
        }
        // 给所有子评论加工到父评论列表里
        for (Comment comment : records) {
            // 判断是否有父id评论
            // System.out.println(comment);
            if (findComment(comment.getPid()) == null) {
                continue;
            }
            // System.out.println(comment.getPid());
            // 如果有则获取该评论,加到其孩子队列里
            Integer parentId = findParent(comment.getId());
            Comment father = records.get(hm.get(parentId));
            // 初始化列表
            if (father.getChildren() == null) {
                father.setChildren(new ArrayList<>());
            }
            // 在子评论列表头部添加新的子评论,达到二级回复升序排列,一级回复降序
            // 给列表加值
            father.getChildren().add(comment);
            // 升序排序
            // Collections.reverse(father.getChildren());
        }

        // 获取所有一级评论也就是没有pid的评论,因为二级已经被加到一级里了
        records = records.stream()
                .filter(p -> p.getPid() == null)
                .collect(Collectors.toList());
        // 2. 对每个顶级评论的子评论进行时间升序排序
        for (Comment comment : records) {
            List<Comment> children = comment.getChildren();
            if (children != null && !children.isEmpty()) {
                // 对子评论列表进行时间升序排序
                Collections.sort(children, Comparator.comparing(Comment::getCreateTime));
            }
        }
        pageOne.setRecords(records);
        return Result.success(pageOne);
    }

    // 递归到最顶层的Pid对应的节点,也就是一级评论,如果没有父Id则为顶级,有的话继续调用自身函数查找
    public Integer findParent(Integer id) {
        Comment comment = findComment(id);
        if (comment.getPid() == null) {
            return comment.getId();
        }
        return findParent(comment.getPid());
    }

    // ID找comment
   	 public Comment findComment(Integer id) {
        Comment comment = commentService.getById(id);
        return comment;
    }

    // 对子回复进行升序处理

    /**
     * 获取用户详细信息
     */
    @GetMapping(value = "/{id}")
    public Result getInfo(@PathVariable("id") Long id) {
        return Result.success(commentService.getById(id));
    }

别问为什么没有所有详细代码,思路很重要,有了思路其他敲着敲着就出来了,无非是样式不同dom结构不一样而已,那玩意自行diy即可

  • 22
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值