「这是我参与11月更文挑战的第15天,活动详情查看:2021最后一次更文挑战」
前言
在上一篇中,我们完成了小程序的点赞和分享功能,这一篇我们来实现相对复杂的评论功能。
页面开发
首先还是老规矩,我们从页面维度出发,先把评论相关的页面元素及交互做出来,以此来引导串联其背后的逻辑实现。
评论图标
由于评论内容可能较多,所以我们将实际的评论交互放到内容详情页
进行,所以这里对于图标的点击只需要做详情页的跳转。同样,我们不能直接在内容项组件去实现详情页的跳转逻辑,因为内容详情页也用到了内容项组件
,所以我们将这里的评论点击响应实现为抽象处理,将动作派发给组件使用方
,然后在列表组件内进行具体的跳转实现。
handleComment() {
const { item } = this.properties
this.triggerEvent('clickComment', { id: item._id })
},
在列表组件内,由于点击内容项和点击评论图标目前的响应完全一致,所以我们将跳转单独抽离为一个方法
jumpDetailWithId(id) {
wx.navigateTo({
url: `/pages/detail/index?id=${id}`,
})
},
handleClickItem(e) {
const { detail: { id } } = e
if (id) {
this.jumpDetailWithId(id)
}
},
handleClickComment(e) {
const { detail: { id } } = e
if (id) {
this.jumpDetailWithId(id)
}
}
评论输入框
评论输入框是一个相对独立的功能,所以我们可以将其做成一个自定义组件,这样它的实现细节
可以封装在组件内部,然后通过组件暴露出的属性
和方法
与调用方交互。
<view class="{{ focusing ? 'reply-wrap focus-wrap' : 'reply-wrap' }}">
<view class="user-avatar">
<open-data type="userAvatarUrl"></open-data>
</view>
<view class="{{ focusing ? 'input-block focus-block' : 'input-block' }}">
<textarea
placeholder="{{placeText}}"
placeholder-style="color: #999; font-size: 28rpx;"
bindfocus="onFocus"
bindblur="onBlur"
bindlinechange="onLineChange"
class="{{ focusing ? 'focus-area' : 'default-area' }}"
auto-height
cursor-spacing="{{cursorSpacing}}"
confirm-type="send"
bindconfirm="onConfirm"
show-confirm-bar="{{false}}"
></textarea>
</view>
</view>
这里我们使用小程序textarea
组件作为评论输入框,这个组件是原生组件,所以使用起来有很多问题需要注意,比如调试相关样式时最好使用开发者工具的真机调试
功能检查该组件在真机上的显示效果。
评论存储
那么在页面侧有用于输入评论的组件后,我们需要将评论存入内容对象,这里我们继续使用更新内容的云函数,并将评论内容以comment
字段进行传入。
在云函数侧,我们将解析到的评论内容
结合当前发表评论的用户信息
以及评论时间
一并存入内容对象。
对于用户信息的获取与点赞
类似,我们需要判断用户是否授权过其头像和昵称,这部分逻辑由于已经重复被使用,所以我们可以将其实现逻辑封装为公共方法
公共方法只需要像下图这样在使用的地方引入即可
另外,我们还需要将要评论的内容ID通过组件属性传入,在提交评论时一并作为参数传递。
数据结构
最终,对于存在评论的内容,数据结构如下图
这样,我们就可以在读取内容的同时,解析其是否有相关的评论内容,如果有则进行展示即可。
评论展示
评论列表又是一块相对独立的内容,因此我们同样可以新建一个自定义组件用于处理评论列表的展示。
布局方面,与内容列表类似,因此我们可以参考内容列表的布局和样式。
时间格式化
另外,对于评论时间的展示,由于数据库中存储的格式为时间戳,而展现给用户的应该是经过格式化的,所以我们同样在公共方法中创建一个用于将时间戳格式化为时间信息的方法。
实现逻辑如下
const formatDate = timestamp => {
const now = Date.now()
const gap = now - timestamp
const gapDay = parseInt(gap / (24 * 60 * 60 * 1000))
if (gapDay > 0) {
return `${gapDay}天前`
} else {
const gapHour = parseInt(gap / (60 * 60 * 1000))
if (gapHour > 0) {
return `${gapHour}小时前`
} else {
const gapMin = parseInt(gap / (60 * 1000))
if (gapMin > 0) {
return `${gapMin}分钟前`
} else {
return '刚刚'
}
}
}
}
这里的逻辑就是将当前时间
与评论发生时间
相减,然后计算这个时间差
是否超出1天,如果超出一天则展示N天前;否则继续判断是否超出1小时,如果超出1小时且不足1天,则展示N小时前;最后判断是否超出1分钟,如果超出1分钟且不足1天,则展示N分钟前;如果小于1分钟则展示“刚刚”即可。
评论更新
最后,我们在评论发送成功后做一下后续的交互优化,适当的进行操作成功的提示,并触发详情页的内容刷新。
总结
本篇我们完成了小程序最基本的评论功能
实现,虽然具体细节方面还有待优化,但结合上篇所做的点赞
和分享
功能,我们集齐了点赞、评论和分享三大模块,这为我们小程序的社交属性又增添了更多的可互动性。
也因此,我们小程序的复杂度上升到了一个新的台阶,包括云函数逻辑
、数据结构
、组件逻辑
和页面交互
等多个方面。
这为后续的功能升级或优化创造了不小的挑战,这也解释了为什么我们要做代码管理
、版本控制
、拆分组件
和公共方法
这些事情。因为一个项目势必是会走向复杂的,只有在前期做好各方面的管理和基础建设,后续再在此基础上进行迭代升级才不会发生“牵一发而动全身”的情况。