微信小程序云开发实现多用户评论点赞收藏功能
在微信小程序开发过程中,很多时候有这样的需求,如购物评价商品,收藏商品,给商品点赞等,本文做一个小demo给大家实现。
1.页面效果图如下
下图为一个课程列表页,点击某个课程可实现跳转到课程详情页
下图为课程详情页,页面展示不太美观,有需求可自己修改样式,详情页包括课程相关信息以及评论区域,重点为评论区域,评论区域包括发送评论,以及对评论进行点赞,显示评论时间等,评论按点赞数目多少从上到下排列。
2.实现过程
(1)课程列表courses实现代码如下:
//courses.wxml
<block wx:for="{{courseList}}" wx:key="index">
<view class="itemRoot" bindtap="goCourseDetail" data-id="{{item._id}}">
<text>{{item.course_name}}</text>
<text>{{item.course_desc}}</text>
</view>
</block>
//courses.wxss
.itemRoot{
border-bottom:1px solid gainsboro;
margin: 15rpx;
}
//courses.js
Page({
data: {
courseList: []
},
onLoad: function (options) {
wx.cloud.database().collection('courseList').get()
.then(res => {
console.log("获取成功", res)
this.setData({
courseList: res.data
})
}).catch(err => {
console.log("获取失败", err)
})
},
//跳转到详情页
goCourseDetail(e) {
wx.navigateTo({
url: '../course_detail/courses_detail?id=' + e.currentTarget.dataset.id,
})
},
})
(2)课程详情页course_detail实现代码如下:
//course_detail.wxml
<!--标题和描述-->
<view>
<image class="sourseImg" src="../../images/sourse.png"></image>
<view class="sourseInfo">
<view class="sourceName">{{course_detail.course_name}}</view>
<view class="desc">{{course_detail.course_desc}}</view>
</view>
</view>
<!--评论-->
<view class="tip">评论区域</view>
<block wx:for="{{pinglun}}" wx:key="index">
<view class="pinglun">
<image class="touxiang" src="{{item.touxiang}}"></image>
<view class="pinglin_text">
<text class="studentName">{{item.studentName}}:</text>
<view class="content">{{item.content}}</view>
<view class="dianzantubiao" bindtap="commentDianzan" data-index="{{index}}">
<image wx:if="{{item.comIsIncloudMan&&userInfo}}" src="../../images/dianzan-yes.png" mode="widthFix"></image>
<image wx:if="{{!userInfo||!item.comIsIncloudMan}}" src="../../images/dianzan-no.png" mode="widthFix"></image>
<view class="dianzanNum" wx:if="{{item.dianzanMan.length>0}}">{{item.dianzanMan.length}}</view>
</view>
</view>
</view>
<view class="pinglun_time">{{item.time}}</view>
</block>
<!--发表评论-->
<input class="input" bindinput="getContent" placeholder="请留下您精彩点评吧" value="{{content}}"></input>
<button type="primary" bindtap="publishComment">发表评论</button>
//course_detail.wxss
image {
width: 100rpx;
height: 100rpx;
}
.tip {
margin-top: 30rpx;
font-size: 50rpx;
color: goldenrod;
}
.pinglun {
border-bottom: 2rpx solid gainsboro;
margin-left: 50rpx;
margin-top: 50rpx;
display: flex;
justify-content: left;
align-items: center;
}
.input {
border: 1rpx solid gainsboro;
margin-top: 150rpx;
margin-bottom: 60rpx;
}
.sourseImg {
width: 400rpx;
height: 400rpx;
}
.sourseInfo {
display: flex;
justify-content: space-between
}
.desc {
padding-right: 40rpx;
}
.sourceName {
padding-left: 40rpx;
}
.touxiang {
border-radius: 50%;
overflow: hidden;
width: 85rpx;
height: 85rpx;
}
.studentName {
padding-left: 10rpx;
}
.content {
display: -webkit-box;
overflow: hidden;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
}
.pinglin_text {
display: flex;
justify-content: left;
align-items: center;
flex: 5;
}
.pinglun_time {
font-size: 25rpx;
color: coral;
text-align: right;
padding-right: 30rpx;
}
.dianzantubiao{
display: flex;
flex-direction: row;
justify-content: right;
align-items: right;
}
.dianzantubiao image{
size: 20rpx;
width: 55rpx;
}
.dianzanNum{
display: flex;
justify-content: center;
align-items: center;
margin-top: 6rpx;
float: right;
}
course_detail.js代码如下:
let ID = ''
let dianzan = false
Page({
data: {
course_detail: '',
//shoucangUrl: "../../images/shoucang-no.png",
dianzanUrl: "../../images/dianzan-no.png",
pinglun: [],
pingfen: [],
content: '',
userInfo: [],
dianzanStatus: false,
dianzanNum: 0,
comIsIncloudMan: false,
ID: '',
isIncloudStudentInpingfen:0
},
//点击点赞
clickDianZan() {
let that = this
that.authPublic()
this.setData({
dianzanUrl: dianzan ? "../../images/dianzan-no.png" : "../../images/dianzan-yes.png"
})
dianzan = !dianzan
wx.cloud.callFunction({
name: "commentPlus",
data: {
action: "dianzan",
id: ID,
dianzan: dianzan
}
}).then(res => {
console.log("改变点赞状态成功", res)
}).catch(err => {
console.log("改变点赞状态失败", err)
})
},
onShow: function () {
},
onLoad: function (options) {
let deleteDoublePage=options.deleteDoublePage
console.log(deleteDoublePage)
if(deleteDoublePage==1){
wx.navigateBack({ //返回
delta: 1
});
}
ID = options.id
console.log(ID)
this.setData({
ID: ID
})
//根据课程ID获取课程详情
wx.cloud.callFunction({
name: "commentPlus",
data: {
action: "getCourseDetailById",
id: ID,
}
}).then(res => {
console.log("根据课程ID获取课程详情成功", res)
let pinglun=res.result.data.pinglun
//pinglun: 1 8 4 0 2 5
for(var i=0;i<pinglun.length-1;i++){
for(var j=i+1;j<pinglun.length;j++){
if(pinglun[i].dianzanMan.length<pinglun[j].dianzanMan.length){
let tmp=pinglun[i]
pinglun[i]=pinglun[j]
pinglun[j]=tmp
}
}
}
console.log("hahahaahahhahhahh")
console.log(pinglun)
console.log("hahahaahahhahhahh")
wx.setStorageSync('pingfen', res.result.data.pingfen)
wx.setStorageSync('pinglun', res.result.data.pinglun)
this.setData({
course_detail: res.result.data,
pinglun: res.result.data.pinglun,
pingfen: res.result.data.pingfen
})
let userInfo = wx.getStorageSync('userInfo')
this.setData({
userInfo: userInfo
})
let studentName = userInfo.nickName
///let pinglun = wx.getStorageSync('pinglun')
if (studentName) {
for (var i = 0; i < pinglun.length; i++) {
if (pinglun[i].dianzanMan.indexOf(studentName) === -1) {
pinglun[i].comIsIncloudMan = false
} else {
pinglun[i].comIsIncloudMan = true
}
}
//wx.setStorageSync('pinglun', pinglun)
this.setData({
pinglun: pinglun
})
wx.cloud.callFunction({
name: "commentPlus",
data: {
action: "pinglun",
id: ID,
pinglun: pinglun
}
}).then(res => {
console.log("改变点赞状态成功", res)
}).catch(err => {
console.log("改变点赞状态失败", err)
})
}
}).catch(err => {
console.log("根据课程ID获取课程详情失败", err)
})
},
//获取学生输入的评论内容
getContent(e) {
this.setData({
content: e.detail.value
})
},
//发表评论
publishComment() {
let userInfo = wx.getStorageSync('userInfo')
let that = this
//判断评论是否太短
if (!userInfo) {
that.authPublic()
} else {
let content = this.data.content
if (content.length < 4) {
wx.showToast({
title: '评论太短了',
icon: "none"
})
return
}
let pinglunItem = {}
//获取当前时间
let time = ''
var timestamp = Date.parse(new Date());
var date = new Date(timestamp);
//获取年
var Y = date.getFullYear();
//获取月
var M = (date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1);
//获取当日
var D = date.getDate() < 10 ? '0' + date.getDate() : date.getDate();
time = Y + '-' + M + '-' + D
console.log(time)
let dianzanStatus = false
pinglunItem.studentName = userInfo.nickName
pinglunItem.content = content
pinglunItem.touxiang = userInfo.avatarUrl
pinglunItem.time = time
pinglunItem.dianzanStatus = dianzanStatus
pinglunItem.dianzanNum = 0
pinglunItem.dianzanMan = []
pinglunItem.comIsIncloudMan = false
let pinglunArr = this.data.pinglun
pinglunArr.push(pinglunItem)
console.log("发表后的评论数组", pinglunArr)
wx.showLoading({
title: '发表中。。。',
})
wx.cloud.callFunction({
name: "commentPlus",
data: {
action: "pinglun",
id: ID,
pinglun: pinglunArr
}
}).then(res => {
console.log("评论成功", res)
wx.setStorageSync('pinglun', pinglunArr)
this.setData({
pinglun: pinglunArr,
content: ''
})
wx.hideLoading()
}).catch(err => {
console.log("评论失败", err)
wx.hideLoading()
})
}
},
//用户授权
authPublic() {
let userInfo = wx.getStorageSync('userInfo')
if (!userInfo) {
wx.getUserProfile({
desc: '获取你的昵称、头像、地区及性别',
success: (res) => {
//把用户信息缓存到本地
let userInfo = res.userInfo
this.setData({
userInfo: res.userInfo
})
let studentName = userInfo.nickName
wx.setStorageSync('userInfo', userInfo)
let pinglun = wx.getStorageSync('pinglun')
for (var i = 0; i < pinglun.length; i++) {
//for (var j = 0; j < pinglun[i].dianzanMan.length; j++) {
if (pinglun[i].dianzanMan.indexOf(studentName) === -1) {
console.log(i)
pinglun[i].comIsIncloudMan = false
} else {
console.log("1111", i)
pinglun[i].comIsIncloudMan = true
// }
}
}
wx.setStorageSync('pinglun', pinglun)
this.setData({
pinglun: pinglun
})
wx.cloud.callFunction({
name: "commentPlus",
data: {
action: "pinglun",
id: ID,
pinglun: pinglun
}
}).then(res => {
console.log("改变点赞状态成功", res)
}).catch(err => {
console.log("改变点赞状态失败", err)
})
}, fail: (error) => {
console.log(error)
}
})
}
},
//点击点赞图标
commentDianzan(e) {
//判断是否授权
let userInfo = wx.getStorageSync('userInfo')
let that = this
if (!userInfo) {
that.authPublic()
} else {
let userInfo = wx.getStorageSync('userInfo')
//index表示当前点赞评论的数组坐标
let index = e.currentTarget.dataset.index
//1 获取缓存中的评论数组
let pinglun = wx.getStorageSync("pinglun") || [];
let studentName = userInfo.nickName
if (pinglun[index].comIsIncloudMan == true) {
for (var i = 0; i < pinglun[index].dianzanMan.length; i++) {
if (pinglun[index].dianzanMan[i] == studentName) {
pinglun[index].dianzanMan.splice(i, 1)
}
}
}
if (pinglun[index].comIsIncloudMan == false) {
pinglun[index].dianzanMan.push(studentName)
pinglun[index].comIsIncloudMan = true
}
console.log("点赞后的评论数组为:", pinglun)
for (var i = 0; i < pinglun.length; i++) {
//for (var j = 0; j < pinglun[i].dianzanMan.length; j++) {
if (pinglun[i].dianzanMan.indexOf(studentName) === -1) {
pinglun[i].comIsIncloudMan = false
} else {
pinglun[i].comIsIncloudMan = true
// }
}
}
this.setData({
//dianzanStatus: !dianzanStatus,
pinglun: pinglun
});
wx.setStorageSync('pinglun', pinglun)
wx.cloud.callFunction({
name: "commentPlus",
data: {
action: "pinglun",
id: ID,
pinglun: pinglun
}
}).then(res => {
console.log("改变点赞状态成功", res)
}).catch(err => {
console.log("改变点赞状态失败", err)
})
}
},
//跳转到评分页面
goScorePage(e) {
console.log(getCurrentPages())
//判断是否授权
let userInfo = wx.getStorageSync('userInfo')
let that = this
if (!userInfo) {
that.authPublic()
} else {
this.setData({
isIncloudStudentInpingfen:0
})
let ID = this.data.ID
//根据ID获取课程评分详情
wx.cloud.callFunction({
name: "commentPlus",
data: {
action: "getCourseDetailById",
id: ID,
}
}).then(res => {
console.log("根据ID获取课程评分成功", res)
let pingfen = res.result.data.pingfen
this.setData({
pingfen: pingfen
})
for (var i = 0; i < pingfen.length; i++) {
if (pingfen[i].studentName == userInfo.nickName) {
this.setData({
isIncloudStudentInpingfen:1
})
}
}
if (this.data.isIncloudStudentInpingfen==1) {
wx.showToast({
title: '您已评分',
icon: 'error'
})
} else {
wx.navigateTo({
url: '../score/score?id=' + e.currentTarget.dataset.id,
})
}
}).catch(err => {
console.log("根据ID获取课程评分失败", err)
})
}
}
})
(3)云函数commentPlus实现代码如下:
// 云函数入口文件
const cloud = require('wx-server-sdk')
cloud.init({
env: cloud.DYNAMIC_CURRENT_ENV
})
// 云函数入口函数
exports.main = async (event, context) => {
if (event.action == 'getAllCourse') {
return await cloud.database().collection("courseList").get()
.then(res => {
console.log("获取所有课程成功", res)
return res
}).catch(err => {
console.log("获取所有课程失败", err)
return err
})
} else if (event.action == 'getCourseDetailById') {
return await cloud.database().collection("courseList").doc(event.id).get()
.then(res => {
console.log("根据ID获取课程详情成功", res)
return res
}).catch(err => {
console.log("根据ID获取课程详情失败", err)
})
} else if (event.action == 'shoucang') {
return await cloud.database().collection("courseList").doc(event.id).update({
data: {
shoucang: event.shoucang
}
}).then(res => {
console.log("改变收藏状态成功", res)
}).catch(err => {
console.log("改变收藏状态失败", err)
})
} else if (event.action == 'dianzan') {
return await cloud.database().collection("courseList").doc(event.id).update({
data: {
dianzan: event.dianzan
}
}).then(res => {
console.log("改变点赞状态成功", res)
}).catch(err => {
console.log("改变点赞状态失败", err)
})
} else if (event.action == 'pinglun') {
console.log(event)
return await cloud.database().collection("courseList").doc(event.id).update({
data: {
pinglun: event.pinglun
}
}).then(res => {
console.log("评论成功", res)
}).catch(err => {
console.log("评论失败", err)
})
} else if (event.action == 'getTitleList') {
return await cloud.database().collection("giveScore").get()
.then(res => {
console.log("获取标题列表成功", res)
return res
}).catch(err => {
console.log("获取标题列表失败", err)
return err
})
} else if (event.action == 'addpingdfenToDb') {
console.log(event)
return await cloud.database().collection("courseList").doc(event.id).update({
data: {
pingfen: cloud.database().command.push(event.pingfen)
}
}).then(res => {
console.log("添加评分成功", res)
}).catch(err => {
console.log("添加评分失败", err)
})
}
//else if(event.action == 'getAllScore'){
// return await cloud.database().collection("giveScore").get()
// .then(res => {
// console.log("获取标题列表成功", res)
// return res
// }).catch(err => {
// console.log("获取标题列表失败", err)
// return err
// })
// }
}
(4)数据库表的设计:
如上图所示,云数据库表名为sourseList,有五条课程数据,在此重点介绍字段comIsIncloudMan和dianzanMan,实现点赞功能需要就是使用这两个字段。
dianzanMan:
此字段类型为数组类型,当学生点赞评论时,就会push方法将该学生名称存入到数组中,当学生再次点赞已点评论时,就会执行splice方法将该学生名称从数组中移除,而当页面显示点赞条数就是根据该数组的长度。
comIsIncloudMan:
此字段也很重要,应为评论学生很多,该字段时判断某一条评论是否包括当前用户,如果包括则点赞状态显示为点赞状态,即红色状态,不包括则点赞状态为未点赞状态,即为灰色。