Browse Source

feat: 完善对话部分的功能

zhupei@smartai.com 1 year ago
parent
commit
382980655d

+ 54 - 1
src/modules/conversation-board/api-conversation-board.js

@@ -78,7 +78,7 @@ export const conversationBoardAgentListBackup = (params) => {
  * 正式环境需要使用的会话列表
  * @see https://app.apifox.com/link/project/5404857/apis/api-233232945
  */
-export const conversationBoardAgentList = (params) => {
+export const conversationBoardAgentListV1 = (params) => {
   const request_url = `/api/dialog/list`;
 
   const send_data = {
@@ -116,6 +116,59 @@ export const conversationBoardAgentList = (params) => {
   });
 };
 
+/**
+ * 小数侧边栏菜单项目
+ * @param {object} params
+ * @returns
+ */
+export const conversationBoardAgentList = (params) => {
+  const request_url = `/api/user/menus`;
+
+  const send_data = {};
+
+  if ('search' in params && params.search) {
+    send_data.keyword = params.search;
+  }
+
+  return new Promise((resolve, reject) => {
+    ajax({
+      url: request_url,
+      method: 'get',
+      params: send_data
+    })
+      .then((res) => {
+        if (res.code / 1 === 200) {
+          const menu_list = res.data;
+
+          const arr = [];
+          if (menu_list && Array.isArray(menu_list)) {
+            const len = menu_list.length;
+            for (let i = 0; i < len; i++) {
+              const item = menu_list[i];
+
+              arr.push({
+                name: item.name,
+                description: item.desc,
+                id: item.id,
+                agentType: item.agent_type,
+                menuId: item.menuId
+              });
+            }
+          }
+
+          resolve({
+            code: res.code,
+            data: arr,
+            msg: res.msg
+          });
+        } else {
+          resolve(res);
+        }
+      })
+      .catch(reject);
+  });
+};
+
 /**
  * 获取标签列表
  * @see https://app.apifox.com/link/project/5404857/apis/api-243257552

+ 1 - 1
src/modules/conversation-board/get-agent-client-config.js → src/modules/conversation-board/get-chat-agent-client-config.js

@@ -36,7 +36,7 @@ const client_agent_config = {
  *    支持什么类型的文件上传
  * @param {string} agent_id
  */
-export default function getAgentClientConfig(agent_id) {
+export default function getChatAgentClientConfig(agent_id) {
   if (!agent_id) {
     return null;
   }

+ 42 - 0
src/modules/conversation-board/get-chat-agent-infomation.js

@@ -0,0 +1,42 @@
+import { useAllAgentStore } from '../../base/store/use-all-agents';
+import getChatAgentClientConfig from './get-chat-agent-client-config';
+
+/**
+ * 根据
+ * @param {string} agent_id
+ * @returns
+ */
+export default function getChatAgentInfomation(agent_id) {
+  const allAgents = useAllAgentStore();
+
+  /**
+   * 根据agent_id 查询到agent的基本信息
+   * 从而确定agent的相关功能支持,例如:是否需要上传文件,支持上传什么类型的文件、支持上传多少个文件?
+   * 例如:主题、背景、等等
+   */
+  const agentInfo = allAgents.getInfo(agent_id);
+
+  if (agentInfo) {
+    const client_config = getChatAgentClientConfig(agent_id);
+
+    if (client_config) {
+      return {
+        id: agent_id,
+        config: client_config,
+        agent: agentInfo
+      };
+    } else {
+      return {
+        id: agent_id,
+        config: {},
+        agent: agentInfo
+      };
+    }
+  } else {
+    return {
+      id: agent_id,
+      config: {},
+      agent: {}
+    };
+  }
+}

+ 7 - 3
src/modules/conversation-chat/DialogInput.vue

@@ -8,7 +8,7 @@
           :model-value="input_text_value"
           allow-clear
           :auto-size="{
-            minRows: 1,
+            minRows: 2,
             maxRows: 4
           }"
           @input="handleTextInput"
@@ -29,7 +29,11 @@
         @submit="handleSubmit"
       />
 
-      <FileUploadPreview v-if="input_file_list.length !== 0" :file_list="input_file_list" @delete="handleFileDelete" />
+      <FileUploadPreviewV1
+        v-if="input_file_list.length !== 0"
+        :file_list="input_file_list"
+        @delete="handleFileDelete"
+      />
     </div>
   </div>
 </template>
@@ -38,7 +42,7 @@
 import { ref, onMounted, onUnmounted } from 'vue';
 import InputActions from './dialog-input/InputActions.vue';
 
-import FileUploadPreview from '../file-preview/FileUploadPreview.vue';
+import FileUploadPreviewV1 from '../file-preview/FileUploadPreviewV1.vue';
 import useFileSelect from './use-file-select';
 import useFileSelectedV2 from './use-file-select-v2';
 import useEleSizeV2 from '../../utils/use-ele-size-v2';

+ 4 - 3
src/modules/conversation-chat/DialogItem.vue

@@ -15,8 +15,8 @@
 <script setup>
 import { ref, computed } from 'vue';
 
-import DialogQuestion from './dialog-item/DialogQuestion.vue';
-import DialogAnswer from './dialog-item/DialogAnswer.vue';
+import DialogQuestion from '../conversation-item/DialogQuestion.vue';
+import DialogAnswer from '../conversation-item/DialogAnswer.vue';
 
 const props = defineProps({
   dialog: Object
@@ -41,7 +41,8 @@ const item_type = computed(() => {
 const questionInfo = computed(() => {
   return {
     content: props.dialog.question,
-    client_custom_icon: props.dialog.client_custom_icon
+    client_custom_icon: props.dialog.client_custom_icon,
+    client_custom_upload_file_list: props.dialog.client_custom_upload_file_list
   };
 });
 

+ 1 - 1
src/modules/conversation-chat/dialog-content/DialogCode.vue → src/modules/conversation-content/DialogCode.vue

@@ -7,7 +7,7 @@
 <script setup>
 import { onMounted, computed } from 'vue';
 
-import { applyHighLight } from '../../conversation-dialog/format-code-string';
+import { applyHighLight } from '../conversation-dialog/format-code-string';
 
 /**
  * 此组件展示对话中的代码

+ 0 - 0
src/modules/conversation-chat/dialog-content/DialogContent.vue → src/modules/conversation-content/DialogContent.vue


+ 7 - 1
src/modules/conversation-dialog/format-dialog-question-by-client.js

@@ -5,7 +5,7 @@ import { getIcon4Question } from './get-icon-4-dialog';
 /**
  * 组装客户端用户提问的数据
  */
-export default function formatDialogQuestionByClient(text, file_list, agentInfo) {
+export default function formatDialogQuestionByClient(text, client_file_list, agentInfo) {
   const cur_stamp = Date.now();
 
   const icon_src = getIcon4Question(agentInfo);
@@ -20,6 +20,12 @@ export default function formatDialogQuestionByClient(text, file_list, agentInfo)
     client_custom_create_time: cur_stamp, // 创建的时间戳
     client_custom_unique_id: generateClientUniqueId(text + cur_stamp),
 
+    /**
+     * 客户端上传的文件列表
+     * 如果有此参数,则需要在问题处进行展示
+     */
+    client_custom_upload_file_list: client_file_list,
+
     // 图标
     client_custom_icon: icon_src,
 

+ 77 - 11
src/modules/conversation-dialog/format-dialog-send-options.js

@@ -1,22 +1,83 @@
 import { jsonParse, jsonStringify } from '../../utils/utils';
 
-/**
- * 格式化websocket发送消息的参数
- */
-
 /**
  * 组装发送websocket消息时的参数
  * @param {string} text - 纯文本数据
- * @param {object[]} file_list - 文件上传后,接口返回的数据信息,某些场景需要携带文件数据
+ * @param {object[]} file_list - 文件上传后,接口返回的数据信息,某些场景需要携带文件数据,此值可能为null
+ * @param {object} agentInfo - agent信息,发送websocket的数据根据agent的不同而不同
+ * @param {object} agentInfo.agent - Agent基本信息
+ * @param {object} agentInfo.config - 客户端对于agent的配置项目
  * @returns
  */
-export default function formatDialogSendOptions(text, file_list) {
-  /**
-   * 组装socket参数
-   */
-  const send_opts = jsonStringify({
+export default function formatDialogSendOptions(text, file_list, agentInfo) {
+  const agent = agentInfo.agent || {};
+  const agent_id = agentInfo.id; // agent的ID值
+  const menu_id = agent.menuId / 1;
+
+  let send_data = {
     message: text
-  });
+  };
+
+  switch (menu_id) {
+    case 1:
+      // 报表合并
+      break;
+    case 2:
+      // 知识问答
+      break;
+    case 3:
+      // 文档智能
+      send_data = {
+        message: text
+        // upload_file_id: [] // 文件ID的列表
+      };
+
+      if (file_list && Array.isArray(file_list)) {
+        const upload_file_ids = [];
+        const len = file_list.length;
+        for (let i = 0; i < len; i++) {
+          const file_item = file_list[i];
+          upload_file_ids.push(file_item.id);
+        }
+        send_data.upload_file_id = upload_file_ids;
+      }
+
+      break;
+    case 4:
+      // 智能问答
+      break;
+    case 5:
+      // 智能数据
+      break;
+    case 6:
+      // 小数绘图
+      send_data = {
+        message: text
+        // upload_file_id: [] // 文件ID的列表
+      };
+
+      if (file_list && Array.isArray(file_list)) {
+        const upload_file_ids = [];
+        const len = file_list.length;
+        for (let i = 0; i < len; i++) {
+          const file_item = file_list[i];
+          upload_file_ids.push(file_item.id);
+        }
+        send_data.upload_file_id = upload_file_ids[0];
+      }
+
+      break;
+    case 7:
+      // 文档出卷
+      break;
+    case 8:
+      // 出题组卷
+      break;
+    case 9:
+      break;
+    default:
+    // 默认处理
+  }
 
   // 文档报告参数
   // send_opts = {
@@ -48,5 +109,10 @@ export default function formatDialogSendOptions(text, file_list) {
   //   doc_ids: []
   // };
 
+  /**
+   * 组装socket参数
+   */
+  const send_opts = jsonStringify(send_data);
+
   return send_opts;
 }

+ 0 - 0
src/modules/conversation-chat/dialog-item/BtnSelectItem.vue → src/modules/conversation-item/BtnSelectItem.vue


+ 0 - 0
src/modules/conversation-chat/dialog-item/BtnStopChat.vue → src/modules/conversation-item/BtnStopChat.vue


+ 0 - 0
src/modules/conversation-chat/dialog-item/DialogActions.vue → src/modules/conversation-item/DialogActions.vue


+ 5 - 4
src/modules/conversation-chat/dialog-item/DialogAnswer.vue → src/modules/conversation-item/DialogAnswer.vue

@@ -35,15 +35,16 @@
 import { computed } from 'vue';
 import { Message } from '@arco-design/web-vue';
 import BtnSelectItem from './BtnSelectItem.vue';
-import DialogContent from '../dialog-content/DialogContent.vue';
+
 import DialogActions from './DialogActions.vue';
 import BtnStopChat from './BtnStopChat.vue';
+import DialogContent from '../conversation-content/DialogContent.vue';
 
-import formatAnwserString from '../../conversation-dialog/format-anwser-string';
+import formatAnwserString from '../conversation-dialog/format-anwser-string';
 
-import { getIconIntelligent } from '../../common/icon-map';
+import { getIconIntelligent } from '../common/icon-map';
 
-import copyText from '../../../3rd-libs/clipboard';
+import copyText from '../../3rd-libs/clipboard';
 
 const props = defineProps({
   /**

+ 20 - 2
src/modules/conversation-chat/dialog-item/DialogQuestion.vue → src/modules/conversation-item/DialogQuestion.vue

@@ -5,7 +5,13 @@
     <div class="dialog-main question-main">
       <div class="dialog-content question-content">
         <div>
-          <DialogContent :content="question.content" class="question-content-core-text" />
+          <DialogContent :content="question.content" class="question-content-core-text">
+            <FileUploadPreviewV2
+              v-if="question_attached_files.length !== 0"
+              :file_list="question_attached_files"
+              :enable_remove="false"
+            />
+          </DialogContent>
         </div>
       </div>
 
@@ -19,7 +25,9 @@
 <script setup>
 import { computed } from 'vue';
 import BtnSelectItem from './BtnSelectItem.vue';
-import DialogContent from '../dialog-content/DialogContent.vue';
+import DialogContent from '../conversation-content/DialogContent.vue';
+
+import FileUploadPreviewV2 from '../file-preview/FileUploadPreviewV2.vue';
 
 const props = defineProps({
   show_select: true,
@@ -34,6 +42,16 @@ const select_btn_visible = computed(() => {
   return props.show_select === true;
 });
 
+const question_attached_files = computed(() => {
+  if (Array.isArray(props.question.client_custom_upload_file_list)) {
+    console.log(props.question.client_custom_upload_file_list, '所有的文件列表');
+
+    return props.question.client_custom_upload_file_list;
+  } else {
+    return [];
+  }
+});
+
 const emit = defineEmits(['select']);
 
 const handleSelectChange = () => {

+ 0 - 0
src/modules/conversation-chat/dialog-item/dialog-item-common.css → src/modules/conversation-item/dialog-item-common.css


+ 6 - 7
src/modules/file-preview/FileItemDocPreview.vue

@@ -10,7 +10,7 @@
       <FileSize :file_size="file.size" />
     </div>
 
-    <FileItemRemoveBtn class="file-item-remove-button" @delete="handleRemove" />
+    <FileItemRemoveBtn v-if="enable_remove" class="file-item-remove-button" @delete="handleRemove" />
   </div>
 </template>
 
@@ -21,12 +21,11 @@ import FileSize from './FileSize.vue';
 import FileItemRemoveBtn from './FileItemRemoveBtn.vue';
 
 const props = defineProps({
-  file: Object
+  file: Object,
+  enable_remove: Boolean
 });
 const emit = defineEmits(['delete']);
 
-console.log(props.file, '文档内容');
-
 /**
  * lastModified: 1733385125049
  * lastModifiedDate: Date,
@@ -76,10 +75,10 @@ const handleRemove = () => {
   padding: 8px;
   padding-right: 20px;
 
-  background-color: #fff;
-  border-radius: 8px;
+  background-color: var(--color-bg-1);
+  color: var(--color-text-2);
 
-  /* max-width: 210px; */
+  border-radius: 8px;
 }
 
 .file-item-remove-button {

+ 3 - 2
src/modules/file-preview/FileItemImagePreview.vue

@@ -5,7 +5,7 @@
       <a-image v-if="img_status === 2" width="100" :src="img_src" />
     </div>
 
-    <FileItemRemoveBtn class="file-item-remove-button" @delete="handleRemove" />
+    <FileItemRemoveBtn v-if="enable_remove" class="file-item-remove-button" @delete="handleRemove" />
   </div>
 </template>
 
@@ -14,7 +14,8 @@ import { ref, computed, onMounted, getCurrentInstance } from 'vue';
 import FileItemRemoveBtn from './FileItemRemoveBtn.vue';
 
 const props = defineProps({
-  file: Object
+  file: Object,
+  enable_remove: Boolean
 });
 
 const emit = defineEmits(['delete']);

+ 10 - 1
src/modules/file-preview/FileUploadPreview.vue → src/modules/file-preview/FileUploadPreviewV1.vue

@@ -6,6 +6,7 @@
         class="file-preview-item"
         :key="item.id + item.index"
         :file="item.file"
+        :enable_remove="enable_remove"
         @delete="handleRemoteFileItem(item)"
       />
     </div>
@@ -16,6 +17,7 @@
         class="file-preview-item"
         :key="item.id + item.index"
         :file="item.file"
+        :enable_remove="enable_remove"
         @delete="handleRemoteFileItem(item)"
       />
     </div>
@@ -28,8 +30,15 @@ import FileItemDocPreview from './FileItemDocPreview.vue';
 import FileItemImagePreview from './FileItemImagePreview.vue';
 import isImageByFileName from './is-image-by-filename';
 
+/**
+ * 此组件用于文件上传区域的文件预览
+ */
 const props = defineProps({
-  file_list: Array
+  /**
+   * 参数数据结构为:[{type: 2, id: '', index: 3, file: File}]
+   */
+  file_list: Array,
+  enable_remove: Boolean
 });
 
 const emit = defineEmits(['delete']);

+ 99 - 0
src/modules/file-preview/FileUploadPreviewV2.vue

@@ -0,0 +1,99 @@
+<template>
+  <div class="file-upload-preview-area">
+    <div class="file-doc-preview-container">
+      <FileItemDocPreview
+        v-for="(item, index) of file_doc_list"
+        class="file-preview-item"
+        :key="item.name + index"
+        :file="item"
+      />
+    </div>
+
+    <div class="file-image-preview-container">
+      <FileItemImagePreview
+        v-for="(item, index) of file_images_list"
+        class="file-preview-item"
+        :key="item.name + index"
+        :file="item"
+      />
+    </div>
+  </div>
+</template>
+
+<script setup>
+import { ref, computed } from 'vue';
+import FileItemDocPreview from './FileItemDocPreview.vue';
+import FileItemImagePreview from './FileItemImagePreview.vue';
+import isImageByFileName from './is-image-by-filename';
+
+const props = defineProps({
+  /**
+   * 参数数据结构为:[File, File, File]
+   */
+  file_list: Array,
+  enable_remove: Boolean
+});
+
+const emit = defineEmits(['delete']);
+
+/**
+ * 图片类型的列表
+ */
+const file_images_list = computed(() => {
+  const arr = [];
+
+  props.file_list.forEach((item, index) => {
+    const file_name = item.name;
+
+    const is_image = isImageByFileName(file_name);
+
+    if (is_image) {
+      arr.push(item);
+    }
+  });
+
+  return arr;
+});
+
+/**
+ * 文档类型的列表
+ */
+const file_doc_list = computed(() => {
+  const arr = [];
+
+  props.file_list.forEach((item, index) => {
+    const file_name = item.name;
+
+    const is_image = isImageByFileName(file_name);
+
+    if (!is_image) {
+      arr.push(item);
+    }
+  });
+
+  return arr;
+});
+</script>
+
+<style lang="css" scoped>
+.file-upload-preview-area {
+  /* background-color: var(--color-border-2); */
+  padding: 5px;
+}
+
+.file-doc-preview-container {
+  display: flex;
+  flex-wrap: wrap;
+  /* margin-bottom: 5px; */
+}
+.file-image-preview-container {
+  display: flex;
+  flex-wrap: wrap;
+}
+
+.file-doc-preview-container .file-preview-item {
+  /* width: 21%; */
+  margin-right: 5px;
+  margin-bottom: 5px;
+}
+</style>

+ 14 - 22
src/views/conversation/ConversationChatContainer.vue

@@ -58,8 +58,8 @@ import DialogInput from '../../modules/conversation-chat/DialogInput.vue';
 import DialogFooter from '../../modules/conversation-chat/DialogFooter.vue';
 import DialogAsideRight from '../../modules/conversation-chat/DialogAsideRight.vue';
 
-import { useAllAgentStore } from '../../base/store/use-all-agents';
-import getAgentClientConfig from '../../modules/conversation-board/get-agent-client-config';
+// import { useAllAgentStore } from '../../base/store/use-all-agents';
+import getChatAgentInfomation from '../../modules/conversation-board/get-chat-agent-infomation';
 
 import useWebSocketChat from '../../modules/conversation-dialog/use-websocket-chat';
 import { getHistoryLogs, getDialogChatId, uploadFiles } from '../../modules/conversation-dialog/api-dialog';
@@ -99,7 +99,7 @@ const props = defineProps({
   chat_type: String
 });
 
-const allAgents = useAllAgentStore();
+// const allAgents = useAllAgentStore();
 
 /**
  * 对话使用到的agent信息
@@ -374,10 +374,16 @@ const handleInputSubmit = async (options) => {
         // 文件上传成功
         const file_uploaded = uploadResult.data;
 
-        if (Array.isArray(file_uploaded)) {
-          files_response = file_uploaded;
+        // TODO: 统一文件上传数据
+        // 文档智能的文件上传文件信息在 files 里
+        if (file_uploaded.files) {
+          files_response = file_uploaded.files;
         } else {
-          files_response = [file_uploaded];
+          if (Array.isArray(file_uploaded)) {
+            files_response = file_uploaded;
+          } else {
+            files_response = [file_uploaded];
+          }
         }
       } else {
         // 文件上传异常
@@ -402,7 +408,7 @@ const handleInputSubmit = async (options) => {
    * 获取文件参数
    * 组装socket参数
    */
-  const msg = formatDialogSendOptions(ask_text, files_response);
+  const msg = formatDialogSendOptions(ask_text, files_response, CHAT_AGENT_INFOMATION.value);
 
   // 发送websocket消息
   handleWSSend(chat_new_id.value, msg);
@@ -552,21 +558,7 @@ onMounted(() => {
    * 从而确定agent的相关功能支持,例如:是否需要上传文件,支持上传什么类型的文件、支持上传多少个文件?
    * 例如:主题、背景、等等
    */
-  const agentInfo = allAgents.getInfo(agent_id);
-  if (agentInfo) {
-    const client_config = getAgentClientConfig(agent_id);
-    if (client_config) {
-      CHAT_AGENT_INFOMATION.value = {
-        config: client_config,
-        agent: agentInfo
-      };
-    } else {
-      CHAT_AGENT_INFOMATION.value = {
-        config: {},
-        agent: agentInfo
-      };
-    }
-  }
+  CHAT_AGENT_INFOMATION.value = getChatAgentInfomation(agent_id);
 
   // 组件挂载
   if (session_id) {