feat: 根据排课lessonType直接进入子课程,子课程结束即上课结束;右侧添加课程类型展示
- 后端:LessonDetailResponse 新增 lessonType,从 SchedulePlan 读取 - 前端:根据 lessonType 直接进入对应子课程,子课程结束即上课结束 - 前端:右侧面板课程资源上方添加课程类型标签展示 Made-with: Cursor
This commit is contained in:
parent
5a05af18dd
commit
f90037dd17
@ -16,4 +16,6 @@ export interface LessonDetailResponse {
|
|||||||
lesson?: LessonResponse;
|
lesson?: LessonResponse;
|
||||||
course?: CourseResponse;
|
course?: CourseResponse;
|
||||||
class?: ClassResponse;
|
class?: ClassResponse;
|
||||||
|
/** 排课选择的课程类型(子课程模式时用于直接进入对应子课程) */
|
||||||
|
lessonType?: string;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -49,10 +49,10 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 课程进度条(多课程时显示) -->
|
<!-- 课程进度条(多课程时显示,子课程模式不显示) -->
|
||||||
<div v-if="lessons.length > 1" class="course-progress-bar">
|
<div v-if="displayLessons.length > 1" class="course-progress-bar">
|
||||||
<a-steps :current="currentLessonIndex" size="small" class="course-steps">
|
<a-steps :current="currentLessonIndex" size="small" class="course-steps">
|
||||||
<a-step v-for="(lesson, index) in lessons" :key="lesson.id" :title="getLessonShortName(lesson)"
|
<a-step v-for="(lesson, index) in displayLessons" :key="lesson.id" :title="getLessonShortName(lesson)"
|
||||||
:status="getLessonStatus(index)" :disabled="index > currentLessonIndex" @click="handleLessonClick(index)"
|
:status="getLessonStatus(index)" :disabled="index > currentLessonIndex" @click="handleLessonClick(index)"
|
||||||
class="clickable-step" />
|
class="clickable-step" />
|
||||||
</a-steps>
|
</a-steps>
|
||||||
@ -218,6 +218,19 @@
|
|||||||
|
|
||||||
<!-- 右侧:工具面板 -->
|
<!-- 右侧:工具面板 -->
|
||||||
<div class="tool-panel">
|
<div class="tool-panel">
|
||||||
|
<!-- 课程类型 -->
|
||||||
|
<div v-if="currentLesson?.lessonType" class="panel-card lesson-type-card">
|
||||||
|
<div class="panel-header">
|
||||||
|
<BookOutlined />
|
||||||
|
<span>课程类型</span>
|
||||||
|
</div>
|
||||||
|
<div class="panel-body">
|
||||||
|
<a-tag size="large" class="lesson-type-tag" :style="getLessonTagStyle(currentLesson.lessonType)">
|
||||||
|
{{ getLessonTypeName(currentLesson.lessonType) }}
|
||||||
|
</a-tag>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- 课程核心资源 -->
|
<!-- 课程核心资源 -->
|
||||||
<div v-if="hasCourseResources" class="panel-card materials-card">
|
<div v-if="hasCourseResources" class="panel-card materials-card">
|
||||||
<div class="panel-header">
|
<div class="panel-header">
|
||||||
@ -412,6 +425,7 @@ import {
|
|||||||
import { message, Modal } from 'ant-design-vue';
|
import { message, Modal } from 'ant-design-vue';
|
||||||
import * as teacherApi from '@/api/teacher';
|
import * as teacherApi from '@/api/teacher';
|
||||||
import FilePreviewModal from '@/components/FilePreviewModal.vue';
|
import FilePreviewModal from '@/components/FilePreviewModal.vue';
|
||||||
|
import { getLessonTypeName, getLessonTagStyle } from '@/utils/tagMaps';
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
@ -434,6 +448,8 @@ let timerInterval: number | null = null;
|
|||||||
const course = ref<any>({});
|
const course = ref<any>({});
|
||||||
const classInfo = ref<any>({});
|
const classInfo = ref<any>({});
|
||||||
const lessons = ref<any[]>([]);
|
const lessons = ref<any[]>([]);
|
||||||
|
/** 排课选择的课程类型(子课程模式:仅展示该子课程,子课程结束即上课结束) */
|
||||||
|
const scheduleLessonType = ref<string | undefined>(undefined);
|
||||||
|
|
||||||
const studentEvaluation = ref({
|
const studentEvaluation = ref({
|
||||||
overall: 0,
|
overall: 0,
|
||||||
@ -449,8 +465,37 @@ const lessonRecord = ref({
|
|||||||
completionNote: '',
|
completionNote: '',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/** 判断排课 lessonType 与课程 lessonType 是否匹配(兼容 INTRODUCTION/INTRO、LANGUAGE/DOMAIN_LANGUAGE 等变体) */
|
||||||
|
const lessonTypeMatches = (scheduleType: string, lessonType: string): boolean => {
|
||||||
|
if (!scheduleType || !lessonType) return false;
|
||||||
|
const s = scheduleType.toUpperCase();
|
||||||
|
const l = lessonType.toUpperCase();
|
||||||
|
if (s === l) return true;
|
||||||
|
const pairs: [string, string][] = [
|
||||||
|
['INTRODUCTION', 'INTRO'],
|
||||||
|
['LANGUAGE', 'DOMAIN_LANGUAGE'],
|
||||||
|
['HEALTH', 'DOMAIN_HEALTH'],
|
||||||
|
['SCIENCE', 'DOMAIN_SCIENCE'],
|
||||||
|
['SOCIAL', 'DOMAIN_SOCIAL'],
|
||||||
|
['SOCIETY', 'DOMAIN_SOCIAL'],
|
||||||
|
['ART', 'DOMAIN_ART'],
|
||||||
|
];
|
||||||
|
for (const [a, b] of pairs) {
|
||||||
|
if ((s === a || s === b) && (l === a || l === b)) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** 展示的课程列表:子课程模式时仅包含排课选中的子课程,否则为全部 */
|
||||||
|
const displayLessons = computed(() => {
|
||||||
|
const type = scheduleLessonType.value;
|
||||||
|
if (!type || lessons.value.length === 0) return lessons.value;
|
||||||
|
const matched = lessons.value.filter((l) => lessonTypeMatches(type, l.lessonType || ''));
|
||||||
|
return matched.length > 0 ? matched : lessons.value;
|
||||||
|
});
|
||||||
|
|
||||||
// 当前课程
|
// 当前课程
|
||||||
const currentLesson = computed(() => lessons.value[currentLessonIndex.value] || null);
|
const currentLesson = computed(() => displayLessons.value[currentLessonIndex.value] || null);
|
||||||
|
|
||||||
// 当前环节
|
// 当前环节
|
||||||
const currentStep = computed(() => {
|
const currentStep = computed(() => {
|
||||||
@ -467,9 +512,9 @@ const stepProgressPercent = computed(() => {
|
|||||||
// 是否有上一个课程
|
// 是否有上一个课程
|
||||||
const hasPreviousLesson = computed(() => currentLessonIndex.value > 0);
|
const hasPreviousLesson = computed(() => currentLessonIndex.value > 0);
|
||||||
|
|
||||||
// 是否是最后一个课程的最后一个环节
|
// 是否是最后一个课程的最后一个环节(子课程模式下,当前子课程最后一环节即视为最后)
|
||||||
const isLastStepOfLastLesson = computed(() => {
|
const isLastStepOfLastLesson = computed(() => {
|
||||||
if (currentLessonIndex.value < lessons.value.length - 1) return false;
|
if (currentLessonIndex.value < displayLessons.value.length - 1) return false;
|
||||||
const totalSteps = currentLesson.value?.steps?.length || 1;
|
const totalSteps = currentLesson.value?.steps?.length || 1;
|
||||||
return currentStepIndex.value >= totalSteps - 1;
|
return currentStepIndex.value >= totalSteps - 1;
|
||||||
});
|
});
|
||||||
@ -648,6 +693,9 @@ const loadLessonData = async () => {
|
|||||||
course.value = data.course || {};
|
course.value = data.course || {};
|
||||||
classInfo.value = data.class || {};
|
classInfo.value = data.class || {};
|
||||||
|
|
||||||
|
// 排课选择的课程类型(子课程模式:直接进入该子课程,子课程结束即上课结束)
|
||||||
|
scheduleLessonType.value = data.lessonType || undefined;
|
||||||
|
|
||||||
// 获取课程列表
|
// 获取课程列表
|
||||||
// 如果授课记录包含多个课程,使用该列表;否则使用课程包的所有课程
|
// 如果授课记录包含多个课程,使用该列表;否则使用课程包的所有课程
|
||||||
if (data.lessonCourses && data.lessonCourses.length > 0) {
|
if (data.lessonCourses && data.lessonCourses.length > 0) {
|
||||||
@ -701,51 +749,62 @@ const loadLessonData = async () => {
|
|||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 子课程模式:根据排课 lessonType 直接进入对应子课程(优先于进度恢复和 URL 参数)
|
||||||
|
const matchedLessons = scheduleLessonType.value
|
||||||
|
? lessons.value.filter((l) => lessonTypeMatches(scheduleLessonType.value!, l.lessonType || ''))
|
||||||
|
: [];
|
||||||
|
if (matchedLessons.length > 0) {
|
||||||
|
currentLessonIndex.value = 0;
|
||||||
|
currentStepIndex.value = 0;
|
||||||
|
}
|
||||||
|
|
||||||
// 尝试恢复进度
|
// 尝试恢复进度
|
||||||
try {
|
try {
|
||||||
const progress = await teacherApi.getLessonProgress(lessonId.value);
|
const progress = await teacherApi.getLessonProgress(lessonId.value);
|
||||||
if (progress && (progress.currentLessonId || progress.currentStepId)) {
|
if (progress && (progress.currentLessonId !== undefined || progress.currentStepId !== undefined)) {
|
||||||
// 有保存的进度,询问用户是否恢复
|
const isSub = matchedLessons.length > 0;
|
||||||
|
const matchedLesson = matchedLessons[0];
|
||||||
|
const progressIsForMatched = isSub && progress.currentLessonId !== undefined
|
||||||
|
&& matchedLesson && progress.currentLessonId === matchedLesson.id;
|
||||||
|
|
||||||
Modal.confirm({
|
Modal.confirm({
|
||||||
title: '检测到上次上课进度',
|
title: '检测到上次上课进度',
|
||||||
content: `上次上课到:${getProgressDescription(progress)},是否继续?`,
|
content: `上次上课到:${getProgressDescription(progress)},是否继续?`,
|
||||||
okText: '继续上课',
|
okText: '继续上课',
|
||||||
cancelText: '重新开始',
|
cancelText: '重新开始',
|
||||||
onOk: () => {
|
onOk: () => {
|
||||||
// 恢复进度
|
if (isSub && progressIsForMatched && progress.currentStepId !== undefined) {
|
||||||
|
// 子课程模式:仅恢复环节进度
|
||||||
|
currentLessonIndex.value = 0;
|
||||||
|
currentStepIndex.value = progress.currentStepId;
|
||||||
|
} else if (!isSub) {
|
||||||
|
// 非子课程模式:恢复课程和环节进度
|
||||||
if (progress.currentLessonId !== undefined) {
|
if (progress.currentLessonId !== undefined) {
|
||||||
const lessonIndex = lessons.value.findIndex((l) => l.id === progress.currentLessonId);
|
const lessonIndex = lessons.value.findIndex((l) => l.id === progress.currentLessonId);
|
||||||
if (lessonIndex >= 0) {
|
if (lessonIndex >= 0) currentLessonIndex.value = lessonIndex;
|
||||||
currentLessonIndex.value = lessonIndex;
|
|
||||||
}
|
}
|
||||||
}
|
if (progress.currentStepId !== undefined) currentStepIndex.value = progress.currentStepId;
|
||||||
if (progress.currentStepId !== undefined) {
|
|
||||||
currentStepIndex.value = progress.currentStepId;
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onCancel: () => {
|
onCancel: () => clearProgress(),
|
||||||
// 清除进度,从头开始
|
|
||||||
clearProgress();
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} catch (progressError) {
|
} catch (progressError) {
|
||||||
// 没有保存的进度或获取失败,忽略
|
|
||||||
console.log('No saved progress found');
|
console.log('No saved progress found');
|
||||||
}
|
}
|
||||||
|
|
||||||
// 如果URL指定了课程索引,跳转到该课程(优先级高于恢复的进度)
|
// 非子课程模式时,URL 参数可覆盖
|
||||||
|
if (matchedLessons.length === 0) {
|
||||||
const queryLessonIndex = route.query.lessonIndex ? parseInt(route.query.lessonIndex as string) : 0;
|
const queryLessonIndex = route.query.lessonIndex ? parseInt(route.query.lessonIndex as string) : 0;
|
||||||
if (queryLessonIndex >= 0 && queryLessonIndex < lessons.value.length) {
|
if (queryLessonIndex >= 0 && queryLessonIndex < lessons.value.length) {
|
||||||
currentLessonIndex.value = queryLessonIndex;
|
currentLessonIndex.value = queryLessonIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 如果URL指定了环节索引,跳转到该环节(优先级高于恢复的进度)
|
|
||||||
const queryStepIndex = route.query.stepIndex ? parseInt(route.query.stepIndex as string) : 0;
|
const queryStepIndex = route.query.stepIndex ? parseInt(route.query.stepIndex as string) : 0;
|
||||||
const totalSteps = lessons.value[currentLessonIndex.value]?.steps?.length || 0;
|
const totalSteps = lessons.value[currentLessonIndex.value]?.steps?.length || 0;
|
||||||
if (queryStepIndex >= 0 && queryStepIndex < totalSteps) {
|
if (queryStepIndex >= 0 && queryStepIndex < totalSteps) {
|
||||||
currentStepIndex.value = queryStepIndex;
|
currentStepIndex.value = queryStepIndex;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 启动计时器
|
// 启动计时器
|
||||||
startTimer();
|
startTimer();
|
||||||
@ -770,10 +829,11 @@ const getProgressDescription = (progress: any): string => {
|
|||||||
// 保存进度
|
// 保存进度
|
||||||
const saveProgress = async () => {
|
const saveProgress = async () => {
|
||||||
try {
|
try {
|
||||||
|
const list = displayLessons.value;
|
||||||
await teacherApi.saveLessonProgress(lessonId.value, {
|
await teacherApi.saveLessonProgress(lessonId.value, {
|
||||||
lessonIds: lessons.value.map((l) => l.id),
|
lessonIds: list.map((l) => l.id),
|
||||||
completedLessonIds: lessons.value.slice(0, currentLessonIndex.value).map((l) => l.id),
|
completedLessonIds: list.slice(0, currentLessonIndex.value).map((l) => l.id),
|
||||||
currentLessonId: lessons.value[currentLessonIndex.value]?.id,
|
currentLessonId: list[currentLessonIndex.value]?.id,
|
||||||
currentStepId: currentStepIndex.value,
|
currentStepId: currentStepIndex.value,
|
||||||
progressData: {
|
progressData: {
|
||||||
timerSeconds: timerSeconds.value,
|
timerSeconds: timerSeconds.value,
|
||||||
@ -1559,6 +1619,19 @@ onUnmounted(() => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.lesson-type-card {
|
||||||
|
.panel-header {
|
||||||
|
background: #F5F5F5;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lesson-type-tag {
|
||||||
|
font-size: 14px;
|
||||||
|
padding: 6px 14px;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.materials-card {
|
.materials-card {
|
||||||
.panel-header {
|
.panel-header {
|
||||||
background: #FFF5EB;
|
background: #FFF5EB;
|
||||||
|
|||||||
@ -4,7 +4,9 @@
|
|||||||
<h2>我的课表</h2>
|
<h2>我的课表</h2>
|
||||||
<a-space>
|
<a-space>
|
||||||
<a-button type="primary" @click="showCreateModal">
|
<a-button type="primary" @click="showCreateModal">
|
||||||
<template #icon><PlusOutlined /></template>
|
<template #icon>
|
||||||
|
<PlusOutlined />
|
||||||
|
</template>
|
||||||
预约上课
|
预约上课
|
||||||
</a-button>
|
</a-button>
|
||||||
</a-space>
|
</a-space>
|
||||||
@ -12,7 +14,9 @@
|
|||||||
|
|
||||||
<!-- 今日课程 -->
|
<!-- 今日课程 -->
|
||||||
<div class="today-section" v-if="todaySchedules.length > 0">
|
<div class="today-section" v-if="todaySchedules.length > 0">
|
||||||
<h3><CalendarOutlined /> 今日课程</h3>
|
<h3>
|
||||||
|
<CalendarOutlined /> 今日课程
|
||||||
|
</h3>
|
||||||
<a-row :gutter="16">
|
<a-row :gutter="16">
|
||||||
<a-col :span="6" v-for="schedule in todaySchedules" :key="schedule.id">
|
<a-col :span="6" v-for="schedule in todaySchedules" :key="schedule.id">
|
||||||
<a-card size="small" class="today-card" :class="{ 'has-lesson': schedule.hasLesson }">
|
<a-card size="small" class="today-card" :class="{ 'has-lesson': schedule.hasLesson }">
|
||||||
@ -21,31 +25,20 @@
|
|||||||
</template>
|
</template>
|
||||||
<div class="course-name">{{ schedule.courseName || schedule.coursePackageName || '-' }}</div>
|
<div class="course-name">{{ schedule.courseName || schedule.coursePackageName || '-' }}</div>
|
||||||
<div class="class-name">{{ schedule.className || '-' }}</div>
|
<div class="class-name">{{ schedule.className || '-' }}</div>
|
||||||
<a-tag v-if="schedule.lessonType" size="small" class="today-lesson-type" :style="getLessonTagStyle(schedule.lessonType)">
|
<a-tag v-if="schedule.lessonType" size="small" class="today-lesson-type"
|
||||||
|
:style="getLessonTagStyle(schedule.lessonType)">
|
||||||
{{ getLessonTypeName(schedule.lessonType) }}
|
{{ getLessonTypeName(schedule.lessonType) }}
|
||||||
</a-tag>
|
</a-tag>
|
||||||
<div class="card-actions">
|
<div class="card-actions">
|
||||||
<a-button
|
<a-button v-if="schedule.hasLesson && schedule.lessonStatus === 'PLANNED'" type="primary" size="small"
|
||||||
v-if="schedule.hasLesson && schedule.lessonStatus === 'PLANNED'"
|
@click="goToLesson(schedule.lessonId!)">
|
||||||
type="primary"
|
|
||||||
size="small"
|
|
||||||
@click="goToLesson(schedule.lessonId!)"
|
|
||||||
>
|
|
||||||
开始上课
|
开始上课
|
||||||
</a-button>
|
</a-button>
|
||||||
<a-button
|
<a-button v-else-if="schedule.hasLesson && schedule.lessonStatus === 'IN_PROGRESS'" type="primary"
|
||||||
v-else-if="schedule.hasLesson && schedule.lessonStatus === 'IN_PROGRESS'"
|
size="small" @click="goToLesson(schedule.lessonId!)">
|
||||||
type="primary"
|
|
||||||
size="small"
|
|
||||||
@click="goToLesson(schedule.lessonId!)"
|
|
||||||
>
|
|
||||||
继续上课
|
继续上课
|
||||||
</a-button>
|
</a-button>
|
||||||
<a-button
|
<a-button v-else size="small" @click="handleStartLessonFromSchedule(schedule)">
|
||||||
v-else
|
|
||||||
size="small"
|
|
||||||
@click="handleStartLessonFromSchedule(schedule)"
|
|
||||||
>
|
|
||||||
创建课堂
|
创建课堂
|
||||||
</a-button>
|
</a-button>
|
||||||
</div>
|
</div>
|
||||||
@ -58,13 +51,17 @@
|
|||||||
<div class="week-navigation">
|
<div class="week-navigation">
|
||||||
<a-space>
|
<a-space>
|
||||||
<a-button @click="goToPrevWeek">
|
<a-button @click="goToPrevWeek">
|
||||||
<template #icon><LeftOutlined /></template>
|
<template #icon>
|
||||||
|
<LeftOutlined />
|
||||||
|
</template>
|
||||||
上一周
|
上一周
|
||||||
</a-button>
|
</a-button>
|
||||||
<a-button @click="goToCurrentWeek">本周</a-button>
|
<a-button @click="goToCurrentWeek">本周</a-button>
|
||||||
<a-button @click="goToNextWeek">
|
<a-button @click="goToNextWeek">
|
||||||
下一周
|
下一周
|
||||||
<template #icon><RightOutlined /></template>
|
<template #icon>
|
||||||
|
<RightOutlined />
|
||||||
|
</template>
|
||||||
</a-button>
|
</a-button>
|
||||||
<span class="week-range">{{ weekRangeText }}</span>
|
<span class="week-range">{{ weekRangeText }}</span>
|
||||||
</a-space>
|
</a-space>
|
||||||
@ -74,12 +71,7 @@
|
|||||||
<div class="timetable-container">
|
<div class="timetable-container">
|
||||||
<a-spin :spinning="loading">
|
<a-spin :spinning="loading">
|
||||||
<div class="timetable-header">
|
<div class="timetable-header">
|
||||||
<div
|
<div v-for="day in weekDays" :key="day.date" class="day-header" :class="{ 'is-today': day.isToday }">
|
||||||
v-for="day in weekDays"
|
|
||||||
:key="day.date"
|
|
||||||
class="day-header"
|
|
||||||
:class="{ 'is-today': day.isToday }"
|
|
||||||
>
|
|
||||||
<div class="day-name">{{ day.dayName }}</div>
|
<div class="day-name">{{ day.dayName }}</div>
|
||||||
<div class="day-date">{{ day.dateDisplay }}</div>
|
<div class="day-date">{{ day.dateDisplay }}</div>
|
||||||
</div>
|
</div>
|
||||||
@ -87,27 +79,17 @@
|
|||||||
|
|
||||||
<div class="timetable-body">
|
<div class="timetable-body">
|
||||||
<div class="timetable-grid">
|
<div class="timetable-grid">
|
||||||
<div
|
<div v-for="day in weekDays" :key="day.date" class="day-column" :class="{ 'is-today': day.isToday }">
|
||||||
v-for="day in weekDays"
|
<div v-for="schedule in day.schedules" :key="schedule.id" class="schedule-card" :class="{
|
||||||
:key="day.date"
|
|
||||||
class="day-column"
|
|
||||||
:class="{ 'is-today': day.isToday }"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
v-for="schedule in day.schedules"
|
|
||||||
:key="schedule.id"
|
|
||||||
class="schedule-card"
|
|
||||||
:class="{
|
|
||||||
'school-schedule': schedule.source === 'SCHOOL',
|
'school-schedule': schedule.source === 'SCHOOL',
|
||||||
'teacher-schedule': schedule.source === 'TEACHER',
|
'teacher-schedule': schedule.source === 'TEACHER',
|
||||||
'has-lesson': schedule.hasLesson,
|
'has-lesson': schedule.hasLesson,
|
||||||
}"
|
}" @click="showScheduleDetail(schedule)">
|
||||||
@click="showScheduleDetail(schedule)"
|
|
||||||
>
|
|
||||||
<div class="schedule-time">{{ schedule.scheduledTime || '待定' }}</div>
|
<div class="schedule-time">{{ schedule.scheduledTime || '待定' }}</div>
|
||||||
<div class="schedule-course">{{ schedule.courseName || schedule.coursePackageName || '-' }}</div>
|
<div class="schedule-course">{{ schedule.courseName || schedule.coursePackageName || '-' }}</div>
|
||||||
<div class="schedule-class">{{ schedule.className || '-' }}</div>
|
<div class="schedule-class">{{ schedule.className || '-' }}</div>
|
||||||
<a-tag v-if="schedule.lessonType" size="small" class="schedule-lesson-type" :style="getLessonTagStyle(schedule.lessonType)">
|
<a-tag v-if="schedule.lessonType" size="small" class="schedule-lesson-type"
|
||||||
|
:style="getLessonTagStyle(schedule.lessonType)">
|
||||||
{{ getLessonTypeName(schedule.lessonType) }}
|
{{ getLessonTypeName(schedule.lessonType) }}
|
||||||
</a-tag>
|
</a-tag>
|
||||||
<div class="schedule-source">
|
<div class="schedule-source">
|
||||||
@ -133,18 +115,15 @@
|
|||||||
<TeacherCreateScheduleModal ref="createScheduleModalRef" @success="onCreateScheduleSuccess" />
|
<TeacherCreateScheduleModal ref="createScheduleModalRef" @success="onCreateScheduleSuccess" />
|
||||||
|
|
||||||
<!-- 排课详情弹窗 -->
|
<!-- 排课详情弹窗 -->
|
||||||
<a-modal
|
<a-modal v-model:open="detailVisible" title="排课详情" :footer="null" width="500px">
|
||||||
v-model:open="detailVisible"
|
|
||||||
title="排课详情"
|
|
||||||
:footer="null"
|
|
||||||
width="500px"
|
|
||||||
>
|
|
||||||
<template v-if="selectedSchedule">
|
<template v-if="selectedSchedule">
|
||||||
<a-descriptions :column="1" bordered>
|
<a-descriptions :column="1" bordered>
|
||||||
<a-descriptions-item label="班级">{{ selectedSchedule.className || '-' }}</a-descriptions-item>
|
<a-descriptions-item label="班级">{{ selectedSchedule.className || '-' }}</a-descriptions-item>
|
||||||
<a-descriptions-item label="课程">{{ selectedSchedule.courseName || selectedSchedule.coursePackageName || '-' }}</a-descriptions-item>
|
<a-descriptions-item label="课程">{{ selectedSchedule.courseName || selectedSchedule.coursePackageName || '-'
|
||||||
|
}}</a-descriptions-item>
|
||||||
<a-descriptions-item label="课程类型">
|
<a-descriptions-item label="课程类型">
|
||||||
<a-tag v-if="selectedSchedule.lessonType" size="small" :style="getLessonTagStyle(selectedSchedule.lessonType)">
|
<a-tag v-if="selectedSchedule.lessonType" size="small"
|
||||||
|
:style="getLessonTagStyle(selectedSchedule.lessonType)">
|
||||||
{{ getLessonTypeName(selectedSchedule.lessonType) }}
|
{{ getLessonTypeName(selectedSchedule.lessonType) }}
|
||||||
</a-tag>
|
</a-tag>
|
||||||
<span v-else>-</span>
|
<span v-else>-</span>
|
||||||
@ -167,25 +146,16 @@
|
|||||||
</a-descriptions>
|
</a-descriptions>
|
||||||
<div style="margin-top: 16px; text-align: right;">
|
<div style="margin-top: 16px; text-align: right;">
|
||||||
<a-space>
|
<a-space>
|
||||||
<a-button
|
<a-button v-if="selectedSchedule.hasLesson && selectedSchedule.lessonId" type="primary"
|
||||||
v-if="selectedSchedule.hasLesson && selectedSchedule.lessonId"
|
@click="goToLesson(selectedSchedule.lessonId)">
|
||||||
type="primary"
|
|
||||||
@click="goToLesson(selectedSchedule.lessonId)"
|
|
||||||
>
|
|
||||||
{{ selectedSchedule.lessonStatus === 'IN_PROGRESS' ? '继续上课' : '查看课堂' }}
|
{{ selectedSchedule.lessonStatus === 'IN_PROGRESS' ? '继续上课' : '查看课堂' }}
|
||||||
</a-button>
|
</a-button>
|
||||||
<a-button
|
<a-button v-else-if="selectedSchedule.status === 'ACTIVE'" type="primary"
|
||||||
v-else-if="selectedSchedule.status === 'ACTIVE'"
|
@click="handleStartLessonFromSchedule(selectedSchedule)">
|
||||||
type="primary"
|
|
||||||
@click="handleStartLessonFromSchedule(selectedSchedule)"
|
|
||||||
>
|
|
||||||
开始上课
|
开始上课
|
||||||
</a-button>
|
</a-button>
|
||||||
<a-popconfirm
|
<a-popconfirm v-if="selectedSchedule.source === 'TEACHER' && selectedSchedule.status === 'ACTIVE'"
|
||||||
v-if="selectedSchedule.source === 'TEACHER' && selectedSchedule.status === 'ACTIVE'"
|
title="确定要取消此预约吗?" @confirm="handleCancelSchedule(selectedSchedule.id)">
|
||||||
title="确定要取消此预约吗?"
|
|
||||||
@confirm="handleCancelSchedule(selectedSchedule.id)"
|
|
||||||
>
|
|
||||||
<a-button danger>取消预约</a-button>
|
<a-button danger>取消预约</a-button>
|
||||||
</a-popconfirm>
|
</a-popconfirm>
|
||||||
</a-space>
|
</a-space>
|
||||||
|
|||||||
@ -72,6 +72,7 @@ public class TeacherLessonController {
|
|||||||
@Operation(summary = "根据 ID 获取课时(含课程、班级,供上课页面使用)")
|
@Operation(summary = "根据 ID 获取课时(含课程、班级,供上课页面使用)")
|
||||||
@GetMapping("/{id}")
|
@GetMapping("/{id}")
|
||||||
public Result<LessonDetailResponse> getLesson(@PathVariable Long id) {
|
public Result<LessonDetailResponse> getLesson(@PathVariable Long id) {
|
||||||
|
Long tenantId = SecurityUtils.getCurrentTenantId();
|
||||||
Lesson lesson = lessonService.getLessonById(id);
|
Lesson lesson = lessonService.getLessonById(id);
|
||||||
LessonResponse lessonVo = lessonMapper.toVO(lesson);
|
LessonResponse lessonVo = lessonMapper.toVO(lesson);
|
||||||
enrichWithCourseAndClass(lessonVo);
|
enrichWithCourseAndClass(lessonVo);
|
||||||
@ -93,6 +94,17 @@ public class TeacherLessonController {
|
|||||||
detail.setClassInfo(classMapper.toVO(clazz));
|
detail.setClassInfo(classMapper.toVO(clazz));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// 从排课计划读取 lessonType,供前端判断子课程模式并直接进入对应子课程
|
||||||
|
if (lesson.getSchedulePlanId() != null) {
|
||||||
|
try {
|
||||||
|
SchedulePlan schedule = schoolScheduleService.getScheduleById(lesson.getSchedulePlanId(), tenantId);
|
||||||
|
if (schedule != null && schedule.getLessonType() != null) {
|
||||||
|
detail.setLessonType(schedule.getLessonType());
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
// 排课不存在或无权访问时忽略
|
||||||
|
}
|
||||||
|
}
|
||||||
return Result.success(detail);
|
return Result.success(detail);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -20,4 +20,7 @@ public class LessonDetailResponse {
|
|||||||
@JsonProperty("class")
|
@JsonProperty("class")
|
||||||
@Schema(description = "班级信息")
|
@Schema(description = "班级信息")
|
||||||
private ClassResponse classInfo;
|
private ClassResponse classInfo;
|
||||||
|
|
||||||
|
@Schema(description = "排课选择的课程类型(子课程模式时用于直接进入对应子课程)")
|
||||||
|
private String lessonType;
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user