前端效果图
前端数据格式
前端代码
<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; //暴露模块,其它地方才能调用此模块