You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
180 lines
4.9 KiB
180 lines
4.9 KiB
<template> |
|
<div class="relative h-full w-full"> |
|
<el-button type="primary" @click="onBack" class="absolute top-0 z-99"> |
|
<i class="i-line-md:arrow-left"></i>返回站点数据 |
|
</el-button> |
|
<el-empty v-if="!topologyTree && !loading" class="w-full h-full"></el-empty> |
|
<div |
|
id="container" |
|
class="w-full h-full" |
|
v-loading="loading" |
|
element-loading-text="设备加载中请稍等..." |
|
style="width: 100%;height: 100%"> |
|
</div> |
|
</div> |
|
|
|
<detail-drawer ref="detailDrawerRef"/> |
|
</template> |
|
|
|
<script setup lang="ts"> |
|
import { DeviceType, NODE_SIZE } from "./utils"; |
|
import { useG6 } from "@/hooks/useG6"; |
|
import Node from './components/Node.vue' |
|
import { flattenTree } from "@/views/testG6/utils"; |
|
import { type PluginOptions } from "@antv/g6" |
|
import type { IDevice, IOfflineDevice, IOnlineDevice } from "@/views/stationData/type"; |
|
import { getPointGroup, type IPointGroupParams, type IPointGroupOV } from "@/api/module/transfer"; |
|
import DetailDrawer from "./components/detailDrawer.vue"; |
|
import { VueNode } from "g6-extension-vue"; |
|
|
|
const router = useRouter() |
|
|
|
const route = useRoute() |
|
const deviceInfo = route.query?.deviceInfo |
|
? JSON.parse(route.query?.deviceInfo as string) |
|
: {} as unknown as IDevice & { isonLine: boolean, siteName: string } |
|
|
|
const detailDrawerRef = ref<InstanceType<typeof DetailDrawer>>() |
|
const topologyTree = ref<IPointGroupOV>() |
|
const loading = ref(true) |
|
|
|
async function loadDeviceTopology() { |
|
const params: IPointGroupParams = {} |
|
if (deviceInfo.isonLine) { |
|
const onlineDevice = deviceInfo as IOnlineDevice |
|
params.isLocal = false |
|
params.host = onlineDevice.clientIp |
|
} else { |
|
const offlineDevice = deviceInfo as unknown as IOfflineDevice & { siteName: string } |
|
params.sn = offlineDevice.sn |
|
params.site = offlineDevice!.siteName |
|
params.isLocal = true |
|
} |
|
|
|
const res = await getPointGroup(params) |
|
if (res.code === 0) { |
|
topologyTree.value = { |
|
id: deviceInfo.sn, |
|
ip: deviceInfo.ip, |
|
name: deviceInfo?.sn, |
|
cnName: deviceInfo?.name ?? deviceInfo?.sn, |
|
port: deviceInfo.port, |
|
slave_addr: 'root', |
|
type: deviceInfo?.type ?? 'emu', |
|
children: [] |
|
} |
|
topologyTree.value.children = Array.isArray(res?.data) ? res.data : [] |
|
} |
|
} |
|
|
|
const getNodeData = (target: VueNode) => { |
|
const targetId = target.config.id |
|
const context = target.config!.context |
|
return context.model.getNodeData(targetId).find((r: any) => r.id === targetId) |
|
} |
|
|
|
const canvas = ref<HTMLElement | undefined>(undefined) |
|
|
|
const plugins: PluginOptions = [ |
|
{ |
|
type: 'contextmenu', |
|
enable: (e: any) => e.targetType === 'node', |
|
getItems: (e: any) => { |
|
const { data } = getNodeData(e.target) |
|
const menu = [{ name: '查看详情', value: 'detail' }] |
|
if (data.type === DeviceType.bms) { |
|
menu.unshift({ name: '固件升级', value: 'firmwareUpdate' }) |
|
} |
|
return menu; |
|
}, |
|
onClick: onContextmenuClick, |
|
}, |
|
{ |
|
type: 'tooltip', |
|
enable: (e: any) => e.targetType === 'node', |
|
getContent: (e: any, items: any) => { |
|
return `<div>${items[0].data.cnName}</div>`; |
|
}, |
|
}, |
|
] |
|
|
|
function onContextmenuClick(e: string, item: HTMLElement, current: any) { |
|
|
|
switch (e) { |
|
case 'detail': |
|
onDetail(item, current) |
|
break; |
|
case 'firmwareUpdate': |
|
onFirmwareUpdate(item, current) |
|
break; |
|
default: |
|
break; |
|
} |
|
} |
|
|
|
function init() { |
|
if (!topologyTree.value) return |
|
const filteredTree = filterTree(topologyTree.value) |
|
const device = flattenTree([filteredTree]) |
|
|
|
const { container, graph } = useG6({ |
|
tree: device, |
|
nodeComponent: Node, |
|
nodeSize: NODE_SIZE |
|
}, plugins) |
|
canvas.value = container.value |
|
graph.render(); |
|
} |
|
|
|
function onDetail(item: HTMLElement, current: any) { |
|
const { data } = getNodeData(current) |
|
const device = deviceInfo as unknown as IDevice & { |
|
isonLine: boolean |
|
siteName: string |
|
} |
|
detailDrawerRef.value?.open(device, data) |
|
} |
|
|
|
function onFirmwareUpdate(item: HTMLElement, current: any) { |
|
const { data } = getNodeData(current) |
|
window.open(`http://${data.ip}:${data.port}`, '_blank', 'noopener'); |
|
} |
|
|
|
onMounted(async () => { |
|
await loadDeviceTopology() |
|
loading.value = false |
|
init() |
|
}) |
|
|
|
function filterTree(node: IPointGroupOV) { |
|
if (node.type === 'bms_cluster' && Array.isArray(node.children)) { |
|
if (node.children.length > 1) { |
|
const customNode = node.children[0] as any |
|
node.children.forEach(r => { |
|
r.customCnName = r.cnName |
|
}) |
|
customNode.cnName = `${node.children[0].cnName} ~ ${node.children[node.children.length - 1].cnName}` |
|
customNode.customNode = true |
|
customNode.brother = node.children |
|
node.children = [customNode] |
|
} |
|
} |
|
|
|
if (Array.isArray(node.children)) { |
|
node!.children = node.children.map((child: any) => { |
|
child.parentName = node.name |
|
return filterTree(child) |
|
}) |
|
} |
|
|
|
return node |
|
} |
|
|
|
function onBack() { |
|
router.push('/station') |
|
} |
|
</script> |
|
|
|
<style scoped lang="scss"> |
|
|
|
</style> |