From ce5d4a0d224aaab33b4d8ddd7b4af170882d406e Mon Sep 17 00:00:00 2001
From: wujingjing <gersonwu@qq.com>
Date: 星期二, 04 三月 2025 14:30:28 +0800
Subject: [PATCH] 预览业务表格

---
 src/components/table/colFilter/ColFilter.vue                          |    6 +
 src/components/chat/components/playBar/PlayBar.vue                    |   26 ++++-
 src/components/chat/user/index.vue                                    |   18 +++
 src/components/chat/components/playBar/businessTable/index.vue        |   59 ++++++++---
 src/components/chat/components/playBar/businessTablePreview/index.vue |  153 ++++++++++++++++++++++++++++++
 5 files changed, 237 insertions(+), 25 deletions(-)

diff --git a/src/components/chat/components/playBar/PlayBar.vue b/src/components/chat/components/playBar/PlayBar.vue
index 104d981..f1874bd 100644
--- a/src/components/chat/components/playBar/PlayBar.vue
+++ b/src/components/chat/components/playBar/PlayBar.vue
@@ -25,12 +25,14 @@
 				</el-button>
 			</div>
 
-			<div class="set-input ">
+			<div class="set-input">
 				<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 attachList"
 						:key="index"
 						class="flex items-center gap-2 bg-[#f5f5f5] px-2 py-3 rounded-lg w-[220px] relative group"
+						:class="{ 'cursor-pointer': item.type === 'table' }"
+						@click="openAttachPreview(item)"
 					>
 						<template v-if="item.type === 'file'">
 							<el-image
@@ -56,7 +58,7 @@
 						</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="deleteAttachInIndex(index)"
+							@click.stop="deleteAttachInIndex(index)"
 						>
 							<span class="ywifont ywicon-guanbi text-white !text-[10px] font-bold"></span>
 						</div>
@@ -142,6 +144,7 @@
 				@updateInput="updateInputValue"
 			/>
 			<BusinessTable v-model="businessTableIsShow" @submit="submitBusinessTable" />
+			<BusinessTablePreview :data="attachPreviewData" v-model="attachPreviewIsShow" @submit="submitBusinessTable" />
 		</div>
 	</div>
 </template>
@@ -149,15 +152,18 @@
 <script setup lang="ts">
 import type { InputInstance } from 'element-plus';
 import { nextTick, ref } from 'vue';
+import BusinessTable from './businessTable/index.vue';
+import BusinessTablePreview from './businessTablePreview/index.vue';
+import type { Attach } from './hook/useAttach';
+import { useAttach } from './hook/useAttach';
 import { useInputEvent } from './hook/useInputEvent';
+import { useUploadFile } from './hook/useUploadFile';
 import InputTip from './inputTip/index.vue';
 import CommonPhrases from './phrase/CommonPhrases.vue';
 import SceneSwitch from './SceneSwitch.vue';
 import VoicePage from './voicePage/VoicePage.vue';
 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,
@@ -269,6 +275,16 @@
 	businessTableIsShow.value = true;
 };
 //#endregion
+
+//#region ====================== 闄勪欢棰勮 ======================
+const attachPreviewIsShow = ref(false);
+const attachPreviewData = ref<Attach>();
+const openAttachPreview = (item: Attach) => {
+	if (item.type === 'file') return;
+	attachPreviewIsShow.value = true;
+	attachPreviewData.value = item;
+};
+//#endregion
 defineExpose({
 	addPhrase,
 	showSyncTip,
diff --git a/src/components/chat/components/playBar/businessTable/index.vue b/src/components/chat/components/playBar/businessTable/index.vue
index e886004..369b1d3 100644
--- a/src/components/chat/components/playBar/businessTable/index.vue
+++ b/src/components/chat/components/playBar/businessTable/index.vue
@@ -46,19 +46,19 @@
 			</template>
 			<template #main>
 				<div class="w100 h100">
-					<div class="h-full">
+					<div class="h-full flex-column">
+						<ColFilter class="flex-0 ml-auto mb-2" :column-list="tableCheckData" />
 						<el-table
 							v-loading="tableLoading"
 							ref="draggableTableRef"
-							class="h100"
+							class="flex-auto"
 							border
-							:row-class-name="isDragStatus ? 'cursor-move' : 'cursor-pointer'"
 							:data="tableData"
 							highlight-current-row
 							@sort-change="handleSortChange"
 						>
 							<el-table-column
-								v-for="item in tableColumns"
+								v-for="item in visibleTableColumns"
 								:prop="item.name"
 								sortable="custom"
 								:key="item.name"
@@ -89,6 +89,7 @@
 import { useCompRef } from '/@/utils/types';
 import { convertListToTree, debounce, travelTree } from '/@/utils/util';
 import { ElMessage } from 'element-plus';
+import ColFilter from '/@/components/table/colFilter/ColFilter.vue';
 const dialogIsShow = defineModel({
 	type: Boolean,
 });
@@ -106,13 +107,18 @@
 		submitLoading.value = false;
 	});
 	const tables = checkedList.map((item) => {
+		const indexMapItem = getIndexMapItem(item.columns);
 		return {
 			title: item.title,
-			columns: item.columns.map((item) => {
-				return item.title;
-			}),
+			columns: item.columns
+				.filter((item) => item.isShow)
+				.map((item) => {
+					return item.title;
+				}),
 			values: item.tableData.map((item) => {
-				return Object.values(item);
+				return Object.values(item).filter((item, index) => {
+					return indexMapItem.get(index).isShow;
+				});
 			}),
 		};
 	});
