feat: 排课流程增加选择套餐步骤,课程详情预约跳过套餐选择
- 学校端/教师端排课:新增第一步「选择套餐」,支持租户一对多套餐 - 从课程详情预约上课:跳过套餐与课程包选择,从选择课程类型开始 - 课程详情页传递正确的 courseId/packageId 避免预约失败 Made-with: Cursor
This commit is contained in:
parent
1b1679585d
commit
dc0ce2bf78
@ -39,6 +39,14 @@
|
|||||||
margin-top: 24px;
|
margin-top: 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.collections-grid {
|
||||||
|
.collection-card {
|
||||||
|
.package-count {
|
||||||
|
color: #FF8C42;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.packages-grid {
|
.packages-grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
|
grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
|
||||||
|
|||||||
@ -9,6 +9,7 @@
|
|||||||
destroy-on-close
|
destroy-on-close
|
||||||
>
|
>
|
||||||
<a-steps :current="currentStep" size="small" class="steps-navigator">
|
<a-steps :current="currentStep" size="small" class="steps-navigator">
|
||||||
|
<a-step title="选择套餐" />
|
||||||
<a-step title="选择课程包" />
|
<a-step title="选择课程包" />
|
||||||
<a-step title="选择课程类型" />
|
<a-step title="选择课程类型" />
|
||||||
<a-step title="选择班级" />
|
<a-step title="选择班级" />
|
||||||
@ -16,8 +17,38 @@
|
|||||||
</a-steps>
|
</a-steps>
|
||||||
|
|
||||||
<div class="step-content">
|
<div class="step-content">
|
||||||
<!-- 步骤1: 选择课程包(租户仅一个套餐,直接展示套餐下课程包) -->
|
<!-- 步骤1: 选择套餐(租户可拥有多个套餐,一对多) -->
|
||||||
<div v-show="currentStep === 0" class="step-panel">
|
<div v-show="currentStep === 0" class="step-panel">
|
||||||
|
<h3>选择套餐</h3>
|
||||||
|
<a-alert
|
||||||
|
message="请先选择要排课的套餐,再选择套餐下的课程包"
|
||||||
|
type="info"
|
||||||
|
show-icon
|
||||||
|
style="margin-bottom: 16px"
|
||||||
|
/>
|
||||||
|
<a-spin :spinning="loadingPackages">
|
||||||
|
<div v-if="collections.length > 0" class="packages-section">
|
||||||
|
<div class="packages-grid collections-grid">
|
||||||
|
<div
|
||||||
|
v-for="coll in collections"
|
||||||
|
:key="coll.id"
|
||||||
|
:class="['package-card collection-card', { active: formData.collectionId === coll.id }]"
|
||||||
|
@click="selectCollection(coll)"
|
||||||
|
>
|
||||||
|
<div class="package-name">{{ coll.name }}</div>
|
||||||
|
<div class="package-grade">{{ Array.isArray(coll.gradeLevels) ? coll.gradeLevels.join(', ') : (coll.gradeLevels || '-') }}</div>
|
||||||
|
<div class="package-count">{{ coll.packageCount ?? 0 }} 个课程包</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-else-if="!loadingPackages" class="packages-section">
|
||||||
|
<a-alert message="暂无课程套餐,请联系管理员" type="info" show-icon />
|
||||||
|
</div>
|
||||||
|
</a-spin>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 步骤2: 选择课程包 -->
|
||||||
|
<div v-show="currentStep === 1" class="step-panel">
|
||||||
<h3>选择课程包</h3>
|
<h3>选择课程包</h3>
|
||||||
<a-spin :spinning="loadingPackages">
|
<a-spin :spinning="loadingPackages">
|
||||||
<div v-if="selectedCollection && selectedCollection.packages && selectedCollection.packages.length > 0" class="packages-section">
|
<div v-if="selectedCollection && selectedCollection.packages && selectedCollection.packages.length > 0" class="packages-section">
|
||||||
@ -34,11 +65,11 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-else-if="!loadingPackages && collections.length > 0" class="packages-section">
|
<div v-else-if="!loadingPackages && selectedCollection && (!selectedCollection.packages || selectedCollection.packages.length === 0)" class="packages-section">
|
||||||
<a-alert message="暂无课程包" type="warning" show-icon />
|
<a-alert message="该套餐暂无课程包" type="warning" show-icon />
|
||||||
</div>
|
</div>
|
||||||
<div v-else-if="!loadingPackages && collections.length === 0" class="packages-section">
|
<div v-else-if="!loadingPackages && !selectedCollection" class="packages-section">
|
||||||
<a-alert message="暂无课程套餐,请联系管理员" type="info" show-icon />
|
<a-alert message="请先选择套餐" type="info" show-icon />
|
||||||
</div>
|
</div>
|
||||||
</a-spin>
|
</a-spin>
|
||||||
|
|
||||||
@ -70,8 +101,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 步骤2: 选择课程类型 -->
|
<!-- 步骤3: 选择课程类型 -->
|
||||||
<div v-show="currentStep === 1" class="step-panel">
|
<div v-show="currentStep === 2" class="step-panel">
|
||||||
<h3>选择课程类型</h3>
|
<h3>选择课程类型</h3>
|
||||||
<a-alert
|
<a-alert
|
||||||
message="请选择一个课程类型进行排课"
|
message="请选择一个课程类型进行排课"
|
||||||
@ -101,8 +132,8 @@
|
|||||||
</a-spin>
|
</a-spin>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 步骤3: 选择班级并分配教师 -->
|
<!-- 步骤4: 选择班级并分配教师 -->
|
||||||
<div v-show="currentStep === 2" class="step-panel">
|
<div v-show="currentStep === 3" class="step-panel">
|
||||||
<h3>选择班级并分配教师</h3>
|
<h3>选择班级并分配教师</h3>
|
||||||
<a-alert
|
<a-alert
|
||||||
message="选择班级后,为每个班级指定授课教师"
|
message="选择班级后,为每个班级指定授课教师"
|
||||||
@ -151,8 +182,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 步骤4: 设置时间 -->
|
<!-- 步骤5: 设置时间 -->
|
||||||
<div v-show="currentStep === 3" class="step-panel">
|
<div v-show="currentStep === 4" class="step-panel">
|
||||||
<h3>设置时间</h3>
|
<h3>设置时间</h3>
|
||||||
<a-form layout="vertical">
|
<a-form layout="vertical">
|
||||||
<a-form-item label="排课日期" required>
|
<a-form-item label="排课日期" required>
|
||||||
@ -212,7 +243,7 @@
|
|||||||
<template #footer>
|
<template #footer>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<a-button v-if="currentStep > 0" @click="prevStep">上一步</a-button>
|
<a-button v-if="currentStep > 0" @click="prevStep">上一步</a-button>
|
||||||
<a-button v-if="currentStep < 3" type="primary" @click="nextStep">下一步</a-button>
|
<a-button v-if="currentStep < 4" type="primary" @click="nextStep">下一步</a-button>
|
||||||
<a-button v-else type="primary" :loading="loading" @click="handleSubmit">创建排课</a-button>
|
<a-button v-else type="primary" :loading="loading" @click="handleSubmit">创建排课</a-button>
|
||||||
<a-button @click="handleCancel">取消</a-button>
|
<a-button @click="handleCancel">取消</a-button>
|
||||||
</div>
|
</div>
|
||||||
@ -397,21 +428,14 @@ const resetForm = () => {
|
|||||||
classTeacherMap.value = {};
|
classTeacherMap.value = {};
|
||||||
};
|
};
|
||||||
|
|
||||||
// 加载课程套餐列表,租户仅一个套餐时自动加载其课程包
|
// 加载课程套餐列表(租户可拥有多个套餐)
|
||||||
const loadCollections = async () => {
|
const loadCollections = async () => {
|
||||||
loadingPackages.value = true;
|
loadingPackages.value = true;
|
||||||
try {
|
try {
|
||||||
collections.value = await getCourseCollections();
|
collections.value = await getCourseCollections();
|
||||||
if (collections.value.length > 0) {
|
// 若仅有一个套餐,自动选中并加载其课程包,提升体验
|
||||||
const first = collections.value[0];
|
if (collections.value.length === 1) {
|
||||||
formData.collectionId = first.id as number;
|
await selectCollection(collections.value[0]);
|
||||||
const packages = await getCourseCollectionPackages(first.id);
|
|
||||||
if (first) {
|
|
||||||
(first as any).packages = packages;
|
|
||||||
}
|
|
||||||
if (!packages || packages.length === 0) {
|
|
||||||
message.warning('该套餐暂无课程包');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('❌ 加载课程套餐失败:', error);
|
console.error('❌ 加载课程套餐失败:', error);
|
||||||
@ -421,6 +445,30 @@ const loadCollections = async () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 选择套餐,并加载该套餐下的课程包
|
||||||
|
const selectCollection = async (coll: CourseCollection) => {
|
||||||
|
formData.collectionId = coll.id as number;
|
||||||
|
formData.packageId = undefined;
|
||||||
|
formData.courseId = undefined;
|
||||||
|
scheduleRefData.value = [];
|
||||||
|
lessonTypes.value = [];
|
||||||
|
|
||||||
|
if (!coll.id) return;
|
||||||
|
loadingPackages.value = true;
|
||||||
|
try {
|
||||||
|
const packages = await getCourseCollectionPackages(coll.id);
|
||||||
|
(coll as any).packages = packages || [];
|
||||||
|
if (!packages || packages.length === 0) {
|
||||||
|
message.warning('该套餐暂无课程包');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('加载套餐课程包失败:', error);
|
||||||
|
message.error('加载课程包失败');
|
||||||
|
} finally {
|
||||||
|
loadingPackages.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// 加载班级列表
|
// 加载班级列表
|
||||||
const loadClasses = async () => {
|
const loadClasses = async () => {
|
||||||
try {
|
try {
|
||||||
@ -568,7 +616,13 @@ const validateStep = (): boolean => {
|
|||||||
switch (currentStep.value) {
|
switch (currentStep.value) {
|
||||||
case 0:
|
case 0:
|
||||||
if (!formData.collectionId) {
|
if (!formData.collectionId) {
|
||||||
message.warning('请选择课程套餐');
|
message.warning('请选择套餐');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
if (!formData.collectionId) {
|
||||||
|
message.warning('请选择套餐');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!formData.packageId) {
|
if (!formData.packageId) {
|
||||||
@ -580,13 +634,13 @@ const validateStep = (): boolean => {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 2:
|
||||||
if (!formData.lessonType) {
|
if (!formData.lessonType) {
|
||||||
message.warning('请选择课程类型');
|
message.warning('请选择课程类型');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 3:
|
||||||
if (formData.classIds.length === 0) {
|
if (formData.classIds.length === 0) {
|
||||||
message.warning('请至少选择一个班级');
|
message.warning('请至少选择一个班级');
|
||||||
return false;
|
return false;
|
||||||
@ -600,7 +654,7 @@ const validateStep = (): boolean => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 4:
|
||||||
if (!formData.scheduledDate) {
|
if (!formData.scheduledDate) {
|
||||||
message.warning('请选择排课日期');
|
message.warning('请选择排课日期');
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@ -124,6 +124,10 @@ const lessons = ref<any[]>([]);
|
|||||||
const classes = ref<any[]>([]);
|
const classes = ref<any[]>([]);
|
||||||
const selectedClassId = ref<number>();
|
const selectedClassId = ref<number>();
|
||||||
|
|
||||||
|
/** 预约上课时使用的 courseId(Course.id)和 packageId,从加载数据中解析 */
|
||||||
|
const scheduleCourseId = ref<number>();
|
||||||
|
const schedulePackageId = ref<number>();
|
||||||
|
|
||||||
// 预约上课相关
|
// 预约上课相关
|
||||||
const scheduleModalRef = ref<InstanceType<typeof TeacherCreateScheduleModal>>();
|
const scheduleModalRef = ref<InstanceType<typeof TeacherCreateScheduleModal>>();
|
||||||
|
|
||||||
@ -192,6 +196,10 @@ const loadCourseData = async () => {
|
|||||||
const res = await getTeacherSchoolCourseFullDetail(courseId.value as any);
|
const res = await getTeacherSchoolCourseFullDetail(courseId.value as any);
|
||||||
data = res.data || res;
|
data = res.data || res;
|
||||||
|
|
||||||
|
// 预约上课:校本课程使用 sourceCourseId 作为 courseId
|
||||||
|
scheduleCourseId.value = data.sourceCourseId ?? data.sourceCourse?.id;
|
||||||
|
schedulePackageId.value = data.id;
|
||||||
|
|
||||||
// 转换校本课程包数据结构
|
// 转换校本课程包数据结构
|
||||||
course.value = {
|
course.value = {
|
||||||
id: data.id,
|
id: data.id,
|
||||||
@ -246,6 +254,10 @@ const loadCourseData = async () => {
|
|||||||
} else {
|
} else {
|
||||||
// 加载标准课程包(id 传 string 避免 Long 精度丢失)
|
// 加载标准课程包(id 传 string 避免 Long 精度丢失)
|
||||||
data = await teacherApi.getTeacherCourse(courseId.value);
|
data = await teacherApi.getTeacherCourse(courseId.value);
|
||||||
|
// 预约上课:标准课程包,data.id 为 Course.id,首课的 courseId 与之相同
|
||||||
|
scheduleCourseId.value = data.courseLessons?.[0]?.courseId ?? data.id;
|
||||||
|
schedulePackageId.value = data.id;
|
||||||
|
|
||||||
course.value = {
|
course.value = {
|
||||||
...data,
|
...data,
|
||||||
courseLessons: data.courseLessons || [],
|
courseLessons: data.courseLessons || [],
|
||||||
@ -383,14 +395,15 @@ const showScheduleModal = () => {
|
|||||||
message.warning('课程数据异常,暂无课程');
|
message.warning('课程数据异常,暂无课程');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const pkgId = parseInt(courseId.value, 10);
|
const pkgId = schedulePackageId.value ?? parseInt(courseId.value, 10);
|
||||||
if (isNaN(pkgId)) {
|
const cid = scheduleCourseId.value ?? firstLesson.courseId ?? firstLesson.id;
|
||||||
message.warning('课程 ID 无效');
|
if (!cid) {
|
||||||
|
message.warning('课程数据异常,无法预约');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
scheduleModalRef.value?.openWithPreset({
|
scheduleModalRef.value?.openWithPreset({
|
||||||
packageId: pkgId,
|
packageId: pkgId,
|
||||||
courseId: firstLesson.id,
|
courseId: cid,
|
||||||
lessonType: firstLesson.lessonType || 'INTRODUCTION',
|
lessonType: firstLesson.lessonType || 'INTRODUCTION',
|
||||||
classId: selectedClassId.value,
|
classId: selectedClassId.value,
|
||||||
});
|
});
|
||||||
|
|||||||
@ -9,6 +9,7 @@
|
|||||||
destroy-on-close
|
destroy-on-close
|
||||||
>
|
>
|
||||||
<a-steps :current="currentStep" size="small" class="steps-navigator">
|
<a-steps :current="currentStep" size="small" class="steps-navigator">
|
||||||
|
<a-step title="选择套餐" />
|
||||||
<a-step title="选择课程包" />
|
<a-step title="选择课程包" />
|
||||||
<a-step title="选择课程类型" />
|
<a-step title="选择课程类型" />
|
||||||
<a-step title="选择班级" />
|
<a-step title="选择班级" />
|
||||||
@ -16,8 +17,38 @@
|
|||||||
</a-steps>
|
</a-steps>
|
||||||
|
|
||||||
<div class="step-content">
|
<div class="step-content">
|
||||||
<!-- 步骤1: 选择课程包(租户仅一个套餐,直接展示套餐下课程包) -->
|
<!-- 步骤1: 选择套餐(租户可拥有多个套餐,一对多) -->
|
||||||
<div v-show="currentStep === 0" class="step-panel">
|
<div v-show="currentStep === 0" class="step-panel">
|
||||||
|
<h3>选择套餐</h3>
|
||||||
|
<a-alert
|
||||||
|
message="请先选择要预约的套餐,再选择套餐下的课程包"
|
||||||
|
type="info"
|
||||||
|
show-icon
|
||||||
|
style="margin-bottom: 16px"
|
||||||
|
/>
|
||||||
|
<a-spin :spinning="loadingPackages">
|
||||||
|
<div v-if="collections.length > 0" class="packages-section">
|
||||||
|
<div class="packages-grid collections-grid">
|
||||||
|
<div
|
||||||
|
v-for="coll in collections"
|
||||||
|
:key="coll.id"
|
||||||
|
:class="['package-card collection-card', { active: formData.collectionId === coll.id }]"
|
||||||
|
@click="selectCollection(coll)"
|
||||||
|
>
|
||||||
|
<div class="package-name">{{ coll.name }}</div>
|
||||||
|
<div class="package-grade">{{ Array.isArray(coll.gradeLevels) ? coll.gradeLevels.join(', ') : (coll.gradeLevels || '-') }}</div>
|
||||||
|
<div class="package-count">{{ coll.packageCount ?? 0 }} 个课程包</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-else-if="!loadingPackages" class="packages-section">
|
||||||
|
<a-alert message="暂无课程套餐,请联系管理员" type="info" show-icon />
|
||||||
|
</div>
|
||||||
|
</a-spin>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 步骤2: 选择课程包 -->
|
||||||
|
<div v-show="currentStep === 1" class="step-panel">
|
||||||
<h3>选择课程包</h3>
|
<h3>选择课程包</h3>
|
||||||
<a-spin :spinning="loadingPackages">
|
<a-spin :spinning="loadingPackages">
|
||||||
<div v-if="selectedCollection && selectedCollection.packages && selectedCollection.packages.length > 0" class="packages-section">
|
<div v-if="selectedCollection && selectedCollection.packages && selectedCollection.packages.length > 0" class="packages-section">
|
||||||
@ -34,11 +65,11 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-else-if="!loadingPackages && collections.length > 0" class="packages-section">
|
<div v-else-if="!loadingPackages && selectedCollection && (!selectedCollection.packages || selectedCollection.packages.length === 0)" class="packages-section">
|
||||||
<a-alert message="暂无课程包" type="warning" show-icon />
|
<a-alert message="该套餐暂无课程包" type="warning" show-icon />
|
||||||
</div>
|
</div>
|
||||||
<div v-else-if="!loadingPackages && collections.length === 0" class="packages-section">
|
<div v-else-if="!loadingPackages && !selectedCollection" class="packages-section">
|
||||||
<a-alert message="暂无课程套餐,请联系管理员" type="info" show-icon />
|
<a-alert message="请先选择套餐" type="info" show-icon />
|
||||||
</div>
|
</div>
|
||||||
</a-spin>
|
</a-spin>
|
||||||
|
|
||||||
@ -70,8 +101,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 步骤2: 选择课程类型 -->
|
<!-- 步骤3: 选择课程类型 -->
|
||||||
<div v-show="currentStep === 1" class="step-panel">
|
<div v-show="currentStep === 2" class="step-panel">
|
||||||
<h3>选择课程类型</h3>
|
<h3>选择课程类型</h3>
|
||||||
<a-alert
|
<a-alert
|
||||||
message="请选择一个课程类型进行排课"
|
message="请选择一个课程类型进行排课"
|
||||||
@ -101,8 +132,8 @@
|
|||||||
</a-spin>
|
</a-spin>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 步骤3: 选择班级(教师端单选) -->
|
<!-- 步骤4: 选择班级(教师端单选) -->
|
||||||
<div v-show="currentStep === 2" class="step-panel">
|
<div v-show="currentStep === 3" class="step-panel">
|
||||||
<h3>选择班级</h3>
|
<h3>选择班级</h3>
|
||||||
<a-alert
|
<a-alert
|
||||||
message="请选择要排课的班级"
|
message="请选择要排课的班级"
|
||||||
@ -124,8 +155,8 @@
|
|||||||
<div v-if="myClasses.length === 0" class="empty-hint">暂无可用班级</div>
|
<div v-if="myClasses.length === 0" class="empty-hint">暂无可用班级</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 步骤4: 设置时间 -->
|
<!-- 步骤5: 设置时间 -->
|
||||||
<div v-show="currentStep === 3" class="step-panel">
|
<div v-show="currentStep === 4" class="step-panel">
|
||||||
<h3>设置时间</h3>
|
<h3>设置时间</h3>
|
||||||
<a-form layout="vertical">
|
<a-form layout="vertical">
|
||||||
<a-form-item label="排课日期" required>
|
<a-form-item label="排课日期" required>
|
||||||
@ -165,7 +196,7 @@
|
|||||||
<template #footer>
|
<template #footer>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<a-button v-if="currentStep > 0" @click="prevStep">上一步</a-button>
|
<a-button v-if="currentStep > 0" @click="prevStep">上一步</a-button>
|
||||||
<a-button v-if="currentStep < 3" type="primary" @click="nextStep">下一步</a-button>
|
<a-button v-if="currentStep < 4" type="primary" @click="nextStep">下一步</a-button>
|
||||||
<a-button v-else type="primary" :loading="loading" @click="handleSubmit">创建排课</a-button>
|
<a-button v-else type="primary" :loading="loading" @click="handleSubmit">创建排课</a-button>
|
||||||
<a-button @click="handleCancel">取消</a-button>
|
<a-button @click="handleCancel">取消</a-button>
|
||||||
</div>
|
</div>
|
||||||
@ -206,6 +237,8 @@ const visible = ref(false);
|
|||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
const loadingLessonTypes = ref(false);
|
const loadingLessonTypes = ref(false);
|
||||||
const currentStep = ref(0);
|
const currentStep = ref(0);
|
||||||
|
/** 从课程详情进入时,跳过套餐与课程包选择 */
|
||||||
|
const isPresetMode = ref(false);
|
||||||
|
|
||||||
const collections = ref<CourseCollection[]>([]);
|
const collections = ref<CourseCollection[]>([]);
|
||||||
const loadingPackages = ref(false);
|
const loadingPackages = ref(false);
|
||||||
@ -301,9 +334,12 @@ export interface SchedulePreset {
|
|||||||
courseId: number;
|
courseId: number;
|
||||||
lessonType: string;
|
lessonType: string;
|
||||||
classId?: number;
|
classId?: number;
|
||||||
|
/** 可选,若已知所属套餐可传入以节省请求 */
|
||||||
|
collectionId?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
const open = () => {
|
const open = () => {
|
||||||
|
isPresetMode.value = false;
|
||||||
visible.value = true;
|
visible.value = true;
|
||||||
currentStep.value = 0;
|
currentStep.value = 0;
|
||||||
resetForm();
|
resetForm();
|
||||||
@ -311,8 +347,9 @@ const open = () => {
|
|||||||
loadMyClasses();
|
loadMyClasses();
|
||||||
};
|
};
|
||||||
|
|
||||||
/** 从课程中心打开,预填课程包、课程类型、班级,直接进入选择班级或设置时间 */
|
/** 从课程详情打开:跳过套餐与课程包,从选择课程类型开始 */
|
||||||
const openWithPreset = async (preset: SchedulePreset) => {
|
const openWithPreset = async (preset: SchedulePreset) => {
|
||||||
|
isPresetMode.value = true;
|
||||||
visible.value = true;
|
visible.value = true;
|
||||||
resetForm();
|
resetForm();
|
||||||
formData.packageId = preset.packageId;
|
formData.packageId = preset.packageId;
|
||||||
@ -321,13 +358,17 @@ const openWithPreset = async (preset: SchedulePreset) => {
|
|||||||
formData.classId = preset.classId;
|
formData.classId = preset.classId;
|
||||||
|
|
||||||
await loadMyClasses();
|
await loadMyClasses();
|
||||||
await loadLessonTypes(preset.packageId);
|
try {
|
||||||
|
await loadLessonTypes(preset.packageId);
|
||||||
if (preset.classId) {
|
} catch {
|
||||||
currentStep.value = 3; // 直接到设置时间
|
// 校本课程等场景可能无对应课程包接口,使用预设类型填充
|
||||||
} else {
|
lessonTypes.value = preset.lessonType
|
||||||
currentStep.value = 2; // 到选择班级
|
? [{ lessonType: preset.lessonType, count: 1 }]
|
||||||
|
: [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 从选择课程类型(步骤2)开始,跳过套餐与课程包
|
||||||
|
currentStep.value = 2;
|
||||||
};
|
};
|
||||||
|
|
||||||
const resetForm = () => {
|
const resetForm = () => {
|
||||||
@ -342,21 +383,14 @@ const resetForm = () => {
|
|||||||
lessonTypes.value = [];
|
lessonTypes.value = [];
|
||||||
};
|
};
|
||||||
|
|
||||||
// 租户仅一个套餐,自动加载其课程包
|
// 加载课程套餐列表(租户可拥有多个套餐)
|
||||||
const loadCollections = async () => {
|
const loadCollections = async () => {
|
||||||
loadingPackages.value = true;
|
loadingPackages.value = true;
|
||||||
try {
|
try {
|
||||||
collections.value = await getCourseCollections();
|
collections.value = await getCourseCollections();
|
||||||
if (collections.value.length > 0) {
|
// 若仅有一个套餐,自动选中并加载其课程包,提升体验
|
||||||
const first = collections.value[0];
|
if (collections.value.length === 1) {
|
||||||
formData.collectionId = first.id as number;
|
await selectCollection(collections.value[0]);
|
||||||
const packages = await getCourseCollectionPackages(first.id);
|
|
||||||
if (first) {
|
|
||||||
(first as any).packages = packages;
|
|
||||||
}
|
|
||||||
if (!packages || packages.length === 0) {
|
|
||||||
message.warning('该套餐暂无课程包');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('加载课程套餐失败:', error);
|
console.error('加载课程套餐失败:', error);
|
||||||
@ -366,6 +400,30 @@ const loadCollections = async () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 选择套餐,并加载该套餐下的课程包
|
||||||
|
const selectCollection = async (coll: CourseCollection) => {
|
||||||
|
formData.collectionId = coll.id as number;
|
||||||
|
formData.packageId = undefined;
|
||||||
|
formData.courseId = undefined;
|
||||||
|
scheduleRefData.value = [];
|
||||||
|
lessonTypes.value = [];
|
||||||
|
|
||||||
|
if (!coll.id) return;
|
||||||
|
loadingPackages.value = true;
|
||||||
|
try {
|
||||||
|
const packages = await getCourseCollectionPackages(coll.id);
|
||||||
|
(coll as any).packages = packages || [];
|
||||||
|
if (!packages || packages.length === 0) {
|
||||||
|
message.warning('该套餐暂无课程包');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('加载套餐课程包失败:', error);
|
||||||
|
message.error('加载课程包失败');
|
||||||
|
} finally {
|
||||||
|
loadingPackages.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const loadMyClasses = async () => {
|
const loadMyClasses = async () => {
|
||||||
try {
|
try {
|
||||||
myClasses.value = await getTeacherClasses();
|
myClasses.value = await getTeacherClasses();
|
||||||
@ -456,7 +514,13 @@ const validateStep = (): boolean => {
|
|||||||
switch (currentStep.value) {
|
switch (currentStep.value) {
|
||||||
case 0:
|
case 0:
|
||||||
if (!formData.collectionId) {
|
if (!formData.collectionId) {
|
||||||
message.warning('请选择课程套餐');
|
message.warning('请选择套餐');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
if (!formData.collectionId) {
|
||||||
|
message.warning('请选择套餐');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!formData.packageId) {
|
if (!formData.packageId) {
|
||||||
@ -468,19 +532,19 @@ const validateStep = (): boolean => {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 2:
|
||||||
if (!formData.lessonType) {
|
if (!formData.lessonType) {
|
||||||
message.warning('请选择课程类型');
|
message.warning('请选择课程类型');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 3:
|
||||||
if (!formData.classId) {
|
if (!formData.classId) {
|
||||||
message.warning('请选择班级');
|
message.warning('请选择班级');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 4:
|
||||||
if (!formData.scheduledDate) {
|
if (!formData.scheduledDate) {
|
||||||
message.warning('请选择排课日期');
|
message.warning('请选择排课日期');
|
||||||
return false;
|
return false;
|
||||||
@ -499,6 +563,11 @@ const nextStep = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const prevStep = () => {
|
const prevStep = () => {
|
||||||
|
// 预设模式下从步骤2点击上一步时关闭弹窗(不展示套餐/课程包选择)
|
||||||
|
if (isPresetMode.value && currentStep.value === 2) {
|
||||||
|
handleCancel();
|
||||||
|
return;
|
||||||
|
}
|
||||||
currentStep.value--;
|
currentStep.value--;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -580,6 +649,12 @@ defineExpose({ open, openWithPreset });
|
|||||||
|
|
||||||
.packages-section { margin-top: 24px; }
|
.packages-section { margin-top: 24px; }
|
||||||
|
|
||||||
|
.collections-grid {
|
||||||
|
.collection-card {
|
||||||
|
.package-count { color: #722ed1; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.packages-grid {
|
.packages-grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
|
grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user