首页 > 编程 > JavaScript > 正文

微信小程序仿知乎实现评论留言功能

2019-11-19 12:27:29
字体:
来源:转载
供稿:网友

 最近沉迷学习无法自拔,太久没有码字,码一个小程序留言功能实现。先上一波最后效果图:

(删除按钮,是用户自己的留言时才会显示该按钮)

实现技术

后台:SSM框架

数据库:MySQL数据库

数据库设计

评论功能的实现主要涉及三个表

comment:存储留言评论信息,表结构如下:

表中,必须的字段:id,user_id,reply_comment_id,comment,insert_time,source_id

添加了冗余字段username,reply_user_name,userphoto

主要用于存储微信名、回复的微信名、微信头像(这三个字段完全不应该冗余,当小程序用户更换用户名时,该表要跟着更新,可维护性差,不建议存储这些冗余信息,我就是懒得写SQL了)

source:存储你在小程序需要回复的内容。

user:存储小程序使用的用户信息,主要包括用户名、用户头像等微信用户信息。

小程序端

wxml

<scroll-view scroll-top="{{scrollTop}}" scroll-y="true" style="height:{{scrollHeight}}px;" class="list" bindscrolltolower="bindDownLoad" bindscrolltoupper="refresh"> <view class="pro-con"> <block wx:for="{{list}}" wx:key="{{index}}">  <view class="pro-box">  <view class="head">   <image class="img" src="{{item.userPhoto}}" mode="aspectFit"></image>   <view class="box">   <view class="shead clear">    <view class="names fl">{{item.userName}}     <view wx:if="{{!item.replyUserName == /" /"}}">     -> {{item.replyUserName}}    </view>    </view>   </view>   </view>  </view>  <view class="addr-info">   <view class="addr-text">   {{item.comment}}   </view>  </view>  <view class="info">   <view class="text">   <text decode="true">{{item.insertTime}}</text>   </view>   <view class="text">   <button class="sharebtn" data-commentId="{{item.id}}" data-commentUserName="{{item.userName}}" bindtap="bindReply">回复</button>   </view>    <view wx:if="{{item.userId == userId}}" class="status text fr">    <text class="delete" decode="true" bindtap='deleteComment' data-CommentId="{{item.id}}">删除</text>    </view>  </view>  </view> </block> </view></scroll-view><form bindsubmit="submitForm" report-submit="true"> <view class="release"> <view wx:if="{{reply}}" class="replyinfo1">  回复<text class="text">{{replyUserName}}</text>  <button class="cancel" bindtap="cancleReply">取消回复</button> </view> <view class="replyinfo2">  <textarea placeholder-class="input_null" fixed="true" maxlength="-1" show-confirm-bar="false" cursor-spacing="15" auto-height="true" placeholder="请输入回复" name="comment"></textarea>  <button form-type="submit" class="submit">发送</button> </view> </view></form>

css

