| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426 |
- <template>
- <Dialog :title="dialogTitle" v-model="dialogVisible" width="1000px">
- <el-form
- ref="formRef"
- :model="formData"
- :rules="formRules"
- label-width="120px"
- v-loading="formLoading"
- >
- <el-form-item label="课程类型" prop="courseType">
- <el-tree-select
- v-model="formData.courseType"
- :data="courseTypeTree"
- :props="{
- ...defaultProps,
- label: (node) => `${node.ctType}${node.ctTypeNode === '0' ? '(年级)' : node.ctTypeNode === '1' ? '(ai通识课)' : '(ai实操课)'}`,
- // 根据 ctTypeNode 字段判断是否禁用选项
- disabled: (node) => node.ctTypeNode === '0',
- // 明确指定 value 字段为 id
- value: 'id'
- }"
- placeholder="请选择课程类型"
- :default-expand-all="true"
- />
- </el-form-item>
- <el-form-item label="课程名称" prop="courseName">
- <el-input v-model="formData.courseName" placeholder="请输入课程名称" />
- </el-form-item>
- <el-form-item label="内容类型" prop="courseName">
- <el-segmented v-model="formData.courseContentType" :options="getStrDictOptions(DICT_TYPE.COURSE_COUTNET_TYPE)" />
- </el-form-item>
- <el-form-item v-if="formData.courseContentType === 'all'" label="课程内容" prop="courseContent">
- <Editor v-model="formData.courseContent" height="150px" />
- </el-form-item>
- <el-form-item v-if="formData.courseContentType === 'text'" label="纯文本" prop="coursePath">
- <Editor v-model="formData.courseContent" height="150px" />
- </el-form-item>
- <el-form-item v-if="formData.courseContentType === 'image'" label="课程图片集" prop="coursePath">
- <UploadImgs v-model="formData.courseImagePath" />
- </el-form-item>
- <el-form-item v-if="formData.courseContentType === 'music'" label="课程音频" prop="coursePath">
- <UploadMusic v-model="formData.courseMusicPath" />
- </el-form-item>
- <el-form-item v-if="formData.courseContentType === 'video'" label="课程视频" prop="coursePath">
- <UploadVideo v-model="formData.courseVideoPath" />
- </el-form-item>
- <el-form-item v-if="formData.courseContentType === 'ppt'" label="课程PPT" prop="courseFilePath">
- <UploadFile v-model="formData.courseFilePath" :fileType="['ppt','pptx']" :fileSize="50"/>
- </el-form-item>
- <!-- <el-form-item label="课程大小" prop="courseSize">-->
- <!-- <el-input-number v-model="formData.courseSize" :min="1" :step="1" step-strictly>-->
- <!-- <template #suffix><span>MB</span></template>-->
- <!-- </el-input-number>-->
- <!-- </el-form-item>-->
- <template v-if="formData.courseContentType === 'ailab'">
- <el-form-item >
- <br/><h2>AI实验室</h2><br/>
- </el-form-item>
- </template>
- <template v-else-if="formData.courseContentType === 'quest'">
- <el-form-item >
- <br/><h2>问题</h2><br/>
- </el-form-item>
- <div style="width: 80%;margin: 0px auto">
- <!-- 基础信息 -->
- <div class="mb-4">
- <label class="block text-gray-700 font-medium mb-1">题目标题</label>
- <input
- v-model="formData.courseBlocklyJson.title"
- class="w-full border border-gray-300 rounded p-2"
- placeholder="输入题目标题"
- />
- </div>
- <div class="mb-4">
- <label class="block text-gray-700 font-medium mb-1">题目描述</label>
- <textarea
- v-model="formData.courseBlocklyJson.description"
- class="w-full border border-gray-300 rounded p-2"
- rows="3"
- placeholder="输入题目描述"
- ></textarea>
- </div>
- <!-- 迷宫地图配置 -->
- <div class="mb-4">
- <label class="block text-gray-700 font-medium mb-1">迷宫尺寸</label>
- <div class="flex gap-2">
- <input
- v-model.number="formData.courseBlocklyJson.mazeConfig.width"
- class="w-1/2 border border-gray-300 rounded p-2"
- type="number"
- min="5"
- max="20"
- placeholder="宽度"
- />
- <input
- v-model.number="formData.courseBlocklyJson.mazeConfig.height"
- class="w-1/2 border border-gray-300 rounded p-2"
- type="number"
- min="5"
- max="20"
- placeholder="高度"
- />
- </div>
- </div>
- <div class="mb-4">
- <label class="block text-gray-700 font-medium mb-1">墙坐标(JSON 数组,如 [[0,1]])</label>
- <textarea
- v-model="formData.courseBlocklyJson.mazeConfig.walls"
- class="w-full border border-gray-300 rounded p-2"
- rows="3"
- placeholder="输入墙坐标"
- ></textarea>
- </div>
- <div class="mb-4">
- <label class="block text-gray-700 font-medium mb-1">起点坐标</label>
- <div class="flex gap-2">
- <input
- v-model.number="formData.courseBlocklyJson.mazeConfig.start[0]"
- class="w-1/2 border border-gray-300 rounded p-2"
- type="number"
- placeholder="X"
- />
- <input
- v-model.number="formData.courseBlocklyJson.mazeConfig.start[1]"
- class="w-1/2 border border-gray-300 rounded p-2"
- type="number"
- placeholder="Y"
- />
- </div>
- </div>
- <div class="mb-4">
- <label class="block text-gray-700 font-medium mb-1">终点坐标</label>
- <div class="flex gap-2">
- <input
- v-model.number="formData.courseBlocklyJson.mazeConfig.end[0]"
- class="w-1/2 border border-gray-300 rounded p-2"
- type="number"
- placeholder="X"
- />
- <input
- v-model.number="formData.courseBlocklyJson.mazeConfig.end[1]"
- class="w-1/2 border border-gray-300 rounded p-2"
- type="number"
- placeholder="Y"
- />
- </div>
- </div>
- <!-- 初始积木配置 -->
- <div class="mb-4">
- <label class="block text-gray-700 font-medium mb-1">初始积木(JSON 数组,如 {"type":"move_forward","count":3} )</label>
- <textarea
- v-model="formData.courseBlocklyJson.initialBlocks"
- class="w-full border border-gray-300 rounded p-2"
- rows="3"
- placeholder="输入初始积木配置"
- ></textarea>
- </div>
- <!-- 答案配置 -->
- <div class="mb-4">
- <label class="block text-gray-700 font-medium mb-1">参考解法代码</label>
- <textarea
- v-model="formData.courseBlocklyJson.solutionCode"
- class="w-full border border-gray-300 rounded p-2"
- rows="3"
- placeholder="输入参考代码(如 moveForward(3); turnRight(); )"
- ></textarea>
- </div>
- </div>
- </template>
- <template v-else>
- <el-form-item label="课程是否有检查" prop="courseIsInspect">
- <el-radio-group v-model="formData.courseIsInspect">
- <el-radio
- v-for="dict in getStrDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING)"
- :key="dict.value"
- :label="dict.value"
- >
- {{ dict.label }}
- </el-radio>
- </el-radio-group>
- </el-form-item>
- <el-form-item label="课程耗时" prop="courseTime">
- <el-input-number v-model="formData.courseTime" :min="1" :step="1" step-strictly>
- <template #suffix><span>分</span></template>
- </el-input-number>
- </el-form-item>
- <!-- <el-form-item label="课程作者" prop="courseAuthor">-->
- <!-- <el-input v-model="formData.courseAuthor" placeholder="请输入课程作者" />-->
- <!-- </el-form-item>-->
- <!-- <el-form-item label="课程老师" prop="courseTeacher">-->
- <!-- <el-input v-model="formData.courseTeacher" placeholder="请输入课程老师" />-->
- <!-- </el-form-item>-->
- <el-form-item label="课程标签" prop="courseLabel">
- <el-select
- v-model="formData.courseLabel"
- placeholder="请选择课程标签"
- clearable
- >
- <el-option
- v-for="dict in getStrDictOptions(DICT_TYPE.COURSE_LABEL)"
- :key="dict.value"
- :label="dict.label"
- :value="dict.value"
- />
- </el-select>
- </el-form-item>
- <el-form-item label="课程排序" prop="courseOrder">
- <el-input-number v-model="formData.courseOrder" placeholder="请输入课程排序" class="!w-1/1" />
- </el-form-item>
- </template>
- <el-form-item label="课程状态" prop="courseStatus">
- <el-radio-group v-model="formData.courseStatus">
- <el-radio
- v-for="dict in getStrDictOptions(DICT_TYPE.COMMON_STATUS)"
- :key="dict.value"
- :label="dict.value"
- >
- {{ dict.label }}
- </el-radio>
- </el-radio-group>
- </el-form-item>
- </el-form>
- <template #footer>
- <el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button>
- <el-button @click="dialogVisible = false">取 消</el-button>
- </template>
- </Dialog>
- </template>
- <script setup lang="ts">
- import { getStrDictOptions, DICT_TYPE } from '@/utils/dict'
- import { CourseApi, CourseVO } from '@/api/bjdx/course'
- import { defaultProps, handleTree } from '@/utils/tree'
- import { CourseTypeApi } from '@/api/bjdx/coursetype'
- /** 课程 表单 */
- defineOptions({ name: 'CourseForm' })
- const { t } = useI18n() // 国际化
- const message = useMessage() // 消息弹窗
- const dialogVisible = ref(false) // 弹窗的是否展示
- const dialogTitle = ref('') // 弹窗的标题
- const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
- const formType = ref('') // 表单的类型:create - 新增;update - 修改
- const formData = ref({
- id: undefined,
- courseName: undefined,
- courseContentType: undefined,
- courseImagePath: undefined,
- courseVideoPath: undefined,
- courseMusicPath: undefined,
- courseFilePath: undefined,
- courseContent: undefined,
- courseBlocklyJson:{
- title: '',
- description: '',
- mazeConfig: {
- width: 10,
- height: 10,
- walls: [],
- start: [0, 0],
- end: [9, 9]
- },
- initialBlocks: [],
- solutionCode: ''
- },
- courseAuthor: undefined,
- courseTeacher: undefined,
- courseSize: undefined,
- courseTime: undefined,
- courseIsInspect: "false",
- courseType: undefined,
- courseLabel: undefined,
- courseOrder: undefined,
- courseStatus: "0",
- tenantId: undefined,
- })
- const formRules = reactive({
- courseType: [{ required: true, message: '课程类型不能为空', trigger: 'blur' }],
- courseName: [{ required: true, message: '课程名称不能为空', trigger: 'blur' }],
- courseContent: [{ required: true, message: '课程内容不能为空', trigger: 'blur' }],
- courseLabel: [{ required: true, message: '课程标签不能为空', trigger: 'blur' }],
- courseOrder: [{ required: true, message: '课程排序不能为空', trigger: 'blur' }]
- })
- const formRef = ref() // 表单 Ref
- const courseTypeTree = ref() // 树形结构
- /** 打开弹窗 */
- const open = async (type: string, id?: number) => {
- dialogVisible.value = true
- dialogTitle.value = t('action.' + type)
- formType.value = type
- resetForm()
- // 修改时,设置数据
- if (id) {
- formLoading.value = true
- try {
- const courseData = await CourseApi.getCourse(id)
- formData.value = {
- ...formData.value,
- ...courseData,
- // courseLabel: courseData.courseLabel?.split(','),
- courseImagePath: courseData.courseImagePath?.split(',')
- }
- // 确保 courseType 为正确的 id 类型
- formData.value.courseType = courseData.courseType ? Number(courseData.courseType) : undefined
- } finally {
- formLoading.value = false
- }
- }
- await getCourseTypeTree()
- }
- defineExpose({ open }) // 提供 open 方法,用于打开弹窗
- /** 提交表单 */
- const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
- const submitForm = async () => {
- // 校验表单
- await formRef.value.validate()
- // 提交请求
- formLoading.value = true
- try {
- const data = { ...formData.value } as unknown as CourseVO
- if (data.courseContentType === 'image') {
- data.courseImagePath = data.courseImagePath?.join(',')
- }
- // data.courseLabel = data.courseLabel?.join(',')
- if (formType.value === 'create') {
- await CourseApi.createCourse(data)
- message.success(t('common.createSuccess'))
- } else {
- await CourseApi.updateCourse(data)
- message.success(t('common.updateSuccess'))
- }
- dialogVisible.value = false
- // 发送操作成功的事件
- emit('success')
- } finally {
- formLoading.value = false
- }
- }
- /** 重置表单 */
- const resetForm = () => {
- formData.value = {
- id: undefined,
- courseName: undefined,
- courseContentType: undefined,
- courseImagePath: undefined,
- courseVideoPath: undefined,
- courseFilePath: undefined,
- courseContent: undefined,
- courseBlocklyJson:{
- title: '',
- description: '',
- mazeConfig: {
- width: 10,
- height: 10,
- walls: [],
- start: [0, 0],
- end: [9, 9]
- },
- initialBlocks: [],
- solutionCode: ''
- },
- courseAuthor: undefined,
- courseTeacher: undefined,
- courseSize: undefined,
- courseTime: undefined,
- courseIsInspect: "false",
- courseType: undefined,
- courseLabel: undefined,
- courseOrder: undefined,
- courseStatus: "0",
- tenantId: undefined,
- }
- formRef.value?.resetFields()
- }
- /** 获得课程-类型树 */
- const getCourseTypeTree = async () => {
- courseTypeTree.value = []
- const data = await CourseTypeApi.getCourseTypeList()
- const root: Tree = { id: 0, ctType: '课程类型', ctTypeNode: '0', children: [] }
- root.children = handleTree(data, 'id', 'ctParentId')
- courseTypeTree.value.push(root)
- }
- </script>
- <style>
- .demo-tabs > .el-tabs__content {
- padding: 32px;
- color: #6b778c;
- font-size: 32px;
- font-weight: 600;
- }
- .demo-tabs .custom-tabs-label .el-icon {
- vertical-align: middle;
- }
- .demo-tabs .custom-tabs-label span {
- vertical-align: middle;
- margin-left: 4px;
- }
- </style>
|