Ver Fonte

feat: 完善对话部分的文件上传组件

zhupei@smartai.com há 1 ano atrás
pai
commit
ce97dcbee0

+ 3 - 9
src/modules/conversation-board/api-conversation-board.js

@@ -4,12 +4,6 @@ const res_list_example = {
   code: 200,
   msg: '',
   data: [
-    {
-      id: '94873c72-d194-4306-a965-286affe426bf',
-      name: '报告生成',
-      agent_type: 2,
-      type: 'report'
-    },
     {
       id: 'basic_excel_merge',
       name: '报表合并',
@@ -23,10 +17,10 @@ const res_list_example = {
       type: 'knowledgeQA'
     },
     {
-      id: 'c684bab4-f05b-423e-8727-f50497e73ebf',
+      id: '7638f006-38a2-4c21-a68e-c6c49b304a35',
       name: '文档智能',
-      agent_type: 2,
-      type: 'report'
+      agent_type: 4,
+      type: 'documentIa'
     },
     {
       id: '90d533729c0211efbf6b0242ac160006',

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

@@ -29,8 +29,7 @@
         @submit="handleSubmit"
       />
 
-      <!-- <input type="file" ref="file_upload_input" class="file-input-btn" /> -->
-      <!-- <FileUploadPreview :file_list="input_file_list" /> -->
+      <FileUploadPreview :file_list="input_file_list" />
     </div>
   </div>
 </template>
@@ -41,6 +40,7 @@ import InputActions from './dialog-input/InputActions.vue';
 
 import FileUploadPreview from '../file-upload/FileUploadPreview.vue';
 import useFileSelect from './use-file-select';
+import useFileSelectedV2 from './use-file-select-v2';
 
 const props = defineProps({
   /**
@@ -145,6 +145,7 @@ const input_file_list = ref([]);
 const emit = defineEmits(['inputTextChange', 'inputFileChange', 'submit']);
 
 // const fileSelect = useFileSelect('file_upload_input');
+const { select, remove } = useFileSelectedV2();
 
 const handleSubmit = () => {
   // 检查是否是禁用状态
@@ -163,7 +164,43 @@ const clear = () => {
  * 文件上传
  */
 const handleFileUpload = () => {
-  // fileSelect();
+  select((e) => {
+    const files = e.target.files;
+    const id = e.target.id; // input ID
+
+    const file_total = files.length;
+
+    if (file_total === 0) {
+      // 没有文件
+    }
+
+    if (file_total === 1) {
+      // 1个文件
+      input_file_list.value.push({
+        type: 1, // 单文件
+        id: id,
+        index: 0,
+        file: files[0]
+      });
+    }
+
+    if (file_total > 1) {
+      // 多个文件
+      for (let i = 0; i < file_total; i++) {
+        const item = files[i];
+        const index = i;
+
+        input_file_list.value.push({
+          type: 2, // 多文件
+          id: id,
+          index: index,
+          file: item
+        });
+      }
+    }
+
+    console.log(files, '文件列表', id, input_file_list.value);
+  });
 };
 /**
  * 常用问题定义

+ 1 - 1
src/modules/conversation-chat/DialogItem.vue

@@ -1,6 +1,6 @@
 <template>
   <div class="dialog-item-container">
-    <DialogQuestion v-if="item_type === 'q'" :question="questionInfo" />
+    <DialogQuestion v-if="item_type === 'q'" :question="questionInfo" @select="handleSelectChat" />
 
     <DialogAnswer
       v-if="item_type === 'a'"

+ 13 - 1
src/modules/conversation-chat/DialogList.vue

@@ -1,7 +1,13 @@
 <template>
   <a-scrollbar type="track" :style="container_style" ref="scroll_container">
     <div class="dialog-list">
-      <DialogItem v-for="(item, index) of list" :dialog="item" :index="index" :key="item.client_custom_unique_id" />
+      <DialogItem
+        v-for="(item, index) of list"
+        :dialog="item"
+        :index="index"
+        :key="item.client_custom_unique_id"
+        @select="handleSelectChat(item)"
+      />
 
       <DialogSuggestion v-if="suggestion.length !== 0" :suggestion="suggestion" />
     </div>
@@ -44,6 +50,12 @@ const container_style = computed(() => {
   };
 });
 
+/**
+ * 选择某个项目
+ * @param item
+ */
+const handleSelectChat = (item) => {};
+
 defineExpose({
   scroll2Bottom: () => {
     scroll2Bottom(1000000);

+ 25 - 4
src/modules/conversation-chat/dialog-input/InputActions.vue

@@ -1,11 +1,16 @@
 <template>
   <div class="dialog-input-actions">
     <div class="dialog-input-action-left">
-      <div>联网查询</div>
+      <!-- <div>联网查询</div> -->
+      <a-tooltip content="文件上传" position="top">
+        <div v-if="enable_file_upload" class="action-left-item-btn" @click="handleFileUpload">
+          <img src="../../../assets/file-upload.png" />
+        </div>
+      </a-tooltip>
     </div>
 
     <div class="dialog-input-action-right">
-      <a-tooltip content="常用问题定义" position="top">
+      <!-- <a-tooltip content="常用问题定义" position="top">
         <div v-if="enable_frequent_question" class="action-right-item-btn" @click="handleQuestionDefine">
           <img src="../../../assets/question-define.png" />
         </div>
@@ -19,10 +24,10 @@
 
       <a-tooltip content="语音识别" position="top">
         <div v-if="enable_voice_recognition" class="action-right-item-btn" @click="handleVoiceRecognize">语音识别</div>
-      </a-tooltip>
+      </a-tooltip> -->
 
       <a-tooltip content="请输入您的问题" position="top">
-        <div class="action-right-item-btn" @click="handleSubmit">发送</div>
+        <div class="action-right-item-btn submit-input-btn" @click="handleSubmit">发送</div>
       </a-tooltip>
     </div>
   </div>
@@ -132,12 +137,28 @@ const handleSubmit = () => {
   align-items: center;
 }
 
+.action-left-item-btn {
+  margin-right: 10px;
+  cursor: pointer;
+}
+
 .action-right-item-btn {
   margin-left: 10px;
   cursor: pointer;
 }
 
+.action-left-item-btn img {
+  width: 24px;
+}
 .action-right-item-btn img {
   width: 24px;
 }
+
+.submit-input-btn {
+  background-color: rgb(var(--primary-6));
+  color: #fff;
+  padding: 5px 15px;
+  border-radius: 15px;
+  font-size: 14px;
+}
 </style>

+ 5 - 1
src/modules/conversation-chat/dialog-item/DialogQuestion.vue

@@ -32,7 +32,11 @@ const select_btn_visible = computed(() => {
   return props.show_select === true;
 });
 
-const handleSelectChange = () => {};
+const emit = defineEmits(['select']);
+
+const handleSelectChange = () => {
+  emit('select');
+};
 </script>
 
 <style src="./dialog-item-common.css" />

+ 30 - 0
src/modules/conversation-chat/use-file-select-v2.js

@@ -0,0 +1,30 @@
+export default function useFileSelectedV2(options) {
+  const input_list = [];
+
+  return {
+    select(fn) {
+      /**
+       * https://developer.mozilla.org/zh-CN/docs/Web/HTML/Element/input/file
+       */
+      const input = document.createElement('input');
+      input.type = 'file';
+      input.id = Date.now();
+      input.setAttribute('multiple', true);
+      document.body.appendChild(input);
+
+      input.click();
+      input.onchange = (e) => {
+        if (typeof fn === 'function') {
+          fn(e);
+        }
+      };
+
+      input_list.push(input);
+    },
+    remove() {
+      input_list.forEach((item) => {
+        document.body.removeChild(item);
+      });
+    }
+  };
+}

+ 0 - 1
src/modules/conversation-chat/use-file-select.js

@@ -1,4 +1,3 @@
-// dialog_input
 import { ref, getCurrentInstance, onMounted, onUnmounted, computed } from 'vue';
 
 /**

+ 15 - 0
src/modules/file-upload/FileItemDocPreview.vue

@@ -0,0 +1,15 @@
+<template>
+  <div>
+    <div>{{ file.name }}</div>
+  </div>
+</template>
+
+<script setup>
+import { ref, computed } from 'vue';
+
+const props = defineProps({
+  file: Object
+});
+</script>
+
+<style lang="css" scoped></style>

+ 15 - 0
src/modules/file-upload/FileItemImagePreview.vue

@@ -0,0 +1,15 @@
+<template>
+  <div>
+    <div>{{ file.name }}</div>
+  </div>
+</template>
+
+<script setup>
+import { ref, computed } from 'vue';
+
+const props = defineProps({
+  file: Object
+});
+</script>
+
+<style lang="css" scoped></style>

+ 0 - 34
src/modules/file-upload/FileUploadBtn.vue

@@ -1,34 +0,0 @@
-<template>
-  <div class="dialog-input-actions">
-    <div class="dialog-input-action-left">
-      <div>文件上传</div>
-    </div>
-  </div>
-</template>
-
-<script setup>
-const props = defineProps({
-  /**
-   * 是否禁用操作
-   * 默认 false
-   * 当此值为 true 时,此组件的任何功能都不可用
-   * 当对话正在进行时,此值应为 true,意味着不允许继续对话
-   * 直到一轮对话结束,或者一轮对话终止,此值才应该变为 false
-   */
-  disabled: {
-    type: Boolean,
-    default: false,
-    required: false
-  }
-});
-
-const emit = defineEmits(['fileUpload', 'questionDefine', 'voiceRecognize', 'submit']);
-</script>
-
-<style lang="css" scoped>
-.dialog-input-actions {
-  display: flex;
-  justify-content: space-between;
-  align-items: center;
-}
-</style>

+ 62 - 44
src/modules/file-upload/FileUploadPreview.vue

@@ -1,68 +1,86 @@
 <template>
   <div class="file-upload-preview-area">
-    <div>文件预览区域</div>
-
-    <a-upload
-      :file-list="file_list"
-      :multiple="true"
-      :limit="MAX_ACCEPT_FILE"
-      :auto-upload="false"
-      :show-upload-button="{ showOnExceedLimit: false }"
-      accept=".pdf,.docx"
-      @change="handleFileChange"
-    >
-      <template #upload-button>
-        <a-tooltip :content="upload_warning_text">
-          <a-button class="upload-button">
-            <template #icon>
-              <icon-upload />
-            </template>
-
-            <template #default>点击上传</template>
-          </a-button>
-        </a-tooltip>
-      </template>
-
-      <template #file-name="{ fileItem }">
-        <a-tooltip :content="fileItem.name">
-          <span>{{ fileItem.name }}</span>
-        </a-tooltip>
-      </template>
-
-      <!-- <template #upload-item="{ fileItem, index }">
-                <div>
-                    <div>{{ fileItem.name }}</div>
-                </div>
-            </template> -->
-    </a-upload>
+    <div class="file-doc-preview-container">
+      <FileItemDocPreview v-for="(item, index) of file_doc_list" :key="item.id + item.index" :file="item.file" />
+    </div>
+
+    <div class="file-image-preview-container">
+      <FileItemImagePreview v-for="(item, index) of file_images_list" :key="item.id + item.index" :file="item.file" />
+    </div>
   </div>
 </template>
 
 <script setup>
 import { ref, computed } from 'vue';
+import FileItemDocPreview from './FileItemDocPreview.vue';
+import FileItemImagePreview from './FileItemImagePreview.vue';
 
 const props = defineProps({
   file_list: Array
 });
 
 /**
- * 文件处理
+ * 判断一个文件的后缀名是否是图片
+ * @param endfix
  */
-const file_list = ref([]);
-const MAX_ACCEPT_FILE = 30;
-const file_total = computed(() => {
-  return file_list.value.length;
+const isImageEndfix = (endfix) => {
+  return endfix === 'jpg' || endfix === 'png' || endfix === 'jpeg' || endfix === 'gif';
+};
+
+/**
+ * 图片类型的列表
+ */
+const file_images_list = computed(() => {
+  const arr = [];
+
+  props.file_list.forEach((item, index) => {
+    const file_name = item.file.name;
+    const name_info_arr = file_name.split('.');
+    const end_fix = name_info_arr[name_info_arr.length - 1];
+
+    const is_image = isImageEndfix(end_fix);
+
+    if (is_image) {
+      arr.push(item);
+    }
+  });
+
+  return arr;
 });
 
-const upload_warning_text = '';
+/**
+ * 文档类型的列表
+ */
+const file_doc_list = computed(() => {
+  const arr = [];
+
+  props.file_list.forEach((item, index) => {
+    const file_name = item.file.name;
+    const name_info_arr = file_name.split('.');
+    const end_fix = name_info_arr[name_info_arr.length - 1];
+
+    const is_image = isImageEndfix(end_fix);
+
+    if (!is_image) {
+      arr.push(item);
+    }
+  });
 
-const handleFileChange = () => {};
+  return arr;
+});
 </script>
 
 <style lang="css" scoped>
 .file-upload-preview-area {
-  display: flex;
+  /* display: flex;
   justify-content: space-between;
-  align-items: center;
+  align-items: center; */
+}
+
+.file-doc-preview-container {
+  display: flex;
+}
+.file-image-preview-container {
+  display: flex;
 }
 </style>

+ 1 - 1
src/views/conversation/ConversationChatContainer.vue

@@ -484,7 +484,7 @@ onUnmounted(() => {
   display: flex;
   flex-direction: column;
 
-  /* background-color: #aaa; */
+  background-color: #aaa;
 }
 .conversation-chat-content {
   flex-grow: 1;