.names { display: flex; font-size: 30rpx; line-height: 40rpx;} .input_null { color: #c9c9c9;} .replyAll { position:absolute;} .release { align-items: flex-end; /*底部对齐*/ box-sizing: border-box; position: fixed; left: 0; bottom: 0; width: 100%; padding: 18rpx 0 18rpx 30rpx; background-color: #f7f8f7; font-size: 28rpx; z-index: 999;} .replyinfo1{  display: flex; justify-content: space-between; /*两端对齐*/ font-size: 35rpx;}.replyinfo2{  display: flex; justify-content: space-between; /*两端对齐*/} .release textarea { width: 550rpx; min-height: 34rpx; max-height: 102rpx; /*最多显示三行*/ border-width: 15rpx 20rpx; /*使用padding与预期留白不一致,故使用border*/ border-style: solid; border-color: #fff; line-height: 34rpx; font-size: 28rpx; background-color: #fff; border-radius: 4rpx;} .release .text { font-size: 40rpx; color: #c9c9c9;} .cancel { width: 240rpx; height: 64rpx; line-height: 64rpx; text-align: center; color: #6c0; margin: 0 3px; padding: 0;} .release .submit { width: 120rpx; height: 64rpx; line-height: 64rpx; text-align: center; color: #6c0; margin: 0 3px; padding: 0;} .pro-box .info .text .delete { color: #f68135; border-radius: 50rpx; border: 1px solid #f68135; font-size: 28 rpx; width: 150rpx; height: 48rpx; text-align: center;}

js

// pages/comment/comment.jsconst model = require('../cityChoose/cityChoose.js')const config = require('../../utils/config.js')const util = require('../../utils/util.js')const app = getApp()var mydata = { end: 0, replyUserName: ""}Page({  /** * 页面的初始数据 */ data: { list: [], },  /** * 生命周期函数--监听页面加载 */ onLoad: function(options) { var that = this; mydata.sourceId = options.sourceId mydata.commentId = ""; mydata.replyUserName = ""; //设置scroll的高度 wx.getSystemInfo({  success: function(res) {  that.setData({   scrollHeight: res.windowHeight,   userId:app.globalData.haulUserInfo.id  });  } }); mydata.page = 1; that.getPageInfo(mydata.page); }, /** * 页面下拉刷新事件的处理函数 */ refresh: function() { console.log('refresh'); mydata.page = 1 this.getPageInfo(mydata.page, function() {  this.setData({  list: []  }) }); mydata.end = 0; }, /** * 页面上拉触底事件的处理函数 */ bindDownLoad: function() { console.log("onReachBottom"); var that = this; if (mydata.end == 0) {  mydata.page++;  that.getPageInfo(mydata.page); } }, bindReply: function(e) { console.log(e); mydata.commentId = e.target.dataset.commentid; mydata.replyUserName = e.target.dataset.commentusername; this.setData({  replyUserName: mydata.replyUserName,  reply: true }) }, // 合并数组 addArr(arr1, arr2) { for (var i = 0; i < arr2.length; i++) {  arr1.push(arr2[i]); } return arr1; }, deleteComment:function(e){ console.log(e); var that = this; var commentId = e.target.dataset.commentid;  wx.showModal({  title: '删除评论',  content: '请确认是否删除该评论?',  success: function (res) {  if (res.confirm) {   wx.request({   url: config.deleteComment,   method: "POST",   data: {    commentId: commentId   },   header: {    "content-type": "application/x-www-form-urlencoded;charset=utf-8",   },   success: res => {    that.refresh();    wx.showToast({    title: "删除成功"    })   }   })  } else if (res.cancel) {   console.log('用户点击取消')  }  } }) }, cancleReply: function(e) { mydata.commentId = ""; mydata.replyUserName = ""; this.setData({  replyUserName: mydata.replyUserName,  reply: false }) }, // 更新页面信息 // 此处的回调函数在 传入新值之前执行 主要用来清除页面信息 getPageInfo(page, callback) { var that = this; util.showLoading(); console.log("getPageInfo"); console.log("page" + page); var limited = 6; var offset = (page - 1) * 6; wx.request({  url: config.getComments,  method: "POST",  data: {  sourceId: mydata.sourceId,  limited: limited,  offset: offset  },  header: {  "content-type": "application/x-www-form-urlencoded;charset=utf-8",  },  success: res => {  console.log(res);  if (page == 1) {   that.data.list = res.data;   that.setData({   list: that.data.list   })   mydata.end = 0;  } else {   // 当前页为其他页   var list = that.data.list;   if (res.data.length != 0) {   list = that.addArr(list, res.data);   that.setData({    list: list   })   mydata.end = 0;   } else {   mydata.end = 1;   }  }  wx.hideLoading();  } }) }, submitForm(e) { var form = e.detail.value; var that = this; console.log(app.globalData.haulUserInfo); if(form.comment == ""){  util.showLog('请输入评论');  return; } // 提交评论 wx.request({  url: config.insertComment,  method: "POST",  data: {  sourceId: mydata.sourceId,  comment: form.comment,  userId: app.globalData.haulUserInfo.id,  userName: app.globalData.haulUserInfo.userName,  replyCommentId: mydata.commentId,  replyUserName: mydata.replyUserName,  userPhoto: app.globalData.haulUserInfo.userPhoto  },  header: {  "content-type": "application/x-www-form-urlencoded;charset=utf-8",  //token: app.globalData.token  },  success: res => {  console.log(res)  if (res.data.success) {   wx.showToast({   title: "回复成功"   })   that.refresh();   mydata.commentId = "";   mydata.replyUserName = "";   this.setData({   replyUserName: mydata.replyUserName,   reply: false   })  } else {   wx.showToast({   title: '回复失败,请检查您的网络',   })  }  } }) }})

后台

后台功能:获取评论、删除评论、插入评论,都是简单的数据库操作,放在一个controller类中实现即可

package com.melon.haul.web; import java.sql.Date;import java.text.SimpleDateFormat;import java.util.ArrayList;import java.util.HashMap;import java.util.List;import java.util.Map; import javax.servlet.http.HttpServletRequest; import net.sf.json.JSONObject; import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Controller;import org.springframework.test.context.web.WebAppConfiguration;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RequestMethod;import org.springframework.web.bind.annotation.RequestParam;import org.springframework.web.bind.annotation.ResponseBody;import org.slf4j.Logger;import org.slf4j.LoggerFactory; import com.melon.haul.dto.DataUtil;import com.melon.haul.dto.GetLocation;import com.melon.haul.dto.Result;import com.melon.haul.entity.Comment;import com.melon.haul.entity.District;import com.melon.haul.entity.Source;import com.melon.haul.service.CommentService;import com.melon.haul.service.DistrictService;import com.melon.haul.service.SourceService; @Controller@WebAppConfiguration@RequestMapping("/Comment")public class CommentController { private Logger logger = LoggerFactory.getLogger(this.getClass());  @Autowired private CommentService commentService;  @RequestMapping(value = "/getComments", method = RequestMethod.POST) private @ResponseBody List<Comment> getComments(@RequestParam("sourceId") int sourceId, @RequestParam("limited") int limited,@RequestParam("offset") int offset) { logger.info("getComments"); List<Comment> list = new ArrayList<Comment>(); try{ list = commentService.getComment(sourceId, limited, offset); }catch(Exception e){  } return list; }  @RequestMapping(value = "/insertComment", method = RequestMethod.POST) private @ResponseBody Result<Map<String,String>>insertComment(@RequestParam("sourceId") String sourceId, @RequestParam("comment") String comment,@RequestParam("userId") int userId, @RequestParam("userName") String userName,@RequestParam("replyCommentId") String replyCommentId, @RequestParam("replyUserName") String replyUserName,@RequestParam("userPhoto")String userPhoto) { logger.info("insertComment"); Map<String, String> resultMap = new HashMap<String, String>(); try{ Integer rCId = -1; if(!replyCommentId.equals("")) rCId = Integer.parseInt(replyCommentId); commentService.insertComment(Integer.parseInt(sourceId), comment, userId,userName,rCId,replyUserName,userPhoto); resultMap.put("msg", "insertComment success"); }catch(Exception e){ System.out.print(e); resultMap.put("msg", "insertComment error"); } return new Result<Map<String, String>>(true, resultMap); }  @RequestMapping(value = "/deleteComment", method = RequestMethod.POST) private @ResponseBody Result<Map<String,String>>deleteComment(@RequestParam("commentId") String commentId) { logger.info("deleteComment"); Map<String, String> resultMap = new HashMap<String, String>(); try{ commentService.deleteComment(commentId); resultMap.put("msg", "deleteComment success"); }catch(Exception e){ System.out.print(e); resultMap.put("msg", "deleteComment error"); } return new Result<Map<String, String>>(true, resultMap); }}

公共CSS(app.wxss)

 /**app.wxss**/.container { height: 100%; display: flex; flex-direction: column; align-items: center; justify-content: space-between; padding: 200rpx 0; box-sizing: border-box;} /* large button style */.large-btn{ background: #f68135; border-radius: 50rpx; border: 1px solid #f68135; color: #fff; height: 100rpx; line-height: 100rpx; margin: 0 auto; width: 96%; text-align: center;}.large-btn.empty{ background: transparent; color: #f68135; margin-top: 50rpx;}.large-btn.disabled{ border-color: #ccc; background: #ccc; color: #fff;}/* public style to clear default styles */.fl{ float: left;}.fr{ float: right;}.fc{ float:none;}.col-gray{ color: #999!important;}  /* the message of auction about goods & cars */.pro-con{ padding: 20rpx; background: #f1f1f1;}.pro-box{ background: #fff; padding: 20rpx; box-sizing: border-box; border-radius: 10rpx; margin-bottom: 20rpx;}.pro-box .img{ display: inline-block; vertical-align: top; width: 80rpx; height: 80rpx; border-radius: 50%; overflow: hidden; margin-right: 10rpx;}.pro-box .box{ display: inline-block; vertical-align: top; width: calc(98% - 80rpx);}.pro-box .shead{ padding-bottom: 20rpx;}.pro-box .shead .name{ font-size: 30rpx; line-height: 40rpx;}.pro-box .shead .stxt{ font-size: 26rpx; color: #999;}.pro-box .shead .fr{ padding-top: 10rpx;}.pro-box .shead .fr navigator{ font-size: 0;}.pro-box .shead .fr image{ width: 48rpx; height: 48rpx;} .pro-box .sharebtn{ height:48rpx; background: #f68135; border-radius: 50rpx; border: 1px solid #f68135; color: #fff; text-align: center; line-height: 50rpx; font-size:30rpx;}  .pro-box .addr-info{ align-items: center; justify-content: space-between; border-bottom: 1px dashed #ccc; margin: 0 -20rpx; margin-bottom: 20rpx; padding-bottom: 20rpx; padding-left: 20rpx; padding-right: 20rpx; display: inline-block;} .pro-box .addr-info .addr-text{ font-size: 35rpx; line-height: 40rpx; width:100%;} .pro-box .addr-info .addr-text .color1{ color:lightskyblue; border-color: #ccc; border: 1px solid lightskyblue; border-radius:15px; margin-right: 5px; padding: 0rpx,2rpx,0rpx,2rpx;} .pro-box .addr-info .addr-text .color2{ color: #f68135; border-color: #ccc; border: 1px solid #f68135; border-radius:10px; margin-right: 5px; margin-left: 5px; padding: 0rpx,2rpx,0rpx,2rpx;}  .pro-box .position{ width: 48rpx; height: 48rpx;}  .pro-box .comment{ width: 55rpx; height: 48rpx;}  .pro-box .addr{ align-items: center; justify-content: space-between; border-bottom: 1px dashed #ccc; margin: 0 -20rpx; margin-bottom: 20rpx; padding-bottom: 20rpx; padding-left: 20rpx; padding-right: 20rpx; display: flex;} .pro-box .addr .addr-text{ font-size: 34rpx; line-height: 40rpx; max-width: 240rpx; min-width:200rpx; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;}.pro-box .addr .addr-text .color-text{ color: #f68135;}.pro-box .addr .time{ font-size: 26rpx; line-height: 36rpx; text-align: center;}.pro-box .addr .line{ background: #ccc; height: 1px; margin: 6rpx -20rpx; position: relative;}.pro-box .info{ display: flex; align-items: center; justify-content: space-between;}.pro-box .info .text{ vertical-align:text-top; font-size: 26rpx;}.pro-box .info .text .delete{ color: #f68135; border-radius: 50rpx; border: 1px solid #f68135; width: 100rpx; height: 48rpx; text-align: center;}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持武林网。

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表