uniapp+node.js 开发小程序留言、点赞、回复

前端效果图
在这里插入图片描述
前端数据格式
在这里插入图片描述
前端代码

<template>
	<view>
		<view class="home-pic">
			<view class="home-pic-base">
				<view class="top-pic">
					<image class="header" src="../../static/tou.jpg" @click="imgup"></image>
				</view>
				<view class="top-name">{{username}}</view>
			</view>
		</view>
		<view class="page__bd" style="padding-top:10rpx; width:100%;">
			<!--外循环 包含用户名,标题,内容,时间-->
			<view class='box' v-for="(item, index) in grouplist" :key="index" >					
				<view class='boxtext'>
					<text style="font-size:16px; text-shadow: 3px 3px 3px #483D8B;"> >> {{item.username}} </text>
					<text style="font-size:10px;"> {{item.addtime}} </text>
					<text class='memotext'>{{item.note}}</text>					      
				</view>
				<!--内循环 包含图片-->	
				<view class='img0'>
					<view class="img1" v-for="(name, index1) in item.imgname" :key="index1"  >
					    <image class='img2'  mode="aspectFill"  @click="clickimg" :src="srcurl + name" :data-src="srcurl + name" :data-itemindex="index" ></image>
					</view>
				</view>
				<!--点赞、评论区-->
				<view>
					<view style="float:right; padding-right:40rpx;">
						<image class="zan" v-if="item.like_flag==0" src="../../static/zan.png" mode="aspectFill" @click="zan" :data-zanid="item.groupID"></image>
						<image class="zan" v-else src="../../static/zan1.png" mode="aspectFill" @click="zan" :data-zanid="item.groupID"></image>
						<text> {{item.likes}} </text>							
						<image class="ping" src="../../static/ping.png" mode="aspectFill" @click="ping" :data-groupid="item.groupID"></image>
						<text> {{item.commentNum}} </text>
						<image class="del" src="../../static/delete.png" mode="aspectFill" @click="del" 
							   :data-delid="index" :data-deluserid="item.userid" :data-delgroupid="item.groupID"></image>						
					</view>
				</view>
			</view> 
		</view> 
	</view>	
</template>

