kindergarten_java/reading-platform-frontend/src/components/course-edit/Step4IntroLesson.vue
zhonghua 877acf33b8 fix(admin): 课程包编辑页问题修复
- 封面回显与保存:使用 getFileUrl 统一处理,修复 watch 逻辑
- 课程介绍/排课参考/环创建设回显:修复 API 字段映射和解析
- 测评内容 JSON 格式:新增 parseAssessmentDataForDisplay 前后端统一
- 保存后跳转列表:修复新建/编辑流程的 router.replace
- 表单校验:导入课、集体课、领域课各必填一条,下一步时校验
- 保存按钮:修复 @click 将 event 误传为 isDraft 导致不跳转
- Lesson API:updateLesson/updateStep 传入正确的 courseId

Made-with: Cursor
2026-03-23 15:15:56 +08:00

228 lines
5.7 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="step4-intro-lesson">
<div class="section-header">
<span class="title">导入课配置</span>
<a-tag color="blue">激发兴趣引入主题</a-tag>
</div>
<a-alert
message="导入课说明"
type="info"
show-icon
style="margin-bottom: 16px"
>
<template #description>
<div>导入课用于激发幼儿兴趣引入课程主题建议时长 5-15 分钟重点在于吸引注意力建立学习期待</div>
<div class="form-hints">
<strong>必填项</strong>课程名称教学目标教学准备核心资源至少 1 教学环节至少 1 含环节名称内容目标<strong>时长</strong>5-15 分钟
</div>
</template>
</a-alert>
<a-spin :spinning="loading">
<div v-if="!lessonData" class="create-section">
<a-empty description="暂未配置导入课">
<a-button type="primary" @click="createNewLesson">
<PlusOutlined /> 创建导入课
</a-button>
</a-empty>
</div>
<LessonConfigPanel
v-else
ref="configPanelRef"
v-model="lessonData"
lesson-type="INTRO"
:min-duration="5"
:max-duration="15"
:show-resources="true"
:show-extension="true"
:show-template="false"
@change="handleLessonChange"
/>
</a-spin>
<div v-if="lessonData" class="action-bar">
<a-popconfirm
title="确定删除此导入课吗?删除后不可恢复。"
@confirm="deleteLesson"
>
<a-button danger>删除导入课</a-button>
</a-popconfirm>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, watch, onMounted } from 'vue';
import { message } from 'ant-design-vue';
import { PlusOutlined } from '@ant-design/icons-vue';
import LessonConfigPanel from '@/components/course/LessonConfigPanel.vue';
import type { LessonData } from '@/components/course/LessonConfigPanel.vue';
import { getLessonByType, createLesson, updateLesson, deleteLesson as deleteLessonApi } from '@/api/lesson';
import { parseAssessmentDataForDisplay } from '@/utils/assessmentData';
interface Props {
courseId: number;
}
const props = defineProps<Props>();
const emit = defineEmits<{
(e: 'change'): void;
}>();
const loading = ref(false);
const lessonData = ref<LessonData | null>(null);
const configPanelRef = ref<InstanceType<typeof LessonConfigPanel> | null>(null);
// 获取导入课数据
const fetchLesson = async () => {
if (!props.courseId) return;
loading.value = true;
try {
const res = await getLessonByType(props.courseId, 'INTRO') as any;
const lesson = res.data || res;
if (lesson) {
lessonData.value = {
id: lesson.id,
lessonType: 'INTRO',
name: lesson.name || '导入课',
description: lesson.description || '',
duration: lesson.duration || 10,
videoPath: lesson.videoPath || '',
videoName: lesson.videoName || '',
pptPath: lesson.pptPath || '',
pptName: lesson.pptName || '',
pdfPath: lesson.pdfPath || '',
pdfName: lesson.pdfName || '',
objectives: lesson.objectives || '',
preparation: lesson.preparation || '',
extension: lesson.extension || '',
reflection: lesson.reflection || '',
assessmentData: parseAssessmentDataForDisplay(lesson.assessmentData),
useTemplate: lesson.useTemplate || false,
steps: lesson.steps || [],
isNew: false,
};
}
} catch (error) {
console.error('获取导入课失败', error);
} finally {
loading.value = false;
}
};
// 创建导入课
const createNewLesson = async () => {
lessonData.value = {
lessonType: 'INTRO',
name: '导入课',
description: '',
duration: 10,
videoPath: '',
videoName: '',
pptPath: '',
pptName: '',
pdfPath: '',
pdfName: '',
objectives: '',
preparation: '',
extension: '',
reflection: '',
assessmentData: '',
useTemplate: false,
steps: [],
isNew: true,
};
};
// 删除导入课
const deleteLesson = async () => {
if (!lessonData.value?.id) {
lessonData.value = null;
return;
}
try {
await deleteLessonApi(props.courseId, lessonData.value.id);
lessonData.value = null;
message.success('删除成功');
emit('change');
} catch (error) {
message.error('删除失败');
}
};
// 处理课程数据变化
const handleLessonChange = () => {
emit('change');
};
// 验证:导入课为必填,至少配置一条
const validate = async () => {
if (!lessonData.value) {
return { valid: false, errors: ['请配置导入课(至少一条)'] };
}
return configPanelRef.value?.validate() ?? { valid: true, errors: [] as string[] };
};
// 获取保存数据
const getSaveData = () => {
return lessonData.value;
};
watch(() => props.courseId, fetchLesson, { immediate: true });
onMounted(() => {
if (props.courseId) {
fetchLesson();
}
});
defineExpose({
validate,
getSaveData,
lessonData,
});
</script>
<style scoped lang="scss">
.step4-intro-lesson {
.section-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16px;
.title {
font-size: 16px;
font-weight: 500;
}
}
.create-section {
padding: 40px 0;
text-align: center;
background: #fafafa;
border-radius: 8px;
}
.action-bar {
margin-top: 16px;
padding-top: 16px;
border-top: 1px solid #f0f0f0;
text-align: right;
}
.form-hints {
margin-top: 8px;
padding-top: 8px;
border-top: 1px dashed rgba(24, 144, 255, 0.3);
font-size: 12px;
color: #595959;
}
}
</style>