<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="flex-0 mb-3 flex items-center gap-2 justify-between font-bold">
|
<div class="">请勾选下方的业务表,按条件筛选数据</div>
|
<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: 'logicalId',
|
label: 'title',
|
children: 'children',
|
}"
|
@check="handleCheck"
|
defaultExpandAll
|
:treedata="listTreeData"
|
:show-title="false"
|
:show-more-operate="false"
|
:show-add="false"
|
:current-node-key="currentListID"
|
: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" 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 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="flex-auto"
|
border
|
:data="tableData"
|
highlight-current-row
|
@sort-change="handleSortChange"
|
>
|
<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>
|
</AHMContainer>
|
<template v-slot:footer>
|
<div>
|
<el-button @click="closeDialog">取 消</el-button>
|
<el-button type="primary" @click="submitFormValue">确 定</el-button>
|
</div>
|
</template>
|
</el-dialog>
|
</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 (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 ======================
|
const leftTreeRef = useCompRef(LeftTreeByMgr);
|
const treeLoading = ref(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 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.type === 'item') {
|
value.columns = value.columns.map((item) => {
|
return {
|
...item,
|
// 初始查询值
|
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.logicalId);
|
});
|
currentNode.value = data;
|
resetRight();
|
|
setOrderMap(data);
|
getTableData();
|
};
|
const getListTreeData = async () => {
|
treeLoading.value = true;
|
const res = await attachApi.getAttachTableList().finally(() => {
|
treeLoading.value = false;
|
});
|
listData.value = res.tables || [];
|
let firstListTreeNode;
|
travelTree(listTreeData.value, (value, index, array, parent) => {
|
if (value.type === 'item') {
|
firstListTreeNode = value;
|
return true;
|
}
|
});
|
if (firstListTreeNode) {
|
handleClickNode(firstListTreeNode);
|
} else {
|
currentNode.value = null;
|
}
|
};
|
//#endregion
|
//#region ====================== 指标管理表格数据,table init ======================
|
const tableLoading = ref(false);
|
const tableData = computed(() => {
|
return currentNode.value?.tableData || [];
|
});
|
|
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);
|
// return item;
|
// });
|
// travelTree(listTreeData.value, (value, index, array, parent) => {
|
// const len = getLenById(allTableData.value, value.id, value);
|
// value.title = `${value.title} (${len})`;
|
// });
|
if (!currentNode.value.tableData) {
|
handleSearchInput();
|
}
|
};
|
//#endregion
|
|
const getIndexMapItem = (columns) => {
|
return new Map<number, any>(
|
columns.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 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,
|
} 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 parseRecordData = (res, columns) => {
|
const indexMapItem = getIndexMapItem(columns);
|
return (res.values || []).map((item, index) => {
|
const row = {} as any;
|
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 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;
|
// 请求 order
|
let sendOrder;
|
if (order === 'descending') {
|
sendOrder = 'DESC';
|
} else if (order === 'ascending') {
|
sendOrder = 'ASC';
|
} else {
|
sendOrder = '';
|
}
|
handleSearchInput(prop, sendOrder, column);
|
};
|
|
watch(
|
() => dialogIsShow.value,
|
(val) => {
|
if (!val) return;
|
getListTreeData();
|
}
|
);
|
</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;
|
display: flex;
|
flex-direction: column;
|
overflow: hidden;
|
}
|
}
|
|
.yw-layout-main {
|
.el-card__body {
|
padding: 10px;
|
}
|
}
|
</style>
|