目录
首先,你有更好的方法,那一定是你的方法更好,我赞同。
一、工具
1.微信开发者工具
2.微信云开发能力
二、实现
以我的小程序项目--南苑口袋中的食堂评论功能为例(以下简称为口袋),欢迎大家参观哟~
1.数据存储准备
1.1在云开发中的云数据库创建一个集合用来存储评论(我命名为“comment”)
1.2先自己确定集合中的每条记录需要的属性是什么
以我为例:
①口袋中每个店铺都需要有针对的评论,所以我需要一个标识食堂的属性“foodId”;
②最基本的我要区分谁发的,用户的openid;
③我要显示发送评论的用户的信息,头像“avatarUrl(头像链接)”,昵称“nickName”;
④最重要的评论内容“comment”
2.用户头像和昵称等信息的保存
在评论显示前,我就要有用户的头像显示,如下
所以我将头像链接以及用户昵称以对象形式保存在data: { }中,方便渲染。
至于头像和昵称的获取,看自己的项目要求,我是在个人信息页面使用了微信开放能力中的“头像昵称填写”来获取和保存的;
如何把这些数据传到到带评论的这个页面也看你们自己,是用本地存储还是globalData还是直接数据库获取或者其他方法都可以随便你。
3.获取并保存用户输入的内容(保存input的内容)
假设你用的input标签
3.1获取input标签的输入内容
这个没什么好说的,就是代码,微信开放文档一看就懂(标签自带input事件,js参数获取)
<input placeholder="说点什么..." type="text" bindinput="comment" />
//js代码
comment: function (e) {
// console.log(e.detail.value)
comment = e.detail.value
},
这里comment我用全局变量保存下来。
3.2评论提交按钮提交事件(保存下来)
①这里口袋会先判断用户登录状态(也是看自己项目之前是怎么判断用户登录状态的,直接用过来做个if判断就好了),登录了正常存储并提交发布,没登陆的提示状态,增强用户体验;
②存储内容,还记得我们的全局变量comment吗,把comment、avatarUrl、nickName以及foodId对应我们先前定义的属性,使用云数据库的数据库操作语法进行添加(开放文档直接用);
③完善功能,就是提交后,input里面应该是空的了,这是个小细节。我的实现方法是,使用input标签的value属性,value就是输入的实际内容,即在用户输入时,value就在不断变化;在响应数据data中定义inval初值为空,并把inval对应到前端的value属性;那在提交事件的最后,我就可以通过再将data中的inval置为空值来实现清空输入框了。
④另一个小完善,如果用户没有输入或者一开始就输入空格怎么办,我们做个判断,拿我们的全局变量comment,如果comment是空值或者第一个字符是一个空格,我们就做出提示。(其他非法字符,看自己的需求实现)
所以代码如下
//button标签的bindtap=“summit”
summit: function (e) {
if (this.data.login) { //判断登陆状态
wx.showLoading({
title: '上传中',
})
if (comment == "" || comment[0] == " ") { //非法输入判断
wx.showToast({
title: '含不恰当空格',
icon: 'error'
})
} else { //数据添加
db.collection('comment').add({
data: {
avatarUrl: this.data.userInfo.avatarUrl,
nickName: this.data.userInfo.nickName,
foodId: foodId,
comment: comment
},
success: res => {
// console.log(res)
this.onShow() //数据刷新显示
this.setData({ //清空输入框
inval: "",
})
comment = ""
wx.showToast({
title: '提交成功',
})
},
fail: err => {
console.log(err);
}
})
}
} else { //没有登录
wx.showToast({
title: '您还未登录',
icon: 'none'
})
}
},
4.评论的显示
那我们已经有了所有准备展示的信息了(所有店铺分别对应的评论以及对应发布者的信息)
①获取数据,我在生命周期onload方法中加载数据,具体流程也简单,使用云数据库的查询语法,条件就是foodId等于我们点进去的这个店铺的id(不知道怎么获取吗,其实前面就该提到这个了。。想想你应该怎么跳到对应点击的这个店铺的详情页嘞,不卖关子了,我的方法是在有所有店铺的页面,把点击到的店铺id传递给这个详情页面,用到的就是标签自定义data属性以及页面带参跳转,onload接收传递过来的参数,根据传过来的这个id来找到对应的店铺详情并展示,那这个id后面也就接着用来存储和这里的判断咯),获取到之后,给响应数据data中的commentList给它setData一下就好了;
②前端展示,wx:for就行了,遍历这个commentList来展示。
//wxml
<view wx:for="{{commentList}}" wx:key="_id">
<view class="你自己定">
<image src="{{item.avatarUrl}}" />
<view class="你自己定">
<text>{{item.nickName}}</text>:
<view class="你自己定">
{{item.comment}}
</view>
</view>
</view>
</view>
//js js js
onLoad(options) { //跳转过来带的参数
let that = this
//获取店铺详细信息
db.collection("foodlist").where({
_id: options.id
}).get({
success: res => {
that.setData({
foodDetail: res.data[0]
})
},
})
// 获取评论
db.collection('comment').where({
foodId: foodId
}).get().then(res => {
that.setData({
commentList: res.data.reverse()
})
})
})
},
但是还没结束,要记住小程序查询数据库默认只查20条记录噢!
这意味着什么呢,意味着我们要做分页啦~
5.功能完善,实现分页
那么提到数据库的分页,我们就要立刻想到 limit() 以及 skip() 了。
首先说一下,如上所述我的评论信息是没有存储发布时间这一项的,所以我的分页会很另类;如果你有存储时间,那简单了,你可以使用asc或desc配合查询语句来实现评论的后发先展示,然后分页就配合 limit() 和 skip() 做查询再进行数据拼接就好了,百度一找一大堆噢,你可以跳过这个第5点了噢~~
5.1评论的正常显示顺序(后发的在前面)
云数据库中,每条记录存下来成一个集合,每次添加记录都会添加在集合的最后,所以我的理解是最后一条记录就是最新的评论,所以怎么办嘞,我选择反转查询数据库查询下来的数组,这样也算是实现了最新评论的效果吧。
数组反转:
let newArr = arr.reverse()
这就带来了问题,集合中超过20条记录,那我就要从后往前拿,再做反转才能实现最新评论了。那就开始吧,配合上我们的 limit() 以及 skip() ,我要每次拿20条,我的首次加载的limit就给20咯;skip怎么给呢,按照上面说法,我要拿的就是倒数20条记录了,非常简单,我们用记录总数减去20不就行啦,获取记录总数,数据库查询语句的count拿就行了,拿到count,那么currentSkip就等于count - 20咯,最后把这些嵌到前边写的onload中的数据查询就好啦(记得反转噢)。
onLoad(options) {
let that = this
······
// 获取评论
db.collection('axx_comment').where({
foodId: foodId
}).count().then(res => {
let count = res.total //总数拿到了
let currentSkip = count - 20 //当前skip
//开始拿数据
db.collection('comment').limit(20).skip(currentSkip).where({
foodId: foodId
}).get().then(res => {
that.setData({
//记得反转
commentList: res.data.reverse()
})
})
})
},
5.2分页(加载更多)
做分页,变化的重点就是 skip() 的参数了。因为我的上述做法,这一步又多了许多要考虑的问题,那就是之后的skip参数我肯定不是加20了,而是减20,那就要考虑不够20减的情况了,这又带出另一个问题,skip不够20减,我们肯定就让它为0开始咯,那我的limit再拿20条的话,就会拿到重复数据了。
当然首次加载数据的情况,即onload中,我们不需要考虑limit的问题,记录多于20条和少于20条都没有影响;只要加一个currentSkip的判断就好了。
onLoad(options) {
let that = this
······
// 获取评论
db.collection('axx_comment').where({
foodId: foodId
}).count().then(res => {
let count = res.total //总数拿到了
let currentSkip //当前skip
//做判断
if (count - 20 < 0) {
currentSkip = 0
} else {
currentSkip = count - 20
}
//开始拿数据
db.collection('comment').limit(20).skip(currentSkip).where({
foodId: foodId
}).get().then(res => {
that.setData({
//记得反转
commentList: res.data.reverse()
})
})
})
},
开始分页 加载更多
首先我们将刚刚提到的count变量和currentSkip变量,都做变量提升,我把它们设置为全局变量;另外,我们还要设置一个全局变量showLength(已显示的评论数,主要用来判断到底够不够20减),方便做分页。
var currentSkip //从哪开始
var count //记录总数
var showLength //已显示评论数
定义一个加载更多的方法,我是 moreComment() ,然后要触底加载还是点击事件加载随便你。这一步我们就需要考虑上面提出的问题了。
首先showLength的初值在首次onload加载的回调中进行赋值就好了,就是 showLength = res.data.length,然后在每次加载更多中都进行更新,就是自身再加 res.data.length;
在 moreComment() 我们首先就可以使用 count - showLength 来判断还有没有数据可以加载,并给出反馈。有数据的情况,这里又分两种情况,①剩余数据小于20条的情况,那么我们的skip参数就是0,limit参数就是 count - showLength 了;②剩余数据大于20条的情况,简单了,skip参数就是 currentSkip - 20 ,limit参数就还是20了。
最后数据拼接,要注意的就是新数据反转后再拼接,showLength更新。
onLoad(options) {
let that = this
······
// 获取评论
db.collection('axx_comment').where({
foodId: foodId
}).count().then(res => {
count = res.total //总数(变量已提升,下同)
//开始位置
if (count - 20 < 0) {
currentSkip = 0
} else {
currentSkip = count - 20
}
//开始拿数据
db.collection('comment').limit(20).skip(currentSkip).where({
foodId: foodId
}).get().then(res => {
that.setData({
//记得反转
commentList: res.data.reverse()
})
//已显示评论数
showLength = res.data.length
})
})
},
//加载更多
moreComment() {
let that = this
wx.showLoading({
title: '加载更多',
})
//没有数据了
if (count - showLength == 0) {
wx.showToast({
title: '没有更多了',
icon: 'none'
})
}
else {
let skip //开始位置
let max //加载条数
//少于20条
if (count - showLength < 20) {
max = count - showLength
skip = 0
} else { //多于20条
max = 20
skip = currentSkip - 20
}
db.collection('comment').limit(max).skip(skip).where({
foodId: foodId
}).get().then(res => {
that.setData({
//新数据反转后与老数据拼接
commentList: that.data.commentList.concat(res.data.reverse())
})
//showLength更新
showLength = showLength + res.data.length
wx.showToast({
title: '加载成功',
icon: 'none'
})
})
}
},
最后,用你想要的方式来触发。
这里我自己碰到的问题是异步问题,当然不是上面的写法会出现的。
6.自己发的自己可以删除
这个就简单了,根据我们存云数据库的openid来设置谁可删除(当然是自己),点击删除,就从数据库中删除这条记录(如何获取这条记录?参考上面获取foodId的方法,获取这条评论的id),最后做一个数据刷新。这块内容网上一找也是一大堆噢~
三、总结
用了奇奇怪怪的方法;
碰到异步执行引发的相关问题(以后再说);
新评论发布时,做数据刷新显示,记得count、currentSkip、showLength也要刷新。