|
|
@@ -11,10 +11,12 @@
|
|
|
<el-tree-select
|
|
|
v-model="formData.acType"
|
|
|
:data="acTypeTree"
|
|
|
- :props="{...defaultProps,
|
|
|
- label: (node) => `${node.ctTypeNode === undefined ? node.ctType : node.ctTypeSort + '、' + node.ctType}`,
|
|
|
+ :props="{
|
|
|
+ ...defaultProps,
|
|
|
+ label: (node) =>
|
|
|
+ `${node.ctTypeNode === undefined ? node.ctType : node.ctTypeSort + '、' + node.ctType}`,
|
|
|
disabled: (node) => node.ctTypeNode === undefined || node.ctTypeNode === '0'
|
|
|
- }"
|
|
|
+ }"
|
|
|
placeholder="请选择课程类型"
|
|
|
:default-expand-all="true"
|
|
|
/>
|
|
|
@@ -27,16 +29,18 @@
|
|
|
<el-select
|
|
|
v-model="formData.acLabel"
|
|
|
placeholder="请选择课程标签"
|
|
|
- @change="()=>{
|
|
|
- if(!formData.acName){
|
|
|
- const selectedDict = getStrDictOptions(DICT_TYPE.AICOURSE_COURSE_LABEL).find(
|
|
|
- dict => dict.value === formData.acLabel
|
|
|
- );
|
|
|
- if(selectedDict){
|
|
|
- formData.acName = selectedDict.label;
|
|
|
+ @change="
|
|
|
+ () => {
|
|
|
+ if (!formData.acName) {
|
|
|
+ const selectedDict = getStrDictOptions(DICT_TYPE.AICOURSE_COURSE_LABEL).find(
|
|
|
+ (dict) => dict.value === formData.acLabel
|
|
|
+ )
|
|
|
+ if (selectedDict) {
|
|
|
+ formData.acName = selectedDict.label
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
- }"
|
|
|
+ "
|
|
|
clearable
|
|
|
>
|
|
|
<el-option
|
|
|
@@ -50,7 +54,11 @@
|
|
|
|
|
|
<el-form-item label="内容类型" prop="acContentType">
|
|
|
<el-radio-group v-model="formData.acContentType">
|
|
|
- <el-radio-button v-for="dict in getStrDictOptions(DICT_TYPE.COURSE_COUTNET_TYPE)" :key="dict.value" :label="dict.value">
|
|
|
+ <el-radio-button
|
|
|
+ v-for="dict in getStrDictOptions(DICT_TYPE.COURSE_COUTNET_TYPE)"
|
|
|
+ :key="dict.value"
|
|
|
+ :label="dict.value"
|
|
|
+ >
|
|
|
{{ dict.label }}
|
|
|
</el-radio-button>
|
|
|
</el-radio-group>
|
|
|
@@ -61,21 +69,25 @@
|
|
|
</el-form-item>
|
|
|
|
|
|
<el-form-item v-if="formData.acContentType === 'text'" label="纯文本" prop="acContent">
|
|
|
- <Editor v-model="formData.acContent" height="150px" />
|
|
|
+ <Editor v-model="formData.acContent" height="150px" />
|
|
|
</el-form-item>
|
|
|
|
|
|
<el-form-item v-if="formData.acContentType === 'image'" label="课程图片集" prop="acContent">
|
|
|
- <UploadImgs v-model="formData.acContent"
|
|
|
- @upload-progress="handleUploadProgress"
|
|
|
- @upload-start="handleUploadStart"
|
|
|
- @upload-complete="handleUploadComplete"/>
|
|
|
+ <UploadImgs
|
|
|
+ v-model="formData.acContent"
|
|
|
+ @upload-progress="handleUploadProgress"
|
|
|
+ @upload-start="handleUploadStart"
|
|
|
+ @upload-complete="handleUploadComplete"
|
|
|
+ />
|
|
|
</el-form-item>
|
|
|
|
|
|
<el-form-item v-if="formData.acContentType === 'music'" label="课程音频" prop="acContent">
|
|
|
- <UploadMusic v-model="formData.acContent"
|
|
|
- @upload-progress="handleUploadProgress"
|
|
|
- @upload-start="handleUploadStart"
|
|
|
- @upload-complete="handleUploadComplete"/>
|
|
|
+ <UploadMusic
|
|
|
+ v-model="formData.acContent"
|
|
|
+ @upload-progress="handleUploadProgress"
|
|
|
+ @upload-start="handleUploadStart"
|
|
|
+ @upload-complete="handleUploadComplete"
|
|
|
+ />
|
|
|
</el-form-item>
|
|
|
|
|
|
<el-form-item v-if="formData.acContentType === 'video'" label="课程视频" prop="acContent">
|
|
|
@@ -88,10 +100,14 @@
|
|
|
</el-form-item>
|
|
|
|
|
|
<el-form-item v-if="formData.acContentType === 'ppt'" label="课程PPT" prop="acContent">
|
|
|
- <UploadFile v-model="formData.acContent" :fileType="['ppt','pptx']" :fileSize="50"
|
|
|
- @upload-progress="handleUploadProgress"
|
|
|
- @upload-start="handleUploadStart"
|
|
|
- @upload-complete="handleUploadComplete"/>
|
|
|
+ <UploadFile
|
|
|
+ v-model="formData.acContent"
|
|
|
+ :fileType="['ppt', 'pptx']"
|
|
|
+ :fileSize="50"
|
|
|
+ @upload-progress="handleUploadProgress"
|
|
|
+ @upload-start="handleUploadStart"
|
|
|
+ @upload-complete="handleUploadComplete"
|
|
|
+ />
|
|
|
</el-form-item>
|
|
|
|
|
|
<!-- 上传进度条 -->
|
|
|
@@ -102,7 +118,63 @@
|
|
|
</div>
|
|
|
</el-form-item>
|
|
|
|
|
|
- <template v-if="formData.acContentType === 'aiCourse'">
|
|
|
+ <template v-if="formData.acContentType === 'aiTextToText' ||
|
|
|
+ formData.acContentType === 'aiTextToImage' ||
|
|
|
+ formData.acContentType === 'aiImageToImage' ||
|
|
|
+ formData.acContentType === 'aiImageToVideo'">
|
|
|
+ <!-- 前置对话 -->
|
|
|
+ <el-form-item label="前置对话" prop="aiPreDialogues">
|
|
|
+ <div class="pre-dialogue-container">
|
|
|
+ <div
|
|
|
+ v-for="(item, index) in formData.aiPreDialogues"
|
|
|
+ :key="index"
|
|
|
+ class="pre-dialogue-item"
|
|
|
+ >
|
|
|
+ <div class="dialog-type-radio">
|
|
|
+ <el-radio-group v-model="item.type">
|
|
|
+ <el-radio-button label="system">数字人</el-radio-button>
|
|
|
+ <el-radio-button label="user">用户</el-radio-button>
|
|
|
+ </el-radio-group>
|
|
|
+ </div>
|
|
|
+ <div class="dialog-content">
|
|
|
+ <el-input
|
|
|
+ type="textarea"
|
|
|
+ rows="1"
|
|
|
+ v-model="item.content"
|
|
|
+ placeholder="请输入对话内容"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ <div class="dialog-delete">
|
|
|
+ <el-button
|
|
|
+ type="danger"
|
|
|
+ size="small"
|
|
|
+ :icon="Delete"
|
|
|
+ @click="formData.aiPreDialogues.splice(index, 1)"
|
|
|
+ circle
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <el-button
|
|
|
+ type="primary"
|
|
|
+ size="small"
|
|
|
+ :icon="Plus"
|
|
|
+ @click="addPreDialogue"
|
|
|
+ class="add-dialogue-btn"
|
|
|
+ >
|
|
|
+ 添加对话
|
|
|
+ </el-button>
|
|
|
+ </div>
|
|
|
+ </el-form-item>
|
|
|
+
|
|
|
+ <!-- 回复补充 -->
|
|
|
+ <el-form-item label="回复补充" prop="aiReplySupplement">
|
|
|
+ <el-input
|
|
|
+ v-model="formData.aiReplySupplement"
|
|
|
+ type="textarea"
|
|
|
+ placeholder="请输入回复补充内容"
|
|
|
+ :rows="2"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
</template>
|
|
|
|
|
|
<template v-else>
|
|
|
@@ -136,18 +208,19 @@
|
|
|
</el-form-item>
|
|
|
</el-form>
|
|
|
<template #footer>
|
|
|
- <el-button @click="submitForm" type="primary" :disabled="formLoading || isUploading">确 定</el-button>
|
|
|
+ <el-button @click="submitForm" type="primary" :disabled="formLoading || isUploading"
|
|
|
+ >确 定</el-button
|
|
|
+ >
|
|
|
<el-button @click="dialogVisible = false">取 消</el-button>
|
|
|
</template>
|
|
|
</Dialog>
|
|
|
-
|
|
|
</template>
|
|
|
<script setup lang="ts">
|
|
|
-import { getStrDictOptions, DICT_TYPE } from '@/utils/dict'
|
|
|
+import { DICT_TYPE, getStrDictOptions } from '@/utils/dict'
|
|
|
import { AiCourseApi, AiCourseVO } from '@/api/aiCourse/aiCourse'
|
|
|
import { defaultProps, handleTree } from '@/utils/tree'
|
|
|
import { AiCourseTypeApi } from '@/api/aiCourse/aiCourseType'
|
|
|
-import { BlocklyTypeApi } from '@/api/blockly/blocklyType'
|
|
|
+import { Delete, Plus } from '@element-plus/icons-vue'
|
|
|
|
|
|
/** 课程 表单 */
|
|
|
defineOptions({ name: 'AiCourseForm' })
|
|
|
@@ -166,30 +239,40 @@ const formData = ref({
|
|
|
acContentType: undefined,
|
|
|
acContent: undefined,
|
|
|
|
|
|
- acIsInspect: "false",
|
|
|
+ acIsInspect: 'false',
|
|
|
acType: undefined,
|
|
|
acLabel: undefined,
|
|
|
acOrder: undefined,
|
|
|
- acStatus: "0",
|
|
|
+ acStatus: '0',
|
|
|
tenantId: undefined,
|
|
|
+ // 前置对话(集合类型)
|
|
|
+ aiPreDialogues: [],
|
|
|
+ // 回复补充
|
|
|
+ aiReplySupplement: undefined
|
|
|
})
|
|
|
|
|
|
// 添加上传进度相关的状态
|
|
|
const uploadProgress = ref(0)
|
|
|
const isUploading = ref(false)
|
|
|
|
|
|
+// 添加前置对话
|
|
|
+const addPreDialogue = () => {
|
|
|
+ formData.value.aiPreDialogues.push({
|
|
|
+ type: 'user',
|
|
|
+ content: ''
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
const formRules = reactive({
|
|
|
acType: [{ required: true, message: '课程类型不能为空', trigger: 'blur' }],
|
|
|
acName: [{ required: true, message: '课程名称不能为空', trigger: 'blur' }],
|
|
|
acLabel: [{ required: true, message: '课程标签不能为空', trigger: 'blur' }],
|
|
|
- acOrder: [{ required: true, message: '课程排序不能为空', trigger: 'blur' }],
|
|
|
+ acOrder: [{ required: true, message: '课程排序不能为空', trigger: 'blur' }]
|
|
|
})
|
|
|
|
|
|
-
|
|
|
const formRef = ref() // 表单 Ref
|
|
|
const acTypeTree = ref() // 树形结构
|
|
|
|
|
|
-
|
|
|
/** 打开弹窗 */
|
|
|
const open = async (type: string, id?: number) => {
|
|
|
dialogVisible.value = true
|
|
|
@@ -203,19 +286,24 @@ const open = async (type: string, id?: number) => {
|
|
|
formData.value.acType = id ? Number(id) : undefined
|
|
|
}
|
|
|
// 修改时,设置数据
|
|
|
- if (type === 'update' && id) {
|
|
|
- formLoading.value = true
|
|
|
- try {
|
|
|
- const aiCourseData = await AiCourseApi.getAiCourse(id)
|
|
|
- formData.value = {
|
|
|
- ...formData.value,
|
|
|
- ...aiCourseData,
|
|
|
- acContent: aiCourseData.acType === "image" ? aiCourseData.acContent?.split(',') : aiCourseData.acContent,
|
|
|
- acInfo: aiCourseData.acInfo || ''
|
|
|
- }
|
|
|
- // 确保 acType 为正确的 id 类型
|
|
|
- formData.value.acType = aiCourseData.acType ? Number(aiCourseData.acType) : undefined
|
|
|
-
|
|
|
+ if (type === 'update' && id) {
|
|
|
+ formLoading.value = true
|
|
|
+ try {
|
|
|
+ const aiCourseData = await AiCourseApi.getAiCourse(id)
|
|
|
+ formData.value = {
|
|
|
+ ...formData.value,
|
|
|
+ ...aiCourseData,
|
|
|
+ acContent:
|
|
|
+ aiCourseData.acType === 'image'
|
|
|
+ ? aiCourseData.acContent?.split(',')
|
|
|
+ : aiCourseData.acContent,
|
|
|
+ acInfo: aiCourseData.acInfo || '',
|
|
|
+ // 处理前置对话的回显,将json字符串转成数组
|
|
|
+ aiPreDialogues: aiCourseData.aiPreDialogues ? JSON.parse(aiCourseData.aiPreDialogues) : [],
|
|
|
+ aiReplySupplement: aiCourseData.aiReplySupplement || ''
|
|
|
+ }
|
|
|
+ // 确保 acType 为正确的 id 类型
|
|
|
+ formData.value.acType = aiCourseData.acType ? Number(aiCourseData.acType) : undefined
|
|
|
} finally {
|
|
|
formLoading.value = false
|
|
|
}
|
|
|
@@ -246,6 +334,10 @@ const submitForm = async () => {
|
|
|
if (data.acContentType === 'image') {
|
|
|
data.acContent = data.acContent?.join(',')
|
|
|
}
|
|
|
+ // 处理前置对话,将数组转成json字符串
|
|
|
+ if (data.aiPreDialogues) {
|
|
|
+ data.aiPreDialogues = JSON.stringify(data.aiPreDialogues)
|
|
|
+ }
|
|
|
if (formType.value === 'create') {
|
|
|
await AiCourseApi.createAiCourse(data)
|
|
|
message.success(t('common.createSuccess'))
|
|
|
@@ -277,7 +369,6 @@ const handleUploadComplete = () => {
|
|
|
uploadProgress.value = 100
|
|
|
}
|
|
|
|
|
|
-
|
|
|
/** 重置表单 */
|
|
|
const resetForm = () => {
|
|
|
formData.value = {
|
|
|
@@ -286,12 +377,15 @@ const resetForm = () => {
|
|
|
acContentType: undefined,
|
|
|
acContent: undefined,
|
|
|
|
|
|
- acIsInspect: "false",
|
|
|
+ acIsInspect: 'false',
|
|
|
acType: undefined,
|
|
|
acLabel: undefined,
|
|
|
acOrder: undefined,
|
|
|
- acStatus: "0",
|
|
|
+ acStatus: '0',
|
|
|
tenantId: undefined,
|
|
|
+ // 重置新增字段
|
|
|
+ aiPreDialogues: [{ type: 'user', content: '' }],
|
|
|
+ aiReplySupplement: undefined
|
|
|
}
|
|
|
}
|
|
|
</script>
|
|
|
@@ -308,5 +402,60 @@ const resetForm = () => {
|
|
|
margin-top: 10px;
|
|
|
}
|
|
|
|
|
|
+.pre-dialogue-container {
|
|
|
+ border: 1px solid #ebeef5;
|
|
|
+ border-radius: 8px;
|
|
|
+ padding: 12px;
|
|
|
+ background-color: #fafafa;
|
|
|
+ width: 100%;
|
|
|
+}
|
|
|
+
|
|
|
+.pre-dialogue-item {
|
|
|
+ background-color: #ffffff;
|
|
|
+ border: 1px solid #e4e7ed;
|
|
|
+ border-radius: 6px;
|
|
|
+ padding: 8px;
|
|
|
+ margin-bottom: 8px;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 10px;
|
|
|
+ transition: all 0.3s ease;
|
|
|
+}
|
|
|
+
|
|
|
+.pre-dialogue-item:hover {
|
|
|
+ border-color: #409eff;
|
|
|
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
|
|
+}
|
|
|
+
|
|
|
+.dialog-type-radio {
|
|
|
+ width: 120px;
|
|
|
+ flex-shrink: 0;
|
|
|
+}
|
|
|
+
|
|
|
+.dialog-type-radio :deep(.el-radio-button__inner) {
|
|
|
+ padding: 4px 8px;
|
|
|
+ font-size: 12px;
|
|
|
+}
|
|
|
+
|
|
|
+.dialog-content {
|
|
|
+ flex: 1;
|
|
|
+}
|
|
|
+
|
|
|
+.dialog-content :deep(.el-input__wrapper) {
|
|
|
+ border-radius: 4px;
|
|
|
+}
|
|
|
|
|
|
+.dialog-delete {
|
|
|
+ flex-shrink: 0;
|
|
|
+}
|
|
|
+
|
|
|
+.dialog-delete :deep(.el-button) {
|
|
|
+ padding: 6px;
|
|
|
+}
|
|
|
+
|
|
|
+.add-dialogue-btn {
|
|
|
+ display: block;
|
|
|
+ margin-top: 10px;
|
|
|
+ width: 100%;
|
|
|
+}
|
|
|
</style>
|