| | |
| | | <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" class="h100" v-loading="submitLoading"> |
| | | <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"> |
| | | <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" |
| | | v-for="item in visibleTableColumns" |
| | | :prop="item.name" |
| | | sortable="custom" |
| | | :key="item.name" |
| | |
| | | > |
| | | </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> |
| | |
| | | </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 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 () => { |
| | | const getSubmitData = async (checkedList: any[]) => { |
| | | submitLoading.value = true; |
| | | await getUnGetData().finally(() => { |
| | | await getUnGetData(checkedList).finally(() => { |
| | | submitLoading.value = false; |
| | | }); |
| | | const tables = listTreeData.value.map((item) => { |
| | | 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; |
| | | }); |
| | | }), |
| | | }; |
| | | }); |
| | |
| | | return tables; |
| | | }; |
| | | const submitFormValue = async () => { |
| | | const data = await getSubmitData(); |
| | | 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; |
| | | }; |
| | |
| | | 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, |
| | | // 初始查询值 |
| | | values: [''], |
| | | isShow: true, |
| | | }; |
| | | }); |
| | | } |
| | | }); |
| | | |
| | | return treeData; |
| | | }); |
| | | /** |
| | |
| | | } |
| | | }; |
| | | |
| | | 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(); |
| | | }; |
| | |
| | | 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 { |
| | |
| | | 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) => { |
| | |
| | | ); |
| | | }; |
| | | |
| | | 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[]; |
| | |
| | | const filterColumns = computed(() => { |
| | | return getFilterColumns(currentNode.value); |
| | | }); |
| | | |
| | | const getSearchParams = (node) => { |
| | | const params = { |
| | | id: node.id, |
| | | } as any; |
| | | const filterColumns = getFilterColumns(node); |
| | | if (filterColumns && filterColumns.length) { |
| | | params.filter = filterColumns.map((item) => { |
| | | const getUnEmptyFilter = (columns) => { |
| | | const result = columns |
| | | ?.map((item) => { |
| | | return { |
| | | col: item.name, |
| | | filter: item.filter, |
| | | 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, |
| | | }; |
| | | }); |
| | | const orderList = Array.from(orderMap.entries()) |
| | | .map(([key, value]) => { |
| | | return { |
| | | col: key, |
| | | order: value, |
| | | }; |
| | | }) |
| | | .filter((item) => !!item.order); |
| | | if (orderList?.length > 0) { |
| | | params.order = orderList; |
| | | params.order = JSON.stringify(orderList); |
| | | } |
| | | return params; |
| | | }; |
| | |
| | | const indexMapItem = getIndexMapItem(columns); |
| | | return (res.values || []).map((item, index) => { |
| | | const row = {} as any; |
| | | item?.forEach((item, index) => { |
| | | row[indexMapItem.get(index).name] = item; |
| | | item?.forEach((item, index, array) => { |
| | | if (indexMapItem.get(index)) { |
| | | row[indexMapItem.get(index).name] = item; |
| | | } |
| | | }); |
| | | return row; |
| | | }); |
| | |
| | | // if (foundItem) { |
| | | // foundItem.order = order; |
| | | // } |
| | | // 排序只会有一个字段 |
| | | params.order = [ |
| | | { |
| | | col: prop, |
| | | 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(() => { |
| | |
| | | orderMap.set(prop, order); |
| | | column.order = getEleOrder(order); |
| | | } |
| | | node.tableData = parseRecordData(res, tableColumns.value); |
| | | node.tableData = parseRecordData(res, node.columns ?? []); |
| | | }; |
| | | |
| | | const handleSearchInput = async (prop?, order?, column?) => { |
| | | handleSearchItem(currentNode.value, prop, order, column); |
| | | }; |
| | | |
| | | const getUnGetData = async () => { |
| | | const getUnGetData = async (checkedList: any[]) => { |
| | | const getDataPromiseList = []; |
| | | for (const item of listTreeData.value) { |
| | | for (const item of checkedList) { |
| | | if (!item.tableData) { |
| | | getDataPromiseList.push(handleSearchItem(item)); |
| | | } |
| | |
| | | .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> |