From 337dcc43d882c1c9ca8e1e56ba0730abc0c18080 Mon Sep 17 00:00:00 2001 From: zhonghua Date: Wed, 18 Mar 2026 16:59:42 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E8=AF=BE=E7=A8=8B=E5=8C=85):=20=E4=B8=89?= =?UTF-8?q?=E8=AF=BE=E8=A1=A8=E5=8D=95formRules=E6=A0=A1=E9=AA=8C=E4=B8=8E?= =?UTF-8?q?UI=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - LessonConfigPanel: 添加a-form+formRules表单校验 - 导入课/集体课/领域课: 核心资源均必填校验 - 修复教学目标/教学准备/教学延伸等重复校验提示 - 核心资源校验错误提示可见展示 - 教学环节: 环节名称改为水平布局(标签左对齐) Made-with: Cursor --- .../course-edit/Step4IntroLesson.vue | 44 ++-- .../course-edit/Step5CollectiveLesson.vue | 47 ++-- .../course-edit/Step6DomainLessons.vue | 59 +++-- .../components/course/LessonConfigPanel.vue | 242 +++++++++++++----- .../components/course/LessonStepsEditor.vue | 8 +- 5 files changed, 258 insertions(+), 142 deletions(-) diff --git a/reading-platform-frontend/src/components/course-edit/Step4IntroLesson.vue b/reading-platform-frontend/src/components/course-edit/Step4IntroLesson.vue index 1597d66..70d1626 100644 --- a/reading-platform-frontend/src/components/course-edit/Step4IntroLesson.vue +++ b/reading-platform-frontend/src/components/course-edit/Step4IntroLesson.vue @@ -12,7 +12,10 @@ style="margin-bottom: 16px" > @@ -27,6 +30,7 @@ (null); +const configPanelRef = ref | null>(null); // 获取导入课数据 const fetchLesson = async () => { @@ -154,35 +159,12 @@ const handleLessonChange = () => { emit('change'); }; -// 验证:若配置了导入课,则教学目标、教学准备、教学过程环节必填 -const validate = () => { +// 验证:若配置了导入课,则通过 formRules 校验 +const validate = async () => { if (!lessonData.value) { return { valid: true, errors: [] as string[], warnings: ['未配置导入课'] }; } - - const errors: string[] = []; - if (!lessonData.value.objectives?.trim()) { - errors.push('请输入教学目标'); - } - if (!lessonData.value.preparation?.trim()) { - errors.push('请输入教学准备'); - } - const duration = lessonData.value.duration; - if (duration != null && (duration < 5 || duration > 15)) { - errors.push('导入课时长需在 5-15 分钟之间'); - } - const steps = lessonData.value.steps || []; - if (steps.length < 1) { - errors.push('请至少添加一个教学环节'); - } else { - steps.forEach((step, i) => { - if (!step.name?.trim()) errors.push(`第${i + 1}个环节:请填写环节名称`); - if (!step.content?.trim()) errors.push(`第${i + 1}个环节:请填写环节内容`); - if (!step.objective?.trim()) errors.push(`第${i + 1}个环节:请填写教学目标`); - }); - } - - return { valid: errors.length === 0, errors }; + return configPanelRef.value?.validate() ?? { valid: true, errors: [] as string[] }; }; // 获取保存数据 @@ -232,5 +214,13 @@ defineExpose({ 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; + } } diff --git a/reading-platform-frontend/src/components/course-edit/Step5CollectiveLesson.vue b/reading-platform-frontend/src/components/course-edit/Step5CollectiveLesson.vue index cf70cb4..0505752 100644 --- a/reading-platform-frontend/src/components/course-edit/Step5CollectiveLesson.vue +++ b/reading-platform-frontend/src/components/course-edit/Step5CollectiveLesson.vue @@ -12,7 +12,10 @@ style="margin-bottom: 16px" > @@ -27,6 +30,7 @@ (null); +const configPanelRef = ref | null>(null); // 获取集体课数据 const fetchLesson = async () => { @@ -155,38 +160,12 @@ const handleLessonChange = () => { emit('change'); }; -// 验证:若配置了集体课,则教学目标、教学准备、核心资源、时长 15-45 分钟必填 -const validate = () => { +// 验证:若配置了集体课,则通过 formRules 校验 +const validate = async () => { if (!lessonData.value) { return { valid: true, errors: [] as string[], warnings: ['未配置集体课'] }; } - - const errors: string[] = []; - if (!lessonData.value.objectives?.trim()) { - errors.push('请输入教学目标'); - } - if (!lessonData.value.preparation?.trim()) { - errors.push('请输入教学准备'); - } - if (!lessonData.value.videoPath && !lessonData.value.pptPath && !lessonData.value.pdfPath) { - errors.push('请至少上传一个核心资源(动画/课件/电子绘本)'); - } - const duration = lessonData.value.duration; - if (duration != null && (duration < 15 || duration > 45)) { - errors.push('集体课时长需在 15-45 分钟之间'); - } - const steps = lessonData.value.steps || []; - if (steps.length < 1) { - errors.push('请至少添加一个教学环节'); - } else { - steps.forEach((step, i) => { - if (!step.name?.trim()) errors.push(`第${i + 1}个环节:请填写环节名称`); - if (!step.content?.trim()) errors.push(`第${i + 1}个环节:请填写环节内容`); - if (!step.objective?.trim()) errors.push(`第${i + 1}个环节:请填写教学目标`); - }); - } - - return { valid: errors.length === 0, errors }; + return configPanelRef.value?.validate() ?? { valid: true, errors: [] as string[] }; }; // 获取保存数据 @@ -236,5 +215,13 @@ defineExpose({ 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; + } } diff --git a/reading-platform-frontend/src/components/course-edit/Step6DomainLessons.vue b/reading-platform-frontend/src/components/course-edit/Step6DomainLessons.vue index 9819f1a..b310834 100644 --- a/reading-platform-frontend/src/components/course-edit/Step6DomainLessons.vue +++ b/reading-platform-frontend/src/components/course-edit/Step6DomainLessons.vue @@ -12,7 +12,10 @@ style="margin-bottom: 16px" > @@ -46,8 +49,9 @@ -
+
(); const loading = ref(false); +const configPanelRefs = reactive | null>>({}); + +const setConfigPanelRef = (type: string, el: any) => { + if (el) { + configPanelRefs[type] = el; + } else { + configPanelRefs[type] = null; + } +}; const domains = reactive([ { @@ -253,34 +266,22 @@ const handleLessonChange = () => { emit('change'); }; -// 验证:若启用某领域,则需填写教学目标,时长 15-45 分钟 -const validate = () => { +// 验证:若启用某领域,则通过 formRules 校验各领域 +const validate = async () => { const enabledDomains = domains.filter((d) => d.enabled); - const errors: string[] = []; + const allErrors: string[] = []; - enabledDomains.forEach((domain) => { - if (domain.lessonData) { - if (!domain.lessonData.objectives?.trim()) { - errors.push(`${domain.name}:请填写教学目标`); - } - const duration = domain.lessonData.duration; - if (duration != null && (duration < 15 || duration > 45)) { - errors.push(`${domain.name}:时长需在 15-45 分钟之间`); - } - const steps = domain.lessonData.steps || []; - if (steps.length < 1) { - errors.push(`${domain.name}:请至少添加一个教学环节`); - } else { - steps.forEach((step, i) => { - if (!step.name?.trim()) errors.push(`${domain.name}:第${i + 1}个环节请填写环节名称`); - if (!step.content?.trim()) errors.push(`${domain.name}:第${i + 1}个环节请填写环节内容`); - if (!step.objective?.trim()) errors.push(`${domain.name}:第${i + 1}个环节请填写教学目标`); - }); + for (const domain of enabledDomains) { + const panel = configPanelRefs[domain.type]; + if (panel?.validate) { + const result = await panel.validate(); + if (!result.valid && result.errors?.length) { + result.errors.forEach((e) => allErrors.push(`${domain.name}:${e}`)); } } - }); + } - return { valid: errors.length === 0, errors }; + return { valid: allErrors.length === 0, errors: allErrors }; }; // 获取保存数据(仅返回已启用且教学目标已填写的领域) @@ -386,5 +387,13 @@ defineExpose({ gap: 4px; } } + + .form-hints { + margin-top: 8px; + padding-top: 8px; + border-top: 1px dashed rgba(24, 144, 255, 0.3); + font-size: 12px; + color: #595959; + } } diff --git a/reading-platform-frontend/src/components/course/LessonConfigPanel.vue b/reading-platform-frontend/src/components/course/LessonConfigPanel.vue index 3d0f0fb..7bf2005 100644 --- a/reading-platform-frontend/src/components/course/LessonConfigPanel.vue +++ b/reading-platform-frontend/src/components/course/LessonConfigPanel.vue @@ -1,15 +1,23 @@ +
至少添加一个环节,每个环节需填写名称、内容、目标
+ + - + @@ -166,11 +192,13 @@ /> +
@@ -317,6 +431,18 @@ defineExpose({ margin-left: 2px; } + .optional-tag { + font-size: 12px; + color: #8c8c8c; + margin-left: 4px; + } + + .field-hint { + font-size: 12px; + color: #8c8c8c; + margin-bottom: 12px; + } + :deep(.ant-form-item) { margin-bottom: 12px; diff --git a/reading-platform-frontend/src/components/course/LessonStepsEditor.vue b/reading-platform-frontend/src/components/course/LessonStepsEditor.vue index 391e15c..14c7f2d 100644 --- a/reading-platform-frontend/src/components/course/LessonStepsEditor.vue +++ b/reading-platform-frontend/src/components/course/LessonStepsEditor.vue @@ -278,11 +278,15 @@ defineExpose({ .step-name-field { flex: 1; display: flex; - flex-direction: column; - gap: 4px; + flex-direction: row; + align-items: center; + gap: 8px; + min-width: 0; .field-label { + flex-shrink: 0; margin-bottom: 0; + white-space: nowrap; } }