修改首页和登录页统计接口逻辑

This commit is contained in:
田岩
2025-06-24 16:41:10 +08:00
parent 9d354db292
commit c2520d4e8c
31 changed files with 2569 additions and 161 deletions
+237 -43
View File
@@ -1,78 +1,247 @@
<template>
<view class="comment_div">
<view class="message_div" v-for="item in [1,2,3]">
<view class="message_div" v-for="(item,index) in message_list">
<view class="head_div">
<image src="/src/static/images/test/touxiang.png" class="img"></image>
<image-preview v-if="item.userIconImgId" class="img-box img"
:imgId="item.userIconImgId"></image-preview>
<image v-else src="/static/images/me/default_user_avatar.png" class="img"></image>
</view>
<view class="msg_content">
<view class="title_div">
<view class="name">黎明正</view>
<view class="title_tip">建议</view>
<view class="name">{{item.userName}}</view>
<view class="title_tip">{{item.bbsTagDesc}}</view>
</view>
<view class="title">
知识点挺清晰的受益匪浅
{{item.bbsContent}}
</view>
<view class="img_div">
</view>
<view class="time_and_reply">
<view class="time">2025-04-23 14:12</view>
<view class="reply">回复</view>
<view class="time">{{item.bbsTm.substr(0,16)}}</view>
<view class="reply" @click="click_reply(item, item.bbsId, index)">回复</view>
</view>
<view class="show_close_div">
<uv-read-more openText="">展开3条回复</uv-read-more>
<!-- <text></text> -->
<view class="show_close_div" v-if="item.children?.length" @click="click_show_child_reply(item)">
<template v-if="item.show">
<view>收起</view>
<view class="back_div">
<view class="back-icon"></view>
</view>
</template>
<template v-else>
<view>展开{{item.children?.length}}条回复</view>
<view class="back_div">
<view class="back-icon"></view>
</view>
</template>
</view>
<view class="children" v-if="item.show">
<view class="message_div" v-for="childrenItem in item.children">
<view class="head_div">
<image-preview v-if="childrenItem.userIconImgId" class="img-box img"
:imgId="childrenItem.userIconImgId"></image-preview>
<image v-else src="/static/images/me/default_user_avatar.png" class="img"></image>
</view>
<view class="msg_content">
<view class="title_div">
<view class="name">{{childrenItem.userName}}</view>
<view class="arrow"></view>
<view class="name">{{childrenItem.rtnUName}}</view>
</view>
<view class="title">
{{childrenItem.bbsContent}}
</view>
<view class="img_div">
</view>
<view class="time_and_reply">
<view class="time">{{childrenItem.bbsTm.substr(0,16)}}</view>
<view class="reply" @click="click_reply(childrenItem, childrenItem.parentId, index)">回复
</view>
</view>
</view>
</view>
</view>
</view>
</view>
<uni-popup ref="popup" background-color="red">
<view class="bottom_reply_input_div">
<uv-input v-model="reply_data.bbsContent" placeholderStyle='color:#999999;font-size:26rpx'
:placeholder="reply_data.placeholder" :focus="true">
</uv-input>
<view class="" style="display: flex;flex-direction: column; margin:10rpx 20rpx;">
<view class="fl1"></view>
<uv-button class="bottom_reply_button" type="primary" :throttleTime="500"
@click="reply_send">回复</uv-button>
<view class="fl1"></view>
</view>
</view>
</uni-popup>
</view>
</template>
<script setup>
import {
ref,
reactive,
onMounted
} from 'vue'
import {
qryTraCrsBbsDataByCrsNum
qryTraCrsBbsDataByCrsNum,
replyTraCrsBbsInfo,
qryChildTraCrsBbsInfo,
} from '@/api/courseDetail.js';
const message_list = ref([
])
const props = defineProps({
crsNum:String
import common from '@/common/common.js'
const popup = ref(null)
const inputValue = ref('')
const placeholder = ref('')
const message_list = reactive([])
const reply_data_init = {
"index": null,
"parentBbsId": null,
"bbsId": null,
"bbsContent": '',
"placeholder": '',
}
const reply_data = reactive({
...reply_data_init
})
const props = defineProps({
crsNum: String
})
const click_reply = ({
userName,
bbsId
}, parentBbsId, index) => {
popup.value.open('bottom')
reply_data.placeholder = '回复 ' + userName + ""
reply_data.parentBbsId = parentBbsId
reply_data.bbsId = bbsId
reply_data.index = index
}
const reply_send = async () => {
try {
popup.value.close()
common.loading('发送中...')
await replyTraCrsBbsInfo({
parentBbsId: reply_data.parentBbsId,
bbsId: reply_data.bbsId,
bbsContent: reply_data.bbsContent,
imgIdList: []
})
common.msg('发送成功')
const res = await qryChildTraCrsBbsInfo({
bbsId: reply_data.parentBbsId
})
message_list[reply_data.index]['children'] = res.body
Object.assign(reply_data, reply_data_init)
} catch (e) {
common.msg('发送失败')
} finally {
common.hideLoading()
}
}
// 点击展开子评论
const click_show_child_reply = (item) => {
item.show = !item.show
}
onMounted(() => {
qryTraCrsBbsDataByCrsNum({crsNum:props.crsNum}).then(({body}) => {
message_list.value = body
qryTraCrsBbsDataByCrsNum({
crsNum: props.crsNum
}).then(({
body
}) => {
Object.assign(message_list, body)
})
})
</script>
<style lang="scss" scoped>
.message_div:not(:first-child){
// 顶部随着输入法弹出的框
.bottom_reply_input_div {
width: 100%;
height: 100%;
background-color: #FFFFFF;
display: flex;
align-items: center;
padding: 10rpx 10rpx;
.bottom_reply_button {
border: none;
border-radius: 4rpx;
background-color: #0066FF;
color: #FFFFFF;
overflow: hidden;
:deep(.uv-button) {
height: 60rpx;
}
}
}
.back_div {
margin-left: 6rpx;
.back-icon {
position: relative;
width: 30rpx;
height: 30rpx;
cursor: pointer;
}
.back-icon::before,
.back-icon::after {
content: '';
position: absolute;
transition: all 0.3s ease;
}
.back-icon::after {
top: 2%;
left: 25%;
width: 50%;
height: 50%;
border-top: 3rpx solid #0066FF;
border-left: 3rpx solid #0066FF;
transform: rotate(-135deg);
}
}
.message_div:not(:first-child) {
margin-top: 30rpx;
}
.message_div {
// margin-top: 30rpx;
display: flex;
.head_div{
.img{
.head_div {
.img {
border-radius: 100%;
width: 78rpx;
height: 78rpx;
overflow: hidden;
}
}
.msg_content{
margin-left: 8rpx;
.msg_content {
margin-left: 22rpx;
flex: 1;
overflow: hidden;
.title_div{
.title_div {
display: flex;
align-items: center;
.name{
.name {
height: 35rpx;
font-family: PingFangSC, PingFang SC;
font-weight: 400;
@@ -82,22 +251,42 @@
text-align: left;
font-style: normal;
}
.title_tip{
.title_tip {
margin-left: 16rpx;
width: 76rpx;
height: 34rpx;
background: #FFFFFF;
border-radius: 8rpx;
border: 2rpx solid #999999;
font-family: PingFangTC, PingFangTC;
font-family: PingFangSC;
font-weight: 400;
font-size: 23rpx;
color: #666666;
line-height: 36rpx;
text-align: center;
}
.arrow {
margin: 0 12rpx 0 14rpx;
position: relative;
width: 12rpx;
height: 20rpx;
overflow: hidden;
}
.arrow::after {
content: "";
position: absolute;
background-color: #666666;
right: 4rpx;
width: 18rpx;
height: 12rpx;
transform: rotate(45deg);
}
}
.title{
.title {
margin: 4rpx 0;
font-family: PingFangSC, PingFang SC;
font-weight: 400;
@@ -105,53 +294,58 @@
color: #333333;
text-align: left;
line-height: 48rpx;
text-overflow: ellipsis;
white-space: wrap;
overflow-wrap: anywhere;
}
.time_and_reply{
.time_and_reply {
display: flex;
align-items: center;
height: 28rpx;
.time{
.time {
font-family: ArialMT;
font-size: 23rpx;
font-size: 24rpx;
color: #666666;
line-height: 27rpx;
text-align: left;
font-style: normal;
}
.reply{
.reply {
margin-left: 16rpx;
font-family: PingFangSC, PingFang SC;
font-weight: 400;
font-size: 23rpx;
font-size: 24rpx;
color: #0066FF;
line-height: 33rpx;
text-align: left;
font-style: normal;
}
}
.show_close_div{
.show_close_div {
margin-top: 12rpx;
margin-bottom: 20rpx;
font-family: PingFangSC, PingFang SC;
font-weight: 400;
font-size: 23rpx;
font-size: 30rpx;
color: #456A8E;
line-height: 33rpx;
line-height: 30rpx;
text-align: left;
font-style: normal;
display: flex;
align-items: center;
}
}
}
.comment_div {
padding:44rpx 40rpx;
padding: 44rpx 40rpx;
background-color: #ffffff;
border-radius: 24rpx;
}
</style>
+58 -9
View File
@@ -280,17 +280,67 @@
const hot_class_list = ref([...init_class_data])
const new_class_list = ref([...init_class_data])
const recommend_class_list = ref([...init_class_data])
// const statistics_data = reactive({
// 'stdtTmLen': 0, // 学习
// 'accmPracticeCnt': 0, // 训练
// 'accmExamCnt': 0, //考试
// 'start_tudy': 0, // 起始学习
// 'start_train': 0, // 起始训练
// 'start_exam': 0, //起始考试
// 'request_time': 0, //上次请求时间
// })
const statistics_data = reactive({
'stdtTmLen': 0, // 学习
'accmPracticeCnt': 0, // 训练
'accmExamCnt': 0, //考试
'start_tudy': 0, // 起始学习
'start_train': 0, // 起始训练
'start_exam': 0, //起始考试
'request_time': 0, //上次请求时间
})
'accmExamCnt': 0, // 考试
'startStdtTmLen': 0, // 起始学习
'startAccmPracticeCnt': 0, // 起始训练
'startAccmExamCnt': 0, // 起始考试
'request_time': 0, // 上次请求时间
"init": function() { // 初始化统计数据
// const index_statistics_data = common.getValue('index_statistics_data') // 获取缓存
// if (index_statistics_data) {
// this.stdtTmLen = index_statistics_data.study;
// this.start_train = index_statistics_data.train;
// this.start_exam = index_statistics_data.exam;
// }
},
"get_statistics_data": async function() {
try {
const time_now = Date.now();
if (time_now - this.request_time > 300 * 1000) {
this.request_time = time_now;
// 获取最新数据
const res = await queryTraStdyStatisticsSelf();
const accmPoint = res.body.accmPoint
const accmTaskCnt = res.body.accmTaskCnt
const stdtTmLen = res.body.stdtTmLen
if (accmPoint != this.accmPoint) { // 不相等就执行
this.startAccmPoint = this.accmPoint
this.accmPoint = accmPoint
accmPointRef.value.start()
}
if (accmTaskCnt != this.accmTaskCnt) { // 不相等就执行
this.startAccmTaskCnt = this.accmTaskCnt
this.accmTaskCnt = accmTaskCnt
accmTaskCntRef.value.start()
}
if (stdtTmLen != this.stdtTmLen) { // 不相等就执行
this.startStdtTmLen = this.stdtTmLen
this.stdtTmLen = stdtTmLen
stdtTmLenRef.value.start()
}
console.log('统计数据已更新');
} else {
console.log('时间没到无需更新');
}
} catch (error) {
console.error('获取统计数据失败:', error);
}
}
});
// 获取首页统计数据方法
@@ -305,7 +355,6 @@
// statistics_data['request_time'] = time_now
// const data = await getStatisticsData()
// console.log(data);
// }
}
+52 -60
View File
@@ -8,8 +8,7 @@
<!-- 用户个人信息 -->
<view class="personal_information">
<view class="profile">
<image v-if="user_info.imageAddr" class="img_100" src="/static/images/test/touxiang.png">
</image>
<image v-if="user_info.imageAddr" class="img_100" src="/static/images/test/touxiang.png"></image>
<image v-else class="img_100" src="/static/images/me/default_user_avatar.png"></image>
</view>
<view class="personal_information_data">
@@ -36,19 +35,22 @@
<view class="item">
<view class="top">累计学习时长</view>
<view class="bottom">
<uv-count-to :startVal="statistics_data['accmPoint']" :endVal="statistics_data['accmPoint']" :useEasing="true" :decimals="1"></uv-count-to>h
<uv-count-to :startVal="statistics_data['startAccmPoint']" :endVal="statistics_data['accmPoint']"
:useEasing="true" :decimals="1" ref="accmPointRef"></uv-count-to>h
</view>
</view>
<view class="item">
<view class="top">累计完成任务</view>
<view class="bottom">
<uv-count-to :startVal="statistics_data['accmTaskCnt']" :endVal="statistics_data['accmTaskCnt']" :useEasing="true"></uv-count-to>
<uv-count-to :startVal="statistics_data['startAccmTaskCnt']"
:endVal="statistics_data['accmTaskCnt']" :useEasing="true" ref="accmTaskCntRef"></uv-count-to>
</view>
</view>
<view class="item">
<view class="top">累计获得积分</view>
<view class="bottom">
<uv-count-to :startVal="statistics_data['stdtTmLen']" :endVal="statistics_data['stdtTmLen']" :useEasing="true"></uv-count-to>
<uv-count-to :startVal="statistics_data['startStdtTmLen']" :endVal="statistics_data['stdtTmLen']"
:useEasing="true" ref="stdtTmLenRef"></uv-count-to>
</view>
</view>
</view>
@@ -117,64 +119,56 @@
import {
queryTraStdyStatisticsSelf
} from '@/api/user.js'
const accmTaskCntRef = ref(null)
const accmPointRef = ref(null)
const stdtTmLenRef = ref(null)
const statistics_data = reactive({
'stdtTmLen': 2, // 学习
'study': 0, // 学习
'train': 0, // 训练
'exam': 0, // 考试
'start_study': 0, // 起始学习
'start_train': 0, // 起始训练
'start_exam': 0, // 起始考试
'stdtTmLen': 0, // 累计学习时长
'accmTaskCnt': 0, // 累计完成任务
'accmPoint': 0, // 累计获得积分
'startStdtTmLen': 0, // 起始累计学习时长
'startAccmTaskCnt': 0, // 起始累计完成任务
'startAccmPoint': 0, // 起始累计获得积分
'request_time': 0, // 上次请求时间
/**
* 初始化统计数据
*/
"init": function() {
// 初始化起始数据
this.start_study = this.study;
this.start_train = this.train;
this.start_exam = this.exam;
"init": function() { // 初始化统计数据
const me_statistics_data = common.getValue('me_statistics_data') // 获取缓存
if (me_statistics_data) {
this.stdtTmLen = me_statistics_data.study;
this.start_train = me_statistics_data.train;
this.start_exam = me_statistics_data.exam;
}
},
/**
* 获取统计数据(带缓存机制)
* @returns {Promise<void>}
*/
"get_statistics_data": async function() {
try {
const time_now = Date.now();
// 检查缓存是否过期(10分钟)
if (time_now - this.request_time > 600 * 1000) {
if (time_now - this.request_time > 300 * 1000) {
this.request_time = time_now;
// 获取最新数据
const {
study,
train,
exam
} = await getStatisticsData();
// 保存起始值(如果是首次获取)
if (this.request_time === 0) {
this.start_study = study;
this.start_train = train;
this.start_exam = exam;
const res = await queryTraStdyStatisticsSelf();
const accmPoint = res.body.accmPoint
const accmTaskCnt = res.body.accmTaskCnt
const stdtTmLen = res.body.stdtTmLen
if (accmPoint != this.accmPoint) { // 不相等就执行
this.startAccmPoint = this.accmPoint
this.accmPoint = accmPoint
accmPointRef.value.start()
}
if (accmTaskCnt != this.accmTaskCnt) { // 不相等就执行
this.startAccmTaskCnt = this.accmTaskCnt
this.accmTaskCnt = accmTaskCnt
accmTaskCntRef.value.start()
}
if (stdtTmLen != this.stdtTmLen) { // 不相等就执行
this.startStdtTmLen = this.stdtTmLen
this.stdtTmLen = stdtTmLen
stdtTmLenRef.value.start()
}
// 更新当前值
this.study = study;
this.train = train; // 修正:之前漏写这一行
this.exam = exam;
console.log('统计数据已更新');
} else {
console.log('使用缓存的统计数据');
console.log('时间没到无需更新');
}
} catch (error) {
console.error('获取统计数据失败:', error);
// 可以在这里添加重试逻辑或使用默认值
}
}
});
@@ -183,19 +177,13 @@
userName: '',
userId: ''
});
onShow(() => {
const user_info_cache = getUserInfo()
Object.assign(user_info, user_info_cache)
queryTraStdyStatisticsSelf().then(({body}) => {
statistics_data['accmPoint'] = body['accmPoint']
statistics_data['accmTaskCnt'] = body['accmTaskCnt']
statistics_data['stdtTmLen'] = body['stdtTmLen']
console.log('res', body);
})
// statistics_data['get_statistics_data']() // 获取首页统计数据
statistics_data['get_statistics_data']() // 获取首页统计数据
})
const click_loginout = () => {
@@ -310,9 +298,9 @@
box-shadow: #F6F8FF 0 0 2rpx;
.personal_information {
padding: 20rpx 0 0 26rpx;
padding: 20rpx 18rpx 0 26rpx;
display: flex;
// 个人头像框框
.profile {
border-radius: 100%;
@@ -324,15 +312,16 @@
// 头像右边的信息
.personal_information_data {
flex: 1;
display: flex;
flex-direction: column;
margin-left: 26rpx;
overflow: hidden;
// 上边的姓名性别盒子
.name_sex_div {
display: flex;
margin-top: 6rpx;
.name {
font-family: PingFangSC;
font-weight: 600;
@@ -356,6 +345,9 @@
line-height: 38rpx;
text-align: left;
font-style: normal;
white-space: nowrap;
text-overflow: ellipsis;
}
}
}
+1 -35
View File
@@ -9,41 +9,7 @@
</view>
</template>
<script setup>
import WebSocketUtil from '@/api/socket.js';
const ws = new WebSocketUtil('ws://127.0.0.1', {
maxReconnectCount: 10,
reconnectInterval: 5000,
heartbeatInterval: 30000,
heartbeatMsg: 'ping'
});
ws.on('open', () => {
console.log('WebSocket连接已打开');
ws.send({
type: 'init',
data: {
userId: 123
}
});
});
ws.on('message', (res) => {
console.log('收到消息:', res);
});
ws.on('close', (res) => {
console.log('WebSocket连接已关闭:', res);
});
ws.on('error', (err) => {
console.error('WebSocket错误:', err);
});
// 一段时间后关闭连接
setTimeout(() => {
ws.close(1000, '正常关闭');
}, 60000);
</script>
<style scoped lang="scss">
.main_div {