feat: 课程详情页面线性进度展示
This commit is contained in:
3
components.d.ts
vendored
3
components.d.ts
vendored
@ -19,6 +19,9 @@ declare module 'vue' {
|
|||||||
WdInput: typeof import('wot-design-uni/components/wd-input/wd-input.vue')['default']
|
WdInput: typeof import('wot-design-uni/components/wd-input/wd-input.vue')['default']
|
||||||
WdProgress: typeof import('wot-design-uni/components/wd-progress/wd-progress.vue')['default']
|
WdProgress: typeof import('wot-design-uni/components/wd-progress/wd-progress.vue')['default']
|
||||||
WdStatusTip: typeof import('wot-design-uni/components/wd-status-tip/wd-status-tip.vue')['default']
|
WdStatusTip: typeof import('wot-design-uni/components/wd-status-tip/wd-status-tip.vue')['default']
|
||||||
|
WdSte: typeof import('wot-design-uni/components/wd-ste/wd-ste.vue')['default']
|
||||||
|
WdStep: typeof import('wot-design-uni/components/wd-step/wd-step.vue')['default']
|
||||||
|
WdSteps: typeof import('wot-design-uni/components/wd-steps/wd-steps.vue')['default']
|
||||||
WdTabbar: typeof import('wot-design-uni/components/wd-tabbar/wd-tabbar.vue')['default']
|
WdTabbar: typeof import('wot-design-uni/components/wd-tabbar/wd-tabbar.vue')['default']
|
||||||
WdTabbarItem: typeof import('wot-design-uni/components/wd-tabbar-item/wd-tabbar-item.vue')['default']
|
WdTabbarItem: typeof import('wot-design-uni/components/wd-tabbar-item/wd-tabbar-item.vue')['default']
|
||||||
WdToast: typeof import('wot-design-uni/components/wd-toast/wd-toast.vue')['default']
|
WdToast: typeof import('wot-design-uni/components/wd-toast/wd-toast.vue')['default']
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
import BussApi from '@/api/BussApi';
|
import BussApi from '@/api/BussApi';
|
||||||
import pageWrapper from '@/components/page-wrapper.vue';
|
import pageWrapper from '@/components/page-wrapper.vue';
|
||||||
import type { Lesson } from '@/types/api/lesson';
|
import type { Lesson } from '@/types/api/lesson';
|
||||||
|
import { calcLessonProgress } from '@/utils/lesson';
|
||||||
import { useRouter } from 'uni-mini-router';
|
import { useRouter } from 'uni-mini-router';
|
||||||
import { onMounted, ref } from 'vue';
|
import { onMounted, ref } from 'vue';
|
||||||
import { useToast } from 'wot-design-uni';
|
import { useToast } from 'wot-design-uni';
|
||||||
@ -10,7 +11,7 @@ const toast = useToast()
|
|||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
|
||||||
const expandedCourse = ref(['lesson'])
|
const expandedCourse = ref(['lesson'])
|
||||||
const groupLessons = ref<{ [key: string]: Lesson[] }>({})
|
const groupedLessons = ref<{ [key: string]: Lesson[] }>({})
|
||||||
|
|
||||||
const openLessonDetail = (courseId: number) => {
|
const openLessonDetail = (courseId: number) => {
|
||||||
router.push({
|
router.push({
|
||||||
@ -21,13 +22,6 @@ const openLessonDetail = (courseId: number) => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const calcProgress = (course: Lesson) => {
|
|
||||||
const total = 4
|
|
||||||
let finished = 0
|
|
||||||
if (!!course?.script_file) finished++
|
|
||||||
return Math.floor((finished / total) * 100)
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
toast.loading({
|
toast.loading({
|
||||||
msg: '加载中...'
|
msg: '加载中...'
|
||||||
@ -41,7 +35,9 @@ onMounted(() => {
|
|||||||
acc[cur.m_lesson_name].push(cur)
|
acc[cur.m_lesson_name].push(cur)
|
||||||
return acc
|
return acc
|
||||||
}, {})
|
}, {})
|
||||||
groupLessons.value = groupData
|
groupedLessons.value = groupData
|
||||||
|
}).catch(err => {
|
||||||
|
toast.error({ msg: err.message })
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
@ -50,19 +46,21 @@ onMounted(() => {
|
|||||||
<page-wrapper>
|
<page-wrapper>
|
||||||
<div>
|
<div>
|
||||||
<wd-collapse v-model="expandedCourse">
|
<wd-collapse v-model="expandedCourse">
|
||||||
<wd-status-tip v-if="Object.keys(groupLessons).length === 0" image="content" tip="没有课程" />
|
<wd-status-tip v-if="Object.keys(groupedLessons).length === 0" image="content" tip="没有微课" />
|
||||||
<wd-collapse-item v-else v-for="(lessons, lessonName) in groupLessons" :title="`${lessonName || '无标题课程'}`"
|
<wd-collapse-item v-else v-for="(courses, courseName) in groupedLessons" :title="`${courseName || '无标题课程'}`"
|
||||||
:name="`${lessonName}`" :key="lessonName">
|
:name="`${courseName}`" :key="courseName">
|
||||||
<div class="w-full">
|
<div class="w-full">
|
||||||
<wd-status-tip v-if="lessons.length === 0" image="content" tip="没有微课视频" />
|
<wd-status-tip v-if="courses.length === 0" image="content" tip="没有课程小节" />
|
||||||
<div v-else v-for="(course, i) in lessons" :key="i" @click="openLessonDetail(course.id)"
|
<div v-else v-for="(lesson, i) in courses" :key="i" @click="openLessonDetail(lesson.id)"
|
||||||
class="w-full py-2 flex justify-between items-center gap-20 border-b border-b-solid border-neutral-200 last:border-b-0 first:pt-0 last:pb-0">
|
class="w-full py-2 flex justify-between items-center gap-20 border-b border-b-solid border-neutral-200 last:border-b-0 first:pt-0 last:pb-0">
|
||||||
<div class="flex items-center gap-1">
|
<div class="flex items-center gap-1">
|
||||||
<wd-icon :name="calcProgress(course) === 100 ? 'check-circle' : 'hourglass'" size="16px"></wd-icon>
|
<wd-icon
|
||||||
<span>{{ course.course_name || '无标题视频' }}</span>
|
:name="calcLessonProgress(lesson) === 100 ? 'check-circle' : (calcLessonProgress(lesson) === 0 ? 'circle1' : 'hourglass')"
|
||||||
|
size="16px"></wd-icon>
|
||||||
|
<span>{{ lesson.course_name || '无标题视频' }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="w-32 flex items-center gap-3">
|
<div class="w-32 flex items-center gap-3">
|
||||||
<wd-progress :percentage="calcProgress(course)" />
|
<wd-progress :percentage="calcLessonProgress(lesson)" hide-text />
|
||||||
<wd-icon name="arrow-right" size="16px" class="op-85"></wd-icon>
|
<wd-icon name="arrow-right" size="16px" class="op-85"></wd-icon>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,14 +1,17 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import BussApi from '@/api/BussApi';
|
import BussApi from '@/api/BussApi';
|
||||||
import type { Lesson } from '@/types/api/lesson';
|
import type { Lesson } from '@/types/api/lesson';
|
||||||
|
import { calcLessonProgress, extractLessonStage, getLessonSteps } from '@/utils/lesson';
|
||||||
import { useRoute } from 'uni-mini-router';
|
import { useRoute } from 'uni-mini-router';
|
||||||
import { onMounted, ref } from 'vue';
|
import { computed, onMounted, ref } from 'vue';
|
||||||
import { useToast } from 'wot-design-uni';
|
import { useToast } from 'wot-design-uni';
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const toast = useToast()
|
const toast = useToast()
|
||||||
|
|
||||||
const course = ref<Lesson | null>(null)
|
const lesson = ref<Lesson | null>(null)
|
||||||
|
const lessonSteps = computed(() => lesson.value ? getLessonSteps(lesson.value) : [])
|
||||||
|
const lessonStages = computed(() => lesson.value ? extractLessonStage(lesson.value) : null)
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
if (!route.params?.courseId) {
|
if (!route.params?.courseId) {
|
||||||
@ -22,17 +25,22 @@ onMounted(() => {
|
|||||||
})
|
})
|
||||||
BussApi.course(route.params.courseId).then(courseData => {
|
BussApi.course(route.params.courseId).then(courseData => {
|
||||||
toast.close()
|
toast.close()
|
||||||
course.value = courseData
|
lesson.value = courseData
|
||||||
|
}).catch(err => {
|
||||||
|
toast.error({ msg: err.message })
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<pre>{{ JSON.stringify(course, null, 2) }}</pre>
|
<div class="p-4">
|
||||||
|
<wd-steps :active="lessonStages?.step || 0" vertical>
|
||||||
|
<wd-step v-for="(step, index) in lessonSteps" :key="index" :title="step.title"
|
||||||
|
:description="step.description" />
|
||||||
|
</wd-steps>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped></style>
|
||||||
|
|
||||||
</style>
|
|
||||||
|
48
src/utils/lesson.ts
Normal file
48
src/utils/lesson.ts
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
import type { Lesson } from "@/types/api/lesson";
|
||||||
|
|
||||||
|
export const extractLessonStage = (lesson: Lesson) => {
|
||||||
|
const stages = {
|
||||||
|
script_upload: !!lesson?.script_upload_time,
|
||||||
|
script_confirm: !!lesson?.script_confirm_time,
|
||||||
|
video_capture: !!lesson?.video_capture_time,
|
||||||
|
post_production: !!lesson?.video_confirm_time,
|
||||||
|
step: 0,
|
||||||
|
};
|
||||||
|
stages.step = Object.values(stages).filter((v) => v).length;
|
||||||
|
return stages;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const calcLessonProgress = (lesson: Lesson, total: number = 4) => {
|
||||||
|
const progress = extractLessonStage(lesson);
|
||||||
|
return Math.floor((progress.step / total) * 100);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getLessonSteps = (lesson: Lesson) => {
|
||||||
|
const progress = extractLessonStage(lesson);
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
title: progress.script_upload ? "脚本文件上传" : undefined,
|
||||||
|
description: progress.script_upload
|
||||||
|
? `已于 ${lesson.script_upload_time} 完成上传`
|
||||||
|
: "脚本文件上传",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: progress.script_confirm ? "脚本文件确认" : undefined,
|
||||||
|
description: progress.script_confirm
|
||||||
|
? `已于 ${lesson.script_confirm_time} 确认`
|
||||||
|
: "脚本文件确认",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: progress.video_capture ? "视频拍摄和上传" : undefined,
|
||||||
|
description: progress.video_capture
|
||||||
|
? `已于 ${lesson.video_capture_time} 完成上传`
|
||||||
|
: "视频拍摄上传",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: progress.post_production ? "视频后期制作" : undefined,
|
||||||
|
description: progress.post_production
|
||||||
|
? `已于 ${lesson.video_confirm_time} 完成上传`
|
||||||
|
: "视频后期制作",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
};
|
Reference in New Issue
Block a user