From 11738cfed9fced6927376d6de27bb8ba9077ba2f Mon Sep 17 00:00:00 2001
From: wujingjing <gersonwu@qq.com>
Date: 星期四, 14 十一月 2024 11:17:09 +0800
Subject: [PATCH] 指标图谱

---
 src/views/project/yw/systemManage/agentGraph/AgentGraph.vue  |  353 ------------------------
 src/views/project/yw/systemManage/agentGraph/types.ts        |   27 -
 src/components/graph/treeGraph/types.ts                      |   28 ++
 src/views/project/yw/systemManage/metricMgr/MetricDetail.vue |   70 ++++
 src/components/graph/treeGraph/TreeGraph.vue                 |  346 ++++++++++++++++++++++++
 5 files changed, 453 insertions(+), 371 deletions(-)

diff --git a/src/components/graph/treeGraph/TreeGraph.vue b/src/components/graph/treeGraph/TreeGraph.vue
new file mode 100644
index 0000000..0e6e7db
--- /dev/null
+++ b/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>
diff --git a/src/components/graph/treeGraph/types.ts b/src/components/graph/treeGraph/types.ts
new file mode 100644
index 0000000..902175e
--- /dev/null
+++ b/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;
+	}
+};
+
diff --git a/src/views/project/yw/systemManage/agentGraph/AgentGraph.vue b/src/views/project/yw/systemManage/agentGraph/AgentGraph.vue
index 7070cf3..b313478 100644
--- a/src/views/project/yw/systemManage/agentGraph/AgentGraph.vue
+++ b/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>
diff --git a/src/views/project/yw/systemManage/agentGraph/types.ts b/src/views/project/yw/systemManage/agentGraph/types.ts
index 320a134..5f6ee36 100644
--- a/src/views/project/yw/systemManage/agentGraph/types.ts
+++ b/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;
diff --git a/src/views/project/yw/systemManage/metricMgr/MetricDetail.vue b/src/views/project/yw/systemManage/metricMgr/MetricDetail.vue
index c37c5dd..0996496 100644
--- a/src/views/project/yw/systemManage/metricMgr/MetricDetail.vue
+++ b/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);

--
Gitblit v1.9.3