<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>
|
<!-- bug -->
|
<!-- <div class="flex items-center gap-2">
|
<p>已选择 {{ checkedCount }} 张业务表,共 {{ totalRecordCount }} 条记录</p>
|
<el-button link type="primary" @click="clearSelected">清空</el-button>
|
</div> -->
|
</div>
|
<AMContainer class="flex-auto" type="card" v-loading="submitLoading">
|
<template #aside>
|
<!-- 目录树 -->
|
<LeftTreeByMgr
|
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>
|
<TimeRange
|
ref="timeRangeRef"
|
:data="{
|
origin: {
|
time_step: TimeStepValue.Month,
|
},
|
}"
|
v-if="currentNode?.dateRange"
|
v-model="currentNode.dateRange"
|
@change="handleSearchInput"
|
/>
|
</template> -->
|
<template #main>
|
<div class="w100 h100">
|
<el-table
|
v-show="metricIsShow"
|
v-loading="tableLoading"
|
ref="tableRef"
|
class="h100"
|
border
|
@select="tableSelect"
|
@select-all="tableSelectAll"
|
:data="displayTableData"
|
highlight-current-row
|
:headerCellStyle="{ backgroundColor: 'var(--el-table-header-bg-color)' }"
|
>
|
<el-table-column type="selection" width="55" fixed="left" align="center"></el-table-column>
|
|
<el-table-column prop="model.TITLE" label="指标标题" show-overflow-tooltip> </el-table-column>
|
|
<el-table-column prop="model.OTYPE" label="指标类型" show-overflow-tooltip> </el-table-column>
|
|
<el-table-column prop="model.ONAME" label="指标名称" show-overflow-tooltip> </el-table-column>
|
|
<el-table-column label="操作" width="80" fixed="right" show-overflow-tooltip>
|
<template #default="scope">
|
<div class="space-x-3 items-center flex">
|
<el-tooltip effect="dark" content="曲线查询" placement="top">
|
<i class="ywifont ywicon-tubiao-zhexiantu !text-[20px] text-blue-400 cursor-pointer" @click="handleChartQuery(scope.row)"></i>
|
</el-tooltip>
|
</div>
|
</template>
|
</el-table-column>
|
</el-table>
|
<div v-show="!metricIsShow" class="h-full">
|
<div class="h-full flex-column gap-2">
|
<div class="flex-0 flex items-center">
|
<TimeRange
|
class="flex-0"
|
ref="timeRangeRef"
|
:data="{
|
origin: {
|
time_step: TimeStepValue.Month,
|
},
|
}"
|
v-if="currentMetric?.dateRange"
|
v-model="currentMetric.dateRange"
|
@change="handleSearchInput"
|
/>
|
<div class="text-blue-400 cursor-pointer ml-auto flex items-center gap-0.5" @click="handleBackList">
|
<span class="ywifont ywicon-pre text-[12px]"></span>
|
<span>返回列表</span>
|
</div>
|
</div>
|
|
<ChartDisplay class="flex-auto" :values="currentMetric?.values" :mapRow="currentMetric" />
|
</div>
|
</div>
|
</div>
|
</template>
|
</AMContainer>
|
<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">
|
import { ElMessage, TableInstance } from 'element-plus';
|
import { computed, nextTick, ref, watch } from 'vue';
|
import { DisplayModeType, TimeStepValue } from '../../../chatComponents/summaryCom/components/recordSet/components/types';
|
|
import TimeRange from '../../../chatComponents/summaryCom/components/recordSet/components/TimeRange.vue';
|
// import ChartDisplay from './ChartDisplay.vue';
|
// import TableSearch from './search/index.vue';
|
import ChartDisplay from './ChartDisplay.vue';
|
import AMContainer from '/@/components/layout/AMContainer.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, getRecentDateRange, travelTree } from '/@/utils/util';
|
|
import { formatDate } from '/@/utils/formatTime';
|
const dialogIsShow = defineModel({
|
type: Boolean,
|
});
|
const checkedNodes = ref([]);
|
|
const checkedItemNodes = computed(() => {
|
return checkedNodes.value.filter((item) => item.type === 'metric');
|
});
|
|
const timeRangeRef = useCompRef(TimeRange);
|
const resetBusinessTable = () => {
|
checkedNodes.value = [];
|
};
|
|
const emit = defineEmits(['submit']);
|
const closeDialog = () => {
|
dialogIsShow.value = false;
|
resetBusinessTable();
|
};
|
|
const tableCheckedMap = new Map<string, string[]>();
|
const handleCheck = async (data, obj) => {
|
checkedNodes.value = obj?.checkedNodes ?? [];
|
await getUnGetData(checkedItemNodes.value);
|
if (data.type === 'metric') {
|
handleClickNode(data);
|
}
|
};
|
|
const updateChecked = (selection: any[]) => {
|
const ids = selection.map((item) => item.id);
|
if (currentNode.value) {
|
if (currentNode.value.type === 'item') {
|
tableCheckedMap.set(currentNode.value.id, ids);
|
} else if (currentNode.value.type === 'group') {
|
currentNode.value.children.forEach((item) => {
|
const tableIds = item.tableData.map((item) => item.id);
|
const checkedIds = tableIds.filter((item) => ids.includes(item));
|
tableCheckedMap.set(item.id, checkedIds);
|
});
|
}
|
}
|
};
|
|
const tableSelect = (selection, row) => {
|
updateChecked(selection);
|
};
|
const tableSelectAll = (selection) => {
|
updateChecked(selection);
|
};
|
//#region ====================== 模式切换 ======================
|
const showMode = ref(DisplayModeType.Chart);
|
|
//#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) => {
|
return {
|
title: item?.model?.TITLE,
|
columns: item.values?.columns,
|
values: item.values?.records,
|
};
|
});
|
return tables;
|
};
|
const submitFormValue = async () => {
|
const checkedList = [];
|
|
const ids = Array.from(tableCheckedMap.values()).flat();
|
travelTree(listTreeData.value, (value, index, array, parent) => {
|
if (value.type === 'item') {
|
for (const item of value.tableData) {
|
if (item.id && ids.includes(item.id)) {
|
checkedList.push(item);
|
}
|
}
|
}
|
});
|
if (!checkedList?.length) {
|
ElMessage.warning('请勾选指标!');
|
return;
|
}
|
const data = await getSubmitData(checkedList);
|
emit('submit', data);
|
dialogIsShow.value = false;
|
};
|
const tableRef = ref<TableInstance>(null);
|
//#region ====================== 左侧树数据,tree init ======================
|
const leftTreeRef = useCompRef(LeftTreeByMgr);
|
const treeLoading = ref(false);
|
const currentListID = computed(() => currentNode.value?.id);
|
|
const listTreeData = ref([]);
|
const currentNode = ref(null);
|
|
const requestListTreeData = async (listData: any[]) => {
|
const listDataWithType = listData.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',
|
});
|
|
const requestParams = listData.map((item) => {
|
return {
|
id: item.id,
|
name: item.title,
|
};
|
});
|
|
const p = Promise.all(
|
requestParams.map((item) => {
|
return attachApi.queryAttachMetricNamesByPost(item);
|
})
|
);
|
|
const res = await p;
|
|
const idMapMetricName = new Map(
|
requestParams.map((item, index) => {
|
return [item.id, res[index].values ?? []];
|
})
|
);
|
|
for (const metricItem of listDataWithType) {
|
const metricId = metricItem.id;
|
if (idMapMetricName.has(metricId)) {
|
const metricNames = idMapMetricName.get(metricId);
|
metricItem.tableData = metricNames.map((item) => {
|
const id = `${item.OTYPE}-${item.ONAME}`;
|
return {
|
metric: metricItem,
|
id,
|
logicalId: `metric-${id}`,
|
title: item.TITLE,
|
type: 'metric',
|
model: item,
|
};
|
});
|
|
tableCheckedMap.set(metricItem.id, []);
|
}
|
}
|
return treeData;
|
};
|
|
const resetRight = () => {
|
showMode.value = DisplayModeType.Chart;
|
metricIsShow.value = true;
|
timeRangeRef.value?.resetQuickPickValue();
|
};
|
|
const resetDlgClose = () => {
|
tableCheckedMap.clear();
|
resetRight();
|
};
|
|
const displayTableData = computed(() => {
|
if (!currentNode.value) return [];
|
const type = currentNode.value.type;
|
if (type === 'item') {
|
const result = currentNode.value.tableData;
|
return result;
|
} else if (type === 'group') {
|
const result = currentNode.value.children?.flatMap((item) => item.tableData) ?? [];
|
return result;
|
}
|
return [];
|
});
|
|
const getCurrentNodeCheckedIds = () => {
|
if (!currentNode.value) return [];
|
const type = currentNode.value.type;
|
if (type === 'item') {
|
return tableCheckedMap.get(currentNode.value.id);
|
} else if (type === 'group') {
|
return currentNode.value.children?.flatMap((item) => tableCheckedMap.get(item.id)) ?? [];
|
}
|
return [];
|
};
|
|
const setTableChecked = () => {
|
tableRef.value?.clearSelection();
|
const ids = getCurrentNodeCheckedIds();
|
const rows = displayTableData.value.filter((item) => ids.includes(item.id));
|
nextTick(() => {
|
for (const item of rows) {
|
tableRef.value?.toggleRowSelection(item, true);
|
}
|
});
|
};
|
|
const handleClickNode = (data) => {
|
// if (data.type === 'group' || data.type === 'item') {
|
// ElMessage.warning('请选择指标');
|
// return;
|
// }
|
nextTick(() => {
|
leftTreeRef.value?.treeRef.setCurrentKey(data.logicalId);
|
});
|
currentNode.value = data;
|
resetRight();
|
setTableChecked();
|
};
|
const getListTreeData = async () => {
|
treeLoading.value = true;
|
const res = await attachApi.getAttachMetricListByPost().finally(() => {
|
treeLoading.value = false;
|
});
|
const listData = res.metrics || [];
|
|
listTreeData.value = await requestListTreeData(listData);
|
let firstListTreeNode;
|
travelTree(listTreeData.value, (value, index, array, parent) => {
|
if (value.type === 'group') {
|
firstListTreeNode = value;
|
return true;
|
}
|
});
|
if (firstListTreeNode) {
|
handleClickNode(firstListTreeNode);
|
} else {
|
currentNode.value = null;
|
}
|
};
|
//#endregion
|
//#region ====================== 指标管理表格数据,table init ======================
|
const tableLoading = ref(false);
|
const currentMetric = ref(null);
|
|
const metricIsShow = ref(true);
|
|
const handleChartQuery = async (row) => {
|
metricIsShow.value = false;
|
currentMetric.value = row;
|
if (!row.values) {
|
handleSearchInput();
|
}
|
};
|
const handleBackList = () => {
|
metricIsShow.value = true;
|
currentMetric.value = null;
|
};
|
//#endregion
|
|
//#region ====================== 查询 ======================
|
|
const getSearchParams = (node) => {
|
if (!node.dateRange) {
|
const dateRange = getRecentDateRange(90).map((item) => formatDate(item));
|
node.dateRange = dateRange;
|
}
|
const metricsNameModel = JSON.stringify(node.model);
|
|
const params = {
|
id: node.metric?.id,
|
start_time: node.dateRange[0],
|
end_time: node.dateRange[1],
|
quota_keys: metricsNameModel,
|
limit: 100,
|
} as any;
|
|
return params;
|
};
|
|
const handleSearchItem = async (node) => {
|
const params = getSearchParams(node);
|
|
tableLoading.value = true;
|
const res = await attachApi.queryAttachMetricValuesByPost(params).finally(() => {
|
tableLoading.value = false;
|
});
|
node.values = res ?? {};
|
// node.values = parseRecordData(res, node.columns ?? []);
|
};
|
|
const handleSearchInput = async () => {
|
handleSearchItem(currentMetric.value).then(() => {
|
// 往 currentMetric.value 中添加 values 会导致 table 数据更新,需要重新设置选中状态
|
setTableChecked();
|
});
|
};
|
|
const getUnGetData = async (checkedList: any[]) => {
|
const getDataPromiseList = [];
|
for (const item of checkedList) {
|
if (!item.values) {
|
getDataPromiseList.push(handleSearchItem(item));
|
}
|
}
|
// 等待所有数据获取完成
|
await Promise.all(getDataPromiseList);
|
setTableChecked();
|
};
|
|
//#endregion
|
|
watch(
|
() => dialogIsShow.value,
|
(val) => {
|
if (!val) {
|
resetDlgClose();
|
} else {
|
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>
|