¶Ô±ÈÐÂÎļþ |
| | |
| | | <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> |
¶Ô±ÈÐÂÎļþ |
| | |
| | | |
| | | 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; |
| | | } |
| | | }; |
| | | |
| | |
| | | <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, |
| | |
| | | }; |
| | | 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 维度æ°é */ |
| | |
| | | level: 2, |
| | | }; |
| | | }), |
| | | // .filter((item, index) => index === 0), |
| | | }; |
| | | dimensionCount += metrics.children.length; |
| | | |
| | |
| | | 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> |
| | |
| | | |
| | | 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; |
| | |
| | | <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"> |
| | |
| | | </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"> |
| | |
| | | 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'; |
| | |
| | | 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(); |
| | |
| | | name: '', |
| | | id: '', |
| | | } as any, |
| | | activeMetricName: 'indicatorExploration', |
| | | activeMetricName: 'basicInformation', |
| | | metricBasicInfo: { |
| | | descriptionQuotaItems: {}, |
| | | } as any, |
| | |
| | | }); |
| | | } |
| | | state.activeMetricName = val; |
| | | if (val === 'metricGraph') { |
| | | if (!graphData.value) { |
| | | getGraphTreeData(); |
| | | } |
| | | } |
| | | }; |
| | | //#endregion |
| | | |
| | |
| | | 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); |
| | | /** |
| | |
| | | |
| | | //#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); |