wujingjing
2024-11-14 11738cfed9fced6927376d6de27bb8ba9077ba2f
指标图谱
已修改3个文件
已添加2个文件
824 ■■■■ 文件已修改
src/components/graph/treeGraph/TreeGraph.vue 346 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/graph/treeGraph/types.ts 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/project/yw/systemManage/agentGraph/AgentGraph.vue 353 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/project/yw/systemManage/agentGraph/types.ts 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/project/yw/systemManage/metricMgr/MetricDetail.vue 70 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/graph/treeGraph/TreeGraph.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,346 @@
<template>
    <div  class="h-full w-full bg-white relative flex flex-col">
        <!-- <div class="toolbar flex-0 border-x-0 border-b border-t-0 border-gray-200 border-solid flex-items-center">
            <span>
                æ ¼å¼
            </span>
        </div> -->
        <div class="graph flex-auto">
            <div class="h-full" ref="graphRef"></div>
            <div
                class="absolute left-4 top-4 z-10 w-16 divide-y-[1.5px] divide-solid divide-gray-100 rounded-lg"
                style="box-shadow: 0 0 15px #dbdee6"
            >
                <div
                    class="flex-column content-center items-center border-x-0 py-2 hover:bg-gray-200 active:bg-gray-300 cursor-pointer"
                    @click="toolBarItemClick(item)"
                    v-for="item in toolBarFunList"
                >
                    <span class="ywifont !text-[20px] mb-1 p-1.5" :class="[`ywicon-${item.icon}`]"></span>
                    <span>{{ item.label }}</span>
                </div>
            </div>
            <div class="absolute right-2 top-4 py-7 px-5 rounded-lg w-96" style="box-shadow: 0 0 15px #dbdee6" v-if="searchIsShow">
                <span
                    class="absolute ywifont ywicon-guanbi right-4 top-4 cursor-pointer hover:bg-gray-200 active:bg-gray-300"
                    @click="closeSearch"
                ></span>
                <div class="flex-column">
                    <span class="mb-5">查找</span>
                    <el-input v-model="searchValue" @input="debounceSearch" placeholder="输入查找内容" v-elInputFocus></el-input>
                </div>
            </div>
            <div class="absolute right-2 top-4 py-7 px-5 rounded-lg w-96" style="box-shadow: 0 0 15px #dbdee6" v-if="layoutIsShow">
                <span
                    class="absolute ywifont ywicon-guanbi right-4 top-4 cursor-pointer hover:bg-gray-200 active:bg-gray-300"
                    @click="closeLayout"
                ></span>
                <div class="flex-column">
                    <span class="mb-5">布局</span>
                    <el-select v-model="activeLayout" @change="layoutChange">
                        <el-option
                            v-for="item in Object.keys(customLayoutMap)"
                            :key="item"
                            :value="parseInt(item)"
                            :label="customLayoutMap[item]"
                        ></el-option>
                    </el-select>
                </div>
            </div>
        </div>
    </div>