<script>	
	export default {
		data() {
			return {
				list:[],  //存储后端返回的原始数据
				grouplist:[],  //存储分组后的数据
				num : 1,
				isShowConfirm:false,
				hasMoreData: true,
				srcurl:'',
				zangroupid:'',
				username:''
			}
		},

		/* 监听页面加载 */
		onLoad: function (options) {
			uni.setStorageSync('isRefresh', 0)
			let url = uni.getStorageSync('url')  //从缓存读取
			this.srcurl = url + '/imgup/'  //图片的网址,用于页面渲染 :src="srcurl + name"
		    this.getInfo('正在加载数据...')			
		},
		
		onShow() {
			let username = uni.getStorageSync('username')
			this.username = username
			let isRefresh = uni.getStorageSync('isRefresh')
			if (isRefresh != 0) {  //数据有变动才自动刷新
				this.num = 1
				this.getInfo('正在加载数据...')
			}
			uni.setStorageSync('isRefresh', 0)
		},
		
		/* 下拉刷新 */
		onPullDownRefresh() {
			this.num = 1
			this.getInfo('正在加载数据...')
			setTimeout(function () {
				uni.stopPullDownRefresh();
			}, 1000);			
		},
		
		/* 上拉触底事件 */
		onReachBottom:function( ) { 
			if (this.hasMoreData) {
				this.getInfo('加载更多数据')
			} else {
				uni.showToast({
				  title: '没有更多数据',
				  icon:'none'
				})
			}      
		},
		
		/* 监听标题栏点击事件 */
		onNavigationBarButtonTap(e) {
			if (e.index == 0) {				
				uni.navigateTo({  //跳转到指定页面
					url: "../imgup/imgup",
				});
			}
		},
		
		/* 自定义函数 */
		methods: {				
			//从其它页面返回后刷新数据
			returnRefresh:function () {
				this.num = 1
				this.getInfo('正在加载数据...')
			},			
			
			// 分页加载数据
			getInfo: function (message) {
				let that = this
				let url = uni.getStorageSync('url') //从缓存读取
				let userid = uni.getStorageSync('userid') //从缓存读取
				uni.showLoading({	//显示 loading 提示框
				  title: message,
				})
				uni.request({
					url: url + '/memo',
					data: {					
						pagenum : that.num ,   //赋值并传到后端
						userid : userid
					},
					method: 'GET',
					header: { 'content-type': 'application/json' },
					success: function (res) {  //请求成功						
						var imgurlTemp = that.list
						if (res.data.length >= 0) {						  
							uni.hideLoading()		//隐藏 loading 提示框
							if (that.num == 1) {
								imgurlTemp = []
							}
							var imgurl = res.data
							if (imgurl.length < 10) {  //对应后端接口分页查询的记录条数  
								that.list = imgurlTemp.concat(imgurl)
								that.hasMoreData = false
							} else {
								that.list = imgurlTemp.concat(imgurl)
								that.hasMoreData = true
								that.num = that.num + 1						    
							}
						}
						
						//以下是把数据进行分组,一个标题对应多张图片,把图片文件名转换成数组
						var arr = that.list
						var map = {}
						var	dest = []
						for(var i = 0; i < arr.length; i++){
							var ai = arr[i];
							if(!map[ai.groupID]){  //分组关键词,以groupID分组
								dest.push({
									groupID: ai.groupID,  //分组关键词也要出现在这里
									userid: ai.userid,
									username: ai.username,
									note: ai.note,
									likes: ai.likes,
									commentNum: ai.commentNum,
									like_flag: ai.like_flag,
									addtime: ai.addtime,
									imgname: [ai.imgname]  //一个标题对应多张图片,这是多张图片的数组
								});
								map[ai.groupID] = ai;  //分组关键词,以groupID分组
							} else {
								for(var j = 0; j < dest.length; j++){
									var dj = dest[j];
									if(dj.groupID == ai.groupID){  //分组关键词,以groupID分组
										dj.imgname.push(ai.imgname);
										break;
									}
								}
							}
						}						
						that.grouplist = dest
						console.log(dest)
						
						//对后台返回的数据集进行遍历,拿到所有已经点过赞的id					
						var tempid=[]
						for(var i=0;i < that.grouplist.length;i++) {
							if(that.grouplist[i].like_flag==1) {    //根据自己的数据字段找到到已经点赞的								
								tempid = tempid.concat(that.grouplist[i].groupID)
							}
						}						
						uni.setStorageSync('zan', tempid)  //把点赞的数据保存到缓存
					}
				})
			},
			
			// 点赞
			zan:function(e) {
				var zanid = e.currentTarget.dataset.zanid
				this.zangroupid = zanid				
				this.zan0(zanid)
			},			
			//点赞处理函数    
			zan0:function(item_id){			    
			    var cookie_id = uni.getStorageSync('zan')||[]  //从缓存中读取点赞的数据
			    for (var i = 0; i < this.grouplist.length;i++) {
					if (this.grouplist[i].groupID==item_id) {  //在数据列表中找到当前对应的id
						var num=this.grouplist[i].likes      //当前点赞数量
						if (cookie_id.includes(item_id)) {    //已经点过赞了,取消点赞
							for(var j in cookie_id){
								if(cookie_id[j]==item_id) {
								  cookie_id.splice(j,1)  //删除取消点赞的id
								}
							}
							--num  //点赞数减1						
							this.grouplist[i].likes = num     //更新点赞数量    
							this.grouplist[i].like_flag = 0    //我的数据中 0 是未点赞						
							uni.setStorageSync('zan', cookie_id)
						} else { //点赞操作			          
							++num    //点赞数加1						
							this.grouplist[i].likes = num      //更新点赞数量   
							this.grouplist[i].like_flag = 1    //我的数据中 1 是点赞						
							cookie_id.unshift(item_id)   //新增点赞的id
							uni.setStorageSync('zan', cookie_id)
						}

						//点赞数据同步到后台数据库
						let url = uni.getStorageSync('url') //从本地存储读取
						let userid = uni.getStorageSync('userid') //从本地存储读取
						let zangroupid = this.zangroupid
						uni.request({
							url:url + '/likes',
							data:{
								userid: userid,
								groupid: zangroupid	
							},
							method:'GET',
							header:{'content-type': 'application/json'},
							success:res => {
								console.log(res)
							}
						})
					}
			    }
			},
			
			//删除主题 DeleteTopic
			del:function(e) {
				var delid = e.currentTarget.dataset.delid  //要删除主题的下标
				var delgroupid = e.currentTarget.dataset.delgroupid
				var deluserid = e.currentTarget.dataset.deluserid  //发布主题的用户id
				var userid = uni.getStorageSync('userid') //当前登录的用户id
				var url = uni.getStorageSync('url') //从本地存储读取
				var grouplist = this.grouplist
				var list = this.list
				
				if (deluserid == userid) {  //判断是否是自己发布的
					uni.showModal({
						title: '主人',
						content: '真的不要我了吗?',
						showCancel:true,
						success: function (res) {
							if (res.confirm) {							   
							    grouplist.splice(delid,1)  //删除前端渲染数组对应的id
							    
								//从源数组中删除对应的id,不然加载下一页数据的时候又会出现已经删除的图片
								for (var n = list.length -1; n > -1; n--) {  //逆向遍历,用于删除数组的多个值
									if (list[n].groupID == delgroupid) {
										list.splice(n,1)
									}
								}
							    /*for(var n=0; n < list.length; n++) {  //正向遍历,用于删除数组的单个值。
								   if (list[n].groupID == delgroupid) {	 //	因为splice方法删除数组一个指定值之后,数组发生改变,后续的值向前挪动一个位置。								
										list.splice(n,1)				 //	如果用于删除多个值,会发生删除不完全的问题				
								    }
							    }*/	
							   
							   //传给后端删除对应的数据
							   uni.request({			  	
									url:url + '/DeleteTopic',
									data:{										
										delgroupid: delgroupid	
									},
									method:'GET',
									header:{'content-type': 'application/json'},
									success:res => {
										console.log(res)
									}
							   })
							} else if (res.cancel) {
							   console.log('用户点击取消')
							}
						}
					})
				} else {
					uni.showToast({
					    title: "只能删除自己发布的内容!",
					    icon: 'none',
					    duration:1000,
					    mask: true
					})
				}
			},
			
			// 图片预览
			clickimg(event) {
				//var imgurl = event.currentTarget.dataset.src
				var currentUrl = event.currentTarget.dataset.src   //获取点击图片的地址, **对应<template>里面的 :data-src="item.src"					
				var itemindex = event.currentTarget.dataset.itemindex	//获取当前点击图片的外循环的下标			
				var that = this
				var urln = []
				var imgname = that.grouplist[itemindex].imgname	 //外循环里面的数组
				
				for (var i=0, h=imgname.length; i < h; i++) {  //进行内循环
					urln.push(that.srcurl + imgname[i])  //以图片的文件名拼接成网址,并追加到数组urln
				}	
			 
				uni.previewImage({       
					//urls: [imgurl],    //这里是单图 . 需要预览的全部图片地址,这个数组是必须的,要用[] 
					urls: urln, 		 //这里是多图 . 需要预览的全部图片地址,这个数组是必须的,要用[]
					current: currentUrl, //当前显示图片的地址					
				})  
			},
			
			//评论
			ping:function(e) {
				var groupid = e.currentTarget.dataset.groupid
				//console.log(groupid)
				uni.navigateTo({
					url: "../comment/comment?groupid=" + groupid ,  //跳转到页面并传值
				})
			},
			
			imgup:function(e) {
				console.log('imgup')
			}
			
		}
	}	