@@ -121,7 +127,6 @@
 };
 const submitFormValue = async () => {
 	const data = await getSubmitData();
-
 	emit('submit', data);
 	dialogIsShow.value = false;
 };
@@ -169,6 +174,7 @@
 					...item,
 					// 鍒濆鏌ヨ鍊�
 					values: [''],
+					isShow: true,
 				};
 			});
 		}
@@ -221,10 +227,28 @@
 const tableData = computed(() => {
 	return currentNode.value?.tableData || [];
 });
-const isDragStatus = ref(false);
 
 const tableColumns = computed(() => {
 	return currentNode.value?.columns || [];
+});
+
+const tableCheckData = computed(() => {
+	return tableColumns.value.map((item) => {
+		return {
+			prop: item.name,
+			label: item.title,
+			get isShow() {
+				return item.isShow;
+			},
+			set isShow(value) {
+				item.isShow = value;
+			},
+		};
+	});
+});
+
+const visibleTableColumns = computed(() => {
+	return tableColumns.value.filter((item) => item.isShow);
 });
 const getTableData = async () => {
 	// allTableData.value = (res.values || []).map((item) => {
@@ -249,16 +273,9 @@
 	);
 };
 
-const indexMapItem = computed<Map<number, any>>(() => {
-	return new Map(
-		tableColumns.value.map((item, index) => {
-			return [index, item];
-		})
-	);
-});
 //#region ====================== 鏌ヨ ======================
 const getFilterColumns = (node) => {
-	return node?.columns?.filter((item) => item.filter) as any[];
+	return node?.columns?.filter((item) => item.filter && item.isShow) as any[];
 };
 const filterColumns = computed(() => {
 	return getFilterColumns(currentNode.value);
@@ -432,4 +449,10 @@
 		height: calc(90vh - 111px) !important;
 	}
 }
+
+.yw-layout-main {
+	.el-card__body {
+		padding: 10px;
+	}
+}
 </style>
diff --git a/src/components/chat/components/playBar/businessTablePreview/index.vue b/src/components/chat/components/playBar/businessTablePreview/index.vue
new file mode 100644
index 0000000..3e2dcc6
--- /dev/null
+++ b/src/components/chat/components/playBar/businessTablePreview/index.vue
@@ -0,0 +1,153 @@
+<template>
+	<el-dialog
+		class="limit-height"
+		:destroy-on-close="true"
+		v-model="dialogIsShow"
+		width="70%"
+		:close-on-click-modal="false"
+		@closed="closeDialog"
+	>
+		<template #header>
+			<div style="color: #fff">
+				<SvgIcon name="ele-Document" :size="16" style="margin-right: 3px; display: inline; vertical-align: middle" />
+				<span>鏌ョ湅涓氬姟琛�</span>
+			</div>
+		</template>
+		<div class="w100 h100">
+			<div class="h-full flex-column">
+				<ColFilter class="flex-0 ml-auto mb-2" :column-list="tableCheckData" />
+				<el-table v-loading="tableLoading" ref="draggableTableRef" class="flex-auto" border :data="tableData" highlight-current-row>
+					<el-table-column
+						v-for="item in visibleTableColumns"
+						:prop="item.name"
+						sortable
+						:key="item.name"
+						:label="item.title"
+						show-overflow-tooltip
+					>
+					</el-table-column>
+				</el-table>
+			</div>
+		</div>
+	</el-dialog>
+</template>
+
+<script setup lang="ts" name="BusinessTablePreview">
+import type { PropType } from 'vue';
+import { computed, ref, watch } from 'vue';
+// import TableSearch from './search/index.vue';
+import type { Attach } from '../hook/useAttach';
+import ColFilter from '/@/components/table/colFilter/ColFilter.vue';
+const dialogIsShow = defineModel({
+	type: Boolean,
+});
+const props = defineProps({
+	data: {
+		type: Object as PropType<Attach>,
+		default: () => {},
+	},
+});
+const closeDialog = () => {
+	dialogIsShow.value = false;
+	tableData.value = [];
+	tableColumns.value = [];
+};
+
+//#region ====================== 鎸囨爣绠$悊琛ㄦ牸鏁版嵁锛宼able init ======================
+const tableLoading = ref(false);
+const tableData = ref([]);
+
+const tableColumns = ref([]);
+
+const tableCheckData = computed(() => {
+	return tableColumns.value.map((item) => {
+		return {
+			prop: item.name,
+			label: item.title,
+			get isShow() {
+				return item.isShow;
+			},
+			set isShow(value) {
+				item.isShow = value;
+			},
+		};
+	});
+});
+
+const visibleTableColumns = computed(() => {
+	return tableColumns.value.filter((item) => item.isShow);
+});
+
+//#endregion
+
+const getIndexMapItem = (columns) => {
+	return new Map<number, any>(
+		columns.map((item, index) => {
+			return [index, item];
+		})
+	);
+};
+
+//#region ====================== 鏌ヨ ======================
+
+const parseRecordData = (values, columns) => {
+	const indexMapItem = getIndexMapItem(columns);
+	return (values || []).map((item, index) => {
+		const row = {} as any;
+		item?.forEach((item, index) => {
+			row[indexMapItem.get(index).name] = item;
+		});
+		return row;
+	});
+};
+
+//#endregion
+watch(
+	() => dialogIsShow.value,
+	(val) => {
+		if (!val) return;
+		tableColumns.value = (props.data?.model?.columns || []).map((item) => {
+			return {
+				name: item,
+				title: item,
+				isShow: true,
+			};
+		});
+		tableData.value = parseRecordData(props.data?.model?.values || [], tableColumns.value);
+	}
+);
+</script>
+<style scoped lang="scss">
+.set-permission {
+	padding-block: 16px;
+	padding-block-end: 0;
+}
+.set-form {
+	display: block;
+	box-sizing: border-box;
+	height: 100%;
+	padding-block: 16px;
+	.set-form-item {
+		font-weight: 500;
+		color: #667085;
+		font-size: 14px;
+	}
+}
+
+:deep(.el-dialog__body) {
+	height: calc(90vh - 111px) !important;
+}
+</style>
+<style lang="scss">
+.limit-height {
+	.el-dialog__body {
+		height: calc(90vh - 111px) !important;
+	}
+}
+
+.yw-layout-main {
+	.el-card__body {
+		padding: 10px;
+	}
+}
+</style>
diff --git a/src/components/chat/user/index.vue b/src/components/chat/user/index.vue
index 3162078..f07fa3a 100644
--- a/src/components/chat/user/index.vue
+++ b/src/components/chat/user/index.vue
@@ -11,6 +11,8 @@
 								v-for="(item, index) in msg.attachList"
 								:key="index"
 								class="flex items-center gap-2 bg-[#e9e9e9] px-2 py-3 rounded-lg w-[220px] relative group"
