微信小程式 評論留言功能實現 仿知乎
最近沉迷學習無法自拔,太久沒有碼字,碼一個小程式留言功能實現。先上一波最後效果圖:
(刪除按鈕,是使用者自己的留言時才會顯示該按鈕)
實現技術
後臺: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;
}
相關推薦
微信小程式 評論留言功能實現 仿知乎
最近沉迷學習無法自拔,太久沒有碼字,碼一個小程式留言功能實現。先上一波最後效果圖: (刪除按鈕,是使用者自己的留言時才會顯示該按鈕) 實現技術 後臺:SSM框架 資料庫:MySQL資料庫 資料庫設計 評論功能的實現主要涉及三個表 comment:
微信小程式評論/留言功能,附:前端+後端程式碼+視訊講解!
前端介面: 演示: <!-- 表單 --> <form bindsubmit="formSubmit"> <input type="text" name="liuyantext" placeholder='輸入留言內容' class
微信小程式 蒐藏功能實現(八)
搜尋功能用到了小程式的快取功能:wx.setStorage 如果沒有向用戶提供removeStorageSync或clearStorageSync,小程式的快取永久存在,沒有失效期,快取的最大不超過10MB 四類操作,八種方法: setStorage,getStorage
微信小程式發紅包功能實現,附效果圖加講解。
有問題可以掃碼加我微信,有償解決問題。承接小程式開發。 微信小程式開發交流qq群 173683895 、 526474645 ; 正文: 目前此功能尚在內測,無法申請。此博文僅示例。 流
微信小程式五星評價功能實現
實現五星評價功能,效果圖如下: .wxml檔案: <view class="star-title">1、品質效果</view> <view class="star
微信小程式與vue的區別,知乎回答!!!
明顯不是嘛,資料屬性更新是這樣的 小程式: Page({ data: { items: [] }, onLoad: function(options) { this.setData({ items: [1,2,3] }) } }) Vue:
微信小程式評論功能實現原始碼,複製貼上
wxml: 傳送 js: var ComContent = ‘’ var CommentList = ‘[]’ var app = getApp() Page({ /** * */ data: { CommentList: [{}], bindContent: null, Co
微信小程式 評論功能實現
前端 <textarea class='the_prw_in' bindinput='bindblur' cursor-spacing="130" placehold
微信小程式評論功能原始碼
wxml: <textarea class="input" bindinput='bindContent' value="" placeholder="請填寫評論內容 "></textarea>
微信小程式——評論點贊功能
實現的最終效果圖 1.點贊功能 前端頁面結構 1 <view class='talk-item-zan'> 2 <view bindtap='favorclick' data-id='{{item.id}}' data-islike="{{
微信小程式+WEB使用JS實現註冊【60s】倒計時功能
微信小程式+WEB使用JS實現註冊【60s】倒計時功能開發步驟: 1、效果圖: 2、頁面僅僅利用了JS的相關功能,包含:wxml、js、wxss 2.1wxml頁面程式碼: <text
微信小程式 右上角分享功能的實現
微信小程式前段時間開放了小程式右上角的分享功能, 可以分享任意一個頁面到好友或者群聊, 但是目前小程式不可以分享到朋友圈onShareAppMessage(options)在 Page 中定義 onShareAppMessage 函式,設定該頁面的轉發資訊。只有定
微信小程式 簡訊驗證 功能的實現(附案例程式碼/前後端/直接用)
模組效果展示(小程式介面) 實現的功能 小程式端: 請求獲取簡訊驗證碼 兩次請求之間間隔至少一分鐘 填寫必填內容後,才能提交表單 手機號合法性檢驗 後臺: 接前臺請求後,
微信小程式開發之radio實現顯示和隱藏功能
我們在開發微信小程式的時候,經常會用到顯示和隱藏,但是我們知道在微信小程式裡面是不是能 使用dom操作的,話不多說,直接上程式碼 第一步.直接在wxml,首先在要選擇的按鈕上註冊一個bindtap事件如下圖 第二步.在js中的pages下的data中新增 showView
使用微信小程式自定義元件實現的tabs選項卡功能
一個自定義元件由 json wxml wxss js 4個檔案組成。要編寫一個自定義元件,首先需要在 json 檔案中進行自定義元件宣告(將 component 欄位設為 true 可這一組檔案設為自定義元件) components/navigator/i
微信小程式-template使用:實現購物車商品數量加減功能
前言上一篇我們實現了購物車功能,裡面有用到template模板功能來實現購物車商品數量加減和價格計算功能,可能篇幅過長介紹的並不清楚,本篇將詳細介紹一下template模板來減少冗餘程式碼。模板WXML提供模板(template),可以在模板中定義程式碼片段,然後在不同的地方
微信小程式(看文件寫例項十)微信小程式課堂寶APP實現我的模組相關介面及邏輯
繼上篇博文,這篇完成最後一個模組,即我的模組。 一、頁面效果 這個模組是和使用者型別相關的,因此老師賬號和學生賬號能看的功能不一樣,老師端效果如下: 點選頭像到達個人資訊如下: 點選後可以做相應的修改。學生端的介面如下: 修改密碼的頁面如下: &nbs
微信小程式(看文件寫例項八)微信小程式課堂寶APP實現練習模組前臺
接上篇博文,這篇主要描述練習模組的前臺顯示,其中包括test頁面,答題detail頁面以及提交答題後答卷answer頁面。 一、練習模組test頁面 練習頁面主要展示的是當前使用者的頭像,暱稱以及學校資訊,另外還有答題資訊,以及每個章節的練習資訊,先來看看效果: grid用的是樣式
微信小程式(看文件寫例項七)微信小程式課堂寶APP實現線上課堂測試
接著上篇博文已經完成簽到功能,這篇來完成課堂測試功能。 一、需求描述 1、在後臺選擇題、主觀題表中上傳測試題 2、客戶端獲取題目資訊 3、把題目資訊格式化載入顯示 4、客戶端答題,主觀題每題能上傳一張答題圖片 5、客戶端答題結束提交到伺服器 二、前臺頁面 提交大量資料
微信小程式(看文件寫例項六)微信小程式課堂寶APP實現簽到邏輯
繼上篇博文,這篇寫下籤到實現的邏輯。 一、實現邏輯 發起簽到 1、先上傳當前自己的定位經緯度 2、學生查詢老師的最後一次簽到記錄,如果發現簽到記錄signComplete為false說明有新的簽到 3、得到簽到的第幾次課 4、系統獲得學生的定位經緯度 5、判斷兩點經緯度轉