</script>

<style>
	@import url("./memo.css");
</style>


以下是样式

.home-pic {
	width: 100%;
	/* margin-top: -150upx; */
	position: relative;
	height: 500upx;
	z-index: 5;
    background: url('../../static/beijing.jpg'); /* 背景图 */
	background-size: cover;
	margin-bottom: 20upx
}

.home-pic-base {
	position: absolute;
	left: -60upx;
	bottom: -40upx;
	width: 100%;
	height: 160upx;
	padding: 0 30upx;
}

.home-pic-base .top-pic {
	width: 360upx;
	height: 360upx;	
	border-radius: 40upx;
	-webkit-transform: scale(0.5);
	-ms-transform: scale(0.5);
	transform: scale(0.5);
	-webkit-transform-origin: 100% 0%;
	-ms-transform-origin: 100% 0%;
	transform-origin: 100% 0%;
	background-color: #ffffff;	
	float: right
}

.home-pic-base .top-name {
	position: absolute;
	/* left: 20px; */
	right: 240upx;
	top: 30upx;
	font-size: 32upx;
	font-weight: 600;
	text-align: right;
	color: #ffffff;
	overflow: hidden
}
.header{
	height: 360upx;
	width: 360upx;
	border-radius: 20px; /*圆角*/
}

.box{	
	display: flex;
	flex-direction:column;
	background-color: #e6f2f2;
	margin-bottom: 25rpx;
	width:100%;		
	/*height: 400rpx; //不指定高度,会自动适应	
	border-bottom-style:solid;  //下边框
	border-width:1px;		
	border-color:#bdd7fd;*/		
}

.boxtext{  
	display: flex;
	padding-left: 40rpx;
	flex-direction:column;  
	justify-content: center;
	overflow-wrap: break-word
}

.memotext{		
	word-wrap: break-word;  //识别单词 不会造成单词断开换行
	//word-break: break-all;  //不识别单词 一行显示不下默认换行
	white-space: pre-line;  //保留换行符
}

