Files
tra-app/src/pages/analytics/study.vue
T
2025-12-04 15:44:55 +08:00

674 lines
17 KiB
Vue

<template>
<view class="study-analytics max_page">
<view class="cont-body">
<view class="top-box">
<view class="top-title">
<view class="back-icon" @click="common.navigateBack()"></view>
</view>
</view>
<!-- <view class="analytics-box loading-box-cont" v-if="loading">
<CommonLoading color="#06f" class="loading-box" />
</view> -->
<view :class="['analytics-box', loading?'loading-box-cont':'']">
<CommonLoading color="#06f" class="loading-box" v-if="loading" />
<view class="tabs-box">
<uv-tabs :list="tabList" @click="changeTab" :scrollable="true" active-style="font-weight:bold; color:#000;"
:current="currentTab" class="font_pf my_tabs"
:inactive-style="{ color: '#333333', fontSize: '28rpx', fontWeight: '500' }"
:activeStyle="{ color: '#333333', fontSize: '28rpx', fontWeight: '600' }"></uv-tabs>
</view>
<!-- <view class="calendar-box" @click="openCalendar">统计周期</view>
<uv-calendar ref="calendarRef" mode="range" @confirm="calendarConfirm" minDate="2025-01-01" :maxDate="maxDate" monthNum="100" :allowSameDay="true"></uv-calendar> -->
<view class="date-input" v-show="activeTabVal === '04'">
<uni-datetime-picker class="custom-datepicker" ref="dateinputRef" type="daterange" v-model="daterange"
start="2025-01-01" :end="maxDate" rangeSeparator="至" @change="changeDate"></uni-datetime-picker>
</view>
<view class="analytics-items">
<view class="analytics-item">
<view class="analytics-item-title">
课程学习时长
</view>
<view class="analytics-item-value">
<text class="analytics-item-value-v">{{statisticsObj.crsStdyTmLen }}</text>
<text class="analytics-item-value-unit">h</text>
</view>
</view>
<view class="analytics-item">
<view class="analytics-item-title">
完成课程数
</view>
<view class="analytics-item-value">
<text class="analytics-item-value-v">{{statisticsObj.completeCrsCnt }}</text>
<text class="analytics-item-value-unit"></text>
</view>
</view>
<view class="analytics-item">
<view class="analytics-item-title">
课程完成率
</view>
<view class="analytics-item-value">
<text class="analytics-item-value-v">{{statisticsObj.crsCompleteRate }}</text>
<text class="analytics-item-value-unit">%</text>
</view>
</view>
<view class="analytics-item">
<view class="analytics-item-title">
完成任务数
</view>
<view class="analytics-item-value">
<text class="analytics-item-value-v">{{statisticsObj.completeTaskCnt }}</text>
<text class="analytics-item-value-unit"></text>
</view>
</view>
<view class="analytics-item">
<view class="analytics-item-title">
完成考试数
</view>
<view class="analytics-item-value">
<text class="analytics-item-value-v">{{statisticsObj.completedExamCnt }}</text>
<text class="analytics-item-value-unit"></text>
</view>
</view>
<view class="analytics-item">
<view class="analytics-item-title">
考试及格率
</view>
<view class="analytics-item-value">
<text class="analytics-item-value-v">{{statisticsObj.passExamRate }}</text>
<text class="analytics-item-value-unit">%</text>
</view>
</view>
<view class="analytics-item">
<view class="analytics-item-title">
问答次数
</view>
<view class="analytics-item-value">
<text class="analytics-item-value-v">{{statisticsObj.askCnt }}</text>
<text class="analytics-item-value-unit"></text>
</view>
</view>
<view class="analytics-item">
<view class="analytics-item-title">
陪练次数
</view>
<view class="analytics-item-value">
<text class="analytics-item-value-v">{{statisticsObj.partnerCnt }}</text>
<text class="analytics-item-value-unit"></text>
</view>
</view>
<view class="analytics-item">
<view class="analytics-item-title">
陪练通过率
</view>
<view class="analytics-item-value">
<text class="analytics-item-value-v">{{statisticsObj.partnerRate }}</text>
<text class="analytics-item-value-unit">%</text>
</view>
</view>
</view>
</view>
<view class="evaluation-box dens" v-if="loading">
<CommonLoading color="#06f" class="loading-box" />
</view>
<view class="evaluation-box dens" v-if="!loading">
<view class="dens-item" v-for="(item, index) in dimesionList">
<view :class="['item-title', index%2===0?'study1-tip': 'study2-tip']">{{item.title}}</view>
<view :class="['item-cont ', index%2===0?'study1-bg': 'study2-bg']">{{item.content}}</view>
</view>
</view>
<view class="evaluation-box study" v-if="loading">
<CommonLoading color="#06f" class="loading-box" />
</view>
<view class="evaluation-box study" v-if="!loading">
<view class="study-item" v-for="(item, index) in studyList">
<view :class="['item-title', index%2===0?'study1-tip': 'study2-tip']">{{item.title}}</view>
<view :class="['item-cont ', index%2===0?'study1-bg': 'study2-bg']">{{item.content}}</view>
</view>
</view>
<view class="evaluation-box study1" v-if="loading">
<CommonLoading color="#06f" class="loading-box" />
</view>
<view class="evaluation-box study1" v-if="!loading">
<view class="study1-item">
<view class="item-cont study3-bg">{{suggestObj.content}}</view>
</view>
</view>
<view class="evaluation-box course" v-if="loading">
<CommonLoading color="#06f" class="loading-box" />
</view>
<view class="evaluation-box course" v-if="!loading">
<CourseItem v-for="item in courseList" :itemInfo="item" @clickItem="toCourseDetail(item)" />
<view class="empty-box" v-if="courseList.length === 0">暂无数据</view>
</view>
</view>
</view>
</template>
<script setup>
import common from '@/common/common'
import {
queryStudyStatistics,
queryRecmdCrsInfo,
queryDimesionInfo,
queryHabitInfo,
querySuggestInfo,
} from '@/api/analytics.js'
import {
onMounted,
ref,
computed,
} from 'vue'
import CourseItem from '@/components/course-item/course-item.vue'
const tabList = ref([{
name: '本月',
value: '01'
},
{
name: '本年',
value: '02'
},
{
name: '累计',
value: '03'
},
{
name: '自定义',
value: '04'
}
])
const courseList = ref([])
const currentTab = ref(0)
const activeTabVal = ref('01')
const daterange = ref(['', ''])
const dateinputRef = ref()
const maxDate = computed(() => {
let _date = new Date()
let _year = _date.getFullYear()
let _month = _date.getMonth() + 1
let _day = new Date(_year, _month, 0).getDate()
return `${_year}-${_month < 10 ? '0' + _month: _month}-${_day < 10 ? '0' + _day: _day}`
})
const calendarRef = ref()
const statisticsObj = ref({
crsStdyTmLen: 0,
completeTaskCnt: 0,
completedExamCnt: 0,
passExamRate: 0,
partnerCnt: 0,
askCnt: 0,
completeCrsCnt: 0,
crsCompleteRate: 0,
partnerRate: 0
})
const dimesionList = ref([])
const studyList = ref([])
const suggestObj = ref({
content: ''
})
// const openCalendar = () => {
// calendarRef.value.open()
// }
// const calendarConfirm = (data) => {
// console.log('data:', data)
// currentTab.value = -1
// }
const changeDate = (date) => {
if (date.length > 0) {
if (!!date[0]) {
getInfoNow({
dateType: '04',
startDate: date[0],
endDate: date[1],
})
}
}
}
const loading = ref(false)
const getInfoNow = async (params) => {
loading.value = true
try {
let _res1 = await queryStudyStatistics(params)
statisticsObj.value = {
..._res1.body
}
let _res2 = await queryDimesionInfo(params)
dimesionList.value = [..._res2.body]
let _res3 = await queryHabitInfo(params)
studyList.value = [..._res3.body]
let _res4 = await querySuggestInfo(params)
if (_res4.body && _res4.body.length > 0) {
suggestObj.value = {
..._res4.body[0]
}
}
} catch {} finally {
loading.value = false
}
}
// 跳转 课程详情
const toCourseDetail = item => {
common.navigateTo('/pages/courseDetail/index?crsNum=' + item.crsNum + '&crsId=' + item.crsId)
}
const changeTab = (info) => {
activeTabVal.value = info.value
if (info.value === '04') {
dateinputRef.value && dateinputRef.value?.clear()
daterange.value = ['', '']
} else {
getInfoNow({
dateType: activeTabVal.value
})
}
}
const getCrsListNow = () => {
queryRecmdCrsInfo().then(res => {
console.log(res)
courseList.value = res.body || []
})
}
onMounted(() => {
getCrsListNow()
getInfoNow({
dateType: activeTabVal.value
})
})
</script>
<style lang="scss" scoped>
.my_tabs {
// 解决这个圆角的图片,开穿透
:deep(.uv-tabs__wrapper__nav__line) {
background: linear-gradient(90deg, #06f 0%, rgb(0, 159, 255) 50%, rgb(255, 255, 255) 100%) !important;
}
:deep(.uv-tabs__wrapper__nav__item) {
padding: 0 30rpx !important;
}
}
.study-analytics {
position: relative;
background-color: #ddeafa;
.cont-body {
position: absolute;
width: 100vw;
height: 100vh;
padding: var(--status-bar-height) 20rpx 24rpx;
// padding: 280rpx 20rpx 24rpx;
overflow-x: hidden;
overflow-y: auto;
box-sizing: border-box;
background-image: url(@/static/images/analytics/bg1.png);
background-repeat: no-repeat;
background-size: 100% auto;
background-position: top center;
background-attachment: local;
.top-box {
height: 280rpx;
width: 100%;
// position: fixed;
// float: left;
// margin-top: var(--status-bar-height);
background-image: url(@/static/images/analytics/title.png);
background-repeat: no-repeat;
background-position: 26rpx 125rpx;
background-size: 277rpx 135rpx;
.top-title {
height: 90rpx;
// background-color: red;
text-align: center;
font-size: 33rpx;
padding: 10rpx 0rpx 0;
line-height: 46rpx;
box-sizing: border-box;
font-weight: 500;
color: #000;
position: relative;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
.back-icon {
position: absolute;
width: 40rpx;
height: 40rpx;
cursor: pointer;
left: 30rpx;
top: 33rpx;
transform: translateY(-50%);
}
.back-icon::before,
.back-icon::after {
content: '';
position: absolute;
transition: all 0.3s ease;
}
.back-icon::after {
top: 25%;
left: 25%;
width: 40%;
height: 40%;
border-top: 4rpx solid #000;
border-left: 4rpx solid #000;
transform: rotate(-45deg);
}
}
}
.analytics-box {
min-height: 490rpx;
border-radius: 15rpx;
// overflow: hidden;
background: url(@/static/images/analytics/bg2.png) no-repeat;
background-size: cover;
background-position: center;
position: relative;
margin-bottom: 24rpx;
backdrop-filter: blur(10rpx);
z-index: 1;
&.loading-box-cont{
background: #ddeafa;
&::before {
content: '';
display: block;
position: absolute;
width: 100%;
height: 100%;
border-radius: 15rpx;
z-index: 2;
// #ddeafa
background: url(@/static/images/analytics/bg2.png) no-repeat;
background-size: cover;
background-position: center;
backdrop-filter: blur(10rpx);
}
}
.loading-box {
position: absolute;
z-index: 3;
top: 120rpx;
left: 50%;
transform: translateX(-50%);
}
.calendar-box {
position: absolute;
right: 25rpx;
top: 25rpx;
color: #06f;
padding-left: 40rpx;
background: url(@/static/images/analytics/date-icon.png) no-repeat;
background-size: 30rpx 30rpx;
background-position: left center;
font-size: 29rpx;
}
.tabs-box {
width: 600rpx;
}
.analytics-items {
padding-top: 20rpx;
padding-bottom: 20rpx;
width: 100%;
overflow: hidden;
position: relative;
.analytics-item {
padding-bottom: 12rpx;
box-sizing: border-box;
width: 33.3%;
float: left;
text-align: center;
height: 128rpx;
display: flex;
flex-direction: column;
justify-content: space-around;
&-title {
font-size: 25rpx;
color: #666;
}
&-value {
font-size: 54rpx;
font-weight: 600;
&-v {
font-family: DINAlternate, DINAlternate;
}
&-unit {
font-size: 28rpx;
margin-left: 8rpx;
}
}
}
}
}
.evaluation-box {
background-color: rgba(255, 255, 255, .8);
background-repeat: no-repeat;
background-size: auto 78rpx;
background-position: 26rpx 26rpx;
padding: 80rpx 26rpx 24rpx;
border-radius: 15rpx;
margin-bottom: 24rpx;
position: relative;
min-height: 200rpx;
.loading-box {
position: absolute;
top: 120rpx;
left: 50%;
transform: translateX(-50%);
}
&::before {
content: '';
display: block;
position: absolute;
width: 208rpx;
height: 76rpx;
position: absolute;
left: -7rpx;
top: -12rpx;
background-size: cover;
}
&.dens {
&::before {
background-image: url(@/static/images/analytics/weidu-1.png);
}
}
&.study {
&::before {
background-image: url(@/static/images/analytics/study1-1.png)
}
}
&.study1 {
&::before {
background-image: url(@/static/images/analytics/study-1.png);
}
}
&.course {
&::before {
background-image: url(@/static/images/analytics/course-1.png);
}
}
// .dens-item {
// padding: 23rpx 0;
// .item-title{
// line-height: 40rpx;
// position: relative;
// padding: 8rpx 46rpx 8rpx 26rpx;
// line-height: 42rpx;
// font-size: 29rpx;
// margin-bottom: 16rpx;
// &::before{
// content: '';
// display: block;
// background-color: #000;
// width: 12rpx;
// height: 12rpx;
// border-radius: 50%;
// position: absolute;
// left: 0rpx;
// top: 22rpx;
// }
// }
// .item-cont {
// font-size: 28rpx;
// line-height: 50rpx;
// }
// }
.study-item,
.study1-item,
.dens-item {
padding: 12rpx 0;
.item-title {
line-height: 40rpx;
position: relative;
padding: 8rpx 46rpx 8rpx 40rpx;
line-height: 42rpx;
font-size: 29rpx;
margin-bottom: 16rpx;
&::before {
content: '';
display: block;
// background-color: #000;
width: 28rpx;
height: 28rpx;
// border-radius: 50%;
position: absolute;
left: 0rpx;
top: 14rpx;
background-image: url(@/static/images/analytics/den-tips.png);
background-repeat: no-repeat;
background-size: 100% 100%;
}
&.study2-tip {
&::before {
background-image: url(@/static/images/analytics/den-tips2.png);
}
}
&.bg-tm {
padding-left: 54rpx;
background-image: url(@/static/images/analytics/time-icon.png);
background-repeat: no-repeat;
background-size: 38rpx 40rpx;
background-position: left center;
&::before {
display: none;
}
}
&.bg-know {
padding-left: 54rpx;
background-image: url(@/static/images/analytics/knowledge.png);
background-repeat: no-repeat;
background-size: 38rpx 40rpx;
background-position: left center;
&::before {
display: none;
}
}
}
.item-cont {
font-size: 28rpx;
line-height: 50rpx;
&.study1-bg {
background-color: #e4efff;
border-radius: 15rpx;
padding: 12rpx 24rpx;
// width: calc(100% - 54rpx);
// margin-left: 54rpx;
}
&.study2-bg {
background-color: #f6f0df;
border-radius: 15rpx;
padding: 12rpx 24rpx;
// width: calc(100% - 54rpx);
// margin-left: 54rpx;
}
&.study3-bg {
background-color: #e4efff;
border-radius: 15rpx;
padding: 12rpx 24rpx;
// width: calc(100% - 54rpx);
// margin-left: 54rpx;
}
}
}
.empty-box {
text-align: center;
font-size: 25rpx;
color: #ccc;
}
}
}
}
.date-input {
.custom-datepicker {
width: 500rpx;
position: relative;
&::after {
content: '';
position: absolute;
top: 50%;
right: 0;
width: 10rpx;
height: 10rpx;
border-top: 3rpx solid #999;
border-left: 3rpx solid #999;
transform: rotate(135deg) translateY(50%);
}
::v-deep .uni-date-x {
background-color: rgba(255, 255, 255, 0);
width: 500rpx;
}
::v-deep .uni-date-x--border {
border: 0;
width: 500rpx;
}
::v-deep .range-separator {
font-size: 13px;
}
}
}
</style>