一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第7天,点击查看活动详情。
前言
大家好,截止目前关于沸点这一模块我们已经实现了:“沸点评论展示”、“发布沸点评论”、“沸点评论排序”以及对一些通用模块(评论模块)的封装。还剩下几个功能点尚未实现:
- 回复评论
- 点赞沸点内容和评论内容
- 评论内容超出5条后显示查看更多按钮,点击后跳转到新页面
- 发布或回复评论时表情及图片处理 本次的分享我们将实现回复评论功能
评论组件封装
在前面的分享中,当我们点击沸点内容的评论图标时会展示出一个评论文本框,通过文本框我们可以对沸点内容进行评论。而在评论列表中的每个评论内容模块也都有一个评论的图标,点击后可以对评论内容进行回复,接下来我们就实现一下该功能。
如上图所示是掘金官方回复评论功能模块,我们会发现这个回复评论模块跟发布沸点评论内容模块是一样的,这就又涉及到了代码重用问题,因此在实现回复功能前,我们先将之前已经实现了的发布评论模块进行抽取封装:
- 新建Reply.vue组件
- 将Fire.vue中发布评论模块相关代码(HTML,js,css)提取出来放到Reply.vue组件中
- 原有的响应式属性及方法保持不变,同时新增3个props属性:
- itemId:当前内容的唯一id(沸点内容ID【msg_id】或评论内容ID【comment_id】)
- isShow:用于区分是沸点模块的评论还是评论模块的评论
- msg_id:沸点内容的id
- 给div添加id属性,值为reply+itemId,用于后期控制文本框样式用
- 修改publisComment方法,根据msg_id值是否为空进行不同的接口调用:
- 如果msg_id为空说明是在沸点内容模块,需要调用对应的发表评论的接口publish
- 如果msg_id不为空则是在评论内容模块,需要调用对应的回复评论的接口reply
- 最后在发布成功后还需要返回给父组件一个自定义事件publised,告诉父组件已经完成评论,可以重新加载评论数据 Reply.vue核心代码如下:
<template>
<div class="comment-reply" :id="`reply${itemId}`" v-show="isShow">
<div class="reply-input">
<van-field
@focus="inputFocus"
@blur="inputBlur"
v-model="reply_content"
placeholder="输入评论(Enter换行,Ctrl + Enter发送)"
></van-field>
</div>
<div class="reply-opt" v-show="showBtn">
<span> <van-icon class="reply-icon" name="smile-o"></van-icon>表情 </span>
<span> <van-icon class="reply-icon" name="photo-o"></van-icon>图片 </span>
<van-button
style="float: right"
:disabled="reply_content === ''"
type="primary"
size="small"
@click="publishComment"
>发表评论</van-button
>
<span style="float: right; margin-top: 9px; color: #8c8c8c"
>Ctrl + Enter</span
>
</div>
</div>
</template>
import { reactive, toRefs } from "vue";
import api from "../api/juejinapi";
export default {
props: {
itemId: {
type: String,
required: true,
},
isShow: {
type: Boolean,
default: false,
},
msg_id: {
type: String,
default: "",
},
},
setup(props, ctx) {
const state = reactive({ showBtn: false, reply_content: "" });
const { itemId, msg_id } = props;
const inputFocus = () => {
state.showBtn = true;
const el_input = document.querySelector(`#reply${itemId}`),
el_con = el_input.querySelector(".van-field__control ");
el_con.classList.add("active");
};
const inputBlur = () => {
const el_input = document.querySelector(`#reply${itemId}`),
el_con = el_input.querySelector(".van-field__control ");
!state.reply_content ? (state.showBtn = false) : null;
!state.reply_content ? el_con.classList.remove("active") : null;
};
const publishComment = async () => {
let res = null;
if (msg_id) {
res = await api.reply(state.reply_content, [], itemId, msg_id);
ctx.emit("published", { res, itemId: msg_id });
} else {
res = await api.publish(state.reply_content, [], itemId);
ctx.emit("published", { res, itemId });
}
state.reply_content = "";
};
return {
...toRefs(state),
inputFocus,
inputBlur,
publishComment,
};
},
};
</script>
发表及回复评论
上面我们已经将发表评论内容封装成了通用组件,因此不管是发表评论还是回复评论直接调用该组件即可,唯一有点复杂的就是在评论列表中最多可能有5条评论内容,我们需要控制点击哪个评论就显示对应的评论文本框,而不是将5条评论的文本框全部显示出来,大概思路如下:
- 添加响应式属性:current_comment用于标识当前点击的是哪条评论
- 给每个comment标签添加click事件并绑定方法replyHandle,用于给current_comment赋值为当前点击的评论内容的id
- 控制图标后面的文本内容:
- 默认如果回复数为0则显示回复两个字,点击后回复文本框出现,同时文字变为:取消回复
- 点击取消回复后,隐藏回复文本框
- 给评论组件绑定自定义事件published,发布成功后重新加载评论数据 Fire.vue核心代码及效果图如下:
<!--沸点内容区块-->
<div class="comment-list-reply">
<!--原有代码省略...-->
<reply
:itemId="msg.msg_id"
@published="publishComment"
:isShow="true"
/>
</div>
<!--评论内容区块-->
<div
class="comment"
@click="
replyHandle(
rep.comment_id,
current_comment === rep.comment_id
)
"
>
<van-icon name="chat-o" />{{
rep.comment_info.reply_count === 0
? current_comment === rep.comment_id
? "取消回复"
: "回复"
: rep.comment_info.reply_count
}}
</div>
<reply
:itemId="rep.comment_id"
@published="publishComment"
:isShow="current_comment === rep.comment_id"
:msg_id="msg.msg_id"
/>
const publishComment = (obj) => {
showComment(obj.itemId, false, 0);
};
const replyHandle = (comment_id, isShow) => {
state.current_comment = !isShow ? comment_id : "";
};
总结
如上效果图,通过对评论区块的抽取和封装,我们实现了对沸点内容发布评论的改造,以及对评论内容回复功能的实现。本次分享内容的主要核心点在于评论组件的封装,实现封装后不管是发表评论还是回复评论也都自然而然的实现了。好了本次分享就先到这里了,喜欢的朋友欢迎点赞关注加评论哦!