</template>
<script setup lang="ts">
import { Graph, GraphOptions, treeToGraphData } from '@antv/g6';
import { defaultsDeep } from 'lodash';
import { PropType, onMounted, ref } from 'vue';
import { CustomLayout, customLayoutMap, getLayoutType } from './types';
import { DeepPartial } from '/@/utils/types';
const props = defineProps({
    maxCount: {
        type: Number,
    },
    data: {
        type: Object as PropType<any>,
    },
});
const graphRef = ref<HTMLDivElement>(null);
let graph: Graph;
const getLayoutOption = (layoutType: CustomLayout) => {
    let option: DeepPartial<GraphOptions> = {};
    switch (layoutType) {
        case CustomLayout.Radial:
            option = {
                // autoFit: 'view',
                edge: {
                    type: 'cubic-radial',
                },
                layout: [
                    {
                        radial: true,
                        direction: 'RL',
                    },
                ],
            };
            break;
        case CustomLayout.Vertical:
            option = {
                edge: {
                    type: 'line',
                },
                layout: [
                    {
                        direction: 'V',
                        // getHeight: () => {
                        //     return 20;
                        // },
                        // getWidth: () => {
                        //     return 20;
                        // },
                        // åž‚ç›´é—´éš™
                        getVGap: () => {
                            return 40;
                        },
                        // æ°´å¹³é—´éš™
                        getHGap: () => {
                            return 10;
                        },
                    },
                ],
            };
            break;
        default:
            option = {
                edge: {
                    type: 'line',
                },
                layout: [
                    {
                        direction: 'TB',
                        // åž‚ç›´é—´éš™
                        getVGap: () => {
                            return 60;
                        },
                        // æ°´å¹³é—´éš™
                        getHGap: () => {
                            return 10;
                        },
                    },
                ],
            };
            break;
    }
    return option;
};
const graphRender = async () => {
    await graph.render();
    if (activeLayout.value === CustomLayout.Radial) {
        selfAdapt();
    }
};
const rebuildGraph = async(extraOption: DeepPartial<GraphOptions>) => {
    const finalOptions = defaultsDeep(extraOption, commonOption);
    graph.setOptions(finalOptions);
    await graphRender();
    graph.fitCenter();
};
let commonOption: GraphOptions;
const initGraph = async (treeData: any, layoutType: CustomLayout) => {
    commonOption = {
        container: graphRef.value,
        data: treeToGraphData(treeData),
        node: {
            style: {
                size: 20,
                labelText: (d) => d.label as string,
                labelBackground: true,
            },
            state: {
                active: {
                    fill: '#00C9C9',
                },
            },
            palette: {
                field: 'type',
                color: 'tableau',
            },
        },
        edge: {
            type: 'cubic-radial',
            state: {
                active: {
                    lineWidth: 3,
                    stroke: '#009999',
                },
            },
        },
        layout: [
            {
                type: 'compact-box',
                getHeight: () => {
                    return 20;
                },
                getWidth: () => {
                    return 20;
                },
                // åž‚ç›´é—´éš™
                getVGap: () => {
                    return 2;
                },
                // æ°´å¹³é—´éš™
                getHGap: () => {
                    return 120;
                },
            },
        ],
        behaviors: [
            'drag-canvas',
            'zoom-canvas',
            // 'drag-element',
            {
                key: 'hover-activate',
                type: 'hover-activate',
                degree: 5,
                direction: 'in',
                inactiveState: 'inactive',
            },
        ],
        transforms: ['place-radial-labels'],
    };
    const extraOption: DeepPartial<GraphOptions> = getLayoutOption(layoutType);
    const finalOption: GraphOptions = defaultsDeep(extraOption, commonOption);
    graph = new Graph(finalOption);
    graphRender();
};
const getLeaf = (item) => {
    if (item.Children && item.Children.length > 0) {
        const first = item.Children[0];
        return getLeaf(first);
    } else {
        return item;
    }
};
const initEvent = () => {
    // graph.on('node:click', ({ cell, e, node, view }) => {
    // });
};
const selfAdapt = () => {
    setTimeout(() => {
        graph.fitView();
    }, 100);
};
//#region ====================== å·¥å…·æ  ======================
const enum ToolBarType {
    /** @description å¸ƒå±€ */
    Layout,
    /** @description æœç´¢ */
    Search,
    /** @description æ”¾å¤§ */
    ZoomIn,
    /** @description ç¼©å° */
    ZoomOut,
    /** @description è‡ªé€‚应 */
    Reset,
}
type ToolBarItem = {
    icon: string;
    label: string;
    type: ToolBarType;
};
const toolBarFunList: ToolBarItem[] = [
    // {
    //     icon: 'sousuo',
    //     label: '搜索',
    //     type: ToolBarType.Search,
    // },
    {
        icon: 'buju',
        label: '布局',
        type: ToolBarType.Layout,
    },
    {
        icon: 'fangda',
        label: '放大',
        type: ToolBarType.ZoomIn,
    },
    {
        icon: 'suoxiao',
        label: '缩小',
        type: ToolBarType.ZoomOut,
    },
    {
        icon: 'zishiying',
        label: '自适应',
        type: ToolBarType.Reset,
    },
];
const searchIsShow = ref(false);
const searchValue = ref('');
const ZOOM_OFFSET = 0.15;
const toolBarItemClick = (item: ToolBarItem) => {
    switch (item.type) {
        case ToolBarType.Layout:
            layoutIsShow.value = true;
            break;
        case ToolBarType.Search:
            searchIsShow.value = true;
            break;
        case ToolBarType.ZoomIn:
            graph.zoomBy(1 + ZOOM_OFFSET);
            break;
        case ToolBarType.ZoomOut:
            graph.zoomBy(1 - ZOOM_OFFSET);
            break;
        case ToolBarType.Reset:
            selfAdapt();
        default:
            break;
    }
};
const debounceSearch = () => {};
const closeSearch = () => {
    searchIsShow.value = false;
};
//#endregion
//#region ====================== å¸ƒå±€ ======================
const layoutIsShow = ref(false);
const closeLayout = () => {
    layoutIsShow.value = false;
};
const activeLayout = ref<CustomLayout>();
const layoutChange = (layoutType: CustomLayout) => {
    const extraOption = getLayoutOption(layoutType);
    rebuildGraph(extraOption);
};
//#endregion
onMounted(() => {
    const layoutType = getLayoutType(props.maxCount);
    activeLayout.value = layoutType;
    initGraph(props.data, layoutType);
});
</script>
<style scoped lang="scss"></style>
src/components/graph/treeGraph/types.ts
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,28 @@
export const enum CustomLayout {
    /** @description ç´§å‡‘树布局 n <= 30*/
    NormalTree,
    /** @description åž‚直紧凑树,根节点在中间 30 < n <= 50*/
    Vertical,
    /** @description è¾å°„紧凑树,> 50 */
    Radial,
}
export const customLayoutMap ={
    [CustomLayout.NormalTree]:'紧凑树布局',
    [CustomLayout.Vertical]:'垂直对称布局',
    [CustomLayout.Radial]:'辐射状布局'
}
export const getLayoutType = (maxLeafLen: number) => {
    if (maxLeafLen > 50) {
        return CustomLayout.Radial;
    } else if (30 < maxLeafLen) {
        return CustomLayout.Vertical;
    } else {
        return CustomLayout.NormalTree;
    }
};
src/views/project/yw/systemManage/agentGraph/AgentGraph.vue
@@ -1,74 +1,20 @@
<template>
    <div v-loading="firstLoading" class="h-full w-full bg-white relative flex flex-col">
        <!-- <div class="toolbar flex-0 border-x-0 border-b border-t-0 border-gray-200 border-solid flex-items-center">
            <span>
                æ ¼å¼
            </span>
        </div> -->
        <div class="graph flex-auto">
            <div class="h-full" ref="graphRef"></div>
            <div
                class="absolute left-4 top-4 z-10 w-16 divide-y-[1.5px] divide-solid divide-gray-100 rounded-lg"
                style="box-shadow: 0 0 15px #dbdee6"
            >
                <div
                    class="flex-column content-center items-center border-x-0 py-2 hover:bg-gray-200 active:bg-gray-300 cursor-pointer"
                    @click="toolBarItemClick(item)"
                    v-for="item in toolBarFunList"
                >
                    <span class="ywifont !text-[20px] mb-1 p-1.5" :class="[`ywicon-${item.icon}`]"></span>
                    <span>{{ item.label }}</span>
                </div>
            </div>
            <div class="absolute right-2 top-4 py-7 px-5 rounded-lg w-96" style="box-shadow: 0 0 15px #dbdee6" v-if="searchIsShow">
                <span
                    class="absolute ywifont ywicon-guanbi right-4 top-4 cursor-pointer hover:bg-gray-200 active:bg-gray-300"
                    @click="closeSearch"
                ></span>
                <div class="flex-column">
                    <span class="mb-5">查找</span>
                    <el-input v-model="searchValue" @input="debounceSearch" placeholder="输入查找内容" v-elInputFocus></el-input>
                </div>
            </div>
            <div class="absolute right-2 top-4 py-7 px-5 rounded-lg w-96" style="box-shadow: 0 0 15px #dbdee6" v-if="layoutIsShow">
                <span
                    class="absolute ywifont ywicon-guanbi right-4 top-4 cursor-pointer hover:bg-gray-200 active:bg-gray-300"
                    @click="closeLayout"
                ></span>
                <div class="flex-column">
                    <span class="mb-5">布局</span>
                    <el-select v-model="activeLayout" @change="layoutChange">
                        <el-option
                            v-for="item in Object.keys(customLayoutMap)"
                            :key="item"
                            :value="parseInt(item)"
                            :label="customLayoutMap[item]"
                        ></el-option>
                    </el-select>
                </div>
            </div>
        </div>
    <div class="h-full" v-loading="firstLoading">
        <TreeGraph v-if="graphData" :data="graphData" class="h-full" :maxCount="maxCount" />
    </div>