+								@click="openAttachPreview(item)"
+								:class="{ 'cursor-pointer': item.type === 'table' }"
 							>
 								<template v-if="item.type === 'file'">
 									<el-image
@@ -42,6 +44,7 @@
 				</div>
 			</div>
 		</div>
+		<BusinessTablePreview :data="attachPreviewData" v-model="attachPreviewIsShow" />
 		<!-- #endregion -->
 
 		<div class="flex px-4 rounded-lg relative flex-row-reverse items-center" :key="`${msg.historyId}_${msg.role}`">
@@ -103,10 +106,13 @@
 </template>
 
 <script setup lang="ts" name="UserMsg">
+import { useClipboard } from '@vueuse/core';
 import { ElMessage } from 'element-plus';
 import { AnswerState, answerTypeMapCom, roleImageMap, type ChatMessage } from '../model/types';
 import { isSharePage } from '/@/stores/chatRoom';
-import { onClickOutside, useClipboard } from '@vueuse/core';
+import { ref } from 'vue';
+import BusinessTablePreview from '../components/playBar/businessTablePreview/index.vue';
+import { Attach } from '../components/playBar/hook/useAttach';
 
 const emit = defineEmits<{
 	(event: 'copyMsg', msgObj: ChatMessage): void;
@@ -135,5 +141,15 @@
 const shareClick = (msg) => {
 	emit('shareClick', msg);
 };
+
+//#region ====================== 闄勪欢棰勮 ======================
+const attachPreviewIsShow = ref(false);
+const attachPreviewData = ref<Attach>();
+const openAttachPreview = (item: Attach) => {
+	if (item.type === 'file') return;
+	attachPreviewIsShow.value = true;
+	attachPreviewData.value = item;
+};
+//#endregion
 </script>
 <style scoped lang="scss"></style>
diff --git a/src/components/table/colFilter/ColFilter.vue b/src/components/table/colFilter/ColFilter.vue
index 315b1ac..9ded84c 100644
--- a/src/components/table/colFilter/ColFilter.vue
+++ b/src/components/table/colFilter/ColFilter.vue
@@ -1,7 +1,7 @@
 <template>
 	<el-dropdown trigger="click" placement="bottom-end" :hideOnClick="false">
 		<span
-			title="鐐瑰嚮閫夋嫨鏄剧ず鍒�"
+			:title="title"
 			class="rounded-full p-1.5 border-[1.5px] hover:text-blue-400 border-gray-200 border-solid cursor-pointer ywifont ywicon-grid !text-[13px] h-fit"
 		></span>
 
@@ -35,6 +35,10 @@
 		type: Boolean,
 		default: true,
 	},
+	title: {
+		type: String,
+		default: '鐐瑰嚮閫夋嫨鏄剧ず鍒�',
+	},
 });
 
 const emit = defineEmits<{

--
Gitblit v1.9.3