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