</template>
<script setup lang="ts">
import { Graph, GraphOptions, treeToGraphData } from '@antv/g6';
import { defaultsDeep } from 'lodash';
import { onMounted, ref } from 'vue';
import { CustomLayout, OrgTreeItem, customLayoutMap, getLayoutType } from './types';
import { getMetricAgentListByPost, getMetricNameListByPost } from '/@/api/metrics';
import TreeGraph from '/@/components/graph/treeGraph/TreeGraph.vue';
import router from '/@/router';
import { DeepPartial } from '/@/utils/types';
import { deepClone } from '/@/utils/other';
const graphRef = ref<HTMLDivElement>(null);
let graph: Graph;
const firstLoading = ref(false);
import { getMetricAgentListByPost, getMetricNameListByPost } from '/@/api/metrics';
import { OrgTreeItem } from './types';
const agentId = router.currentRoute.value.query.id as string;
const maxCount = ref(null);
const graphData = ref(null);
const firstLoading = ref(false);
const convertOrgTreeToTreeNode = (orgTreeData: OrgTreeItem) => {
    const treeData = {
        id: orgTreeData.treeId,
@@ -78,172 +24,6 @@
    };
    return treeData;
};
const getLayoutOption = (layoutType: CustomLayout) => {
    let option: DeepPartial<GraphOptions> = {};
    switch (layoutType) {
        case CustomLayout.Radial:
            option = {
                // autoFit: 'view',
                edge: {
                    type: 'cubic-radial',
                },
                layout: [
                    {
                        radial: true,
                        direction: 'RL',
                    },
                ],
            };
            break;
        case CustomLayout.Vertical:
            option = {
                edge: {
                    type: 'line',
                },
                layout: [
                    {
                        direction: 'V',
                        // getHeight: () => {
                        //     return 20;
                        // },
                        // getWidth: () => {
                        //     return 20;
                        // },
                        // åž‚ç›´é—´éš™
                        getVGap: () => {
                            return 40;
                        },
                        // æ°´å¹³é—´éš™
                        getHGap: () => {
                            return 10;
                        },
                    },
                ],
            };
            break;
        default:
            option = {
                edge: {
                    type: 'line',
                },
                layout: [
                    {
                        direction: 'TB',
                        // åž‚ç›´é—´éš™
                        getVGap: () => {
                            return 60;
                        },
                        // æ°´å¹³é—´éš™
                        getHGap: () => {
                            return 10;
                        },
                    },
                ],
            };
            break;
    }
    return option;
};
const graphRender = async() => {
    await graph.render();
    if (activeLayout.value === CustomLayout.Radial) {
        selfAdapt();
    }
};
const rebuildGraph = (extraOption: DeepPartial<GraphOptions>) => {
    const finalOptions = defaultsDeep(extraOption, commonOption);
    graph.setOptions(finalOptions);
    graphRender();
};
let commonOption: GraphOptions;
const initGraph = async (orgTreeData: OrgTreeItem, layoutType: CustomLayout) => {
    const treeNodeData = convertOrgTreeToTreeNode(orgTreeData);
    commonOption = {
        container: graphRef.value,
        data: treeToGraphData(treeNodeData),
        node: {
            style: {
                size: 20,
                labelText: (d) => d.label as string,
                labelBackground: true,
            },
            state: {
                active: {
                    fill: '#00C9C9',
                },
            },
            palette: {
                field: 'type',
                color: 'tableau',
            },
        },
        edge: {
            type: 'cubic-radial',
            state: {
                active: {
                    lineWidth: 3,
                    stroke: '#009999',
                },
            },
        },
        layout: [
            {
                type: 'compact-box',
                getHeight: () => {
                    return 20;
                },
                getWidth: () => {
                    return 20;
                },
                // åž‚ç›´é—´éš™
                getVGap: () => {
                    return 2;
                },
                // æ°´å¹³é—´éš™
                getHGap: () => {
                    return 120;
                },
            },
        ],
        behaviors: [
            'drag-canvas',
            'zoom-canvas',
            // 'drag-element',
            {
                key: 'hover-activate',
                type: 'hover-activate',
                degree: 5,
                direction: 'in',
                inactiveState: 'inactive',
            },
        ],
        transforms: ['place-radial-labels'],
    };
    const extraOption: DeepPartial<GraphOptions> = getLayoutOption(layoutType);
    const finalOption: GraphOptions = defaultsDeep(extraOption, commonOption);
    graph = new Graph(finalOption);
    graphRender();
};
const getLeaf = (item) => {
    if (item.Children && item.Children.length > 0) {
        const first = item.Children[0];
        return getLeaf(first);
    } else {
        return item;
    }
};
const initEvent = () => {
    // graph.on('node:click', ({ cell, e, node, view }) => {
    // });
};
const getFirstOrgTreeList = async () => {
    // const res = await GetSMCenterFirstOrgTreeList();
    /** @description ç»´åº¦æ•°é‡ */
@@ -299,7 +79,6 @@
                        level: 2,
                    };
                }),
                // .filter((item, index) => index === 0),
            };
            dimensionCount += metrics.children.length;
