开发课程详情页滚动

This commit is contained in:
田岩
2025-06-24 17:31:38 +08:00
parent f59bb3742a
commit 7cd537912a
5 changed files with 316 additions and 297 deletions
+2
View File
@@ -89,4 +89,6 @@ export const loginOut = (data) => request({
}).then(() => {
setToken(null)
setUserInfo(null)
common.setValue('me_statistics_data', null) // 我的页统计数据缓存
common.setValue('index_statistics_data', null) // 首页统计数据缓存
})
+83 -78
View File
@@ -1,87 +1,90 @@
<template>
<view class="comment_div">
<view class="message_div" v-for="(item,index) in message_list">
<view class="head_div">
<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">{{item.userName}}</view>
<view class="title_tip">{{item.bbsTagDesc}}</view>
<scroll-view class="scroll-view" :scroll-y="true" :show-scrollbar="false">
<view class="comment_div">
<view class="message_div" v-for="(item,index) in message_list">
<view class="head_div">
<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="title">
{{item.bbsContent}}
</view>
<view class="img_div">
</view>
<view class="time_and_reply">
<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" 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 class="msg_content">
<view class="title_div">
<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">{{item.bbsTm.substr(0,16)}}</view>
<view class="reply" @click="click_reply(item, item.bbsId, index)">回复</view>
</view>
<view class="show_close_div" v-if="item.children?.length" @click="item.show = !item.show">
<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 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>
</view>
</uni-popup>
</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>
</scroll-view>
</template>
<script setup>
@@ -146,10 +149,7 @@
common.hideLoading()
}
}
// 点击展开子评论
const click_show_child_reply = (item) => {
item.show = !item.show
}
onMounted(() => {
qryTraCrsBbsDataByCrsNum({
crsNum: props.crsNum
@@ -162,6 +162,11 @@
</script>
<style lang="scss" scoped>
.scroll-view{
height: 50vh;
overflow: hidden;
border-radius: 24rpx;
}
// 顶部随着输入法弹出的框
.bottom_reply_input_div {
width: 100%;
+192 -159
View File
@@ -1,17 +1,19 @@
<template>
<view class="max_page detail-main">
<view class="max_page detail-main" style="overflow: hidden;">
<image src="/src/static/images/courseDetail/mipmap-xhdpi/bg.png" mode="widthFix" class="back-image"></image>
<view class="detail-body">
<view class="head-title font_pf">
<view class="title-text">{{ form.crsName }}</view>
<view class="title-num">
<view class="num-item">
<image class="title-icon" src="/src/static/images/courseDetail/mipmap-xhdpi/zhishidian.png" mode="widthFix"></image>
<image class="title-icon" src="/src/static/images/courseDetail/mipmap-xhdpi/zhishidian.png"
mode="widthFix"></image>
<view class="mr-10">知识点</view>
<view class="c-000">{{ form.pgrphNum }}</view>
</view>
<view class="num-item">
<image class="title-icon" src="/src/static/images/courseDetail/mipmap-xhdpi/yixue.png" mode="widthFix"></image>
<image class="title-icon" src="/src/static/images/courseDetail/mipmap-xhdpi/yixue.png"
mode="widthFix"></image>
<view class="mr-10">已学</view>
<view class="c-000">{{ form.styParaGraphNum }}</view>
</view>
@@ -19,185 +21,216 @@
</view>
<view class="body-box">
<view class="body-tab">
<uv-tabs
class="font_pf"
:current="tabAct"
:list="tabList"
@change="tabSwitch"
:scrollable="false"
<uv-tabs class="font_pf" :current="tabAct" :list="tabList" @change="tabSwitch" :scrollable="false"
line-color="#0066FF"
:active-style="{ color: '#323E57', 'font-weight': 600, 'font-size': '30rpx' }"
:inactive-style="{ color: '#666666', 'font-size': '30rpx' }"
></uv-tabs>
</view>
<view class="course-info" v-if="tabAct == 0">
<introduceVue v-model="form"></introduceVue>
</view>
<view class="point" v-if="tabAct == 1">
<knowledge v-model="form"></knowledge>
</view>
<view class="evaluate" v-if="tabAct == 2">
<comment :crsNum="crsNum"></comment>
:inactive-style="{ color: '#666666', 'font-size': '30rpx' }"></uv-tabs>
</view>
<view class="course-info" v-if="tabAct == 0">
<introduceVue v-model="form"></introduceVue>
</view>
<view class="point" v-if="tabAct == 1">
<knowledge v-model="form"></knowledge>
</view>
<view class="evaluate" v-if="tabAct == 2">
<comment :crsNum="crsNum"></comment>
</view>
</view>
</view>
<view class="btn-box font_pf">
<template v-if="[0,1].includes(tabAct)">
<uv-button type="primary" @click="common.navigateTo('/pages/course/study?crsNum='+crsNum+'&crsId='+form.crsId)">去学习</uv-button>
<uv-button type="primary"
@click="common.navigateTo('/pages/course/study?crsNum='+crsNum+'&crsId='+form.crsId)">去学习</uv-button>
<uv-button type="primary">去练习</uv-button>
<uv-button type="primary">去考试</uv-button>
</template>
<template v-if="tabAct == 2">
<uv-button type="primary" class="send_evaluate" @click="common.navigateTo('/pages/courseDetail/submit?crsNum='+crsNum)">发布评价</uv-button>
<uv-button type="primary" class="send_evaluate"
@click="common.navigateTo('/pages/courseDetail/submit?crsNum='+crsNum)">发布评价</uv-button>
</template>
</view>
</view>
</template>
<script setup>
import { reactive, ref } from 'vue';
import { onLoad } from '@dcloudio/uni-app';
import { getCourseDetailApi } from '@/api/courseDetail.js';
// import {formatDate2} from '@/common/common.js'
import common from '@/common/common';
import introduceVue from './components/introduce.vue';
import knowledge from './components/knowledge.vue';
import comment from './components/comment.vue';
import {
reactive,
ref
} from 'vue';
import {
onLoad
} from '@dcloudio/uni-app';
import {
getCourseDetailApi
} from '@/api/courseDetail.js';
// import {formatDate2} from '@/common/common.js'
import common from '@/common/common';
import introduceVue from './components/introduce.vue';
import knowledge from './components/knowledge.vue';
import comment from './components/comment.vue';
// tab栏
const tabList = ref([{ name: '课程介绍' }, { name: '知识点' }, { name: '评价' }]);
const form = ref({});
const crsNum = ref('');
const tabAct = ref(2);
const tabSwitch = (item) => {
tabAct.value = item.index;
};
const getData = async () => {
const { body } = await getCourseDetailApi({ crsNum: crsNum.value });
form.value = body;
};
onLoad((params) => {
crsNum.value = params.crsNum;
getData();
});
// tab栏
const tabList = ref([{
name: '课程介绍'
}, {
name: '知识点'
}, {
name: '评价'
}]);
const form = ref({});
const crsNum = ref('');
const tabAct = ref(2);
const tabSwitch = (item) => {
tabAct.value = item.index;
};
const getData = async () => {
const {
body
} = await getCourseDetailApi({
crsNum: crsNum.value
});
form.value = body;
};
onLoad((params) => {
crsNum.value = params.crsNum;
getData();
});
</script>
<style lang="scss" scoped>
.detail-main {
background-color: #f5f5f5;
position: relative;
}
.detail-body {
box-sizing: border-box;
min-height: calc(100vh - 256rpx);
position: absolute;
top: 256rpx;
border-radius: 23rpx 23rpx 0rpx 0rpx;
width: 100%;
display: flex;
flex-direction: column;
}
.body-head {
height: 460rpx;
background-color: red;
}
.back-image {
position: absolute;
width: 100%;
}
.head-title {
border-radius: 24rpx 24rpx 0rpx 0rpx;
height: 162rpx;
background-color: #ffffff;
display: flex;
flex-direction: column;
padding-top: 36rpx;
padding-left: 42rpx;
padding-bottom: 24rpx;
}
.title-text {
font-size: 34rpx;
color: #333333;
font-weight: 600;
margin-bottom: 20rpx;
}
.title-icon {
width: 22rpx;
height: 30rpx;
margin-right: 10rpx;
}
.title-num {
display: flex;
}
.body-box {
background-color: #f5f5f5;
flex: 1;
padding-left: 16rpx;
padding-right: 16rpx;
padding-top: 20rpx;
padding-bottom: 245rpx;
}
.body-tab {
background-color: #ffffff;
border-radius: 22rpx;
height: 92rpx;
// padding-top: 22rpx;
padding-bottom: 22rpx;
margin-bottom: 24rpx;
// :deep(.uv-tabs__wrapper__nav__line) {
// bottom: 10rpx;
// }
}
.btn-box {
position: fixed;
bottom: 0;
height: 187rpx;
width: 100%;
display: flex;
padding-top: 37rpx;
justify-content: space-around;
background-color: #ffffff;
// background-color: red;
:deep(.uv-button-wrapper) {
flex: 1;
&.send_evaluate{
margin: 0 60rpx;
}
&:not(.send_evaluate){
margin: 0 30rpx;
}
.uv-button {
border-radius: 14rpx;
width: 100%;
height: 92rpx;
}
.detail-main {
background-color: #f5f5f5;
position: relative;
}
}
.num-item {
display: flex;
align-items: center;
font-size: 30rpx;
margin-right: 30rpx;
color: #666666;
}
.point {
width: 100%;
}
.detail-body {
box-sizing: border-box;
min-height: calc(100vh - 256rpx);
position: absolute;
top: 256rpx;
border-radius: 23rpx 23rpx 0rpx 0rpx;
width: 100%;
display: flex;
flex-direction: column;
}
.learn {
color: #1dd17a;
}
.unlearn {
color: #666666;
}
.body-head {
height: 460rpx;
background-color: red;
}
.c-000 {
font-weight: 600;
color: #000000;
}
</style>
.back-image {
position: absolute;
width: 100%;
}
.head-title {
border-radius: 24rpx 24rpx 0rpx 0rpx;
height: 162rpx;
background-color: #ffffff;
display: flex;
flex-direction: column;
padding-top: 36rpx;
padding-left: 42rpx;
padding-bottom: 24rpx;
}
.title-text {
font-size: 34rpx;
color: #333333;
font-weight: 600;
margin-bottom: 20rpx;
}
.title-icon {
width: 22rpx;
height: 30rpx;
margin-right: 10rpx;
}
.title-num {
display: flex;
}
.body-box {
background-color: #f5f5f5;
flex: 1;
padding-left: 16rpx;
padding-right: 16rpx;
padding-top: 20rpx;
padding-bottom: 245rpx;
}
.body-tab {
background-color: #ffffff;
border-radius: 22rpx;
height: 92rpx;
// padding-top: 22rpx;
padding-bottom: 22rpx;
margin-bottom: 24rpx;
// :deep(.uv-tabs__wrapper__nav__line) {
// bottom: 10rpx;
// }
}
.btn-box {
position: fixed;
bottom: 0;
height: 187rpx;
width: 100%;
display: flex;
padding-top: 37rpx;
justify-content: space-around;
background-color: #ffffff;
// background-color: red;
:deep(.uv-button-wrapper) {
flex: 1;
&.send_evaluate {
margin: 0 60rpx;
}
&:not(.send_evaluate) {
margin: 0 30rpx;
}
.uv-button {
border-radius: 14rpx;
width: 100%;
height: 92rpx;
}
}
}
.num-item {
display: flex;
align-items: center;
font-size: 30rpx;
margin-right: 30rpx;
color: #666666;
}
.point {
width: 100%;
}
.learn {
color: #1dd17a;
}
.unlearn {
color: #666666;
}
.c-000 {
font-weight: 600;
color: #000000;
}
</style>
+33 -55
View File
@@ -56,22 +56,22 @@
<view class="item">
<view class="top">本年学习时长</view>
<view class="bottom">
<uv-count-to :startVal="statistics_data['stdtTmLen']" :endVal="statistics_data['stdtTmLen']"
:useEasing="true" :decimals="1"></uv-count-to>h
<uv-count-to :startVal="statistics_data['startStdtTmLen']" :endVal="statistics_data['stdtTmLen']"
:useEasing="true" :decimals="1" ref="stdtTmLenRef"></uv-count-to>h
</view>
</view>
<view class="item">
<view class="top">累计训练次数</view>
<view class="bottom">
<uv-count-to :startVal="statistics_data['accmExamCnt']"
:endVal="statistics_data['accmExamCnt']" :useEasing="true"></uv-count-to><span
<uv-count-to :startVal="statistics_data['startAccmExamCnt']"
:endVal="statistics_data['accmExamCnt']" :useEasing="true" ref="accmExamCntRef"></uv-count-to><span
class="number"></span>
</view>
</view>
<view class="item">
<view class="top">累计考试次数</view>
<view class="bottom"><uv-count-to :startVal="statistics_data['accmPracticeCnt']"
:endVal="statistics_data['accmPracticeCnt']" :useEasing="true"></uv-count-to><span
<view class="bottom"><uv-count-to :startVal="statistics_data['starAccmPracticeCnt']"
:endVal="statistics_data['accmPracticeCnt']" :useEasing="true" ref="accmPracticeCntRef"></uv-count-to><span
class="number"></span>
</view>
</view>
@@ -249,6 +249,10 @@
queryCrsInfoByRecmd,
} from '@/api/index.js'
const scrollTop = ref(0);
const stdtTmLenRef = ref(null);
const accmPracticeCntRef = ref(null);
const accmExamCntRef = ref(null);
// 计算透明度
const opacity = computed(() => {
if (scrollTop.value <= 130) return 0;
@@ -281,16 +285,6 @@
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, // 训练
@@ -300,38 +294,40 @@
'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;
// }
const index_statistics_data = common.getValue('index_statistics_data') // 获取缓存
if (index_statistics_data) {
this.stdtTmLen = index_statistics_data.stdtTmLen;
this.accmPracticeCnt = index_statistics_data.accmPracticeCnt;
this.accmExamCnt = index_statistics_data.accmExamCnt;
}
},
"get_statistics_data": async function() {
try {
const time_now = Date.now();
if (time_now - this.request_time > 300 * 1000) {
if (time_now - this.request_time > 30) {
this.request_time = time_now;
// 获取最新数据
const res = await queryTraStdyStatisticsSelf();
const accmPoint = res.body.accmPoint
const accmTaskCnt = res.body.accmTaskCnt
const res = await getStatisticsData();
common.setValue('index_statistics_data', res.body)
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()
}
const accmPracticeCnt = res.body.accmPracticeCnt
const accmExamCnt = res.body.accmExamCnt
if (stdtTmLen != this.stdtTmLen) { // 不相等就执行
this.startStdtTmLen = this.stdtTmLen
this.stdtTmLen = stdtTmLen
stdtTmLenRef.value.start()
}
if (accmPracticeCnt != this.accmPracticeCnt) { // 不相等就执行
this.startAccmPracticeCnt = this.accmPracticeCnt
this.accmPracticeCnt = accmPracticeCnt
accmPracticeCntRef.value.start()
}
if (accmExamCnt != this.accmExamCnt) { // 不相等就执行
this.startAccmExamCnt = this.accmExamCnt
this.accmExamCnt = accmExamCnt
accmExamCntRef.value.start()
}
console.log('统计数据已更新');
} else {
console.log('时间没到无需更新');
@@ -342,22 +338,6 @@
}
});
// 获取首页统计数据方法
const get_statistics_data = async () => {
const data = await getStatisticsData()
statistics_data['accmExamCnt'] = data['accmExamCnt']
statistics_data['accmPracticeCnt'] = data['accmPracticeCnt']
statistics_data['stdtTmLen'] = data['stdtTmLen']
// const time_now = Date.now()
// // 暂时设置10分钟
// if (time_now - statistics_data['request_time'] > 600 * 1000) {
// statistics_data['request_time'] = time_now
// const data = await getStatisticsData()
// }
}
// 进入搜索页
const goto_search_page = () => {
common.navigateTo('/pages/search/search')
@@ -372,7 +352,7 @@
common.navigateTo('/pages/login/login')
return
}
get_statistics_data() // 获取首页统计数据
statistics_data['get_statistics_data']() // 获取统计数据
})
onMounted(async () => {
queryCrsInfoByLastnew().then(res => {
@@ -384,8 +364,6 @@
queryCrsInfoByRecmd().then(res => {
recommend_class_list.value = res.body
})
// 预加载我的页
})
</script>
+6 -5
View File
@@ -133,18 +133,19 @@
"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;
this.stdtTmLen = me_statistics_data.stdtTmLen;
this.accmTaskCnt = me_statistics_data.accmTaskCnt;
this.accmPoint = me_statistics_data.accmPoint;
}
},
"get_statistics_data": async function() {
try {
const time_now = Date.now();
if (time_now - this.request_time > 300 * 1000) {
if (time_now - this.request_time > 300 ) {
this.request_time = time_now;
// 获取最新数据
const res = await queryTraStdyStatisticsSelf();
common.setValue('me_statistics_data', res.body)
const accmPoint = res.body.accmPoint
const accmTaskCnt = res.body.accmTaskCnt
const stdtTmLen = res.body.stdtTmLen
@@ -183,7 +184,7 @@
onShow(() => {
const user_info_cache = getUserInfo()
Object.assign(user_info, user_info_cache)
statistics_data['get_statistics_data']() // 获取首页统计数据
statistics_data['get_statistics_data']() // 获取统计数据
})
const click_loginout = () => {