From 0159386060edb946f29b5adcd9659dbfac06d6e1 Mon Sep 17 00:00:00 2001
From: wujingjing <gersonwu@qq.com>
Date: 星期一, 10 三月 2025 18:17:32 +0800
Subject: [PATCH] 指标附件

---
 src/components/chat/components/playBar/businessTable/index.vue |  424 ++++++++++++++++++++++++++++++++++++++++++++++------
 1 files changed, 375 insertions(+), 49 deletions(-)

diff --git a/src/components/chat/components/playBar/businessTable/index.vue b/src/components/chat/components/playBar/businessTable/index.vue
index ac99ede..fe3eace 100644
--- a/src/components/chat/components/playBar/businessTable/index.vue
+++ b/src/components/chat/components/playBar/businessTable/index.vue
@@ -1,57 +1,101 @@
 <template>
-	<el-dialog :destroy-on-close="true" v-model="dialogIsShow" width="70%" :close-on-click-modal="false" @closed="closeDialog">
+	<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>
+				<span>閫夋嫨涓氬姟琛ㄦ暟鎹�</span>
 			</div>
 		</template>
-		<AHMContainer type="card">
+		<div class="flex-0 mb-3 flex items-center gap-2 justify-between font-bold">
+			<div class="">璇峰嬀閫変笅鏂圭殑涓氬姟琛紝鎸夋潯浠剁瓫閫夋暟鎹�</div>
+			<!-- bug -->
+			<!-- <div class="flex items-center gap-2">
+				<p>宸查�夋嫨 {{ checkedCount }} 寮犱笟鍔¤〃锛屽叡 {{ totalRecordCount }} 鏉¤褰�</p>
+				<el-button link type="primary" @click="clearSelected">娓呯┖</el-button>
+			</div> -->
+		</div>
+		<AHMContainer class="flex-auto" type="card" v-loading="submitLoading">
 			<template #aside>
 				<!-- 鐩綍鏍� -->
 				<LeftTreeByMgr
+					showCheckbox
 					v-loading="treeLoading"
 					class="h100"
 					ref="leftTreeRef"
 					:defaultProps="{
-						id: 'id',
+						id: 'logicalId',
 						label: 'title',
 						children: 'children',
 					}"
+					@check="handleCheck"
 					defaultExpandAll
 					:treedata="listTreeData"
-					title-name="鍦烘櫙鍒楄〃"
+					:show-title="false"
 					:show-more-operate="false"
 					:show-add="false"
 					:current-node-key="currentListID"
-					:node-icon="() => 'ele-Document'"
+					:folder-icon="(_, data) => data.type === 'group'"
 					@click="handleClickNode"
 				>
 				</LeftTreeByMgr>
 			</template>
 			<template #header>
 				<el-form ref="queryFormRef" :inline="true" class="relative">
-					<el-form-item :label="item.title" prop="title" v-for="item in filterColumns as any" :key="item.name">
-						<TableSearch v-model="item.value" :filter="item.filter" @input="debounceSearch(item)" />
+					<el-form-item :label="item.title" prop="title" v-for="item in filterColumns as any" :key="item.name" class="items-center">
+						<TableSearch v-model="item.values" :filter="item.filter" @input="debounceSearch()" />
 					</el-form-item>
 				</el-form>
 			</template>
 			<template #main>
 				<div class="w100 h100">
-					<div class="h-full">
+					<div class="h-full flex-column gap-2">
+						<div class="flex-0 flex-items-center flex-wrap">
+							<div class="ml-auto space-x-2 flex-items-center">
+								<DisplayMode
+									v-if="isChart"
+									:order="modeChangeOrder"
+									v-model="showMode"
+									:modeTypeMap="displayModeTypeMap"
+									@change="displayModeChange"
+								/>
+								<ColFilter class="flex-0 ml-auto" :column-list="tableCheckData" />
+							</div>
+						</div>
 						<el-table
+							v-show="showTable"
 							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" :prop="item.name" :label="item.title" show-overflow-tooltip>
+							<el-table-column
+								v-for="item in visibleTableColumns"
+								:prop="item.name"
+								sortable="custom"
+								:key="item.name"
+								:label="item.title"
+								show-overflow-tooltip
+							>
 							</el-table-column>
 						</el-table>
+						<template v-if="isRendered">
+							<ChartDisplay
+								class="flex-auto"
+								v-show="showMode === DisplayModeType.Chart"
+								:table-schema="currentNode"
+								:records="tableData"
+							/>
+						</template>
 					</div>
 				</div>
 			</template>
