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

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

实现技术

后台: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.sourceIdmydata.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 = 1this.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;
}

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

  1. 微信小程序评论/留言功能,附:前端+后端代码+视频讲解!

    前端界面: 演示: <!-- 表单 --> <form bindsubmit="formSubmit"> <input type="text ...

  2. php微信小程序 留言功能,微信小程序评论/留言功能,附:前端+后端代码+视频讲解!...

    前端界面: (此图片来源于网络,如有侵权,请联系删除! ) 演示: (此图片来源于网络,如有侵权,请联系删除! ) 授权登录 留言 {{item.result}} 以下是留言内容 {{item.nic ...

  3. php微信小程序实现留言,微信小程序实现留言功能

    需求:留言可以点赞,点过赞之后图标变化,没人只能点赞一次,留言可以在留言 index.wxml 邻居评论({{yanlist.count}}) 我要留言 {{item.nick_name}} {{it ...

  4. 【小程序模板】功能模块+仿vivo手机商城微信小程序+品牌手机APP购物网页模板

    [小程序模板]功能模块+仿vivo手机商城微信小程序+品牌手机APP购物网页模板 源码简介与安装说明: 仿vivo手机商城微信小程序 品牌手机app购物网页模板源码下载. 小程序源码下载地址:(82条 ...

  5. 微信小程序:常用功能8——小程序视频组件中的弹幕功能

    微信小程序:常用功能8--小程序视频组件中的弹幕功能 昨天我们刚说了微信小程序的视频组件和分享功能微信小程序:常用功能8--在小程序添加视频组件,并将页面分享到朋友圈,今天想把弹幕功能说一下,但是感觉 ...

  6. 小程序录音上传服务器,微信小程序录音实现功能并上传(使用node解析接收)

    微信小程序录音实现功能并上传(使用node解析接收) 发布时间:2020-09-04 11:59:06 来源:脚本之家 阅读:97 作者:weixin_43188227 背景 我在开发小程序的时候,有 ...

  7. 微信小程序-评论业务的实现

    微信小程序-评论业务的实现 目录 微信小程序-评论业务的实现 一.效果 二.实现 1.wxml 2.js 3.wxss 一.效果 二.实现 1.wxml <!-- wx:index = &quo ...

  8. 微信小程序实现购物车功能,包含完整小程序代码和运行效果截图

    微信小程序实现购物车功能,在商场比较常见,今天刚刚做好,效果不错. 下面从js文件,json文件,wxml文件和wxss文件,分享给大家. 直接上代码: 目录 1.index.js文件内容 2.ind ...

  9. 微信小程序开源源码,仿淘宝、京东、今日头条等

    wx-gesture-lock  微信小程序的手势密码 WXCustomSwitch 微信小程序自定义 Switch 组件模板 WeixinAppBdNovel 微信小程序demo:百度小说搜索 sh ...

最新文章

  1. 正则严格验证身份证信息
  2. 边缘检测:Sobel、拉普拉斯算子
  3. myeclipse下hibernate入门实例介绍
  4. php发展历,PHP的发展历程
  5. java反射三种方法_Java基础入门要学哪些 怎么掌握反射和枚举
  6. 数学建模 TOPSIS法
  7. [草稿]尝试从 same.com 的视角观察简书的用户社交网络
  8. 以太坊 solidity return 返回值写法 3种格式
  9. IDA Pro、OllyDbg、LordPE和UltraEdit简单实用实验
  10. iapp退出软件按钮代码_一师一优课视频专用转码软件的安装和使用教程
  11. Bitset 源码解析
  12. Java实现支付功能(支付宝)
  13. 数字信号处理(FIR滤波器的设计与原理及基础知识)
  14. Windows 系统托盘图标
  15. Android自定义之仿360Root大师水纹效果
  16. 618 线上摆摊 | 看直播 领京豆
  17. cronolog 安装配置 Centos 7
  18. python里lens什么意思_LENS是什么意思
  19. HTML新年许愿墙代码,网页版春节许愿墙代码,兔年许愿墙代码
  20. 新闻|未名论道-科技赋能数字化经济论坛本周五举办

热门文章

  1. Linux|linux下root用户与普通用户
  2. jQuery之实现电影排行榜
  3. Live555源码阅读笔记(四):groupsock 目录详解
  4. ARM裸机开发:主频与时钟
  5. 招聘季“金三银四”:玩家们蠢蠢欲动,却难获企业与求职者的好评
  6. [创业之路-43] :复盘与自省 - 创业初感悟(冲动->纠结->忐忑)与“不贪、不赌、不悔”做人做事三原则的成形
  7. PointTransformer编译pointops_cuda报错fatal error: THC/THC.h: No such file or directory
  8. 构建基于 Ingress 的全链路灰度能力
  9. Android学习笔记——关于Intent
  10. 物理引擎-Physx的源代码去哪里找