kindergarten_java/reading-platform-frontend/src/views/admin/courses/components/Step2CourseIntro.vue

246 lines
6.0 KiB
Vue

<template>
<div class="step2-course-intro">
<div class="intro-header">
<span class="title">课程介绍</span>
</div>
<a-tabs v-model:activeKey="activeTab" type="card">
<a-tab-pane key="summary" tab="课程简介">
<div class="tab-content">
<a-textarea
v-model:value="formData.introSummary"
:rows="6"
placeholder="请输入课程整体简介,包括课程背景、核心理念、主要内容等"
show-count
:maxlength="1500"
@change="handleChange"
/>
</div>
</a-tab-pane>
<a-tab-pane key="highlights" tab="课程亮点">
<div class="tab-content">
<a-textarea
v-model:value="formData.introHighlights"
:rows="6"
placeholder="请输入课程特色亮点,如创新点、优势、特色活动等"
show-count
:maxlength="1500"
@change="handleChange"
/>
</div>
</a-tab-pane>
<a-tab-pane key="goals" tab="课程总目标">
<div class="tab-content">
<a-textarea
v-model:value="formData.introGoals"
:rows="6"
placeholder="请输入课程总体目标,包括知识目标、能力目标、情感目标等"
show-count
:maxlength="1500"
@change="handleChange"
/>
</div>
</a-tab-pane>
<a-tab-pane key="schedule" tab="内容安排">
<div class="tab-content">
<a-textarea
v-model:value="formData.introSchedule"
:rows="6"
placeholder="请输入课程内容安排,包括各阶段内容、时间安排等"
show-count
:maxlength="1500"
@change="handleChange"
/>
</div>
</a-tab-pane>
<a-tab-pane key="keyPoints" tab="重难点">
<div class="tab-content">
<a-textarea
v-model:value="formData.introKeyPoints"
:rows="6"
placeholder="请输入教学重点和难点,以及突破方法"
show-count
:maxlength="1500"
@change="handleChange"
/>
</div>
</a-tab-pane>
<a-tab-pane key="methods" tab="教学方法">
<div class="tab-content">
<a-textarea
v-model:value="formData.introMethods"
:rows="6"
placeholder="请输入教学方法和策略,如情境教学、游戏教学、互动教学等"
show-count
:maxlength="1500"
@change="handleChange"
/>
</div>
</a-tab-pane>
<a-tab-pane key="evaluation" tab="评价方式">
<div class="tab-content">
<a-textarea
v-model:value="formData.introEvaluation"
:rows="6"
placeholder="请输入评价方式和标准,包括过程评价和结果评价"
show-count
:maxlength="1500"
@change="handleChange"
/>
</div>
</a-tab-pane>
<a-tab-pane key="notes" tab="注意事项">
<div class="tab-content">
<a-textarea
v-model:value="formData.introNotes"
:rows="6"
placeholder="请输入教学注意事项,如安全提示、特殊需求等"
show-count
:maxlength="1500"
@change="handleChange"
/>
</div>
</a-tab-pane>
</a-tabs>
<div class="intro-footer">
<a-button v-if="currentTabIndex > 0" @click="prevTab">
上一个
</a-button>
<a-button v-if="currentTabIndex < 7" type="primary" @click="nextTab">
下一个
</a-button>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, reactive, watch, computed } from 'vue';
interface CourseIntroData {
introSummary: string;
introHighlights: string;
introGoals: string;
introSchedule: string;
introKeyPoints: string;
introMethods: string;
introEvaluation: string;
introNotes: string;
}
interface Props {
modelValue: Partial<CourseIntroData>;
}
const props = defineProps<Props>();
const emit = defineEmits<{
(e: 'update:modelValue', value: CourseIntroData): void;
(e: 'change'): void;
}>();
const activeTab = ref('summary');
const tabKeys = ['summary', 'highlights', 'goals', 'schedule', 'keyPoints', 'methods', 'evaluation', 'notes'];
const formData = reactive<CourseIntroData>({
introSummary: '',
introHighlights: '',
introGoals: '',
introSchedule: '',
introKeyPoints: '',
introMethods: '',
introEvaluation: '',
introNotes: '',
});
// 监听外部值变化
watch(
() => props.modelValue,
(newVal) => {
if (newVal) {
Object.assign(formData, newVal);
}
},
{ immediate: true, deep: true }
);
// 计算当前tab索引
const currentTabIndex = computed(() => tabKeys.indexOf(activeTab.value));
// 上一个tab
const prevTab = () => {
const index = currentTabIndex.value;
if (index > 0) {
activeTab.value = tabKeys[index - 1];
}
};
// 下一个tab
const nextTab = () => {
const index = currentTabIndex.value;
if (index < tabKeys.length - 1) {
activeTab.value = tabKeys[index + 1];
}
};
// 处理变化
const handleChange = () => {
emit('update:modelValue', { ...formData });
emit('change');
};
// 验证
const validate = () => {
// 课程介绍为可选,不强制验证
return { valid: true, errors: [] };
};
defineExpose({
validate,
formData,
});
</script>
<style scoped lang="scss">
.step2-course-intro {
.intro-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16px;
.title {
font-size: 16px;
font-weight: 500;
}
}
.tab-content {
padding: 16px 0;
}
.intro-footer {
margin-top: 16px;
padding-top: 16px;
border-top: 1px solid #f0f0f0;
display: flex;
gap: 12px;
justify-content: flex-end;
}
:deep(.ant-tabs-card) {
.ant-tabs-content {
min-height: 250px;
}
}
}
</style>