222 lines
5.2 KiB
Vue
222 lines
5.2 KiB
Vue
|
|
<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>
|
|||
|
|
导入课用于激发幼儿兴趣,引入课程主题。建议时长5-15分钟,重点在于吸引注意力、建立学习期待。
|
|||
|
|
</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
|
|||
|
|
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';
|
|||
|
|
|
|||
|
|
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 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: 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 = () => {
|
|||
|
|
if (!lessonData.value) {
|
|||
|
|
return { valid: true, errors: [], warnings: ['未配置导入课'] };
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (!lessonData.value.objectives) {
|
|||
|
|
return { valid: false, errors: ['请输入教学目标'] };
|
|||
|
|
}
|
|||
|
|
if (!lessonData.value.preparation) {
|
|||
|
|
return { valid: false, errors: ['请输入教学准备'] };
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return { valid: true, errors: [] };
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 获取保存数据
|
|||
|
|
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;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
</style>
|