From 1075860848d14e3d6d1506b91d9c9039433bf4cc Mon Sep 17 00:00:00 2001
From: wujingjing <gersonwu@qq.com>
Date: 星期一, 03 三月 2025 17:40:13 +0800
Subject: [PATCH] 合并附件和业务表格

---
 vite.config.ts                                               |    2 
 src/components/chat/Chat.vue                                 |   34 +++++---
 src/components/chat/components/playBar/PlayBar.vue           |   78 +++++++++++++------
 src/components/chat/components/playBar/hook/useUploadFile.ts |   42 ++++++----
 src/components/chat/components/playBar/hook/useAttach.ts     |   30 +++++++
 5 files changed, 133 insertions(+), 53 deletions(-)

diff --git a/src/components/chat/Chat.vue b/src/components/chat/Chat.vue
index 1878d17..08badfb 100644
--- a/src/components/chat/Chat.vue
+++ b/src/components/chat/Chat.vue
@@ -82,7 +82,6 @@
 import { getCurrentPosition } from '/@/utils/brower';
 const containerRef = useCompRef(ChatContainer);
 const chatListDom = computed(() => containerRef.value?.chatListDom);
-const attachFileList = computed(() => playBarRef.value?.attachFileList ?? []);
 const scrollToBottom = () => {
 	containerRef.value?.scrollToBottom();
 };
@@ -142,10 +141,11 @@
 		raw_mode: roomConfig.value?.[currentRouteId]?.isAnswerByLLM ?? false,
 		...judgeParams,
 	} as any;
-	if(businessTableData.value?.length > 0) {
-		params.tables = JSON.stringify(businessTableData.value);
+	const tableList = attachList.value.filter((item) => item.type === 'table').map((item) => item.model);
+	if (tableList?.length > 0) {
+		params.tables = JSON.stringify(tableList);
 	}
-	
+
 	// if (!position) {
 	// 	const loadingInstance = ElLoadingService({
 	// 		text: '鑾峰彇浣嶇疆涓�...',
@@ -171,9 +171,11 @@
 		currentSampleId = '';
 	}
 	const formDataParams = toFormData(params);
-	for (const item of attachFileList.value) {
+	const fileList = attachList.value.filter((item) => item.type === 'file').map((item) => item.model);
+	for (const item of fileList) {
 		formDataParams.append('files', item.file);
 	}
+	clearAttach();
 	let lastTimestamp = new Date().getTime();
 	questionRes = {};
 	let lastIsResult = false;
@@ -348,7 +350,9 @@
 					stepList.push(stepItem);
 				} else {
 					const lastItem = stepList.at(-1);
-					lastItem.title += chunkRes.value ?? '';
+					if (lastItem) {
+						lastItem.title += chunkRes.value ?? '';
+					}
 				}
 
 				if (chunkRes.mode === 'begin_stream') {
@@ -384,14 +388,13 @@
 	return content;
 };
 const playBarRef = useCompRef(PlayBar);
-const businessTableData = computed(() => playBarRef.value?.businessTableData ?? []);
-const clearMessageContent = () =>
-	(messageContent.value = {
+const attachList = computed(() => playBarRef.value?.attachList ?? []);
+const clearMessageContent = () => {
+	messageContent.value = {
 		type: AnswerType.Text,
 		values: '',
-	});
-	playBarRef.value?.clearFileList();
-	playBarRef.value?.clearBusinessTable();
+	};
+};
 
 let currentSampleId = '';
 
@@ -442,6 +445,13 @@
 	return [userItem, assistantItem];
 };
 
