fix:优化上课备课路由死循环

This commit is contained in:
zhonghua 2026-03-20 10:07:17 +08:00
parent ed9371b21f
commit 8502d8b2d3
4 changed files with 59 additions and 126 deletions

View File

@ -340,7 +340,7 @@ const prepareCourse = (course: any) => {
message.warning('课程信息异常,无法进入备课');
return;
}
router.push(`/teacher/courses/${id}/prepare`);
router.push(`/teacher/courses/${id}`);
};
onMounted(() => {

View File

@ -4,7 +4,9 @@
<div class="course-header">
<div class="header-left">
<a-button type="text" @click="goBackToDetail" class="back-btn">
<template #icon><LeftOutlined /></template>
<template #icon>
<LeftOutlined />
</template>
返回
</a-button>
<div class="course-cover" v-if="course.coverImagePath">
@ -33,15 +35,21 @@
</div>
<div class="header-right">
<a-button @click="showScheduleModal" :disabled="!selectedClassId" class="schedule-btn">
<template #icon><CalendarOutlined /></template>
<template #icon>
<CalendarOutlined />
</template>
预约上课
</a-button>
<a-button type="primary" @click="startTeaching" :disabled="!selectedClassId" class="start-btn">
<template #icon><PlayCircleOutlined /></template>
<template #icon>
<PlayCircleOutlined />
</template>
开始上课
</a-button>
<a-button @click="handleExit" class="exit-btn">
<template #icon><CloseOutlined /></template>
<template #icon>
<CloseOutlined />
</template>
退出备课
</a-button>
</div>
@ -52,40 +60,23 @@
<a-row :gutter="20">
<!-- 左侧课程导航 -->
<a-col :span="6">
<PrepareNavigation
:course="course"
:lessons="lessons"
:selected-section="selectedSection"
:selected-lesson-id="selectedLessonId"
:selected-item="selectedItem"
@select-section="handleSelectSection"
@select-lesson="handleSelectLesson"
@select-item="handleSelectItem"
/>
<PrepareNavigation :course="course" :lessons="lessons" :selected-section="selectedSection"
:selected-lesson-id="selectedLessonId" :selected-item="selectedItem" @select-section="handleSelectSection"
@select-lesson="handleSelectLesson" @select-item="handleSelectItem" />
</a-col>
<!-- 右侧内容预览 -->
<a-col :span="18">
<PreparePreview
:course="course"
:selected-type="selectedSection"
:selected-lesson="selectedLesson"
:selected-item="selectedItem"
:selected-step="selectedStep"
@select-step="handleSelectStep"
@preview-resource="handlePreviewResource"
/>
<PreparePreview :course="course" :selected-type="selectedSection" :selected-lesson="selectedLesson"
:selected-item="selectedItem" :selected-step="selectedStep" @select-step="handleSelectStep"
@preview-resource="handlePreviewResource" />
</a-col>
</a-row>
</div>
</a-spin>
<!-- 文件预览弹窗 -->
<FilePreviewModal
v-model:open="previewModalVisible"
:file-url="previewFileUrl"
:file-name="previewFileName"
/>
<FilePreviewModal v-model:open="previewModalVisible" :file-url="previewFileUrl" :file-name="previewFileName" />
<!-- 预约上课弹窗四步流程与课表页一致 -->
<TeacherCreateScheduleModal ref="scheduleModalRef" @success="onScheduleSuccess" />
@ -414,13 +405,7 @@ const handleExit = () => {
};
const goBackToDetail = () => {
// 使 ID courseId /courses/undefined
const id = route.params.id || courseId.value;
if (id && id !== 'undefined' && id !== 'null') {
router.push(`/teacher/courses/${id}`);
} else {
router.push('/teacher/courses');
}
router.back();
};
onMounted(() => {

View File

@ -4,7 +4,9 @@
<div class="lesson-toolbar">
<div class="toolbar-left">
<a-button class="exit-btn" @click="exitLesson">
<template #icon><ArrowLeftOutlined /></template>
<template #icon>
<ArrowLeftOutlined />
</template>
退出上课
</a-button>
<div class="course-info" v-if="course.name">
@ -28,11 +30,14 @@
<StepBackwardOutlined /> 上一步
</a-button>
<a-button type="primary" class="next-btn" @click="nextStep" :disabled="isLastStepOfLastLesson">
下一步 <StepForwardOutlined />
下一步
<StepForwardOutlined />
</a-button>
</a-button-group>
<a-button type="primary" class="broadcast-btn" @click="openBroadcastMode">
<template #icon><ExpandOutlined /></template>
<template #icon>
<ExpandOutlined />
</template>
展播模式
</a-button>
<a-button class="toolbar-icon-btn" @click="showNotesDrawer = true">
@ -47,15 +52,9 @@
<!-- 课程进度条多课程时显示 -->
<div v-if="lessons.length > 1" class="course-progress-bar">
<a-steps :current="currentLessonIndex" size="small" class="course-steps">
<a-step
v-for="(lesson, index) in lessons"
:key="lesson.id"
:title="getLessonShortName(lesson)"
:status="getLessonStatus(index)"
:disabled="index > currentLessonIndex"
@click="handleLessonClick(index)"
class="clickable-step"
/>
<a-step v-for="(lesson, index) in lessons" :key="lesson.id" :title="getLessonShortName(lesson)"
:status="getLessonStatus(index)" :disabled="index > currentLessonIndex" @click="handleLessonClick(index)"
class="clickable-step" />
</a-steps>
</div>
@ -69,12 +68,7 @@
环节 {{ currentStepIndex + 1 }}/{{ currentLesson?.steps?.length || 0 }}
</span>
</div>
<a-progress
:percent="stepProgressPercent"
:show-info="false"
stroke-color="#FF8C42"
class="step-progress"
/>
<a-progress :percent="stepProgressPercent" :show-info="false" stroke-color="#FF8C42" class="step-progress" />
</div>
<!-- 主内容区 -->
@ -88,16 +82,10 @@
</div>
<div class="step-list">
<div
v-for="(step, index) in currentLesson?.steps || []"
:key="step.id"
class="step-item"
:class="{
active: currentStepIndex === index,
completed: index < currentStepIndex
}"
@click="handleStepClick(index)"
>
<div v-for="(step, index) in currentLesson?.steps || []" :key="step.id" class="step-item" :class="{
active: currentStepIndex === index,
completed: index < currentStepIndex
}" @click="handleStepClick(index)">
<div class="step-number">
<CheckOutlined v-if="index < currentStepIndex" />
<span v-else>{{ index + 1 }}</span>
@ -168,12 +156,8 @@
<div v-if="currentStep.images?.length" class="resource-group">
<span class="resource-type-label">图片</span>
<div class="resource-items">
<div
v-for="(img, idx) in currentStep.images"
:key="idx"
class="resource-item"
@click="previewResource(img, 'image')"
>
<div v-for="(img, idx) in currentStep.images" :key="idx" class="resource-item"
@click="previewResource(img, 'image')">
<PictureOutlined /> {{ img.name || `图片${idx + 1}` }}
</div>
</div>
@ -183,12 +167,8 @@
<div v-if="currentStep.videos?.length" class="resource-group">
<span class="resource-type-label">视频</span>
<div class="resource-items">
<div
v-for="(vid, idx) in currentStep.videos"
:key="idx"
class="resource-item"
@click="previewResource(vid, 'video')"
>
<div v-for="(vid, idx) in currentStep.videos" :key="idx" class="resource-item"
@click="previewResource(vid, 'video')">
<VideoCameraOutlined /> {{ vid.name || `视频${idx + 1}` }}
</div>
</div>
@ -198,12 +178,8 @@
<div v-if="currentStep.audioList?.length" class="resource-group">
<span class="resource-type-label">音频</span>
<div class="resource-items">
<div
v-for="(aud, idx) in currentStep.audioList"
:key="idx"
class="resource-item"
@click="previewResource(aud, 'audio')"
>
<div v-for="(aud, idx) in currentStep.audioList" :key="idx" class="resource-item"
@click="previewResource(aud, 'audio')">
<AudioOutlined /> {{ aud.name || `音频${idx + 1}` }}
</div>
</div>
@ -213,12 +189,8 @@
<div v-if="currentStep.pptFiles?.length" class="resource-group">
<span class="resource-type-label">课件</span>
<div class="resource-items">
<div
v-for="(ppt, idx) in currentStep.pptFiles"
:key="idx"
class="resource-item"
@click="previewResource(ppt, 'ppt')"
>
<div v-for="(ppt, idx) in currentStep.pptFiles" :key="idx" class="resource-item"
@click="previewResource(ppt, 'ppt')">
<FilePptOutlined /> {{ ppt.name || `课件${idx + 1}` }}
</div>
</div>
@ -228,12 +200,8 @@
<div v-if="currentStep.documents?.length" class="resource-group">
<span class="resource-type-label">文档</span>
<div class="resource-items">
<div
v-for="(doc, idx) in currentStep.documents"
:key="idx"
class="resource-item"
@click="previewResource(doc, 'document')"
>
<div v-for="(doc, idx) in currentStep.documents" :key="idx" class="resource-item"
@click="previewResource(doc, 'document')">
<FileTextOutlined /> {{ doc.name || `文档${idx + 1}` }}
</div>
</div>
@ -258,12 +226,8 @@
</div>
<div class="panel-body">
<div v-if="courseResources.length > 0" class="materials-list">
<div
v-for="item in courseResources"
:key="item.id"
class="material-item"
@click="previewCourseResource(item)"
>
<div v-for="item in courseResources" :key="item.id" class="material-item"
@click="previewCourseResource(item)">
<div class="material-icon" :class="item.type">
<VideoCameraOutlined v-if="item.type === 'video'" />
<AudioOutlined v-else-if="item.type === 'audio'" />
@ -315,12 +279,7 @@
</div>
<div class="panel-body">
<div v-if="stepMaterials.length > 0" class="materials-list">
<div
v-for="item in stepMaterials"
:key="item.id"
class="material-item"
@click="previewMaterial(item)"
>
<div v-for="item in stepMaterials" :key="item.id" class="material-item" @click="previewMaterial(item)">
<div class="material-icon" :class="item.type">
<PlayCircleOutlined v-if="item.type === '视频'" />
<SoundOutlined v-else-if="item.type === '音频'" />
@ -389,12 +348,7 @@
</a-modal>
<!-- 课堂记录抽屉 -->
<a-drawer
v-model:open="showNotesDrawer"
title="课堂记录"
placement="right"
width="400"
>
<a-drawer v-model:open="showNotesDrawer" title="课堂记录" placement="right" width="400">
<a-form layout="vertical">
<a-form-item label="完成情况">
<a-radio-group v-model:value="lessonRecord.completion">
@ -410,11 +364,8 @@
<a-rate v-model:value="lessonRecord.participationRating" />
</a-form-item>
<a-form-item label="完成备注">
<a-textarea
v-model:value="lessonRecord.completionNote"
placeholder="记录课程完成情况、学生表现等..."
:auto-size="{ minRows: 6, maxRows: 10 }"
/>
<a-textarea v-model:value="lessonRecord.completionNote" placeholder="记录课程完成情况、学生表现等..."
:auto-size="{ minRows: 6, maxRows: 10 }" />
</a-form-item>
<a-button type="primary" block size="large" @click="saveLessonRecord">
保存并结束
@ -423,11 +374,7 @@
</a-drawer>
<!-- 文件预览弹窗 -->
<FilePreviewModal
v-model:open="previewModalVisible"
:file-url="previewFileUrl"
:file-name="previewFileName"
/>
<FilePreviewModal v-model:open="previewModalVisible" :file-url="previewFileUrl" :file-name="previewFileName" />
</div>
</template>
@ -919,7 +866,7 @@ const exitLesson = () => {
okText: '确认退出',
cancelText: '继续上课',
onOk: () => {
router.push(`/teacher/courses/${course.value.id}/prepare`);
router.back();
},
});
};
@ -936,7 +883,7 @@ const saveLessonRecord = async () => {
await clearProgress();
message.success('课程记录已保存');
showNotesDrawer.value = false;
router.push(`/teacher/courses/${course.value.id}/prepare`);
router.back();
} catch (error: any) {
message.error(error.message || '保存记录失败');
}
@ -1212,6 +1159,7 @@ onUnmounted(() => {
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.06);
.course-steps {
:deep(.ant-steps-item-process),
:deep(.ant-steps-item-finish) {
cursor: pointer;

View File

@ -334,14 +334,14 @@ const handleStartLessonFromSchedule = async (schedule: TeacherSchedule) => {
try {
const lesson = await startLessonFromSchedule(schedule.id);
message.success('课堂创建成功');
router.push(`/teacher/lessons/${lesson.id}`);
router.push({ path: `/teacher/lessons/${lesson.id}`, query: { from: 'schedule' } });
} catch (error) {
message.error('创建课堂失败');
}
};
const goToLesson = (lessonId: number) => {
router.push(`/teacher/lessons/${lessonId}`);
router.push({ path: `/teacher/lessons/${lessonId}`, query: { from: 'schedule' } });
};
//