@@ -310,123 +89,15 @@
    const maxCount = Math.max(dimensionCount, metricsCount);
    return [resData, maxCount];
};
const selfAdapt = () => {
    setTimeout(() => {
        graph.fitView();
    }, 100);
};
//#region ====================== å·¥å…·æ  ======================
const enum ToolBarType {
    /** @description å¸ƒå±€ */
    Layout,
    /** @description æœç´¢ */
    Search,
    /** @description æ”¾å¤§ */
    ZoomIn,
    /** @description ç¼©å° */
    ZoomOut,
    /** @description è‡ªé€‚应 */
    Reset,
}
type ToolBarItem = {
    icon: string;
    label: string;
    type: ToolBarType;
};
const toolBarFunList: ToolBarItem[] = [
    // {
    //     icon: 'sousuo',
    //     label: '搜索',
    //     type: ToolBarType.Search,
    // },
    {
        icon: 'buju',
        label: '布局',
        type: ToolBarType.Layout,
    },
    {
        icon: 'fangda',
        label: '放大',
        type: ToolBarType.ZoomIn,
    },
    {
        icon: 'suoxiao',
        label: '缩小',
        type: ToolBarType.ZoomOut,
    },
    {
        icon: 'zishiying',
        label: '自适应',
        type: ToolBarType.Reset,
    },
];
const searchIsShow = ref(false);
const searchValue = ref('');
const ZOOM_OFFSET = 0.15;
const toolBarItemClick = (item: ToolBarItem) => {
    switch (item.type) {
        case ToolBarType.Layout:
            layoutIsShow.value = true;
            break;
        case ToolBarType.Search:
            searchIsShow.value = true;
            break;
        case ToolBarType.ZoomIn:
            graph.zoomBy(1 + ZOOM_OFFSET);
            break;
        case ToolBarType.ZoomOut:
            graph.zoomBy(1 - ZOOM_OFFSET);
            break;
        case ToolBarType.Reset:
            selfAdapt();
        default:
            break;
    }
};
const debounceSearch = () => {};
const closeSearch = () => {
    searchIsShow.value = false;
};
//#endregion
//#region ====================== å¸ƒå±€ ======================
const layoutIsShow = ref(false);
const closeLayout = () => {
    layoutIsShow.value = false;
};
const activeLayout = ref<CustomLayout>();
const layoutChange = (layoutType: CustomLayout) => {
    const extraOption = getLayoutOption(layoutType);
    rebuildGraph(extraOption);
};
//#endregion
onMounted(async () => {
    if (!agentId) return;
    firstLoading.value = true;
    const [orgTreeData, maxLevelNodeCount] = await (getFirstOrgTreeList() as any).catch(() => {
    const [orgTreeData, maxLevelNodeCount] = await (getFirstOrgTreeList() as any).finally(() => {
        firstLoading.value = false;
    });
    setTimeout(() => {
        const layoutType = getLayoutType(maxLevelNodeCount);
        activeLayout.value = layoutType;
        initGraph(orgTreeData as OrgTreeItem, layoutType);
        // initEvent();
        // loadData(orgTreeList as any);
        firstLoading.value = false;
    }, 100);
    maxCount.value = maxLevelNodeCount;
    graphData.value = convertOrgTreeToTreeNode(orgTreeData);
});
</script>
<style scoped lang="scss">
:deep(g[data-shape='rect'].x6-node > text:hover) {
    // text-decoration: underline;
    cursor: pointer;
}
</style>
<style scoped lang="scss"></style>
src/views/project/yw/systemManage/agentGraph/types.ts
@@ -1,31 +1,4 @@
export const enum CustomLayout {
    /** @description ç´§å‡‘树布局 n <= 30*/
    NormalTree,
    /** @description åž‚直紧凑树,根节点在中间 30 < n <= 50*/
    Vertical,
    /** @description è¾å°„紧凑树,> 50 */
    Radial,
}
export const customLayoutMap ={
    [CustomLayout.NormalTree]:'紧凑树布局',
    [CustomLayout.Vertical]:'垂直对称布局',
    [CustomLayout.Radial]:'辐射状布局'
}
export const getLayoutType = (maxLeafLen: number) => {
    if (maxLeafLen > 50) {
        return CustomLayout.Radial;
    } else if (30 < maxLeafLen) {
        return CustomLayout.Vertical;
    } else {
        return CustomLayout.NormalTree;
    }
};
export type OrgTreeItem = {
    treeId: string;
src/views/project/yw/systemManage/metricMgr/MetricDetail.vue
@@ -42,7 +42,7 @@
                                    <el-divider />
                                </div>
                            </el-tab-pane>
                            <el-tab-pane label="指标图谱" name="indicatorExploration" class="h-full">
                            <el-tab-pane label="指标探索" name="indicatorExploration" class="h-full">
                                <div class="h-full flex-column">
                                    <el-form :model="dialogFormValue" class="flex-0" ref="dialogFormRef" :rules="dialogFormRules">
                                        <el-form-item label="日期区间:" prop="rangValue" v-if="currentMetrics?.is_time_values ?? true">
@@ -86,6 +86,10 @@
                                    </div>
                                </div>
                            </el-tab-pane>
                            <el-tab-pane label="指标图谱" name="metricGraph" class="h-full">
                                <div class="h-full">
                                    <TreeGraph v-if="graphData" :data="graphData" class="h-full" :maxCount="maxCount" /></div
                            ></el-tab-pane>
                        </el-tabs>
                    </div>
                    <div class="list_btn" @click="handleExitFlow">
@@ -199,6 +203,7 @@
import TimeRange from '/@/components/chat/chatComponents/summaryCom/components/recordSet/components/TimeRange.vue';
import { formatTime, getDefaultPeriod } from '/@/utils/istation/common.js';
import { eMetrics_Ops } from '/@/views/types/metrics';
import TreeGraph from '/@/components/graph/treeGraph/TreeGraph.vue';
import { FormRules } from 'element-plus/es/components/form/src/types';
import _, { debounce } from 'lodash';
@@ -206,6 +211,7 @@
import SummaryCom from './components/SummaryCom.vue';
import { chatMetricsJsonByPost } from '/@/api/metrics';
import { useCompRef } from '/@/utils/types';
import { OrgTreeItem } from '../agentGraph/types';
const defaultTime = ref<[Date, Date]>([new Date(2000, 1, 1, 0, 0, 0), new Date(2000, 2, 1, 23, 59, 59)]);
const router = useRouter();
const route = useRoute();
@@ -216,7 +222,7 @@
        name: '',
        id: '',
    } as any,
    activeMetricName: 'indicatorExploration',
    activeMetricName: 'basicInformation',
    metricBasicInfo: {
        descriptionQuotaItems: {},
    } as any,
@@ -409,6 +415,11 @@
        });
    }
    state.activeMetricName = val;
    if (val === 'metricGraph') {
        if (!graphData.value) {
            getGraphTreeData();
        }
    }
};
//#endregion
@@ -428,7 +439,7 @@
    groupDimList: [],
});
const currentMetricsId = computed(() => router.currentRoute.value.query.id);
const currentMetricsId = computed(() => router.currentRoute.value.query.id as string);
const querySummaryData = ref([]);
const queryLoading = ref(false);
/**
@@ -470,6 +481,59 @@
//#endregion
//#region ====================== æŒ‡æ ‡å›¾è°± ======================
const maxCount = ref(null);
const graphData = ref(null);
const convertOrgTreeToTreeNode = (orgTreeData: OrgTreeItem) => {
    const treeData = {
        id: orgTreeData.treeId,
        label: orgTreeData.label,
        data: orgTreeData,
        children: orgTreeData.children?.length > 0 ? orgTreeData.children.map((item) => convertOrgTreeToTreeNode(item)) : [],
    };
    return treeData;
};
const getGraphTreeData = () => {
    if (!currentMetricsId.value) return;
    /** @description ç»´åº¦æ•°é‡ */
    let dimensionCount = 0;
    /** @description æŒ‡æ ‡æ•°é‡ */
    let metricsCount = 1;
    const metricsTreeId = `metrics-${currentMetricsId.value}`;
    const dimensionList = currentMetrics.value.dimensions ?? [];
    dimensionCount = dimensionList.length;
    let logicTree: OrgTreeItem = {
        treeId: metricsTreeId,
        logicId: currentMetricsId.value,
        type: 'metrics',
        model: currentMetrics.value,
        get label() {
            return this.model.title;
        },
        level: 0,
        children: dimensionList.map((item) => {
            const dimensionTreeId = `${metricsTreeId}-dimension-${item.id}`;
            return {
                treeId: dimensionTreeId,
                logicId: item.id,
                type: 'dimension',
                model: item,
                get label() {
                    return this.model.title;
                },
                level: 1,
            };
        }),
    };
    const resData = logicTree;
    maxCount.value = Math.max(dimensionCount, metricsCount);
    graphData.value = convertOrgTreeToTreeNode(resData);
};
//#endregion
onMounted(() => {
    const { id } = route.query;
    query(true);