@@ -66,21 +110,109 @@
 </template>
 
 <script setup lang="ts" name="BusinessTable">
+import { ElMessage } from 'element-plus';
 import { computed, nextTick, ref, watch } from 'vue';
+import { DisplayModeType, displayModeTypeMap } from '../../../chatComponents/summaryCom/components/recordSet/components/types';
+import ChartDisplay from './ChartDisplay.vue';
 import TableSearch from './search/index.vue';
 import * as attachApi from '/@/api/attach';
+import DisplayMode from '/@/components/chat/chatComponents/summaryCom/components/recordSet/components/DisplayMode.vue';
 import AHMContainer from '/@/components/layout/AHMContainer.vue';
+import ColFilter from '/@/components/table/colFilter/ColFilter.vue';
 import LeftTreeByMgr from '/@/components/tree/leftTreeByMgr.vue';
 import { useCompRef } from '/@/utils/types';
 import { convertListToTree, debounce, travelTree } from '/@/utils/util';
 const dialogIsShow = defineModel({
 	type: Boolean,
 });
+const checkedNodes = ref([]);
+const clearSelected = () => {
+	leftTreeRef.value?.treeRef.setCheckedKeys([]);
+	checkedNodes.value = [];
+};
 
+const checkedItemNodes = computed(() => {
+	return checkedNodes.value.filter((item) => item.type === 'item');
+});
+
+const checkedCount = computed(() => {
+	return checkedItemNodes.value.length;
+});
+const totalRecordCount = computed(() => {
+	return checkedItemNodes.value.reduce((acc, item) => acc + (item.tableData?.length ?? 0), 0);
+});
+
+const resetBusinessTable = () => {
+	checkedNodes.value = [];
+};
+
+const emit = defineEmits(['submit']);
 const closeDialog = () => {
 	dialogIsShow.value = false;
+	resetBusinessTable();
 };
