2025-10-31 17:47:51 +08:00
|
|
|
|
<template>
|
2025-11-04 14:31:37 +08:00
|
|
|
|
<AppLayout :title="title" :show-bg-image="false">
|
|
|
|
|
|
<!-- <template #headerleft>
|
2025-10-31 17:47:51 +08:00
|
|
|
|
<view class="btnback">
|
|
|
|
|
|
<image src="@/static/icon/back.png" @click="navBack"></image>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</template> -->
|
2025-11-04 14:31:37 +08:00
|
|
|
|
<view class="video-box">
|
|
|
|
|
|
<view class="video-detail-container">
|
|
|
|
|
|
<!-- 视频播放组件 -->
|
|
|
|
|
|
<view class="video-wrapper">
|
2025-11-04 19:47:59 +08:00
|
|
|
|
<video id="myVideo" :src="videoInfo.currentUrl" :poster="trainVideoImgUrl+ videoInfo.cover" @seeked="onSeeked"
|
|
|
|
|
|
enable-danmu controls style="width: 100%;" @pause="onPause" @timeupdate="onTimeupdate" @ended="onEnded"></video>
|
2025-11-04 14:31:37 +08:00
|
|
|
|
</view>
|
2025-10-31 17:47:51 +08:00
|
|
|
|
</view>
|
2025-11-04 14:31:37 +08:00
|
|
|
|
<view class="video-info" :style="getItemBackgroundStyle('video-bj2.png')">
|
|
|
|
|
|
<view class="video-title">
|
|
|
|
|
|
<text>视频详情</text>
|
|
|
|
|
|
<view class="title-line"></view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
<view class="info-detail">
|
|
|
|
|
|
<view class="info-left">
|
|
|
|
|
|
<view class="info-item">
|
2025-11-04 19:47:59 +08:00
|
|
|
|
<image class="icon-img" :src="baseUrl+'/train/zs.png'" mode=""></image>
|
2025-11-04 14:31:37 +08:00
|
|
|
|
<view class="info-label">
|
|
|
|
|
|
分类:
|
|
|
|
|
|
</view>
|
2025-11-04 19:47:59 +08:00
|
|
|
|
<view class="info-value" :data-content="getCategoryLabelByValue(videoInfo.category)">
|
2025-11-04 14:31:37 +08:00
|
|
|
|
{{getCategoryLabelByValue(videoInfo.category)}}
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
<view class="info-right">
|
|
|
|
|
|
<view class="info-item">
|
2025-11-04 19:47:59 +08:00
|
|
|
|
<image class="icon-img" :src="baseUrl+'/train/zs.png'" mode=""></image>
|
2025-11-04 14:31:37 +08:00
|
|
|
|
<view class="info-label">
|
|
|
|
|
|
等级:
|
|
|
|
|
|
</view>
|
|
|
|
|
|
<view class="info-value">
|
|
|
|
|
|
{{getLevelLabelByValue(videoInfo.level)}}
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
<view class="info-item">
|
2025-11-04 19:47:59 +08:00
|
|
|
|
<image class="icon-img" :src="baseUrl+'/train/zs.png'" mode=""></image>
|
2025-11-04 14:31:37 +08:00
|
|
|
|
<view class="info-label">
|
|
|
|
|
|
讲师:
|
|
|
|
|
|
</view>
|
|
|
|
|
|
<view class="info-value">
|
|
|
|
|
|
{{videoInfo.teacherName}}
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
<view class="info-detail">
|
|
|
|
|
|
<view class="info-left">
|
|
|
|
|
|
<view class="info-item">
|
2025-11-04 19:47:59 +08:00
|
|
|
|
<image class="icon-img" :src="baseUrl+'/train/zs.png'" mode=""></image>
|
2025-11-04 14:31:37 +08:00
|
|
|
|
<view class="info-label">
|
|
|
|
|
|
时长:
|
|
|
|
|
|
</view>
|
|
|
|
|
|
<view class="info-value">
|
|
|
|
|
|
{{videoInfo.hour}}分钟
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
<view class="info-right">
|
|
|
|
|
|
<view class="info-item">
|
2025-11-04 19:47:59 +08:00
|
|
|
|
<image class="icon-img" :src="baseUrl+'/train/zs.png'" mode=""></image>
|
2025-11-04 14:31:37 +08:00
|
|
|
|
<view class="info-label">
|
|
|
|
|
|
发布时间:
|
|
|
|
|
|
</view>
|
|
|
|
|
|
<view class="info-value">
|
|
|
|
|
|
{{videoInfo.uploadTime}}
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
<view class="video-intro" :style="videoIntroBackgroundStyle('video-bj.png')">
|
|
|
|
|
|
<view class="intro-title">
|
2025-11-04 19:47:59 +08:00
|
|
|
|
<image class="intro-img1" :src="baseUrl+'/train/video-kc.png'" mode=""></image>
|
2025-11-04 14:31:37 +08:00
|
|
|
|
<view class="title1">
|
|
|
|
|
|
课程
|
|
|
|
|
|
</view>
|
|
|
|
|
|
<view class="title2">
|
|
|
|
|
|
简介
|
|
|
|
|
|
</view>
|
2025-11-04 19:47:59 +08:00
|
|
|
|
<image class="intro-img2" :src="baseUrl+'/train/video-sc.png'" mode=""></image>
|
2025-11-04 14:31:37 +08:00
|
|
|
|
</view>
|
|
|
|
|
|
<view class="intro-content">
|
|
|
|
|
|
{{videoInfo.introduce}}
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
<view class="video-title">
|
|
|
|
|
|
<text>学习进度</text>
|
|
|
|
|
|
<view class="title-line"></view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
<view class="progress-box">
|
|
|
|
|
|
<progress :percent="videoInfo.percentage" activeColor="#30A0FF" backgroundColor="#B0DBFF" stroke-width="6" border-radius="10" />
|
|
|
|
|
|
<view class="progress-info">
|
|
|
|
|
|
<view class="progress-left">
|
|
|
|
|
|
已观看
|
|
|
|
|
|
</view>
|
|
|
|
|
|
<view class="progress-right">
|
|
|
|
|
|
{{videoInfo.percentage}}%
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
2025-11-04 19:47:59 +08:00
|
|
|
|
<view class="video-title" v-if="videoInfo.trainClassList && videoInfo.trainClassList.length>0">
|
|
|
|
|
|
<text>课程章节</text>
|
|
|
|
|
|
<view class="title-line"></view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
<view class="chapter-box" v-if="videoInfo.trainClassList && videoInfo.trainClassList.length>0">
|
|
|
|
|
|
<view class="chapter-item" :class="{ active: currentChapter === index}" @click="chapterChange(item,index)" v-for="(item ,index) in videoInfo.trainClassList" :key="index">
|
|
|
|
|
|
<view class="chapter-left">
|
|
|
|
|
|
<view class="chapter-number">
|
|
|
|
|
|
{{ index + 1 }}
|
|
|
|
|
|
</view>
|
|
|
|
|
|
<view class="chapter-info">
|
|
|
|
|
|
{{item.className}}
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
<view class="chapter-icon" v-if="currentChapter === index">
|
|
|
|
|
|
<uni-icons type="videocam" size="24"></uni-icons>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
2025-11-04 14:31:37 +08:00
|
|
|
|
</view>
|
|
|
|
|
|
|
|
|
|
|
|
</view>
|
|
|
|
|
|
|
2025-10-31 17:47:51 +08:00
|
|
|
|
</AppLayout>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<script setup>
|
2025-11-04 14:31:37 +08:00
|
|
|
|
import { inject, reactive,ref, onMounted, onUnmounted, nextTick } from 'vue';
|
2025-11-04 19:47:59 +08:00
|
|
|
|
import { onLoad,onHide,onUnload } from '@dcloudio/uni-app';
|
2025-10-31 17:47:51 +08:00
|
|
|
|
const { $api, navTo, navBack } = inject('globalFunction');
|
2025-11-04 14:31:37 +08:00
|
|
|
|
import config from "@/config.js"
|
2025-10-31 17:47:51 +08:00
|
|
|
|
|
|
|
|
|
|
// state
|
|
|
|
|
|
const title = ref('');
|
2025-11-04 14:31:37 +08:00
|
|
|
|
const videoId=ref('')
|
|
|
|
|
|
const userId=ref('')
|
|
|
|
|
|
const videoInfo=ref({})
|
|
|
|
|
|
const trainVideoImgUrl=config.trainVideoImgUrl
|
|
|
|
|
|
const categories=ref([])
|
|
|
|
|
|
const levalLabels=ref([])
|
2025-11-04 19:47:59 +08:00
|
|
|
|
const latestTime = ref(0)
|
|
|
|
|
|
const totalTime=ref(0)
|
2025-11-04 14:31:37 +08:00
|
|
|
|
const baseUrl = config.imgBaseUrl
|
2025-11-04 19:47:59 +08:00
|
|
|
|
const pageEnterTime = ref(0)
|
|
|
|
|
|
const currentChapter = ref(0)
|
2025-10-31 17:47:51 +08:00
|
|
|
|
const getItemBackgroundStyle = (imageName) => ({
|
2025-11-04 14:31:37 +08:00
|
|
|
|
backgroundImage: `url(${baseUrl}/train/${imageName})`,
|
|
|
|
|
|
backgroundSize: '100% 100%', // 覆盖整个容器
|
2025-10-31 17:47:51 +08:00
|
|
|
|
backgroundPosition: 'center', // 居中
|
|
|
|
|
|
backgroundRepeat: 'no-repeat'
|
|
|
|
|
|
});
|
2025-11-04 14:31:37 +08:00
|
|
|
|
const videoIntroBackgroundStyle = (imageName) => ({
|
|
|
|
|
|
backgroundImage: `url(${baseUrl}/train/${imageName})`,
|
|
|
|
|
|
backgroundSize: '100% 100%', // 覆盖整个容器
|
|
|
|
|
|
backgroundPosition: 'center', // 居中
|
|
|
|
|
|
backgroundRepeat: 'no-repeat'
|
|
|
|
|
|
});
|
|
|
|
|
|
const params = reactive({
|
2025-11-04 19:47:59 +08:00
|
|
|
|
videoId: '',
|
|
|
|
|
|
userId: ''
|
|
|
|
|
|
})
|
2025-11-04 14:31:37 +08:00
|
|
|
|
onLoad((options) => {
|
2025-11-04 19:47:59 +08:00
|
|
|
|
getHeart()
|
|
|
|
|
|
pageEnterTime.value = Date.now() // 记录毫秒时间戳
|
2025-11-04 14:31:37 +08:00
|
|
|
|
videoId.value=options.id
|
|
|
|
|
|
getDictionary()
|
|
|
|
|
|
});
|
2025-11-04 19:47:59 +08:00
|
|
|
|
onHide(() => {
|
|
|
|
|
|
updateVideoInfo() // 用缓存值,不要调 getCurrentTime
|
|
|
|
|
|
reportPageDuration()
|
|
|
|
|
|
})
|
|
|
|
|
|
onUnload(() => {
|
|
|
|
|
|
updateVideoInfo()
|
|
|
|
|
|
reportPageDuration()
|
|
|
|
|
|
})
|
2025-11-04 14:31:37 +08:00
|
|
|
|
function getData() {
|
|
|
|
|
|
params.videoId=videoId.value
|
|
|
|
|
|
params.userId=userId.value
|
|
|
|
|
|
$api.myRequest('/train/public/trainVideo/updateWatchCount',{videoId:videoId.value}).then((resData) => {
|
|
|
|
|
|
console.log("视频更新次数成功")
|
|
|
|
|
|
});
|
|
|
|
|
|
let header={
|
|
|
|
|
|
'Authorization':uni.getStorageSync('Padmin-Token'),
|
|
|
|
|
|
'Content-Type': "application/x-www-form-urlencoded"
|
|
|
|
|
|
}
|
|
|
|
|
|
$api.myRequest('/train/public/trainVideo/model', params,'post',9100,header).then((resData) => {
|
|
|
|
|
|
videoInfo.value=resData
|
|
|
|
|
|
videoInfo.value.currentUrl=trainVideoImgUrl+videoInfo.value.trainClassList[0].url
|
2025-11-04 19:47:59 +08:00
|
|
|
|
videoInfo.value.percentage=((videoInfo.value.process/(videoInfo.value.hour*60))*100).toFixed(2)
|
|
|
|
|
|
videoInfo.value.uploadTime=videoInfo.value.uploadTime.split(' ')[0]
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
function getHeart() {
|
|
|
|
|
|
const raw = uni.getStorageSync("Padmin-Token");
|
|
|
|
|
|
const token = typeof raw === "string" ? raw.trim() : "";
|
|
|
|
|
|
const headers = token ? { Authorization: raw.startsWith("Bearer ") ? raw : `Bearer ${token}` }: {}
|
|
|
|
|
|
$api.myRequest("/dashboard/auth/heart", {}, "POST", 10100, headers).then((resData) => {
|
|
|
|
|
|
if (resData.code == 200) {
|
|
|
|
|
|
getUserInfo();
|
|
|
|
|
|
} else {
|
|
|
|
|
|
navTo('/packageB/login')
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
function getUserInfo(){
|
|
|
|
|
|
let header={
|
|
|
|
|
|
'Authorization':uni.getStorageSync('Padmin-Token')
|
|
|
|
|
|
}
|
|
|
|
|
|
$api.myRequest('/system/user/login/user/info', {},'get',10100,header).then((resData) => {
|
|
|
|
|
|
userId.value=resData.info.userId
|
|
|
|
|
|
getData()
|
2025-11-04 14:31:37 +08:00
|
|
|
|
});
|
2025-10-31 17:47:51 +08:00
|
|
|
|
}
|
2025-11-04 14:31:37 +08:00
|
|
|
|
function getDictionary(){
|
2025-11-04 19:47:59 +08:00
|
|
|
|
$api.myRequest('/system/public/dict/data/type/question_classification', {},'get',9100).then((resData) => {
|
2025-11-04 14:31:37 +08:00
|
|
|
|
categories.value=resData.data
|
|
|
|
|
|
});
|
|
|
|
|
|
$api.myRequest('/system/public/dict/data/type/train_level', {},'get',9100).then((resData) => {
|
|
|
|
|
|
levalLabels.value=resData.data
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
function getCategoryLabelByValue(value) {
|
|
|
|
|
|
if (!Array.isArray(categories.value)) {
|
|
|
|
|
|
console.warn('categories 不是数组:', categories.value)
|
|
|
|
|
|
return ''
|
|
|
|
|
|
}
|
|
|
|
|
|
const item = categories.value.find(item => item.dictValue === String(value))
|
|
|
|
|
|
return item ? item.dictLabel : '暂无分类'
|
|
|
|
|
|
}
|
|
|
|
|
|
function getLevelLabelByValue(value) {
|
|
|
|
|
|
if (!Array.isArray(levalLabels.value)) {
|
|
|
|
|
|
console.warn('levalLabels 不是数组:', levalLabels.value)
|
|
|
|
|
|
return ''
|
|
|
|
|
|
}
|
|
|
|
|
|
const item = levalLabels.value.find(item => item.dictValue === String(value))
|
|
|
|
|
|
return item ? item.dictLabel : '暂无等级'
|
2025-10-31 17:47:51 +08:00
|
|
|
|
}
|
2025-11-04 19:47:59 +08:00
|
|
|
|
function onPause(e){
|
|
|
|
|
|
updateVideoInfo()
|
|
|
|
|
|
}
|
|
|
|
|
|
function onEnded(e){
|
|
|
|
|
|
updateVideoInfo()
|
|
|
|
|
|
}
|
|
|
|
|
|
function onTimeupdate(e){
|
|
|
|
|
|
latestTime.value = e.detail.currentTime
|
|
|
|
|
|
}
|
|
|
|
|
|
function onSeeked(){
|
|
|
|
|
|
updateVideoInfo()
|
|
|
|
|
|
}
|
|
|
|
|
|
// 更新播放时长
|
|
|
|
|
|
function updateVideoInfo(){
|
|
|
|
|
|
totalTime.value=0
|
|
|
|
|
|
if(currentChapter.value>0){
|
|
|
|
|
|
videoInfo.value.trainClassList.forEach((item,index)=>{
|
|
|
|
|
|
if(index<currentChapter.value){
|
|
|
|
|
|
totalTime.value+=Number(item.hour)
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
totalTime.value+=Number(latestTime.value)
|
|
|
|
|
|
let paramsData={
|
|
|
|
|
|
userId:userId.value,
|
|
|
|
|
|
videoId:videoId.value,
|
|
|
|
|
|
collect:'',
|
|
|
|
|
|
process:Math.floor(Number(totalTime.value))
|
|
|
|
|
|
}
|
|
|
|
|
|
let header={
|
|
|
|
|
|
'Authorization':uni.getStorageSync('Padmin-Token'),
|
|
|
|
|
|
'Content-Type': "application/x-www-form-urlencoded"
|
|
|
|
|
|
}
|
|
|
|
|
|
if(videoInfo.value.isCollect===null && videoInfo.value.process ===null){
|
|
|
|
|
|
$api.myRequest('/train/public/videoUser/add', paramsData,'post',9100,header).then((resData) => {
|
|
|
|
|
|
console.log("视频播放时长更新成功")
|
|
|
|
|
|
});
|
|
|
|
|
|
}else{
|
|
|
|
|
|
$api.myRequest('/train/public/videoUser/update', paramsData,'post',9100,header).then((resData) => {
|
|
|
|
|
|
console.log("视频播放时长更新成功")
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
// 计算并上报停留时长
|
|
|
|
|
|
function reportPageDuration() {
|
|
|
|
|
|
const duration = Date.now() - pageEnterTime.value // 毫秒
|
|
|
|
|
|
const durationSeconds = Math.floor(duration / 1000) // 转为秒
|
|
|
|
|
|
if (durationSeconds > 0) {
|
|
|
|
|
|
let paramsData={
|
|
|
|
|
|
type:'video',
|
|
|
|
|
|
hour:durationSeconds,
|
|
|
|
|
|
videoId:videoId.value,
|
|
|
|
|
|
userId:userId.value,
|
|
|
|
|
|
title:videoInfo.value.videoTitle
|
|
|
|
|
|
}
|
|
|
|
|
|
let header={
|
|
|
|
|
|
'Authorization':uni.getStorageSync('Padmin-Token'),
|
|
|
|
|
|
'Content-Type': "application/x-www-form-urlencoded"
|
|
|
|
|
|
}
|
|
|
|
|
|
$api.myRequest('/train/public/userHour/add', paramsData,'post',9100,header).then((resData) => {
|
|
|
|
|
|
console.log("学习时长更新成功")
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
function chapterChange(video,index){
|
|
|
|
|
|
currentChapter.value=index
|
|
|
|
|
|
videoInfo.value.currentUrl=trainVideoImgUrl+video.url
|
|
|
|
|
|
}
|
2025-11-04 14:31:37 +08:00
|
|
|
|
onUnmounted(() => {
|
2025-11-04 19:47:59 +08:00
|
|
|
|
|
2025-11-04 14:31:37 +08:00
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2025-10-31 17:47:51 +08:00
|
|
|
|
|
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
<style lang="stylus" scoped>
|
2025-11-04 14:31:37 +08:00
|
|
|
|
.video-box{
|
|
|
|
|
|
padding: 10rpx 20rpx;
|
2025-10-31 17:47:51 +08:00
|
|
|
|
}
|
2025-11-04 14:31:37 +08:00
|
|
|
|
.video-detail-container{
|
2025-10-31 17:47:51 +08:00
|
|
|
|
width: 100%;
|
2025-11-04 14:31:37 +08:00
|
|
|
|
background-color: #ffffff;
|
2025-10-31 17:47:51 +08:00
|
|
|
|
}
|
2025-11-04 14:31:37 +08:00
|
|
|
|
|
|
|
|
|
|
.video-wrapper{
|
|
|
|
|
|
position: relative;
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
background-color: #000000;
|
|
|
|
|
|
height: auto;
|
2025-10-31 17:47:51 +08:00
|
|
|
|
}
|
2025-11-04 14:31:37 +08:00
|
|
|
|
.video-info{
|
|
|
|
|
|
width:100%;
|
|
|
|
|
|
margin-top:30rpx;
|
|
|
|
|
|
padding: 20rpx 30rpx;
|
|
|
|
|
|
box-sizing: border-box;
|
2025-10-31 17:47:51 +08:00
|
|
|
|
}
|
2025-11-04 14:31:37 +08:00
|
|
|
|
.video-title{
|
|
|
|
|
|
font-size: 32rpx;
|
2025-10-31 17:47:51 +08:00
|
|
|
|
color: #404040;
|
2025-11-04 14:31:37 +08:00
|
|
|
|
font-weight: bold;
|
2025-10-31 17:47:51 +08:00
|
|
|
|
position: relative;
|
2025-11-04 14:31:37 +08:00
|
|
|
|
margin-bottom: 40rpx;
|
2025-10-31 17:47:51 +08:00
|
|
|
|
}
|
|
|
|
|
|
.title-line{
|
|
|
|
|
|
position: absolute;
|
|
|
|
|
|
bottom: -10rpx;
|
|
|
|
|
|
left: 36rpx;
|
2025-11-04 14:31:37 +08:00
|
|
|
|
width: 60rpx;
|
2025-10-31 17:47:51 +08:00
|
|
|
|
height: 8rpx;
|
|
|
|
|
|
background: linear-gradient(90deg, #FFAD58 0%, #FF7A5B 100%);
|
2025-11-04 14:31:37 +08:00
|
|
|
|
border-radius: 4rpx;
|
2025-10-31 17:47:51 +08:00
|
|
|
|
}
|
2025-11-04 14:31:37 +08:00
|
|
|
|
.info-detail{
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-wrap: nowrap;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
margin-bottom: 30rpx;
|
2025-10-31 17:47:51 +08:00
|
|
|
|
}
|
2025-11-04 14:31:37 +08:00
|
|
|
|
.info-left{
|
|
|
|
|
|
width: 35%;
|
2025-10-31 17:47:51 +08:00
|
|
|
|
}
|
2025-11-04 14:31:37 +08:00
|
|
|
|
.info-right{
|
|
|
|
|
|
width: 65%;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: space-between;
|
2025-10-31 17:47:51 +08:00
|
|
|
|
}
|
2025-11-04 14:31:37 +08:00
|
|
|
|
.info-item{
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
2025-10-31 17:47:51 +08:00
|
|
|
|
}
|
2025-11-04 14:31:37 +08:00
|
|
|
|
.icon-img{
|
|
|
|
|
|
width: 24rpx;
|
|
|
|
|
|
height: 28rpx;
|
2025-11-04 19:47:59 +08:00
|
|
|
|
margin-right: 4rpx;
|
2025-10-31 17:47:51 +08:00
|
|
|
|
}
|
2025-11-04 14:31:37 +08:00
|
|
|
|
.info-label{
|
|
|
|
|
|
font-weight: bold;
|
|
|
|
|
|
font-size: 28rpx;
|
|
|
|
|
|
color: #0068C8;
|
2025-11-04 19:47:59 +08:00
|
|
|
|
min-width: 86rpx;
|
2025-11-04 14:31:37 +08:00
|
|
|
|
}
|
|
|
|
|
|
.info-value{
|
|
|
|
|
|
font-size: 28rpx;
|
|
|
|
|
|
color: #404040;
|
2025-11-04 19:47:59 +08:00
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
text-overflow: ellipsis;
|
|
|
|
|
|
white-space: nowrap;
|
2025-11-04 14:31:37 +08:00
|
|
|
|
}
|
|
|
|
|
|
.video-intro{
|
|
|
|
|
|
padding: 20rpx;
|
|
|
|
|
|
margin-bottom: 30rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
.intro-title{
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
margin-bottom: 10rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
.intro-img1{
|
|
|
|
|
|
width: 36rpx;
|
|
|
|
|
|
height: 30rpx;
|
|
|
|
|
|
margin-right: 10rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
.intro-img2{
|
|
|
|
|
|
width: 30rpx;
|
|
|
|
|
|
height: 30rpx;
|
|
|
|
|
|
margin-left: 10rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
.title1{
|
2025-10-31 17:47:51 +08:00
|
|
|
|
font-size: 26rpx;
|
2025-11-04 14:31:37 +08:00
|
|
|
|
font-weight: bold;
|
|
|
|
|
|
color: #333333;
|
2025-10-31 17:47:51 +08:00
|
|
|
|
}
|
2025-11-04 14:31:37 +08:00
|
|
|
|
.title2{
|
|
|
|
|
|
color: #077DF5;
|
|
|
|
|
|
font-size: 26rpx;
|
|
|
|
|
|
font-weight: bold;
|
|
|
|
|
|
}
|
|
|
|
|
|
.intro-content{
|
|
|
|
|
|
font-size: 24rpx;
|
|
|
|
|
|
color: #333333;
|
|
|
|
|
|
line-height: 1.5;
|
|
|
|
|
|
}
|
|
|
|
|
|
.progress-box{
|
|
|
|
|
|
background: linear-gradient(0deg, #DFEDFF 0%, #F8FCFF 100%);
|
|
|
|
|
|
box-shadow: 0px 0px 10px 0px rgba(0,48,107,0.1);
|
|
|
|
|
|
border-radius: 16rpx;
|
|
|
|
|
|
padding: 40rpx 30rpx;
|
2025-11-04 19:47:59 +08:00
|
|
|
|
margin-bottom: 30rpx;
|
2025-11-04 14:31:37 +08:00
|
|
|
|
}
|
|
|
|
|
|
.progress-info{
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
|
font-size: 24rpx;
|
|
|
|
|
|
color: #333333;
|
|
|
|
|
|
margin-top: 20rpx;
|
2025-10-31 17:47:51 +08:00
|
|
|
|
}
|
2025-11-04 19:47:59 +08:00
|
|
|
|
.chapter-box{
|
|
|
|
|
|
background: linear-gradient(0deg, #DFEDFF 0%, #F8FCFF 100%);
|
|
|
|
|
|
box-shadow: 0px 0px 10px 0px rgba(0,48,107,0.1);
|
|
|
|
|
|
border-radius: 16rpx;
|
|
|
|
|
|
padding: 40rpx 30rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
.chapter-item{
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
|
padding: 16rpx 24rpx;
|
|
|
|
|
|
border-radius: 12rpx;
|
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
transition: all 0.3s ease;
|
|
|
|
|
|
margin-bottom: 16rpx;
|
|
|
|
|
|
border: 2rpx solid #F0F0F0;
|
|
|
|
|
|
background: #F9F9F9;
|
|
|
|
|
|
}
|
|
|
|
|
|
.chapter-item.active {
|
|
|
|
|
|
background-color: #e6f7ff;
|
|
|
|
|
|
color: #409EFF;
|
|
|
|
|
|
}
|
|
|
|
|
|
.chapter-left{
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
}
|
|
|
|
|
|
.chapter-number{
|
|
|
|
|
|
width: 50rpx;
|
|
|
|
|
|
height: 50rpx;
|
|
|
|
|
|
background: #cccccc;
|
|
|
|
|
|
border-radius: 50%;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
font-size: 24rpx;
|
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
|
margin-right: 16rpx;
|
|
|
|
|
|
color: #fff;
|
|
|
|
|
|
}
|
|
|
|
|
|
.chapter-item.active .chapter-number {
|
|
|
|
|
|
background: #409EFF;
|
|
|
|
|
|
color: #fff;
|
|
|
|
|
|
}
|
|
|
|
|
|
.chapter-info {
|
|
|
|
|
|
font-size: 28rpx;
|
|
|
|
|
|
color: #303133;
|
|
|
|
|
|
}
|
2025-10-31 17:47:51 +08:00
|
|
|
|
</style>
|