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

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

微信小程序 评论留言功能实现 仿知乎_第1张图片

微信小程序 评论留言功能实现 仿知乎_第2张图片(删除按钮,是用户自己的留言时才会显示该按钮)

实现技术

  后台:SSM框架

  数据库:MySQL数据库

数据库设计

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

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

微信小程序 评论留言功能实现 仿知乎_第3张图片

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

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

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

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

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

小程序端

wxml


  
    
      
        
          
          
            
              {{item.userName}}
                  
                  -> {{item.replyUserName}}
                
              
            
          
        
        
          
            {{item.comment}}
          
        
        
          
            {{item.insertTime}}
          
          
            
          
              
                删除
              
        
      
    
  

回复{{replyUserName}}

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  getComments(@RequestParam("sourceId") int sourceId,
			@RequestParam("limited") int limited,@RequestParam("offset") int offset) {
		logger.info("getComments");
		List list = new ArrayList();
		try{
			list = commentService.getComment(sourceId, limited, offset);
		}catch(Exception e){
			
		}
		return list;
	}
	
	@RequestMapping(value = "/insertComment", method = RequestMethod.POST)
	private @ResponseBody
	Result>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 resultMap = new HashMap();
		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>(true, resultMap);
	}
	
	@RequestMapping(value = "/deleteComment", method = RequestMethod.POST)
	private @ResponseBody
	Result>deleteComment(@RequestParam("commentId") String commentId) {
		logger.info("deleteComment");
		Map resultMap = new HashMap();
		try{
			commentService.deleteComment(commentId);
			resultMap.put("msg", "deleteComment success");
		}catch(Exception e){
			System.out.print(e);
			resultMap.put("msg", "deleteComment error");
		}
		return new Result>(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;
}

 

你可能感兴趣的:(开发中踩过的坑-微信小程序)