feat: 课程进度管理优化

课程列表和详情:
1. 为课程添加了徽标展示
2. 优化了课程完成、未完成、未开始的展示
3. 更新了进度查看界面的icon

进度管理:
1. 优化了课程进度详细界面的进度处理逻辑关系
2. 修复了老师过滤器切换后标签颜色不更新的问题

代码优化:
1. 统一了角色和岗位的注释风格
This commit is contained in:
huertian 2025-01-07 17:13:58 +08:00
parent 2b671bdf78
commit 0dc5269893
5 changed files with 75 additions and 26 deletions

View File

@ -36,7 +36,7 @@ const nameLabelIconMap = {
progress: { progress: {
title: '进度管理', title: '进度管理',
icon: 'transfer', icon: 'transfer',
roles: ['teacher', 'sysadmin'] as const // roles: ['teacher', 'sysadmin'] as const //
// roles: ['teacher', 'admin', 'sysadmin'] as const // // roles: ['teacher', 'admin', 'sysadmin'] as const //
}, },
my: { my: {

View File

@ -3,7 +3,7 @@ import BussApi from '@/api/BussApi';
import pageWrapper from '@/components/page-wrapper.vue'; import pageWrapper from '@/components/page-wrapper.vue';
import { useUser } from '@/stores/useUser'; import { useUser } from '@/stores/useUser';
import type { LessonTask } from '@/types/api/lesson'; import type { LessonTask } from '@/types/api/lesson';
import { calcLessonProgress, getLessonSteps } from '@/utils/lesson'; import { calcLessonProgress, getLessonRole } from '@/utils/lesson';
import { onPageShow, onLoad, onPullDownRefresh } from '@dcloudio/uni-app'; import { onPageShow, onLoad, onPullDownRefresh } from '@dcloudio/uni-app';
import { useRouter } from 'uni-mini-router'; import { useRouter } from 'uni-mini-router';
import { onMounted, ref, watch } from 'vue'; import { onMounted, ref, watch } from 'vue';
@ -73,6 +73,7 @@ const openLessonDetail = (courseId: number) => {
}) })
} }
const loadLessons = async () => { const loadLessons = async () => {
if (!user.userinfo) { if (!user.userinfo) {
toast.error({ msg: '请先登录' }) toast.error({ msg: '请先登录' })
@ -156,7 +157,9 @@ const loadLessons = async () => {
} }
watch(teacherFilterValue, () => { watch(teacherFilterValue, () => {
loadLessons() loadLessons().then(() => {
refreshKey.value++
})
}) })
onPageShow(() => { onPageShow(() => {
@ -207,7 +210,6 @@ onLoad(() => {
<wd-sort-button title="进度" allow-reset v-model="value" @change="handleChange" /> <wd-sort-button title="进度" allow-reset v-model="value" @change="handleChange" />
</view> </view>
</div> </div>
<wd-collapse v-model="expandedCourse"> <wd-collapse v-model="expandedCourse">
<wd-status-tip v-if="Object.keys(groupedLessons).length === 0" <wd-status-tip v-if="Object.keys(groupedLessons).length === 0"
image="https://registry.npmmirror.com/wot-design-uni-assets/*/files/content.png" tip="当前账号没有分配微课" /> image="https://registry.npmmirror.com/wot-design-uni-assets/*/files/content.png" tip="当前账号没有分配微课" />
@ -216,11 +218,14 @@ onLoad(() => {
<template #title="{ expanded, disabled, isFirst }"> <template #title="{ expanded, disabled, isFirst }">
<div class="w-full flex justify-between items-center"> <div class="w-full flex justify-between items-center">
<div class="flex flex-col gap-1"> <div class="flex flex-col gap-1">
<wd-badge is-dot hidden> <!-- <wd-badge is-dot :right="230" :hidden="!courses.some(lesson =>
<p class="pt-1"> (getLessonRole(lesson) === 'teacher' && user.hasRole('teacher')) ||
{{ courseName || '无标题课程' }} (getLessonRole(lesson) === 'admin' && user.hasRole('admin'))
</p> )"> -->
</wd-badge> <p class="pt-1">
{{ courseName || '无标题课程' }}
</p>
<!-- </wd-badge> -->
<div class="flex items-center gap-1"> <div class="flex items-center gap-1">
<wd-tag v-if="(() => { <wd-tag v-if="(() => {
const hasCompleted = courses.some(lesson => calcLessonProgress(lesson) === 100) const hasCompleted = courses.some(lesson => calcLessonProgress(lesson) === 100)
@ -246,9 +251,15 @@ onLoad(() => {
}) })
return allNotStarted return allNotStarted
})()" custom-class="w-fit" type="default">未开始</wd-tag> })()" custom-class="w-fit" type="default">未开始</wd-tag>
<wd-tag custom-class="op-60" plain> <wd-tag custom-class="op-60" type="warning">
{{ courses.length }}节微课 {{ courses.length }}节微课
</wd-tag> </wd-tag>
<wd-tag custom-class="op-60" plain>
已完成{{ courses.filter(lesson => calcLessonProgress(lesson) === 100).length }}·
进行中{{ courses.filter(lesson => calcLessonProgress(lesson) !== 0 && calcLessonProgress(lesson)
!== 100).length }}·
未开始{{ courses.filter(lesson => calcLessonProgress(lesson) === 0).length }}
</wd-tag>
</div> </div>
</div> </div>
<div class="flex items-center gap-4"> <div class="flex items-center gap-4">
@ -287,11 +298,12 @@ onLoad(() => {
<div class="flex items-center gap-1 self-center"> <div class="flex items-center gap-1 self-center">
<div> <div>
<div v-if="calcLessonProgress(lesson) === 100" class="i-tabler-circle-check text-emerald"></div> <div v-if="calcLessonProgress(lesson) === 100" class="i-tabler-circle-check text-emerald"></div>
<div v-else-if="calcLessonProgress(lesson) === 0" class="i-tabler-circle-dashed text-neutral"> <div v-else-if="calcLessonProgress(lesson) === 0" class="i-tabler-progress-x text-neutral">
</div> </div>
<div v-else class="i-tabler-hourglass-empty text-blue"></div> <div v-else class="i-tabler-hourglass-empty text-blue"></div>
</div> </div>
<wd-badge is-dot :hidden="true"> <wd-badge is-dot
:hidden="!(getLessonRole(lesson) === 'teacher' && user.hasRole('teacher') || getLessonRole(lesson) === 'admin' && user.hasRole('admin'))">
<span>{{ lesson.microLessonName || '无标题微课' }}</span> <span>{{ lesson.microLessonName || '无标题微课' }}</span>
</wd-badge> </wd-badge>
</div> </div>

View File

@ -4,10 +4,10 @@ import { useDayjs } from '@/composables/useDayjs';
import { useTabbar } from '@/stores/useTabbar'; import { useTabbar } from '@/stores/useTabbar';
import { useUser } from '@/stores/useUser' import { useUser } from '@/stores/useUser'
import type { LessonTask } from '@/types/api/lesson'; import type { LessonTask } from '@/types/api/lesson';
import { calcLessonProgress, extractLessonStage, getLessonSteps } from '@/utils/lesson'; import { calcLessonProgress, extractLessonStage, getLessonSteps, getLessonRole } from '@/utils/lesson';
import { useRoute, useRouter } from 'uni-mini-router'; import { useRoute, useRouter } from 'uni-mini-router';
import { computed, onMounted, ref } from 'vue'; import { computed, onMounted, ref } from 'vue';
import { useToast } from 'wot-design-uni'; import { useToast, useMessage } from 'wot-design-uni';
import { onPullDownRefresh, onLoad } from '@dcloudio/uni-app' import { onPullDownRefresh, onLoad } from '@dcloudio/uni-app'
const route = useRoute() const route = useRoute()
@ -16,6 +16,8 @@ const tabbar = useTabbar()
const toast = useToast() const toast = useToast()
const dayjs = useDayjs() const dayjs = useDayjs()
const user = useUser() const user = useUser()
const message = useMessage()
const lesson = ref<LessonTask | null>(null) const lesson = ref<LessonTask | null>(null)
const lessonSteps = computed(() => lesson.value ? getLessonSteps(lesson.value) : []) const lessonSteps = computed(() => lesson.value ? getLessonSteps(lesson.value) : [])
@ -23,14 +25,27 @@ const lessonStages = computed(() => lesson.value ? extractLessonStage(lesson.val
const lessonProgress = computed(() => lesson.value ? calcLessonProgress(lesson.value) : 0) const lessonProgress = computed(() => lesson.value ? calcLessonProgress(lesson.value) : 0)
const goProgress = (lessonId: number) => { const goProgress = (lessonId: number) => {
if (!lesson.value) return
const role = getLessonRole(lesson.value)
const canAccess = (role === 'teacher' && user.hasRole('teacher')) ||
(role === 'admin' && user.hasRole('admin'))
if (!canAccess) {
message.alert({
title: '请耐心等待',
msg: user.hasRole('teacher') ? '课程顾问正在处理进度' : '老师正在处理进度'
})
return
}
router.replaceAll({ router.replaceAll({
name: 'progress', name: 'progress',
params: { params: {
courseName: `${lesson.value?.courseName}`, courseName: `${lesson.value.courseName}`,
lessonId: `${lessonId}` lessonId: `${lessonId}`
} }
}) })
// tabbar.activeTab = 'progress'
} }
const loadLesson = async () => { const loadLesson = async () => {
@ -84,6 +99,7 @@ onLoad(() => {
<template> <template>
<div> <div>
<wd-message-box></wd-message-box>
<div <div
:class="`pattern p-4 flex flex-col gap-6 relative ${lessonProgress === 100 ? 'bg-emerald' : (lessonProgress === 0 ? 'bg-neutral' : 'bg-blue')}`"> :class="`pattern p-4 flex flex-col gap-6 relative ${lessonProgress === 100 ? 'bg-emerald' : (lessonProgress === 0 ? 'bg-neutral' : 'bg-blue')}`">
<div class="flex flex-col gap-0"> <div class="flex flex-col gap-0">
@ -98,7 +114,7 @@ onLoad(() => {
</p> </p>
<div class="absolute text-white top-2 right-2 op-35 text-18"> <div class="absolute text-white top-2 right-2 op-35 text-18">
<div v-if="lessonProgress === 100" class="i-tabler-circle-check"></div> <div v-if="lessonProgress === 100" class="i-tabler-circle-check"></div>
<div v-else-if="lessonProgress === 0" class="i-tabler-circle-dashed"></div> <div v-else-if="lessonProgress === 0" class="i-tabler-progress-x"></div>
<div v-else class="i-tabler-hourglass-empty"></div> <div v-else class="i-tabler-hourglass-empty"></div>
</div> </div>
</div> </div>
@ -108,10 +124,13 @@ onLoad(() => {
:description="step.description" /> :description="step.description" />
</wd-steps> </wd-steps>
</div> </div>
<div class="px-4 pt-2"> <div v-if="lessonProgress !== 100" class="px-4 pt-2 ">
<wd-button v-if="!user.hasRole('liaison')" type="primary" :round="false" plain block <wd-button v-if="!user.hasRole('liaison')" type="primary" :round="false" plain block
@click="goProgress(lesson?.id!)">进度处理</wd-button> @click="goProgress(lesson?.id!)">进度处理</wd-button>
</div> </div>
<div v-else class="px-4 pt-2">
<wd-button v-if="!user.hasRole('liaison')" disabled type="success" :round="false" plain block>已完成</wd-button>
</div>
</div> </div>
</template> </template>

View File

@ -20,18 +20,18 @@ export interface Authority {
// 角色枚举 // 角色枚举
export enum Roles { export enum Roles {
TEACHER = 1, // 校方教师 TEACHER = 1, // 教师
GENERAL_ADMIN = 2, // 公司课程顾问 GENERAL_ADMIN = 2, // 管理员
CONTACTOR = 3, // 校方项目负责人 CONTACTOR = 3, // 项目负责人
SYSTEM_ADMIN = 4 // 公司系统管理员 SYSTEM_ADMIN = 4 // 系统管理员
} }
// 岗位枚举 // 岗位枚举
export enum Jobs { export enum Jobs {
COURSE_TEACHER = 1, // 课程制作教师 COURSE_TEACHER = 1, // 课程教师
PROJECT_MANAGER = 2, // 课程审核人员 PROJECT_MANAGER = 2, // 项目经理
COURSE_CONTACTOR = 3, // 校方项目负责人 COURSE_CONTACTOR = 3, // 课程负责人
SYSTEM_MANAGER = 4 // 公司系统管理 SYSTEM_MANAGER = 4 // 系统管理
} }
// 用户状态枚举 // 用户状态枚举

View File

@ -15,6 +15,24 @@ export const extractLessonStage = (lesson: LessonTask) => {
return stages; return stages;
}; };
export const getLessonRole = (lesson: LessonTask): 'teacher' | 'admin' | '' => {
if (!lesson) return '';
switch (lesson.progressStatus) {
case 0: // 未开始
case 2: // 脚本审核
case 4: // 视频拍摄与制作
return 'teacher';
case 1: // 脚本制作
case 3: // 脚本确认
return 'admin';
default:
return '';
}
};
export const calcLessonProgress = (lesson: LessonTask) => { export const calcLessonProgress = (lesson: LessonTask) => {
if (!lesson) return 0; if (!lesson) return 0;