.img0 {
	display: flex;
	flex-direction:row;
	flex-wrap: wrap;		
	align-items: flex-start;
	padding-left: 40rpx; 
}	
.img1 {
	display:flex;		
	width: 221rpx;
	height: 221rpx;
	margin: 5rpx;		
}
.img2 {  
	width: 220rpx;
	height: 220rpx;
	border-radius: 5px; /*圆角*/
}
.zan{
	width:50rpx; 
	height:50rpx;
	margin-top:20rpx; 
	margin:2rpx;
}
.ping{
	width:50rpx; 
	height:50rpx; 
	margin:2rpx;
	margin-top:20rpx;
	margin-left:40rpx;
}
.del{
	width:50rpx; 
	height:50rpx; 
	margin:2rpx;
	margin-top:20rpx;
	margin-left:40rpx;
}

后台数据格式:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
联合查询的存储过程

USE [A001]
GO
/****** Object:  StoredProcedure [dbo].[weiwx_note]    Script Date: 02/02/2021 11:54:57 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROCEDURE [dbo].[weiwx_note]     
      @start as int, 
      @end as int,
      @userid as varchar(10)    
       
AS 
BEGIN
	if object_id('tempdb..#tmp1') is not null   drop table #tmp1
	if object_id('tempdb..#tmp2') is not null   drop table #tmp2 
	BEGIN
		select a.userid,a.username,a.groupID,a.note,a.likes,a.commentNum,b.imgname,substring(CONVERT(varchar(100), a.addtime, 120),6,11) as addtime
		into #tmp1
		from (select *,(SELECT COUNT(*) from uni_bbs where del_flag=0) as total ,ROW_NUMBER()over(order by uni_bbs.ID desc) as rows from uni_bbs where del_flag=0) as a
			 left join uni_bbsimages as b on a.groupID=b.groupID
		where a.rows BETWEEN @start and @end
	END
	
	BEGIN
		select groupid,like_flag into #tmp2 from uni_bbsLike  where userid=@userid
	END
	
	exec('select w.userid,w.username,w.groupID,w.note,w.likes,w.commentNum,w.imgname,addtime,isnull(v.like_flag,0) as like_flag
	      from #tmp1 as w left join #tmp2 as v on  w.groupid=v.groupid ')
	
	if object_id('tempdb..#tmp1') is not null   drop table #tmp1 
	if object_id('tempdb..#tmp2') is not null   drop table #tmp2 
END


后端node.js代码

var express = require('express')  //引用express
var mssql =require("mssql")  //引用mssql
var config = require('../config.js') //引用上级目录的config.js配置文件
var router=express.Router()
var app = express()

// 分页查询 
router.get('/',function (req,res) { 	
	var pagenum = req.query.pagenum;  //解析前端传递过来的页码
	var userid = req.query.userid;
	var start;
	var end;
	
	if(pagenum == undefined){
		pagenum = 1;
		start = 0;
		end = 10;  
	} else {
		start = (pagenum -1)*10+1;   //从第11条开始
		end = pagenum*10;  //每次请求10条记录
	};
	
	mssql.connect(config, function (err) {  //连接数据库
		if (err) {
			return callback(err);
		} else {
		 	var request = new mssql.Request();			
			request.input('start', mssql.Int,parseInt(start));  //整数型
			request.input('end', mssql.Int,parseInt(end));  //整数型
			request.input('userid', mssql.VarChar,userid);  //存储过程的输入参数:名称,类型,值	
			//存储过程的输入参数不能是中文,否则查询不到数据.如果输入参数是中文只能用sql语句,不能用存储过程									
			// request.query 用于sql语句 // request.execute 用于存储过程			
			//request.query("SELECT username,memo,filename,substring(CONVERT(varchar(100), addtime, 120),6,11) as addtime  from (select *,(SELECT COUNT(*) from wx_bbs where del_flag=0) as total ,ROW_NUMBER()over(order by wx_bbs.ID desc) as rows from wx_bbs where del_flag=0) i where i.rows BETWEEN'"+start+"' and '"+end+"'",function (err, recordsets,returnValue) {
			request.execute("weiwx_note",function (err, recordsets,returnValue) { 			
			    arr = JSON.stringify(recordsets.recordset); //将查询结果转换成json格式
				res.send(arr);  //响应请求,将数据发送到前端
				console.log(arr)				
			})
		};		
		mssql.end;  //结束连接数据库
	})   
	
})

module.exports = router;  //暴露模块,其它地方才能调用此模块

  • 6
    点赞
  • 65
    收藏
    觉得还不错? 一键收藏
  • 9
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值