Преглед изворни кода

1、AI实验课课程配置加入前置对话和ai回复补充配置项

liyanbo пре 5 месеци
родитељ
комит
41438ffc3b
1 измењених фајлова са 200 додато и 51 уклоњено
  1. 200 51
      src/views/aiCourse/aiCourse/AiCourseForm.vue

+ 200 - 51
src/views/aiCourse/aiCourse/AiCourseForm.vue

@@ -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>