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

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

浏览量:1790 时间: 来源:蜗牛专注学习

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

小程序

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

实现技术

  后台: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());@Autowiredprivate 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 @ResponseBodyResult<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 @ResponseBodyResult<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;}

 

版权声明

即速应用倡导尊重与保护知识产权。如发现本站文章存在版权问题,烦请提供版权疑问、身份证明、版权证明、联系方式等发邮件至197452366@qq.com ,我们将及时处理。本站文章仅作分享交流用途,作者观点不等同于即速应用观点。用户与作者的任何交易与本站无关,请知悉。

产品经理

手机 : 13312967497

擅长 : 小程序流量变现

扫码领取礼包

最新资讯

热门模板

  • 头条
  • 搜狐
  • 微博
  • 百家
  • 一点资讯
  • 知乎