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


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脚本
Aug 04 Javascript
jQuery 树形结构的选择器
Feb 15 Javascript
JavaScript CSS 修改学习第四章 透明度设置
Feb 19 Javascript
简单实用jquery版三级联动select示例
Jul 04 Javascript
基于javascript实现简单的抽奖系统
Apr 15 Javascript
弹出遮罩层后禁止滚动效果【实现代码】
Apr 29 Javascript
JavaScript实战之菜单特效
Aug 16 Javascript
Bootstrap源码解读模态弹出框(11)
Dec 28 Javascript
JS获取日期的方法实例【昨天,今天,明天,前n天,后n天的日期】
Sep 28 Javascript
vue中如何让子组件修改父组件数据
Jun 14 Javascript
vue 中基于html5 drag drap的拖放效果案例分析
Nov 01 Javascript
jQuery实现的图片点击放大缩小功能案例
Jan 02 jQuery
微信小程序实现评论功能
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
DedeCMS 核心类TypeLink.class.php摘要笔记
2010/04/07 PHP
PHP去掉从word直接粘贴过来的没有用格式的函数
2012/10/29 PHP
php后门URL的防范
2013/11/12 PHP
Zend Framework教程之MVC框架的Controller用法分析
2016/03/07 PHP
php 函数中静态变量使用的问题实例分析
2020/03/05 PHP
js form action动态修改方法
2008/11/04 Javascript
JS类的封装及实现代码
2009/12/02 Javascript
jquery按回车提交数据的代码示例
2013/11/05 Javascript
jQuery实现手机号码输入提示功能实例
2015/04/30 Javascript
基于Css3和JQuery实现打字机效果
2015/08/11 Javascript
javascript同步服务器时间和同步倒计时小技巧
2015/09/24 Javascript
jquery选择器中的空格与大于号&gt;、加号+与波浪号~的区别介绍
2016/06/24 Javascript
jQuery属性选择器用法示例
2016/09/09 Javascript
如何获取元素的最终background-color
2017/02/06 Javascript
zTree获取当前节点的下一级子节点数实例
2017/09/05 Javascript
vue-cli3配置与跨域处理方法
2019/08/17 Javascript
Vue管理系统前端之组件拆分封装详解
2020/08/23 Javascript
vue router-link 默认a标签去除下划线的实现
2020/11/06 Javascript
JS中多层次排序算法的实现代码
2021/01/06 Javascript
Python编程之多态用法实例详解
2015/05/19 Python
Python 类与元类的深度挖掘 I【经验】
2016/05/06 Python
Python实现的从右到左字符串替换方法示例
2018/07/06 Python
python 文本单词提取和词频统计的实例
2018/12/22 Python
详解python中docx库的安装过程
2019/11/08 Python
使用phonegap进行提示操作的具体方法
2017/03/30 HTML / CSS
制药工程专业毕业生推荐信
2013/12/24 职场文书
仓管员岗位责任制
2014/02/19 职场文书
房产公证委托书范本
2014/09/20 职场文书
房屋买卖委托书格式范本格式
2014/10/13 职场文书
党的群众路线教育实践活动领导班子整改措施
2014/10/28 职场文书
党的群众路线教育实践活动个人对照检查材料(公安)
2014/11/05 职场文书
个人学习总结范文
2015/02/15 职场文书
努力工作保证书
2015/02/28 职场文书
公司回复函格式
2015/07/14 职场文书
MySQL修改默认引擎和字符集详情
2021/09/25 MySQL
分享python函数常见关键字
2022/04/26 Python