-const submitFormValue = () => {
+const handleCheck = async (data, obj) => {
+	checkedNodes.value = obj?.checkedNodes ?? [];
+	await getUnGetData(checkedItemNodes.value);
+	if (data.type === 'item') {
+		handleClickNode(data);
+	}
+};
+
+const isChart = computed(() => {
+	return !!currentNode.value?.is_chart;
+});
+
+const showTable = computed(() => {
+	return !isChart.value || (isChart.value && showMode.value === DisplayModeType.List);
+});
+
+//#region ====================== 妯″紡鍒囨崲 ======================
+const showMode = ref(DisplayModeType.List);
+const modeChangeOrder = [DisplayModeType.List, DisplayModeType.Chart];
+
+const isRendered = ref(false);
+const displayModeChange = (val: DisplayModeType) => {
+	if (val === DisplayModeType.Chart) {
+		nextTick(() => {
+			isRendered.value = true;
+		});
+	}
+};
+//#endregion
+const submitLoading = ref(false);
+const getSubmitData = async (checkedList: any[]) => {
+	submitLoading.value = true;
+	await getUnGetData(checkedList).finally(() => {
+		submitLoading.value = false;
+	});
+	const tables = checkedList.map((item) => {
+		const indexMapItem = getIndexMapItem(item.columns);
+		return {
+			title: item.title,
+			columns: item.columns
+				.filter((item) => item.isShow)
+				.map((item) => {
+					return item.title;
+				}),
+			values: item.tableData.map((item) => {
+				return Object.values(item).filter((item, index) => {
+					return indexMapItem.get(index).isShow;
+				});
+			}),
+		};
+	});
+
+	return tables;
+};
+const submitFormValue = async () => {
+	const checkedList = leftTreeRef.value?.treeRef.getCheckedNodes().filter((item) => item.type === 'item');
+	if (!checkedList?.length) {
+		ElMessage.warning('璇峰嬀閫変笟鍔¤〃锛�');
+		return;
+	}
+	const data = await getSubmitData(checkedList);
+	emit('submit', data);
 	dialogIsShow.value = false;
 };
 //#region ====================== 宸︿晶鏍戞暟鎹紝tree init ======================
@@ -89,52 +221,126 @@
 const listData = ref([]);
 const currentListID = computed(() => currentNode.value?.id);
 const currentNode = ref(null);
+const getRowByLogicalId = (logicalId) => {
+	let row;
+	travelTree(listTreeData.value, (value, index, array, parent) => {
+		if (value.logicalId === logicalId) {
+			row = value;
+
+			return true;
+		}
+	});
+	return row;
+};
 const listTreeData = computed(() => {
-	const treeData = convertListToTree(listData.value, {
-		ID: 'id',
-		ParentID: 'group',
+	const listDataWithType = listData.value.map((item) => ({
+		id: item.id,
+		logicalId: `item-${item.id}`,
+		logicalParentId: `group-${item.group}`,
+		...item,
+		type: 'item',
+	}));
+	const groupList = Array.from(new Set(listDataWithType.filter((item) => item.group).map((item) => item.group))).map((item) => ({
+		id: item,
+		title: item,
+		logicalId: `group-${item}`,
+		type: 'group',
+	}));
+	const treeData = convertListToTree(listDataWithType.concat(groupList), {
+		ID: 'logicalId',
+		ParentID: 'logicalParentId',
 		Children: 'children',
 	});
 
 	travelTree(treeData, (value, index, array, parent) => {
-		if (value.columns) {
+		if (value.type === 'item') {
 			value.columns = value.columns.map((item) => {
 				return {
 					...item,
 					// 鍒濆鏌ヨ鍊�
-					value: '',
+					values: [''],
+					isShow: true,
 				};
 			});
 		}
 	});
-
 	return treeData;
 });
+/**
+ * 璁板綍 orderMap
+ */
+const setOrderMap = (node) => {
+	if (!node.orderMap) {
+		node.orderMap = new Map();
+	}
+};
+
+const resetRight = () => {
+	showMode.value = DisplayModeType.List;
+};
+
 const handleClickNode = (data) => {
+	if (data.type === 'group') {
+		ElMessage.warning('璇烽�夋嫨涓氬姟琛�');
+		return;
+	}
 	nextTick(() => {
-		leftTreeRef.value?.treeRef.setCurrentKey(data.id);
+		leftTreeRef.value?.treeRef.setCurrentKey(data.logicalId);
 	});
 	currentNode.value = data;
+	resetRight();
+
+	setOrderMap(data);
 	getTableData();
 };
 const getListTreeData = async () => {
-	const res = await attachApi.getAttachTableList();
+	treeLoading.value = true;
+	const res = await attachApi.getAttachTableList().finally(() => {
+		treeLoading.value = false;
+	});
 	listData.value = res.tables || [];
-	const firstListTreeNode = listTreeData.value[0];
+	let firstListTreeNode;
+	travelTree(listTreeData.value, (value, index, array, parent) => {
+		if (value.type === 'item') {
+			firstListTreeNode = value;
+			return true;
+		}
+	});
 	if (firstListTreeNode) {
 		handleClickNode(firstListTreeNode);
 	} else {
-		tableData.value = [];
 		currentNode.value = null;
 	}
 };
 //#endregion
 //#region ====================== 鎸囨爣绠$悊琛ㄦ牸鏁版嵁锛宼able init ======================
 const tableLoading = ref(false);
-const tableData = ref([]);
-const isDragStatus = ref(false);
+const tableData = computed(() => {
+	return currentNode.value?.tableData || [];
+});
 
-const tableColumns = ref([]);
+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) => {
 	// 	item.create_time = item.create_time?.slice(0, 10);
@@ -144,52 +350,151 @@
 	// 	const len = getLenById(allTableData.value, value.id, value);
 	// 	value.title = `${value.title} (${len})`;
 	// });
-	tableColumns.value = currentNode.value.columns;
+	if (!currentNode.value.tableData) {
+		handleSearchInput();
+	}
 };
 //#endregion
-const indexMapItem = computed(() => {
-	return new Map(
-		tableColumns.value.map((item, index) => {
+
+const getIndexMapItem = (columns) => {
+	return new Map<number, any>(
+		columns.map((item, index) => {
 			return [index, item];
 		})
 	);
-});
-//#region ====================== 鏌ヨ ======================
-const handleSearchInput = async (item) => {
-	const params = {
-		id: currentNode.value.id,
-	} as any;
+};
 
-	if (filterColumns.value && filterColumns.value.length) {
-		params.filter = filterColumns.value.map((item) => {
+//#region ====================== 鏌ヨ ======================
+const getFilterColumns = (node) => {
+	return node?.columns?.filter((item) => item.filter) as any[];
+};
+const filterColumns = computed(() => {
+	return getFilterColumns(currentNode.value);
+});
+const getUnEmptyFilter = (columns) => {
+	const result = columns
+		?.map((item) => {
 			return {
 				col: item.name,
 				filter: item.filter,
-				value: item.value,
+				values: item.values,
 			};
+			// values 涓嶈兘涓虹┖
+		})
+		.filter((item) => {
+			if (item.filter === 'like') {
+				return item.values.every((item) => !!item);
+			} else if (item.filter === 'time_range') {
+				return !!item.values?.some((item) => !!item);
+			} else {
+				return true;
+			}
 		});
+
+	return result;
+};
+const getSearchParams = (node) => {
+	const params = {
+		id: node.id,
+		limit:100
+	} as any;
+	const filterColumns = getFilterColumns(node);
+	const unEmptyFilterColumns = getUnEmptyFilter(filterColumns);
+	if (unEmptyFilterColumns && unEmptyFilterColumns.length) {
+		params.filter = JSON.stringify(unEmptyFilterColumns);
 	}
+	setOrderMap(node);
+	const orderMap = node.orderMap;
+	const orderList = Array.from(orderMap.entries())
+		.map(([key, value]) => {
+			return {
+				col: key,
+				order: value,
+			};
+		})
+		.filter((item) => !!item.order);
+	if (orderList?.length > 0) {
+		params.order = JSON.stringify(orderList);
+	}
+	return params;
+};
 
-	const res = await attachApi.queryAttachTableRecords(params);
-	tableData.value = (res.values || []).map((item, index) => {
+const parseRecordData = (res, columns) => {
+	const indexMapItem = getIndexMapItem(columns);
+	return (res.values || []).map((item, index) => {
 		const row = {} as any;
-
-		item?.forEach((item, index) => {
-			row[indexMapItem.value.get(index).name] = item;
+		item?.forEach((item, index, array) => {
+			if (indexMapItem.get(index)) {
+				row[indexMapItem.get(index).name] = item;
+			}
 		});
 		return row;
 	});
 };
 
+const handleSearchItem = async (node, prop?, order?, column?) => {
+	const params = getSearchParams(node);
+	// 淇涓哄綋鍓嶈淇敼鐨� order
+	if (prop) {
+		// const foundItem = params.order.find((item) => item.col === prop);
+		// if (foundItem) {
+		// 	foundItem.order = order;
+		// }
+		if (order) {
+			// 鎺掑簭鍙細鏈変竴涓瓧娈�
+			params.order = JSON.stringify([
+				{
+					col: prop,
+					order: order,
+				},
+			]);
+		} else {
+			params.order = undefined;
+		}
+	}
+	tableLoading.value = true;
+	const res = await attachApi.queryAttachTableRecords(params).finally(() => {
+		tableLoading.value = false;
+	});
+	if (prop) {
+		const orderMap = node.orderMap;
+		orderMap.clear();
+		orderMap.set(prop, order);
+		column.order = getEleOrder(order);
+	}
+	node.tableData = parseRecordData(res, node.columns ?? []);
+};
+
+const handleSearchInput = async (prop?, order?, column?) => {
+	handleSearchItem(currentNode.value, prop, order, column);
+};
+
+const getUnGetData = async (checkedList: any[]) => {
+	const getDataPromiseList = [];
+	for (const item of checkedList) {
+		if (!item.tableData) {
+			getDataPromiseList.push(handleSearchItem(item));
+		}
+	}
+	// 绛夊緟鎵�鏈夋暟鎹幏鍙栧畬鎴�
+	await Promise.all(getDataPromiseList);
+};
+
 const debounceSearch = debounce(handleSearchInput, 400);
 //#endregion
-
-const filterColumns = computed(() => {
-	return tableColumns.value.filter((item) => item.filter) as any[];
-});
-const orderMap = new Map();
+const getEleOrder = (order) => {
+	if (order === 'DESC') {
+		return 'descending';
+	} else if (order === 'ASC') {
+		return 'ascending';
+	} else {
+		return '';
+	}
+};
 
 const handleSortChange = ({ column, prop, order }) => {
+	setOrderMap(currentNode.value);
+	const orderMap = currentNode.value.orderMap;
 	// 鎭㈠鍘熺姸锛屾洿鏂板悗鍐嶆樉绀烘帓搴忕姸鎬�
 	const curOrder = orderMap.get(prop) ?? null;
 	column.order = curOrder;
@@ -202,6 +507,7 @@
 	} else {
 		sendOrder = '';
 	}
+	handleSearchInput(prop, sendOrder, column);
 };
 
 watch(
@@ -228,4 +534,24 @@
 		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;
+		display: flex;
+		flex-direction: column;
+		overflow: hidden;
+	}
+}
+
+.yw-layout-main {
+	.el-card__body {
+		padding: 10px;
+	}
+}
 </style>

--
Gitblit v1.9.3