评论点赞:
封装请求,给评论项中的like图标注册点击事件,在事件中处理。
判断是否登录,已登录再判断是否点赞,有,取消点赞,没有,点赞。未登录,提示请登录。
// 点赞或取消点赞事件
async onCommentLike () {
// loading 开启
this.commentLoading = true
try{
// 如果已经赞了则取消点赞
if (this.comment.is_liking) {
await deleteCommentLike(this.comment.com_id)
this.comment.like_count--
} else {
// 如果没有赞,则点赞
await addCommentLike(this.comment.com_id)
this.comment.like_count++
}
// 更新视图状态
this.comment.is_liking = !this.comment.is_liking
this.$toast('操作成功')
}catch(e){
this.$toast('操作失败,请重试')
}
// loading 关闭
this.commentLoading = false
}
发表文章评论:
1. 准备弹出层:
使用vant组件进行铺设
<van-popup v-model="isPostShow" position="bottom">
测试内容
</van-popup>
绑定事件打开弹出层,不设置高度,内容会自动撑开弹层高度
<van-button
class="comment-btn"
type="default"
round
size="small"
@click="isPostShow=true"
>写评论</van-button>
2. 封装组件:components/comment-post.vue组件,弹出层中引入
<van-popup v-model="isPostShow" position="bottom">
<comment-post />
</van-popup>
3. 请求发布:
基本思路:
- 找到数据接口
- 封装请求方法
- 注册发布点击事件
- 请求发布
- 成功:将发布的内容展示到列表中
- 失败:提示失败
父子通信,传递文章id:
父:article/index.vue
<van-popup v-model="isPostShow" position="bottom">
<comment-post :target="article.art_id" />
</van-popup>
子:comment-post.vue
props: {
// 目标id,接收文章id或者评论id
target: {
type: [Number, String, Object],
required: true
}
}
comment-post.vue导入请求方法、绑定事件、书写事件函数、发送请求
async onPost () {
this.$toast.loading({
message: '发布中...',
forbidClick: true, // 禁用背景点击
duration: 0 // 持续时间,默认 2000,0 表示持续展示不关闭
})try {
const { data } = await addComment({
target: this.target.toString(), // 评论目标id(评论文章即文章id,对评论进行回复则为评论id) 防止有大数字最好也执行一下toString方法!
content: this.message, // 评论内容
art_id: null // 文章id,对评论内容发表回复时,需要传递此参数,表明所属文章id。对文章进行评论,不要传此参数。
})
this.$toast.success('发布成功')
// TODO==>
// 关闭弹出层
// 将发布内容显示到列表顶部
// 清空文本框
} catch (err) {
this.$toast.fail('发布失败')
}
}
4. 成功处理:
核心要点
- 清空文本框:
子组件`comment-post.vue`中清除`message`内容,通过自定义事件向外抛出 添加成功的数据
- 关闭弹出层:
父组件`article/index.vue` 中监听`post-success`自定义事件,执行关闭弹层和将数据放入顶部的操作
- 将发布内容显示到列表顶部
【父子通信的`props` 如果是一个 数组,我们只要不重新赋值这个`props`里面的数组,就都不算修改`props`,而可以实现父子之间共享数据,实时变化。(其本质是父子用了一个引用数据类型的数据)】
5. 空内容处理:
对输入的内容进行去除空格处理,增加`trim` 修饰符
内容没有输入的时候我们禁用 按钮
评论回复:
1. 准备回复弹层
在详情页中使用弹层用来展示文章的回复
1、在`article/index.vue` 中`data`添加数据用来控制展示回复弹层的显示状态
data () {
return {
// 其他变量...
isReplyShow: false
}
}
2、在详情页`article/index.vue`中添加使用弹层组件
<van-popup
v-model="isReplyShow"
position="bottom"
style="height: 100%"
>
评论回复
</van-popup>
2. 点击回复显示弹出层
`comment-item.vue`组件里面给回复按钮绑定事件,触发向外传递点击行数据(自定义事件)
<van-button
class="reply-btn"
round
@click="$emit('reply-click', comment)"
>回复 {{ comment.reply_count }}</van-button>
父组件`comment-list.vue`监听自定义事件,继续向外传递收到的数据(自定义事件)
<comment-item
v-for="(item, index) in list"
:key="index"
:comment="item"
@reply-click="$emit('reply-click', $event)"
/>
爷爷组件`article/index.vue` 监听自定事件,打开弹框,输出收到的数据
<comment-list
:source="article.art_id"
:list="commentList"
@onload-success="totalCommentCount = $event.total_count"
@reply-click="onReplyClick"
/>
onReplyClick (comment) {
console.log(comment) // comment-item组件传递出来的数据
// 显示评论回复弹出层
this.isReplyShow = true
}
3. 封装内容组件
创建components/comment-reply.vue`组件
4. 传递当前点击回复的评论项
思路:回顾4.2节,我们已经在`article/index.vue`中得到了`comment-item.vue`里面回复按钮传递出来的数据,我们定义变量接收,然后通过父子通信传递给`comment-reply.vue`组件
5. 处理头部及当前评论项
`comment-reply.vue` 布局内容,绑定数据
父组件`article/index.vue`中监听关闭事件
6. 展示评论回复列表
基本思路:- 回复列表和文章的评论列表几乎是一样的,所以我们复用`comment-list.vue`组件
- 修改 `comment-list.vue ` 组件,因为既要让他满足文章列表要求又要满足评论列表要求
1、修改`comment-list.vue`组件
props: {
// 文章id或者评论id===> 此刻 接收的是评论id
source: {
type: [Number, String, Object],
required: true
},
// 用于和父组件共享数据,实现添加评论列表更新,插入
list: {
type: Array,
default: () => []
},
// 【新增这个type】判断是文章还是评论
type: {
type: String,
// 自定义 Prop 数据验证
validator (value) {
return ['a', 'c'].includes(value)
},
default: 'a'
}
}
async onLoad(){
// 很多代码...
// 获取文章的评论和获取评论的回复是同一个接口
// 唯一的区别是接口参数不一样
// type
// a 文章的评论
// c 评论的回复
// source
// 文章的评论,则传递文章的 ID
// 评论的回复,则传递评论的 ID
// 1. 请求获取数据
const { data } = await getComments({
type: this.type, // 评论类型,a-对文章(article)的评论,c-对评论(comment)的回复
source: this.source.toString(), // 源id,文章id或评论id 【文章id或者评论id都有可能存在大数字】
offset: this.offset, // 获取评论数据的偏移量,值为评论id,表示从此id的数据向后取,不传表示从第一页开始读取数据
limit: this.limit // 获取的评论数据个数,不传表示采用后端服务设定的默认每页数据量
})
// 很多代码...
}
2、在`comment-reply.vue`中导入`comment-list.vue`,注册,使用
7. 回复列表数据重复数据问题
原因: `van-list` 组件有个特点,就是只有在可视范围内才会自动调用`load`事件,所以我们之前文章内容过长,就不会触发请求文章评论列表,故而 我们手动在`created`里面调用了`onLoad`方法,但是现在我们这里的回复列表一开始就在可是范围内,故而会触发2次请求,1次是可视范围内自动触发,1次是我们主动调用触发。所以我们要关闭 自动检测可视范围自动调用。
8. 回复列表内容不更新问题
弹层组件:- 如果初始的条件是 false,则弹层的内容不会渲染
- 程序运行期间,当条件变为 true 的时候,弹层才渲染了内容
- 之后切换弹层的展示,弹层只是通过 CSS 控制隐藏和显示
原因: 弹层渲染出来以后就只是简单的切换显示和隐藏,里面的内容也不再重新渲染了,所以会导致我们的评论的回复列表不会动态更新了。解决办法就是在每次弹层显示的时候重新渲染组件。
<!--弹出层是懒渲染的:只有在第一次展示的时候才会渲染里面的内容,之后它的关闭和显示都是在切换内容的显示和隐藏-->
<van-popup v-model="isReplyShow"position="bottom" style="height: 100%;">
<!--
v-if 条件渲染
true:渲染元素节点
false:不渲染
-->
<comment-reply
v-if="isReplyShow"
:comment="currentComment"
@close="isReplyShow = false"
/>
</van-popup>
9. 发布回复
处理底部视图 `comment-reply.vue`
处理参数
评论回复接口的时候我们发现`comment-post.vue`不仅需要评论id,还需要文章的对应id,这个时候 我们父组件`comment-reply.vue`并没有, 而 `article/index.vue` 组件是有的,这个时候就需要有一个祖先后代通信过程(不选择多层父子的原因是因为比较复杂)