+/**
+ * 娓呴櫎闄勪欢
+ */
+const clearAttach = () => {
+	playBarRef.value?.clearAttach();
+};
+
 const sendChatMessage = async (content: ChatContent = messageContent.value) => {
 	if (!checkCanSend(content)) {
 		return;
diff --git a/src/components/chat/components/playBar/PlayBar.vue b/src/components/chat/components/playBar/PlayBar.vue
index 021966b..260bb8c 100644
--- a/src/components/chat/components/playBar/PlayBar.vue
+++ b/src/components/chat/components/playBar/PlayBar.vue
@@ -26,28 +26,37 @@
 			</div>
 
 			<div class="set-input flex-auto">
-				<div v-if="attachFileList?.length > 0" class="flex gap-3.5 w-full overflow-x-auto px-2 pb-2">
+				<div v-if="attachList?.length > 0" class="flex gap-3.5 w-full overflow-x-auto px-2 pb-2">
 					<div
-						v-for="(item, index) in attachFileList"
+						v-for="(item, index) in attachList"
 						:key="index"
 						class="flex items-center gap-2 bg-[#f5f5f5] px-2 py-3 rounded-lg w-[220px] relative group"
 					>
-						<el-image
-							:zoom-rate="1.2"
-							fit="cover"
-							class="w-[24px] rounded cursor-pointer"
-							v-if="item.groupType === 'image'"
-							:src="item.previewUrl"
-							:preview-src-list="[item.previewUrl]"
-						/>
-						<div v-else class="ywifont !text-[24px] flex-0" :class="[`ywicon-${item.icon}`, item.iconClass]"></div>
-						<div class="flex flex-col gap-0.5 w-full flex-auto">
-							<div class="font-bold over-ellipsis w-full">{{ item.name }}</div>
-							<div class="text-info text-sm over-ellipsis w-full">{{ `${item.type}锛�${item.size}` }}</div>
-						</div>
+						<template v-if="item.type === 'file'">
+							<el-image
+								:zoom-rate="1.2"
+								fit="cover"
+								class="w-[24px] rounded cursor-pointer"
+								v-if="item.model.groupType === 'image'"
+								:src="item.model.previewUrl"
+								:preview-src-list="[item.model.previewUrl]"
+							/>
+							<div v-else class="ywifont !text-[24px] flex-0" :class="[`ywicon-${item.icon}`, item.iconClass]"></div>
+							<div class="flex flex-col gap-0.5 w-full flex-auto">
+								<div class="font-bold over-ellipsis w-full">{{ item.title }}</div>
+								<div class="text-info text-sm over-ellipsis w-full">{{ `${item.model.type}锛�${item.model.size}` }}</div>
+							</div>
+						</template>
+						<template v-if="item.type === 'table'">
+							<div class="ywifont !text-[24px] flex-0" :class="[`ywicon-${item.icon}`, item.iconClass]"></div>
+							<div class="flex flex-col gap-0.5 w-full flex-auto">
+								<div class="font-bold over-ellipsis w-full">{{ item.title }}</div>
+								<div class="text-info text-sm over-ellipsis w-full">{{ `涓氬姟琛ㄦ牸锛�${item.model.values?.length} 鏉¤褰昤 }}</div>
+							</div>
+						</template>
 						<div
 							class="group-hover:visible invisible absolute right-0.5 top-0.5 bg-red-500 flex-center rounded-full p-0.5 cursor-pointer"
-							@click="deleteIndexFile(index)"
+							@click="deleteAttachInIndex(index)"
 						>
 							<span class="ywifont ywicon-guanbi text-white !text-[10px] font-bold"></span>
 						</div>
@@ -148,6 +157,7 @@
 import { useCompRef } from '/@/utils/types';
 import { useUploadFile } from './hook/useUploadFile';
 import BusinessTable from './businessTable/index.vue';
+import { useAttach } from './hook/useAttach';
 const emits = defineEmits(['sendClick', 'stopGenClick']);
 const props = defineProps({
 	isTalking: Boolean,
@@ -226,25 +236,45 @@
 	}, syncMsgDuration);
 };
 
-const { clearFileList, openFileClick, attachFileList, deleteIndexFile } = useUploadFile({
+const { attachList, removeAttach, clearAttach } = useAttach();
+
+const { openFileClick, deleteUploadFile } = useUploadFile({
 	pastTarget: inputRef as any,
+	attachFileList: attachList,
 });
-
-const businessTableData = ref([]);
+const deleteAttachInIndex = (index: number) => {
+	const attach = attachList.value[index];
+	if (attach.type === 'file') {
+		deleteUploadFile(attach.model);
+	}
+	removeAttach(index);
+};
 const submitBusinessTable = (data) => {
-	businessTableData.value = data;
+	attachList.value.push(
+		...data.map((item) => {
+			return {
+				title: item.title,
+				type: 'table',
+				model: item,
+				icon: 'biaoge',
+				iconClass: 'ywicon-biaoge text-[#c5e0ff]',
+			};
+		})
+	);
 };
 
-const clearBusinessTable = () => {
-	businessTableData.value = [];
-};
 //#region ====================== 涓氬姟琛ㄦ牸 ======================
 const businessTableIsShow = ref(false);
 const openBusinessTable = () => {
 	businessTableIsShow.value = true;
 };
 //#endregion
-defineExpose({ addPhrase, showSyncTip, attachFileList, clearFileList, businessTableData, clearBusinessTable });
+defineExpose({
+	addPhrase,
+	showSyncTip,
+	attachList,
+	clearAttach,
+});
 </script>
 <style scoped lang="scss">
 @use './index.scss';
diff --git a/src/components/chat/components/playBar/hook/useAttach.ts b/src/components/chat/components/playBar/hook/useAttach.ts
new file mode 100644
index 0000000..4b7c53e
--- /dev/null
+++ b/src/components/chat/components/playBar/hook/useAttach.ts
@@ -0,0 +1,30 @@
+import { ref } from 'vue';
+
+export type AttachType = 'file' | 'table';
+export type Attach<T = any> = {
+	title: string;
+	type: AttachType;
+	model: T;
+	icon?: string;
+	iconClass?: string;
+};
+
+export const useAttach = () => {
+	const attachList = ref<Attach[]>([]);
+
+	
+
+	const removeAttach = (index: number) => {
+		attachList.value.splice(index, 1);
+	};
+
+	const clearAttach = () => {
+		attachList.value = [];
+	};
+
+	return {
+		attachList,
+		removeAttach,
+		clearAttach,
+	};
+};
diff --git a/src/components/chat/components/playBar/hook/useUploadFile.ts b/src/components/chat/components/playBar/hook/useUploadFile.ts
index 912c6c0..5f01e26 100644
--- a/src/components/chat/components/playBar/hook/useUploadFile.ts
+++ b/src/components/chat/components/playBar/hook/useUploadFile.ts
@@ -1,9 +1,11 @@
 import { useEventListener, useFileDialog } from '@vueuse/core';
-import { ref, type Ref } from 'vue';
+import { type Ref } from 'vue';
+import type { Attach } from './useAttach';
 import { convertFileSize } from '/@/utils/file';
 
 export type UseUploadFileOptions = {
 	pastTarget: Ref<HTMLElement | null>;
+	attachFileList: Ref<Attach<UploadFile>[]>;
 };
 
 export type FileType = 'doc' | 'docx' | 'pdf' | 'md' | 'xls' | 'xlsx' | 'png' | 'jpg' | 'jpeg' | 'gif' | 'json' | 'txt' | 'csv';
@@ -93,10 +95,8 @@
 };
 // const supportFileType = ['doc', 'docx', 'md', 'xls', 'xlsx', 'png', 'jpg', 'jpeg', 'gif', 'json', 'pdf'];
 const supportFileType = ['csv', 'txt'];
-const acceptFiles = [].join(',')
 export const useUploadFile = (options: UseUploadFileOptions) => {
-	const attachFileList = ref<UploadFile[]>([]);
-
+	const { attachFileList } = options;
 	const parseFiles = (files: FileList) => {
 		const filterFiles: UploadFile[] = [];
 		for (const file of files) {
@@ -126,7 +126,24 @@
 				filterFiles.push(uploadFile);
 			}
 		}
-		attachFileList.value.push(...filterFiles);
+		attachFileList.value.push(
+			...filterFiles.map(
+				(item) =>
+					({
+						get title() {
+							return item.name;
+						},
+						type: 'file',
+						model: item,
+						get icon() {
+							return item.icon;
+						},
+						get iconClass() {
+							return item.iconClass;
+						},
+					} as Attach<UploadFile>)
+			)
+		);
 	};
 
 	/**
@@ -140,17 +157,14 @@
 		parseFiles(files);
 	};
 
-	const clearFileList = () => {
-		attachFileList.value = [];
-	};
+
 	const {
 		files,
 		open: openFileDialog,
 		reset: resetOpenFileDialog,
 		onChange: onPickFileChange,
 	} = useFileDialog({
-		accept:
-			'text/csv,text/plain', // Only accept csv and txt files
+		accept: 'text/csv,text/plain', // Only accept csv and txt files
 		reset: true,
 	});
 
@@ -161,21 +175,17 @@
 		if (!files) return;
 		parseFiles(files);
 	});
-	const deleteIndexFile = (index) => {
-		const file = attachFileList.value[index];
-		// Revoke object URL for image files
+	const deleteUploadFile = (file: UploadFile) => {
 		if (file.previewUrl) {
 			URL.revokeObjectURL(file.previewUrl);
 		}
-		attachFileList.value.splice(index, 1);
 	};
 
 	useEventListener(options.pastTarget, 'paste', pasteUpload);
 
 	return {
 		attachFileList,
-		clearFileList,
-		deleteIndexFile,
+		deleteUploadFile,
 		openFileClick,
 	};
 };
diff --git a/vite.config.ts b/vite.config.ts
index 1c82ca0..27dae42 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -58,7 +58,7 @@
 			host: '0.0.0.0',
 			port: env.VITE_PORT as unknown as number,
 			open: JSON.parse(env.VITE_OPEN),
-			hmr: false,
+			hmr: true,
 			proxy: {
 				'/events': {
 					target: 'http://localhost:3000',

--
Gitblit v1.9.3