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


Posted in Javascript onNovember 28, 2018

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

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

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

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

实现技术

后台: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.js
const 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;
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
javascript控制swfObject应用介绍
Nov 29 Javascript
js数组操作学习总结
Nov 04 Javascript
js浮点数精确计算(加、减、乘、除)
Dec 26 Javascript
Javascript设计模式之观察者模式的多个实现版本实例
Mar 03 Javascript
jQuery基本选择器(实例及表单域value的获取方法)
May 20 Javascript
Angular下H5上传图片的方法(可多张上传)
Jan 09 Javascript
jquery事件与绑定事件
Mar 16 Javascript
小程序云开发初探(小结)
Oct 24 Javascript
微信小程序实现单选功能
Oct 30 Javascript
JavaScript数组去重的方法总结【12种方法,号称史上最全】
Feb 28 Javascript
Vue+elementUI实现多图片上传与回显功能(含回显后继续上传或删除)
Mar 23 Javascript
JavaScript中while循环的基础使用教程
Aug 11 Javascript
微信小程序实现评论功能
Nov 28 #Javascript
在 Angular-cli 中使用 simple-mock 实现前端开发 API Mock 接口数据模拟功能的方法
Nov 28 #Javascript
小程序点击图片实现自动播放视频
May 29 #Javascript
django使用channels2.x实现实时通讯
Nov 28 #Javascript
在 Vue-CLI 中引入 simple-mock实现简易的 API Mock 接口数据模拟
Nov 28 #Javascript
详解Vue中watch的详细用法
Nov 28 #Javascript
vscode下的vue文件格式化问题
Nov 28 #Javascript
You might like
php数据库连接
2006/10/09 PHP
php处理抢购类功能的高并发请求
2018/02/08 PHP
PHP的mysqli_thread_id()函数讲解
2019/01/24 PHP
PHP中Static(静态)关键字功能与用法实例分析
2019/04/05 PHP
Ajax,UTF-8还是GB2312 eval 还是execScript
2008/11/13 Javascript
通过继承IHttpHandle实现JS插件的组织与管理
2010/07/13 Javascript
jQuery使用slideUp方法实现控制元素缓慢收起
2015/03/27 Javascript
浅谈node中的exports与module.exports的关系
2017/08/01 Javascript
react+redux的升级版todoList的实现
2017/12/18 Javascript
jQuery实现的淡入淡出与滑入滑出效果示例
2018/04/18 jQuery
ES6常用小技巧总结【去重、交换、合并、反转、迭代、计算等】
2019/12/21 Javascript
JavaScript隐式类型转换代码实例
2020/05/29 Javascript
[01:46]DOTA2上海特锦赛小组赛英文解说KotlGuy采访
2016/02/27 DOTA
详细介绍Python函数中的默认参数
2015/03/30 Python
python Django批量导入不重复数据
2016/03/25 Python
python使用selenium登录QQ邮箱(附带滑动解锁)
2019/01/23 Python
numpy数组广播的机制
2019/07/12 Python
django中使用事务及接入支付宝支付功能
2019/09/15 Python
Python3安装pip工具的详细步骤
2019/10/14 Python
pytorch 中pad函数toch.nn.functional.pad()的用法
2020/01/08 Python
Python 3.10 的首个 PEP 诞生,内置类型 zip() 迎来新特性(推荐)
2020/07/03 Python
美国时尚在线:Showpo
2017/09/08 全球购物
TCP协议通讯的过程和步骤是什么
2015/10/18 面试题
多媒体编辑专业毕业生推荐信
2013/11/05 职场文书
大学生的自我鉴定范文
2014/01/21 职场文书
中职生求职信
2014/07/01 职场文书
社会发展项目建议书
2014/08/25 职场文书
国家税务局干部作风整顿整改措施
2014/09/18 职场文书
会计实训报告范文
2014/11/04 职场文书
农村婚礼司仪主持词
2015/06/29 职场文书
2015小学音乐教师个人工作总结
2015/07/21 职场文书
小数乘法教学反思
2016/02/22 职场文书
让文件路径提取变得更简单的Python Path库
2021/05/27 Python
vue 实现上传组件
2021/05/31 Vue.js
再次探讨go实现无限 buffer 的 channel方法
2021/06/13 Golang
python基础之类方法和静态方法
2021/10/24 Python