需求描述
最近在思考一个需求:文章可以评论,并且是无限级带图评论。初次看到这个需求疑惑了很久,由于自己接触小程序时间很短,对小程序机制也了解的很肤浅,所以就按照自己的想法做了一个基础的框架版本出来,里面有些内容还希望有高人指点一下,如果有大能能把它封装为插件,那就再好不过有了。废话不说了,直接上内容:
实现要点分析
- 微信小程序前端界面如何展现:在参考了众多的评论回复展示模块后,决定采用简书的评论回复展示方式
- 评论回复功能如何实现:本例借助于php无限级分类的思路来处理无限级评论
- 评论数据递归如何处理:本例采用的思路是当 评论的主体是文章(即评论的pid为0,这点很重要)时,评论展示为最外层评论,其余的基于本评论主体下的衍生回复评论展示为最内层。基于此思路,我在递归时做出判断,让所有的子项全部置于pid为0的字段children数组中,这样子就行实现前段所需要的数据格式
- 图片上传如何处理:本例前端采用小程序自带的接口,并且在传图时,将上传成功的图片置于临时数据集合中(后台有对应的上传接口),等点击提交按钮时,一起传递给服务器
- 点赞功能如何实现:本例中,点赞的思路是先在本地集合中处理点赞数据标记,利用wx.setStorageSync系列函数实现不同作用于数据的存储、交换,等后台返回执行结果后,在根据结果判断是否要在本地执行本地数据修改结果保存
- json数据查找、递归修改如何处理:这一块我真的没有什么好的方法,来实现递归查找数据并修改数据,如果有朋友有更好的方法,烦请浏览指导
目录结构
|-./app
|-------|–app.js
|-------|–app.json
|-------|–app.wxss
|-------|–project.config.json
|-------|–utils (扩展资源目录)
|-------|------|–util.js
|-------|–pages (页面资源目录)
|-------|-------|–index
|-------|-------|-------|–index.js
|-------|-------|-------|–index.json
|-------|-------|-------|–index.wxml
|-------|-------|-------|–index.wxss
|-------|–static (静态资源目录)
|-------|-------|–img
|-------|–template (模板资源目录)
|-------|-------|–template.js
|-------|-------|–template.wxml
|-------|-------|–template.wxss
准备好了目录结构,接下里就切入正题:
前端功能方法集成
app.js
App({
onLaunch: function () {
var that = this
//调用登录接口(此处内容自行处理)
},
globalData: {
userInfo: {},
swiperInfo: {},
template:template,
baseUrl:'https://xx.xxxx.cn',
}
})
app.wxss
/**app.wxss**/
@import "/template/template.wxss";
page{background:#f8f8f8; maegin:0; padding:0;}
.container {
height: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: space-between;
padding: 200rpx 0;
box-sizing: border-box;
}
button {
position:relative;
display:block;
margin-left:auto;
margin-right:auto;
padding-left:0;
padding-right:0;
box-sizing:border-box;
font-size:18px;
text-align:left;
text-decoration:none;
line-height:2.55555556;
border-radius:5px;
-webkit-tap-highlight-color:transparent;
overflow:hidden;
color:#000000;
background-color:#F8F8F8;
}
.btn-area button{display:block; text-align:center; width:80%; line-height:2;}
view{overflow:hidden;font-size:30rpx;color:#555;}
.swiperBox{width:100%; margin:0 auto; margin:0; background:#fff; border-radius:0rpx;}
.mainBox{width:97.5%; margin:0 auto; margin-top:15rpx; background:#fff; border-radius:8rpx;}
.widyBox{width:100%; margin:0 auto; margin:0; background:#fff; border-radius:8rpx;}
.examBox{width:97.5%; margin:0 auto; background:#fff; border-radius:8rpx;}
.blank{width:100%; height:0rpx; clear:both;}
.blank10{width:100%; height:10rpx; clear:both;}
.blank20{width:100%; height:20rpx; clear:both;}
.blank30{width:100%; height:30rpx; clear:both;}
.blank40{width:100%; height:40rpx; clear:both;}
.blank50{width:100%; height:50rpx; clear:both;}
.blank60{width:100%; height:60rpx; clear:both;}
.blank70{width:100%; height:70rpx; clear:both;}
.blank80{width:100%; height:80rpx; clear:both;}
.blank90{width:100%; height:90rpx; clear:both;}
.blank100{width:100%; height:100rpx; clear:both;}
.blank110{width:100%; height:110rpx; clear:both;}
.blank120{width:100%; height:120rpx; clear:both;}
.blank130{width:100%; height:130rpx; clear:both;}
.split10{width:100%; height:31rpx;position:relative;}
.split10_line{width:100%; height:14rpx;border-bottom:1px solid #ccc;}
.split10-line{width:100rpx; height:11rpx; display:block; position:absolute; background:#ccc; top:calc(50% - 8rpx); left:calc(50% - 50rpx);}
.blockTit{width:100%; height:50rpx; line-height:50rpx; font-size:30rpx; padding-left:0rpx;}
.blockTit::before{width:10rpx; height:50rpx; background:#ff0000; content:" "; display:block; float:left;}
.blockTit::after{width:10rpx; height:50rpx; content:" "; display:block; float:left;}
.commentSubmit{width:97.5%; margin:10rpx auto 30rpx;}
/* comment */
.commentSubmit .conts{ width:calc(90% - 2rpx); height: auto; position:relative; padding-bottom:0rpx; margin:15rpx auto 15rpx;}
.commentSubmit .conts textarea.areas{ width:99.2%; height:152rpx; font-size:30rpx; /*text-indent:28rpx;*/ border:1rpx solid gainsboro; padding-top:30rpx; margin:0 auto; overflow: hidden; position:relative;}
.commentSubmit .currentWordNumber{ font-size: 28rpx; color: gray; position: absolute; right:10rpx; bottom:10rpx;}
.commentSubmit .hint{ font-size: 28rpx; position: absolute; left:20rpx; bottom:10rpx; color: #FF6600;}
/* ͼƬ */
.commentSubmit .img_box{ width:100%; position:relative; display: flex; flex-wrap: wrap; margin:0 auto;}
.commentSubmit .imgs{ width:22%; /*display:flex;*/ justify-content: center; margin-right:4% ;margin-bottom:20rpx;}
.commentSubmit .imgs:last-child{ margin-right:0;}
.commentSubmit .imgs image{ width:100%; max-height:160rpx; border:1px solid rgba(214, 212, 212, 0.1); /* box-shadow: 5rpx 5rpx 1rpx 3rpx #e2e0e0; */}
.commentSubmit .imgs>image{ width:80%; max-height:160rpx;}
.commentSubmit .imgs .images{ position:relative;}
.commentSubmit .images button{ width:100%; height:100%; position:absolute; top:0; left:0; line-height:2;}
.commentSubmit .btn-area{margin-top:30rpx;}
.commentSubmit .img_box .images{ width:100%; max-width:125rpx; height: 125rpx; border:1px solid #ccc; border-radius:4rpx; display: flex; align-items: center; justify-content: center;}
.commentSubmit .img_box .images>image{ width:60rpx; height:60rpx;}
util.js
//获取应用实例
const app = getApp();
//Translate data method
function request(url, paramData, doSuccess, method="GET") {
let host = app.globalData.baseUrl+'/api/';
url = url || '';
paramData = paramData||[];
//console.log("++++++");
wx.request({
url: host + url,
header: {"content-type": "application/json;charset=UTF-8"},
method: method,
data: paramData,
success: function (res) {
doSuccess(res);
},
fail: function () {
wx.hideLoading();
wx.showToast({
title : '请求超时',
icon : 'loading',
duration : 2000
})
},
})
}
function parseParam(param, key, encode) {
if (param==null) return '';
var paramStr = '';
var t = typeof (param);
if (t == 'string' || t == 'number' || t == 'boolean') {
if(paramStr == ''){
paramStr += '&' + key + '=' + ((encode==null||encode) ? encodeURIComponent(param) : param);
}else{
paramStr += '&' + key + '=' + ((encode==null||encode) ? encodeURIComponent(param) : param);
}
} else {
for (var i in param) {
var k = key == null ? i : key + (param instanceof Array ? '[' + i + ']' : '.' + i)
paramStr += parseParam(param[i], k, encode)
}
}
return paramStr;
}
let commentAction = {
// 用户点击评论显示弹窗
replayAct: function(_that,e) {
//let _that = this
if(!_that.data.click) {
_that.setData({
click: true,
})
}
if(_that.data.opt){
_that.setData({
opt: false,
})
// 关闭显示弹窗动画的内容,不设置的话会出现:点击任何地方都会出现弹窗,就不是指定位置点击出现弹窗了
setTimeout(() => {
_that.setData({
click: false,
})
}, 500)
}else{
_that.setData({
opt: true
})
}
// 查验数据是否存在
if(e!==undefined){
// 传递数据到from表单
var ed = e.target.dataset
if(ed.aid!==undefined){
this._ssWindow(_that, ed)
}
}
},
/***开关窗口***/
_ssWindow: function(_that, ed) {
let comment_bInfo = {
aid:ed.aid,
pid:ed.pid,
type:ed.type||'',
nav_id:ed.nav_id,
min:_that.data.min,
max:_that.data.max,
unionID:wx.getStorageSync('openId'),
avatar:wx.getStorageSync('avatarUrl'),
}
console.log(comment_bInfo)
_that.setData({
comment_bInfo:comment_bInfo
})
},
/*** 字数限制*/
inputs: function (that,e) {
//let that = this;
// 获取输入框的内容
var value = e.detail.value
// 获取输入框内容的长度
var len = parseInt(value.length)
//最少字数限制
if (len <that.data.min){
that.setData({
texts: "加油,至少要输入5个字哦",
disabled: true
})
}else if (len >= that.data.min){
that.setData({
texts: "",
disabled: false
})
}
//最多字数限制
if (len > that.data.max) return
// 当输入框内容的长度大于最大长度限制(max)时,终止setData()的执行
that.setData({
currentWordNumber: len //当前字数
})
},
/*** 上传图片方法*/
upload: function (that) {
//let that = this;
wx.chooseImage({
count: 3, // 默认9
sizeType: ['original', 'compressed'], // 可以指定是原图还是压缩图,默认二者都有
sourceType: ['album', 'camera'], // 可以指定来源是相册还是相机,默认二者都有
success: res => {
wx.showToast({
title: '正在上传...',
icon: 'loading',
mask: true,
duration: 500
})
// 返回选定照片的本地文件路径列表,tempFilePath可以作为img标签的src属性显示图片
let tempFilePaths = res.tempFilePaths;
that.setData({
tempFilePaths: tempFilePaths
});
console.log(that.data.tempFilePaths);
/**
* 上传完成后把文件上传到服务器
**/
var count = 0;
for (var i = 0, h = tempFilePaths.length; i < h; i++){
//上传文件
wx.uploadFile({
url: app.globalData.baseUrl+'/api/api/uploads?act=image',
filePath: tempFilePaths[i],
name: 'file',
header: {"Content-Type":"multipart/form-data"},
success: function (res) {
let imgs1=that.data.imgs1;
res=res.data.startsWith("\ufeff")?res.data.slice(1):res.data; //去除bom
let newImgData=JSON.parse(res);
imgs1.push(newImgData.data.imgurl);
that.setData({
imgs1: imgs1
});
count++;
//如果是最后一张,则隐藏等待中
if (count == tempFilePaths.length) {
wx.hideToast();
}
},
fail: function (res) {
wx.hideToast();
wx.showToast({
title: '图片上传失败...',
icon: 'loading',
mask: true,
duration: 500
})
}
});
}
}
})
},
/*** 预览图片方法*/
listenerButtonPreviewImage: function (that,e) {
//let that = this
let index = e.target.dataset.index
console.log(that.data.tempFilePaths[index])
console.log(that.data.tempFilePaths)
wx.previewImage({
current: that.data.tempFilePaths[index],
urls: that.data.tempFilePaths,
//这根本就不走
success: function (res) {
//console.log(res);
},
//也根本不走
fail: function () {
//console.log('fail')
}
})
},
/*** 长按删除图片*/
deleteImage: function (that,e) {
//let that = this
var tempFilePaths = that.data.tempFilePaths
var imgs1 = that.data.imgs1
var index = e.currentTarget.dataset.index //获取当前长按图片下标
wx.showModal({
title: '提示',
content: '确定要删除此图片吗?',
success: function (res) {
if (res.confirm) {
console.log('点击确定了');
tempFilePaths.splice(index, 1);
imgs1.splice(index, 1);
} else if (res.cancel) {
console.log('点击取消了');
return false;
}
that.setData({
tempFilePaths:tempFilePaths,
imgs1:imgs1
});
}
})
},
//表单提交按钮
formSubmit: function (that,e) {
//let that = this
e.detail.value.pic=that.data.imgs1.join(',') //加入图片数据到form数据数组中
let param = parseParam(e.detail.value)
// 提交表单数据
request('api/handleData', {
'act': 'pushComment',
'param': param,
},function(res) {
console.log(res.data.data.commentInfo);
if(res.data.error==0){
// 关闭/打开窗口
// 设置/清除所有的数据
that.setData({
commentInfo: res.data.data.commentInfo,
})
wx.setStorageSync('handleStatus',true)
}
}, "POST")
console.log(wx.getStorageSync('handleStatus'))
if(wx.getStorageSync('handleStatus')){
this.replayAct(that)
that.setData({
form_value: '',
allValue: ''
})
wx.removeStorageSync('handleStatus')
}
//console.log('form发生了submit事件,携带数据为:', e.detail.value)
},
//表单重置按钮
formReset: function (that,e) {
//console.log('form发生了reset事件,携带数据为:', e.detail.value)
this.replayAct(that)
that.setData({
allValue: ''
})
},
//---------------点赞--------------
/***点赞***/
thumbsupAct: function (that,e) {
var ed = e.target.dataset
let thumbsupData = {
cmid:ed.id,
aid:ed.aid,
type:ed.type,
nav_id:ed.nav_id,
unionID:wx.getStorageSync('openId'),
isThumbsup:ed.isthumbsup,
}
console.log(thumbsupData)
// 本地 检索json数组并 修改数据
var commentInfo = that.data.commentInfo
commentInfo.list=this.findle(that, ed.id)
// 是否点赞
request('api/handleData', {
'act': 'thumbsUpDone',
'param': parseParam(thumbsupData),
},function(res) {
if(res.data.data.result==1){
//修正数据
that.setData({
commentInfo: commentInfo
})
}
}, "POST")
},
/***查找**多维数组***开始***/
findle: function(that, str){
if(JSON.stringify(str) == "" || typeof(str) == "object"){
return;
}else{
var commentInfo = that.data.commentInfo, obj=commentInfo.list;
return this.traverse(obj, str);
}
},
/***查找json数组-最多四维数组***/ (这部分太臃肿,如果有哪位朋友有更好的无限级递归修改json数据的方法,希望留言指导一下,谢谢!)
traverse: function (obj, str) {
//obj 就是json对象
if(typeof(obj)=="object" && obj.length){
for(var a=0, le=obj.length; a<le; a++){
if(obj[a].id==str){ // 第一层判断,成立则修改属性值
obj[a].isThumbsup = obj[a].isThumbsup ? 0 : 1
}else{
let _arr0 = obj[a].children
if(typeof(_arr0)==="object" && _arr0.length){
for(var b=0,len=_arr0.length; b<len; b++){
if(_arr0[b].id==str){ // 第二层判断,成立则修改属性值
_arr0[b].isThumbsup = _arr0[b].isThumbsup ? 0 : 1
}else{
let _arr1 = _arr0[b].children
if(typeof(_arr1)==="object" && _arr1.length){
for(var c=0,leng=_arr1.length; c<leng; c++){
if(_arr1[c].id==str){ // 第三层判断,成立则修改属性值
_arr1[c].isThumbsup = _arr1[c].isThumbsup ? 0 : 1
}else{
let _arr2 = _arr2[c].children
if(typeof(_arr2)==="object" && _arr2.length){
for(var d=0,lengt=_arr2.length; d<lengt; d++){
if(_arr2[d].id==str){ // 第四层判断,成立则修改属性值
_arr2[d].isThumbsup = _arr2[d].isThumbsup ? 0 : 1
}
}
}
}
}
}
}
}
}
}
}
}
return obj
},
/***查找**多维数组-最多为四维数组***结束***/
}
module.exports = {
request: request,
parseParam: parseParam,
commentAction: commentAction,
}
template.wxml(这部分的样式是参考简书评论中的评论回复样式)
<!----replay-thumbsUp---->
<!----loading---->
<template name="replayThumbsup">
<block wx:if="{{commentInfo}}">
<view class="commentmut">
<view class="note">
<view>
<view class="note-graceful-button">
<view class="line-container">
<view class="line"></view>
<p class="content"><span>点赞赚钻</span><span class="special">最高日赚数百元</span></p>
</view>
<view class="this-is-graceful-btn"><i class="iconfont ic-H-like"></i></view>
<span class="graceful-words"> 赞 <span class="numbers">(0)</span></span>
</view>
</view>
</view>
<view id="comment-main">
<view class="margin-top"></view>
<view class="top-title">
评论({{commentInfo.num}})
<view class="button write-comment" data-pid="0" data-aid="{{commentInfo.aid}}" data-nav_id="{{commentInfo.nav_id}}" data-type="{{commentInfo.type}}" catchtap="replayAct"><i class="iconfont ic-write"></i>写评论</view>
</view>
<view class="comments-wrap">
<!--------comment-list------->
<block wx:for="{{commentInfo.list}}" wx:for-item="co" wx:for-index="index" wx:key="*.this">
<view class="comment-item">
<a href="javascript:void(0)" class="user-avatar avatar">
<span class="avatar">
<image src="{{co.avatar}}" />
</span>
</a>
<view class="main">
<view class="comment-user">
<view class="nickname-wrap"><a href="javascript:void(0)" class="nickname oneline">{{co.nickname}}</a></view>
</view>
<view class="comment-content">
{{co.content}}
<block wx:if="{{co.pic}}" wx:for="{{co.pic}}" wx:key="*.this" wx:for-item="cop" wx:for-index="copindex">
<image mode="widthFix" src="{{cop}}" />
</block>
</view>
<view class="comment-extra">
<span class="floor">{{index+1}}楼</span>{{co.create_time}}
<view class="social-wrap">
<view class="button reply-btn" data-pid="{{co.id}}" data-aid="{{co.aid}}" data-nav_id="{{co.nav_id}}" data-type="{{co.type}}" catchtap="replayAct">回复</view>
<view class="button" data-id="{{co.id}}" data-aid="{{co.aid}}" data-pid="{{co.pid}}" data-nav_id="{{co.nav_id}}" data-isthumbsup="{{co.isThumbsup}}" data-type="{{co.type}}" catchtap="thumbsupAct">点赞</view>
</view>
</view>
<block wx:if="{{co.children}}">
<view class="sub-comment-list">
<!--------son-list------->
<block wx:for="{{co.children}}" wx:key="*.this" wx:for-item="coc" wx:for-index="cocindex">
<view class="sub-comment-item">
<a href="javascript:void(0)" class="user-avatar">
<span class="avatar son">
<image src="{{coc.avatar}}" />
</span>
</a>
<view class="sub-comment-wrap">
<a href="javascript:void(0)" class="nickname oneline">{{coc.nickname}}</a>
<view class="sub-comment-text">
<a href="javascript:void(0)" class="maleskine-author" target="_blank" data-user="1">@{{coc.replay_nickname}}</a>
{{coc.content}}
<block wx:if="{{coc.pic}}" wx:for="{{coc.pic}}" wx:key="*.this" wx:for-item="cocp" wx:for-index="cocpindex">
<image mode="widthFix" src="{{cocp}}" />
</block>
</view>
<view class="sub-comment-extra">
{{coc.create_time}}
<view class="button reply-btn">
<view class="sbtn" data-aid="{{coc.aid}}" data-pid="{{coc.id}}" data-nav_id="{{coc.nav_id}}" data-type="{{coc.type}}" catchtap="replayAct">回复</view>
<view class="sbtn" data-id="{{coc.id}}" data-aid="{{coc.aid}}" data-pid="{{coc.pid}}" data-nav_id="{{coc.nav_id}}" data-isthumbsup="{{coc.isThumbsup}}" data-type="{{coc.type}}" catchtap="thumbsupAct">点赞</view>
</view>
</view>
</view>
</view>
</block>
<!--------son-list------->
</view>
</block>
<!---->
</view>
</view>
</block>
<!--------comment-list------->
</view>
<view class="no-more-comment"></view>
<!---------------comBoxIng------------------->
<view class="comment-wrapper">
<!-- 底部弹窗动画的内容 -->
<view class='pupContent {{click? "showContent": "hideContent"}} {{opt? "open": "close"}}' hover-stop-propagation='true'>
<view class="pupContent-top"></view>
<view class="commentSubmit">
<form bindsubmit="formSubmit" bindreset="formReset">
<view class="conts">
<textarea name="content" class="areas" placeholder='留下点评,帮助更多人' minlength="{{comment_bInfo.min}}" maxlength="{{comment_bInfo.max}}" bindinput="inputs">{{form_value}}</textarea>
<text class="hint">{{texts}}</text>
<text class="currentWordNumber">{{currentWordNumber|0}}/{{comment_bInfo.max}}</text>
<input bindinput="bindReplaceInput" style="display:none" placeholder="连续的两个1会变成2" value="{{form_value}}" />
</view>
<view class="img_box">
<view class="imgs" wx:for="{{tempFilePaths}}" wx:key="index">
<image src='{{item}}' bindlongpress="deleteImage" bindtap="listenerButtonPreviewImage" data-index="{{index}}" mode='widthFix' />
</view>
<view class="imgs">
<view class="images" bindtap="upload">
<image src='/static/img/plus.png' mode='widthFix' />
</view>
</view>
</view>
<view class="btn-area">
<input name="aid" style="display:none" value="{{comment_bInfo.aid}}" />
<input name="pid" style="display:none" value="{{comment_bInfo.pid}}" />
<input name="type" style="display:none" value="{{comment_bInfo.type}}" />
<input name="nav_id" style="display:none" value="{{comment_bInfo.nav_id}}" />
<input name="unionID" style="display:none" value="{{comment_bInfo.unionID}}" />
<input name="avatar" style="display:none" value="{{comment_bInfo.avatar}}" />
<button type="warn" disabled="{{disabled}}" form-type="submit">提交</button>
<button type="default" form-type="reset">重置</button>
</view>
</form>
</view>
</view>
<!-- 固定的背景 -->
<view class='pupContentBG {{click?"showBG":"hideBG"}} {{opt?"openBG":"closeBG"}}' catchtap='replayAct'></view>
<!---->
</view>
<!---------------comBoxEnd------------------->
</view>
</view>
</block>
</template>
template.wxss(这部分的样式是参考简书评论中的评论回复样式)
/**--------------------------------replay and thumbsUp Start----------------------------------**/
/***thumbsUp***/
.commentmut .note{background-color:#fff;}
.commentmut .note-graceful-button{margin:15px 0 26px;text-align:center}
.commentmut .note-graceful-button .line-container{position:relative;height:31px;text-align:center;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}
.commentmut .note-graceful-button .line-container .line{position:absolute;width:100%;top:15px;height:1px;background-color:#ddd;z-index:0}
.commentmut .note-graceful-button .line-container .content{display:inline-block;margin:0 auto;position:relative;z-index:1;font-size:14px;line-height:31px;padding:0 10px;background-color:#fff}
.commentmut .note-graceful-button .line-container .content{background-color:#fff}
.commentmut .note-graceful-button .line-container .content span{display:inline-block;vertical-align:middle;font-size:16px;margin-right:5px;font-weight:500;line-height:22px;color:#666}
.commentmut .note-graceful-button .line-container .content .special{color:#ea6f5a}
.commentmut .note-graceful-button .this-is-graceful-btn{width:60px;height:60px;line-height:60px;text-align:center;margin:12px auto 3px;border-radius:50%}
.commentmut .note-graceful-button .this-is-graceful-btn.is-active i{color:#ea6f5a}
.commentmut .note-graceful-button .this-is-graceful-btn i{color:#888;font-size:60px}
.commentmut .note-graceful-button .graceful-words{font-size:13px;color:#888}
.commentmut .note-graceful-button .graceful-words.is-active{color:#ea6f5a}
.commentmut .note-graceful-button .graceful-words .numbers{font-size:10px}
/***thumbsUp***/
.commentmut #comment-main{position:relative;margin-left:0;margin-right:0;background-color:#fff}
.commentmut #comment-main .margin-top{height:10px;background-color:#f5f5f5;width:100%;}
.commentmut #comment-main .top-title {padding:15px 18px 10px; border-bottom:none; margin-left:0; margin-right:0; font-size:16px; font-weight:700; color:#545454;}
.commentmut #comment-main .top-title .write-comment{float:right;color:#ea6f5a;line-height:22px;font-size:14px}
.commentmut #comment-main .comments-wrap>.comment-item{border-bottom: 1px solid; border-color: #f0f0f0;}
.commentmut #comment-main .comments-wrap>.comment-item:last-child{border-bottom:none}
.commentmut #comment-main .comment-open-app-btn-wrap{text-align:center;padding-bottom:20px;margin-top:20px}
.commentmut #comment-main .comment-open-app-btn-wrap .comment-open-app-btn{border:2px solid #ea6f5a;color:#ea6f5a;background-color:transparent;border-radius:4px;text-align:center;width:250px;height:45px;padding:10px 30px;font-size:16px}
.commentmut #comment-main .emoji{vertical-align:sub}
.commentmut #comment-main .has-more-comment{padding:16px 0;font-size:15px;color:#969696;text-align:center;background-color:transparent}
.commentmut #comment-main .comment-reply-drawer{padding:15px}
.commentmut #comment-main .comment-reply-drawer .control{text-align:right}
.commentmut #comment-main .comment-reply-drawer .control .button{margin-left:15px;width:80px;height:35px;font-size:17px;line-height:35px}
.commentmut #comment-main .comment-reply-drawer .control .cancel{color:#999}
.commentmut #comment-main .comment-reply-drawer .control .submit{background-color:#42c02e;color:#fff;border-radius:4px}
.commentmut #comment-main .comment-wrapper .commentSubmit{width:100%;}
.commentmut #comment-main .comment-wrapper .commentSubmit .conts{width:100%;}
.commentmut #comment-main .comment-wrapper textarea{padding:15rpx; width:calc(100% - 36rpx); height:150px; font-size:14px; line-height:1.5; background-color:transparent; color:#333}
.commentmut #comment-main .comment-wrapper .commentSubmit .btn-area button{width:44%; margin:0 3%; font-size:16px; float:left;}
.commentmut #comment-main .comment-wrapper .commentSubmit .btn-area button[type=warn]{color:#fff;}
.commentmut #comment-main .comment-wrapper .commentSubmit .btn-area button[type=default]{color:#555;}
.commentmut #comment-main .comment-wrapper textarea{color:#555}
.commentmut #comment-main .comment-wrapper .control{position:absolute;bottom:15px;right:15px}
.commentmut #comment-main .no-content{padding:40px 0;font-size:14px;color:#969696;text-align:center;background-color:#fff}
.commentmut #comment-main .no-content{background-color:#3f3f3f}
.commentmut #comment-main .no-content img{padding-bottom:10px;width:140px}
.commentmut #comment-main .no-content .button{color:#3194d0}
.commentmut .comment-item .main .comment-content{word-break:break-word!important;}
.commentmut .comment-item{padding-top:15px;padding-left:18px}
.commentmut .comment-item .user-avatar{float:left;margin-right:10px}
.commentmut .comment-item .main{overflow:hidden}
.commentmut .comment-item .main .comment-user{margin-bottom:6px}
.commentmut .comment-item .main .comment-user .nickname-wrap{overflow:hidden}
.commentmut .comment-item .main .comment-user .nickname-wrap .nickname{font-size:16px;font-weight:700;vertical-align:middle;color:#484848}
.commentmut .comment-item .main .comment-user .nickname-wrap .label{padding:1px 2px;font-size:12px;color:#ea6f5a;border:1px solid #ea6f5a;border-radius:3px;vertical-align:middle;margin-left:5px}
.commentmut .comment-item .main .comment-user .nickname-wrap img{width:18px;height:18px;vertical-align:middle;margin-left:5px}
.commentmut .comment-item .main .comment-content{font-size:16px;line-height:1.7;padding-right:18px;color:#484848}
.commentmut .comment-item .main .comment-content>image{width:100%;margin-bottom:10rpx;}
.commentmut .sub-comment-item .sub-comment-wrap .sub-comment-text>image{width:100%;margin-bottom:10rpx;}
.commentmut .comment-item .main .comment-images{margin-top:8px;margin-bottom:8px;position:relative;font-size:0}
.commentmut .comment-item .main .comment-images .image{position:relative;border-radius:2px;background-position:50%;background-repeat:no-repeat;overflow:hidden}
.commentmut .comment-item .main .comment-images .image:not(:nth-child(3n)){margin-right:2%}
.commentmut .comment-item .main .comment-images .image:nth-child(n+4){margin-top:2%}
.commentmut .comment-item .main .comment-images .image.is-long:after{content:"\957F\56FE";position:absolute;bottom:0;margin-left:-22px;width:44px;height:28px;font-size:18px;text-align:center;line-height:28px;background-color:#2f2f2f;opacity:.5;color:#fff;-webkit-transform:scale(.5);-ms-transform:scale(.5);transform:scale(.5);-webkit-transform-origin:left bottom;-ms-transform-origin:left bottom;transform-origin:left bottom}
.commentmut .comment-item .main .comment-images .one-image.is-long{height:0;padding-top:67%;min-width:33%;-webkit-background-size:33% 33%;background-size:33%;background-position:0}
.commentmut .comment-item .main .comment-images .one-image.is-long:after{left:33%}
.commentmut .comment-item .main .comment-images .one-image.is-normal{height:98px;width:100%;-webkit-background-size:contain;background-size:contain;background-position:0}
.commentmut .comment-item .main .comment-images .many-image{-webkit-background-size:cover;background-size:cover;display:inline-block;height:0;width:32%;padding-top:32%}
.commentmut .comment-item .main .comment-images .many-image:after{left:100%}
.commentmut .comment-item .main .comment-images .mask{position:absolute;right:0;top:0;height:100%;width:32%;border-radius:2px;background-color:rgba(47,47,47,.6);overflow:hidden;pointer-events:none}
.commentmut .comment-item .main .comment-images .mask span{font-size:15px;color:#fff;position:absolute;left:50%;top:50%;-webkit-transform:translate(-50%,-50%);-ms-transform:translate(-50%,-50%);transform:translate(-50%,-50%);width:100%;text-align:center}
.commentmut .comment-item .main .comment-extra{font-size:12px;height:20px;line-height:20px;color:#b1b1b1;margin-top:5px;margin-bottom:15px}
.commentmut .comment-item .main .comment-extra .floor{margin-right:4px}
.commentmut .comment-item .main .comment-extra .social-wrap{float:right;height:20px;margin-right:18px}
.commentmut .comment-item .main .comment-extra .social-wrap .button{margin-left:25px;color:#b1b1b1; /*width:45px;*/ float:left;}
.commentmut .comment-item .main .comment-extra .social-wrap .button .icoX{margin-right:5px;color:#b1b1b1; width:20px; float:left;}
.commentmut .comment-item .main .comment-extra .social-wrap .button .icoX image{width:100%; max-width:20px; height:100%;}
.commentmut .comment-item .main .comment-extra .social-wrap .button>image{width:100%; max-width:20px; height:100%;}
.commentmut .comment-item .main .comment-extra .social-wrap i{font-size:19px;vertical-align:middle}
.commentmut .comment-item .main .comment-extra .social-wrap span{vertical-align:middle;font-size:13px;margin-right:3px}
.commentmut .comment-item .main .comment-extra .social-wrap .ic-icon_comment_like_active,.comment-item .main .comment-extra .social-wrap span.active{color:#ea6f5a}
.commentmut .comment-item .main .more-sub-comment{display:block;font-size:14px;font-weight:500;color:#3194d0;border-top:1px solid;width:100%;padding:20px 0;text-align:left;border-color:#e6e6e6}
.commentmut .comment-item .main .more-sub-comment{border-color:#1f1f1f}
.commentmut .avatar{position:relative;background-color:transparent;-webkit-transition:.4s linear;-o-transition:.4s linear;transition:.4s linear;overflow:hidden;display:inline-block;border-radius:50%}
.commentmut .avatar image{width:100%;height:100%;display:block}
.commentmut .avatar{width:35px; height:35px;}
.commentmut .son{width: 22px; height: 22px;}
.commentmut .sub-comment-item{padding:15px 0;border-top:1px solid;font-size:14px;color:#484848;border-color:#e6e6e6}
.commentmut .sub-comment-item .user-avatar{float:left;margin-right:8px}
.commentmut .sub-comment-item .sub-comment-wrap{overflow:hidden;padding-right:18px}
.commentmut .sub-comment-item .sub-comment-wrap .nickname{font-size:16px;font-weight:700;vertical-align:middle;color:#484848}
.commentmut .sub-comment-item .sub-comment-wrap .sub-comment-text{font-size:15px}
.commentmut .sub-comment-item .sub-comment-wrap .sub-comment-text a{color:#3194d0}
.commentmut .sub-comment-item .sub-comment-wrap .sub-comment-images{margin-top:8px;margin-bottom:8px;position:relative;font-size:0}
.commentmut .sub-comment-item .sub-comment-wrap .sub-comment-images .image{position:relative;border-radius:2px;background-position:50%;background-repeat:no-repeat;-webkit-background-size:cover;background-size:cover;display:inline-block;height:0;width:32%;padding-top:32%;overflow:hidden}
.commentmut .sub-comment-item .sub-comment-wrap .sub-comment-images .image:not(:nth-child(3n)){margin-right:2%}
.commentmut .sub-comment-item .sub-comment-wrap .sub-comment-images .image:nth-child(n+4){margin-top:2%}
.commentmut .sub-comment-item .sub-comment-wrap .sub-comment-images .image:after{left:100%}
.commentmut .sub-comment-item .sub-comment-wrap .sub-comment-images .image.is-long:after{content:"\957F\56FE";position:absolute;bottom:0;margin-left:-22px;width:44px;height:28px;font-size:18px;text-align:center;line-height:28px;background-color:#2f2f2f;opacity:.5;color:#fff;-webkit-transform:scale(.5);-ms-transform:scale(.5);transform:scale(.5);-webkit-transform-origin:left bottom;-ms-transform-origin:left bottom;transform-origin:left bottom}
.commentmut .sub-comment-item .sub-comment-wrap .sub-comment-images .mask{position:absolute;right:0;top:0;height:100%;width:32%;border-radius:2px;background-color:rgba(47,47,47,.6);overflow:hidden;pointer-events:none}
.commentmut .sub-comment-item .sub-comment-wrap .sub-comment-images .mask span{font-size:15px;color:#fff;position:absolute;left:50%;top:50%;-webkit-transform:translate(-50%,-50%);-ms-transform:translate(-50%,-50%);transform:translate(-50%,-50%);width:100%;text-align:center}
.commentmut .sub-comment-item .sub-comment-wrap .sub-comment-images .collapsed-btn{color:#3194d0;font-size:13px;margin-top:11px}
.commentmut .sub-comment-item .sub-comment-wrap .sub-comment-extra{padding-top:5px;font-size:12px;color:#b1b1b1}
.commentmut .sub-comment-item .sub-comment-wrap .sub-comment-extra .reply-btn{padding-left:6px;width:190rpx;float:right;font-size:13px;color:#b1b1b1;}
.commentmut .sub-comment-item .sub-comment-wrap .sub-comment-extra .reply-btn .sbtn{width:80rpx;margin-right:30rpx;float:left;color:#b1b1b1;}
.commentmut .sub-comment-item .sub-comment-wrap .sub-comment-extra .reply-btn .sbtn:last-child{margin-right:0;color:#b1b1b1;}
/**************bottom-toast*****************/
.commentmut .pupContentBG {width:100vw; height:100vh; position:fixed; top:0;}
.commentmut .pupContent {width:100%; background:white; position:fixed; top:0; box-shadow:0 0 10rpx #555; height:0; z-index:999;}
/**设置显示的背景**/
.commentmut .showBG {display: block;}
.commentmut .hideBG {display: none;}
/* 弹出或关闭动画来动态设置内容高度 */
@keyframes slideBGtUp {
from {
background: transparent;
}
to {
background: rgba(0, 0, 0, 0.5);
}
}
@keyframes slideBGDown {
from {
background: rgba(0, 0, 0, 0.5);
}
to {
background: transparent;
}
}
/* 显示或关闭内容时动画 */
.commentmut .openBG {
animation: slideBGtUp 0.5s ease-in both;
/* animation-fill-mode: both 动画将会执行 forwards 和 backwards 执行的动作。 */
}
.commentmut .closeBG {
animation: slideBGDown 0.5s ease-in both;
/* animation-fill-mode: both 动画将会执行 forwards 和 backwards 执行的动作。 */
}
/* 设置显示内容 */
.commentmut .showContent {
display: block;
width:calc(100% - 39rpx);
padding: 20rpx;
border-top:10px solid #f5f5f5;
}
.commentmut .hideContent {display: none;}
/* 弹出或关闭动画来动态设置内容高度 */
@keyframes slideContentUp {
from {
height: 0;
}
to {
height: 800rpx;
}
}
@keyframes slideContentDown {
from {
height: 800rpx;
}
to {
height: 0;
}
}
/* 显示或关闭内容时动画 */
.commentmut .open {
animation: slideContentUp 0.5s ease-in both;
/* animation-fill-mode: both 动画将会执行 forwards 和 backwards 执行的动作。 */
}
.commentmut .close {
animation: slideContentDown 0.5s ease-in both;
/* animation-fill-mode: both 动画将会执行 forwards 和 backwards 执行的动作。 */
}
/**--------------------------------replay and thumbsUp End----------------------------------**/
index.js
//index.js
//获取应用实例
const app = getApp();
let util = require('../../utils/util.js');
Page({
data: {
userInfo: app.globalData.userInfo,
click: false, //是否显示弹窗内容 + + + + + + + + + + + 评论点赞插件专属
opt: false, //显示弹窗或关闭弹窗的操作动画 + + + + + + 评论点赞插件专属
min:2, //输入框最少字数 + + + + + + + + + + + + + + + 评论点赞插件专属
max:120, //输入框最多字数 + + + + + + + + + + + + + 评论点赞插件专属
form_value:'', //输入框中的内容 + + + + + + + + + + + 评论点赞插件专属
texts: "", //当前输入的字数 + + + + + + + + + + + + + 评论点赞插件专属
imgs1:[], //当前图片资源 + + + + + + + + + + + + + + 评论点赞插件专属
tempFilePaths: [], //临时文件资源 + + + + + + + + + + 评论点赞插件专属
disabled: true, //按钮初始状态 + + + + + + + + + + 评论点赞插件专属
},
onLoad: function (options) {
var that = this;
// 获取评论+++++++++++++++++++完整方式==开始======================
var actParam = {
aid: 1,
nav_id: 2,
type:1,
}
// 服务器段处理,并返回处理结果
util.request('api/index', {
'act': 'getCommentHub',
'param': util.parseParam(actParam),
},function(res) {
wx.setStorageSync('commentInfo', res.data.data)
app.globalData.commentInfo = res.data.data
that.setData({
commentInfo: wx.getStorageSync('commentInfo')
})
}, "GET");
// 获取评论+++++++++++++++++++完整方式==结束======================
},
/*************************评论/点赞组件->开始***************************/
//点击打开弹窗
replayAct: function(e){
var that = this
util.commentAction.replayAct(that,e)
},
//点击打开弹窗
inputs: function(e){
var that = this
util.commentAction.inputs(that,e)
},
//上传文件
upload: function(){
var that = this
util.commentAction.upload(that)
},
//图片预览
listenerButtonPreviewImage: function(e){
var that = this
util.commentAction.listenerButtonPreviewImage(that,e)
},
//长按删除文件
deleteImage: function(e){
var that = this
util.commentAction.deleteImage(that,e)
},
//表单提交
formSubmit: function(e){
var that = this
util.commentAction.formSubmit(that,e)
},
//表单提交
formReset: function(e){
var that = this
util.commentAction.formReset(that,e)
},
thumbsupAct: function(e){
var that = this
util.commentAction.thumbsupAct(that,e)
},
/*************************评论/点赞组件->结束***************************/
})
index.wxml
<!--index.wxml-->
<import src="/template/template.wxml"/>
<!----new-comment&thumbsup---->
<view class="widyBox">
<template is="replayThumbsup" data="{{commentInfo, click, opt, currentWordNumber, disabled, tempFilePaths, imgs1, comment_bInfo}}"/>
</view>
<!----new-comment&thumbsup---->
<template is="copyright" data="{{copyright}}"/>
index.wxss和index.json是默认的内容,这里就不占用地方了。
后端方法
后端采用tp5.0.24开发,目录这里就不展示了,本次评论模块使用的是api模块下的api控制器下的各种方法
/app/application/api/controller/api.php
<?php
namespace app\api\controller;
class Api extends Base
{
public function _initialize(){
$this->host=(isHTTPS() ? 'https://' : 'https://') . $_SERVER['HTTP_HOST']; //获取域名
}
public function index($act='getAllCousrseList',$param){
$data = [
'error' => 0,
'msg' => 'Connected!',
'data' => [],
];
parse_str($param,$param); // 主要处理自己拼接的字符串为数组
switch($act){
case 'getCommentHub':
if(empty($param)){ // 获取指定栏目信息
$data = [
'error' => 0,
'msg' => 'Lost param!',
'data' => [],
];
}else{
// 评价资料
# 个人基础信息
$aid = $param["aid"];
$nav_id=$param['nav_id'];
$return_data = $param;
if(!$aid || !$nav_id) {
$this->error("参数错误");
}
$model = db('newComment');
$_mode_arr = $model->alias('c')->where(['c.aid'=>$aid,'c.nav_id'=>$nav_id,'c.status'=>1])->field('c.*,m.nickname,m.avatar,ct.status as isThumbsup')->join([['member m','m.unionID=c.unionID','LEFT'],['c_thumbsup ct','ct.aid=c.aid and ct.unionID=c.unionID and ct.cmid=c.id','LEFT']])->order('c.id DESC')->select();
foreach($_mode_arr as $k=>&$v){
if(!empty($v['pic'])){
$_pic=[];
foreach(explode(',',$v['pic']) as $vp){
$_pic[]=$this->host.$vp;
}
$v['pic']=$_pic;
}
$v['isThumbsup']=$v['isThumbsup'] ? 1 : 0;
$v['thumbsUp']=db('c_thumbsup')->where(['cmid'=>$v['id']])->count();
$v['create_time']=date('Y.m.d',$v['create_time']);
$v['update_time']=date('Y.m.d',$v['update_time']);
}
$return_data['num'] = count($_mode_arr); //获取评论总数
$return_data['list']=$this->listHub(0, 1, $_mode_arr);//获取评论列表
$data = [
'error' => 0,
'msg' => 'OK!',
'data' => $return_data,
];
}
break;
}
return json($data);
}
/*图片上传*/
public function uploads($act='image'){
$data=[
'error' =>0,
'msg' =>'Connected!',
'data' =>[],
];
$file = request()->file('file');
switch($act){
case 'image':
$path = ROOT_PATH . 'public' . DS . 'static' . DS . 'uploads' . DS . 'comment' . DS . 'images';
$attrPath='images';
break;
case 'file':
$path = ROOT_PATH . 'public' . DS . 'static' . DS . 'uploads' . DS . 'comment' . DS . 'files';
$attrPath='files';
break;
}
if (!is_dir($path)) {
mkdir($path,0777,true);
}
$info = $file->move($path);
if($info){
$filePath = DS .'static' . DS . 'uploads'. DS .'comment'. DS . $attrPath . DS .$info->getSaveName();
$data=[
'error' =>0,
'msg' =>'OK!',
'data' =>[
'imgurl'=>$filePath,
]
];
}else{
$data=[
'error' =>1,
'msg' =>$file->getError(),
'data' =>[]
];
}
return json($data);
}
# 前台API数据获取
/*
+ @param['unionid'] string(max[20],min[0]) 当前用户的unionid
+ @param['uid'] intval(max[10],min[1]) 默认用户ID
+ @param['token'] string (16) 默认用户密码substr(md5($str), 8, 16) 两次
*/
# 数据修改
public function handleData($act='pushComment', $param){
$ret=[
'error'=>0,
'msg'=>'Connected!',
'data'=>[],
];
if(is_array($param)){
$data = $param;
}else{
parse_str($param,$data); // 处理自己拼接的字符串为数组
}
switch($act){
case 'pushComment': # 写入服务日志
if(request()->isPost()){
$data['create_time']=time();
$data['status']=1;
if(db('new_comment')->insert($data)){
$ret['msg']='Ok';
}else{
$ret['msg']='Failed';
}
}
$where=[
'c.aid'=>$data['aid'],
'c.nav_id'=>$data['nav_id'],
'c.type'=>$data['type'],
];
$_commentList = db('new_comment')->alias('c')->where($where)->field('c.*,m.nickname,m.avatar,ct.status as isThumbsup')->join([['member m','m.unionID=c.unionID','LEFT'],['c_thumbsup ct','ct.aid=c.aid and ct.unionID=c.unionID and ct.cmid=c.id','LEFT']])->order('c.id DESC')->select();
if(!empty($_commentList)){
foreach($_commentList as $k=>&$v){
if(!empty($v['pic'])){
$_pic=[];
foreach(explode(',',$v['pic']) as $vp){
$_pic[]=$this->host.$vp;
}
$v['pic']=$_pic;
}
$v['isThumbsup']=$v['isThumbsup'] ? 1 : 0;
$v['thumbsUp']=db('c_thumbsup')->where(['cmid'=>$v['id']])->count();
$v['create_time']=date('Y.m.d',$v['create_time']);
$v['update_time']=date('Y.m.d',$v['update_time']);
}
$_resData = [
'aid'=>$data['aid'],
'nav_id'=>$data['nav_id'],
'type'=>$data['type']
];
$_resData['num'] = count($_commentList); //获取评论总数
$_resData['list']=$this->listHub(0, 1, $_commentList);//获取评论列表
}else{
$_resData=[];
}
$ret['data']['commentInfo']=$_resData;
break;
case 'thumbsUpDone': # 点赞
if(request()->isPost()){
$isup = $data['isThumbsup'];
unset($data['isThumbsup']);
if($isup==0){ // 添加点赞
if(db('c_thumbsup')->insert($data)){
db('new_comment')->where(['id'=>$data['cmid']])->setInc('thumbsUp');
$ret=[
'error'=>0,
'msg'=>'OK!',
'data'=>[
'result'=>1
],
];
}else{
$ret=[
'error'=>0,
'msg'=>'Falied!',
'data'=>[
'result'=>0
],
];
}
}else{
if(db('c_thumbsup')->where($data)->delete()){
db('new_comment')->where(['id'=>$data['cmid']])->setDec('thumbsUp');
$ret=[
'error'=>0,
'msg'=>'OK!',
'data'=>[
'result'=>1
],
];
}else{
$ret=[
'error'=>0,
'msg'=>'Falied!',
'data'=>[
'result'=>0
],
];
}
}
$data['id'] = $data['cmid'];
unset($data['unionID'],$data['cmid']);
$ret['data']['thumbsupNum']=db('new_comment')->where($data)->value('thumbsUp');
}
break;
default:
$ret['msg']='Param lost!';
}
return json($ret);
}
// 受保护方法
protected function listHub($pid=0, $level=1, $array=[]){
static $result = [];
static $key = 0;
if(!empty($array)){
foreach($array as $k => $v){
if($v['pid'] == $pid){
$v['level']=$level;
if($v['pid'] == 0){ // 当前是顶级回复
$v['children']=[];
$result[]=$v;
$key = count($result) -1; //获取当前顶级回复的索引
}else{
$v['replay_nickname'] = db('new_comment')->alias('nc')->where(['nc.status'=>1,'nc.id'=>$v['pid']])->join([['member m','m.unionID=nc.unionID','LEFT']])->value('m.nickname');
$result[$key]['children'][]=$v;
}
unset($array[$k]);
$this->listHub($v['id'], $v['level']+1, $array);
}
}
}
return $result;
}
}
数据库结构
tp_member
字段名称 | 字段类型 | 字段属性 | 字段说明 | 是否主键 | 是否自增 |
---|---|---|---|---|---|
id | int | 11 | 自增ID | 是 | 是 |
username | char | 20 | 用户名 | 否 | 否 |
unionID | char | 50 | 用openID也可以 | 否 | 否 |
avatar | varchar | 200 | 用户头像 | 否 | 否 |
nickname | char | 20 | 用户昵称 | 否 | 否 |
register_time | char | 10 | 注册时间 | 否 | 否 |
status | tinyint | 1 | 用户状态 | 否 | 否 |
tp_c_thumbsup表结构
字段名称 | 字段类型 | 字段属性 | 字段说明 | 是否主键 | 是否自增 |
---|---|---|---|---|---|
id | int | 11 | 自增ID | 是 | 是 |
unionID | char | 50 | 用openID也可以 | 否 | 否 |
cmid | int | 10 | 被点赞的评论ID | 否 | 否 |
aid | int | 10 | 文章ID | 否 | 否 |
nav_id | int | 10 | 文章栏目 | 否 | 否 |
type | tinyint | 1 | 文档属性 | 否 | 否 |
status | tinyint | 1 | 用户状态 | 否 | 否 |
tp_new_comment表结构
字段名称 | 字段类型 | 字段属性 | 字段说明 | 是否主键 | 是否自增 |
---|---|---|---|---|---|
id | int | 11 | 自增ID | 是 | 是 |
pid | int | 20 | 被回复的评论ID | 否 | 否 |
unionID | char | 50 | 用openID也可以 | 否 | 否 |
aid | int | 10 | 文章ID | 否 | 否 |
nav_id | int | 10 | 文章栏目 | 否 | 否 |
type | tinyint | 1 | 文档属性 | 否 | 否 |
pic | varchar | 900 | 评论图片 | 否 | 否 |
avatar | varchar | 200 | 用户头像 | 否 | 否 |
content | varchar | 240 | 评论内容 | 否 | 否 |
thumbsUp | int | 10 | 点赞数量 | 否 | 否 |
create_time | char | 10 | 创建时间 | 否 | 否 |
status | tinyint | 1 | 用户状态 | 否 | 否 |
以上是所有的前后端+数据库内容,希望可以给有需要该功能的朋友一点思路,同时也希望谁可以把我写的不好的地方改写一下,要是可以做成小程序插件,还望指导一下,谢谢!!
效果展示
未解决的问题
虽然基础框架已经完成,但是复用效率较低,我真诚希望有哪位高手能够封装为插件,以方便多次调用,如果有人愿意施以援手,请留言指导,或者做成成品,邮箱 1755773846@qq.com。我会在第一时间发布出来,让知识与技能共享,谢谢!