From 3aea520289b14227cb18f65f37b21ac14e3f7519 Mon Sep 17 00:00:00 2001 From: TsMask <340112800@qq.com> Date: Tue, 26 Dec 2023 10:10:59 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=8B=93=E6=89=91=E7=BC=96=E8=BE=91dem?= =?UTF-8?q?o?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/monitor/topology-build/index.vue | 553 ++++++++++++++++++--- 1 file changed, 485 insertions(+), 68 deletions(-) diff --git a/src/views/monitor/topology-build/index.vue b/src/views/monitor/topology-build/index.vue index 016d7029..be955699 100644 --- a/src/views/monitor/topology-build/index.vue +++ b/src/views/monitor/topology-build/index.vue @@ -4,9 +4,9 @@ import { PageContainer } from 'antdv-pro-layout'; import useI18n from '@/hooks/useI18n'; import { RESULT_CODE_SUCCESS } from '@/constants/result-constants'; import { listNe, stateNe } from '@/api/ne/ne'; -import message from 'ant-design-vue/lib/message'; import { parseDateToStr } from '@/utils/date-utils'; -import { Graph, Util } from '@antv/g6'; +import { Graph, Menu, Tooltip, Util, registerBehavior } from '@antv/g6'; +import { message, Modal, Form, notification } from 'ant-design-vue/lib'; const { t } = useI18n(); /**图DOM节点实例对象 */ @@ -15,6 +15,12 @@ const graphG6Dom = ref(undefined); /**图实例对象 */ const graphG6 = ref(null); +/**图实例状态 */ +const graphG6State = reactive>({ + mode: 'default', + editEdge: {}, +}); + /**图数据 */ const graphG6Data = reactive>({ nodes: [ @@ -309,18 +315,18 @@ function graphEvent(graph: Graph) { // 该边的结束点 const target = edge.getTarget(); // 先将边提前,再将端点提前。这样该边两个端点还是在该边上层,较符合常规。 - edge.toFront(); - source.toFront(); - target.toFront(); + // edge.toFront(); + // source.toFront(); + // target.toFront(); }); graph.on('edge:mouseleave', (ev: any) => { // 获得图上所有边实例 const edges = graph.getEdges(); // 遍历边,将所有边的层级放置在后方,以恢复原样 - edges.forEach(edge => { - edge.toBack(); - }); + // edges.forEach(edge => { + // edge.toBack(); + // }); }); graph.on('node:mouseenter', (ev: any) => { @@ -329,36 +335,122 @@ function graphEvent(graph: Graph) { // 获取该节点的所有相关边 const edges = node.getEdges(); // 遍历相关边,将所有相关边提前,再将相关边的两个端点提前,以保证相关边的端点在边的上方常规效果 - edges.forEach((edge: any) => { - edge.toFront(); - edge.getSource().toFront(); - edge.getTarget().toFront(); - }); + // edges.forEach((edge: any) => { + // edge.toFront(); + // edge.getSource().toFront(); + // edge.getTarget().toFront(); + // }); }); graph.on('node:mouseleave', (ev: any) => { // 获得图上所有边实例 const edges = graph.getEdges(); // 遍历边,将所有边的层级放置在后方,以恢复原样 - edges.forEach(edge => { - edge.toBack(); - }); - }); - - // 使用内置交互 create-edge,创建边之后触发 - graph.on('aftercreateedge', e => { - console.log(JSON.parse(JSON.stringify(graph.save()))); - const edges = graph.save().edges as any; - // Util.processParallelEdges(edges); - graph.getEdges().forEach((edge, i) => { - graph.updateItem(edge, { - curveOffset: edges[i].curveOffset, - curvePosition: edges[i].curvePosition, - }); - }); + // edges.forEach(edge => { + // edge.toBack(); + // }); }); } +/**图节点右击菜单 */ +const graphNodeMenu = new Menu({ + offsetX: 6, + offseY: 10, + itemTypes: ['node'], + getContent(e) { + const outDiv = document.createElement('div'); + outDiv.style.width = '180px'; + outDiv.innerHTML = `
    +
  • 测试01
  • +
  • 测试01
  • +
  • 测试01
  • +
  • 测试01
  • +
  • 测试01
  • +
`; + return outDiv; + }, + handleMenuClick(target, item) { + console.log(target, item); + }, +}); + +/**图节点展示 */ +const graphNodeTooltip = new Tooltip({ + offsetX: 10, + offsetY: 20, + getContent(e: any) { + const outDiv = document.createElement('div'); + outDiv.style.width = '180px'; + outDiv.innerHTML = ` +

自定义tooltip

+
    +
  • Label: ${e.item.getModel().label || e.item.getModel().id}
  • +
`; + return outDiv; + }, + itemTypes: ['node'], +}); + +/**图边右击菜单 */ +const graphEdgeMenu = new Menu({ + offsetX: 6, + offseY: 10, + itemTypes: ['edge'], + getContent(evt) { + return ` +
+
+ 1. 编辑 +
+
+ 2. 隐藏 +
+
+ `; + }, + handleMenuClick(target, item) { + console.log(target, item); + const targetId = target.id; + switch (targetId) { + case 'edit': + const info = item.getModel(); + console.log(Object.assign({}, info)); + + modalState.title = '边信息编辑'; + modalState.formEdge = Object.assign(modalState.formEdge, info); + modalState.visibleByEdge = true; + break; + case 'hide': + graphG6.value.hideItem(item); + break; + } + }, +}); + +/**图边展示 */ +const graphEdgeTooltip = new Tooltip({ + offsetX: 10, + offsetY: 20, + getContent(e: any) { + const outDiv = document.createElement('div'); + outDiv.style.width = '180px'; + outDiv.innerHTML = ` +

graphEdgeTooltip

+
    +
  • Label: ${e.item.getModel().label || e.item.getModel().id}
  • +
`; + return outDiv; + }, + itemTypes: ['edge'], +}); + /**查询全部网元数据列表 */ function fnRanderGraph() { if (!graphG6Dom.value) return; @@ -390,16 +482,20 @@ function fnRanderGraph() { 'drag-canvas', 'zoom-canvas', 'collapse-expand-combo', - { - type: 'create-edge', - trigger: 'click', // 'click' by default. options: 'drag', 'click' - }, ], edit: [ { - type: 'create-edge', - trigger: 'click', // 'click' by default. options: 'drag', 'click' + type: 'click-select', + selectEdge: true, }, + { + type: 'drag-node', + onlyChangeComboSize: true, + shouldEnd: (e: any) => { + return true; + }, + }, + { type: 'create-edge', key: 'alt' }, ], }, groupByTypes: false, @@ -426,6 +522,9 @@ function fnRanderGraph() { lineWidth: 1, }, }, + // defaultEdge: { + // type: 'line', + // }, // 全局框节点 矩形 defaultCombo: { type: 'rect', // Combo 类型 @@ -434,10 +533,12 @@ function fnRanderGraph() { fillOpacity: 0.1, }, }, + plugins: [graphNodeMenu, graphNodeTooltip, graphEdgeMenu, graphEdgeTooltip], }); graph.data(graphG6Data); graph.render(); + // 图绑定事件 graphEvent(graph); graphG6.value = graph; @@ -568,6 +669,185 @@ onMounted(() => { // fnGetList(); fnRanderGraph(); }); + +/**改变图模式 */ +function fnChangeMode(value: any) { + console.log(value, JSON.parse(JSON.stringify(graphG6.value.save()))); + graphG6.value.setMode(value); +} + +/**对话框对象信息状态类型 */ +type ModalStateType = { + /**图边框是否显示 */ + visibleByEdge: boolean; + /**标题 */ + title: string; + /**表单数据 */ + form: Record; + /**图边表单数据 */ + formEdge: Record; + /**确定按钮 loading */ + confirmLoading: boolean; +}; + +/**对话框对象信息状态 */ +let modalState: ModalStateType = reactive({ + visibleByEdge: false, + title: '图信息', + form: { + id: '', + msisdn: '', + }, + formEdge: { + id: '', + source: '', + target: '', + type: '', + style: {}, + label: '', + labelCfg: { + refX: 0, + refY: 0, + position: 'middle', + autoRotate: false, + style: {}, + }, + }, + confirmLoading: false, +}); + +/**对话框内表单属性和校验规则 */ +const modalStateForm = Form.useForm( + modalState.form, + reactive({ + imsi: [{ required: true, message: 'IMSI' + t('common.unableNull') }], + msisdn: [{ required: true, message: 'MSISDN' + t('common.unableNull') }], + staticIp: [ + { required: true, message: 'static ip' + t('common.unableNull') }, + ], + smData: [ + { + required: true, + message: 'Subscribed SM Data' + t('common.unableNull'), + }, + ], + }) +); + +/**对话框内表单属性和校验规则 */ +const modalStateFormEdge = Form.useForm( + modalState.formEdge, + reactive({ + id: [{ required: true, message: '边唯一 ID' }], + source: [{ required: true, message: '起始点 id' }], + target: [{ required: true, message: '结束点 id' }], + type: [{ required: true, message: 'line' }], + }) +); + +/** + * 对话框弹出关闭执行函数 + * 进行表达规则校验 + */ +function fnModalCancel() { + modalState.visibleByEdge = false; + modalStateForm.resetFields(); + modalStateFormEdge.resetFields(); +} + +/**内置边类型 */ +const edgeType = [ + { + type: 'line', + description: '连接两个节点的直线', + }, + { + type: 'polyline', + description: '多段线段构成的折线,连接两个端点', + }, + { + type: 'arc', + description: '连接两个节点的一段圆弧', + }, + { + type: 'quadratic', + description: '只有一个控制点的曲线', + }, + { + type: 'cubic', + description: '有两个控制点的曲线', + }, + { + type: 'cubic-vertical', + description: '垂直方向的三阶贝塞尔曲线', + }, + { + type: 'cubic-horizontal', + description: '水平方向的三阶贝塞尔曲线', + }, + { + type: 'loop', + description: '自环', + }, +]; + +/**边新增 */ +function fnModalOkEdge() { + const model = { + id: '2-190', + source: '2', + target: '190', + type: 'line', + style: { + stroke: 'steelblue', + lineWidth: 5, + }, + label: '边新增', + labelCfg: { + position: 'end', + refY: 20, + }, + }; + graphG6.value.addItem('edge', model); + + console.log(JSON.parse(JSON.stringify(graphG6.value.save()))); +} + +/**边新增 */ +function fnAddEdge() { + const model = { + id: '2-190', + source: '2', + target: '190', + type: 'line', + style: { + stroke: 'steelblue', + lineWidth: 5, + }, + label: '边新增', + labelCfg: { + position: 'end', + refY: 20, + }, + }; + graphG6.value.addItem('edge', model); + + console.log(JSON.parse(JSON.stringify(graphG6.value.save()))); +} + +/**保存图数据 */ +function fnGraphSave() { + sessionStorage.setItem('graph', JSON.stringify(graphG6.value.save())); + + console.log(JSON.parse(JSON.stringify(graphG6.value.save()))); +} + +/**保存图数据 */ +function fnGraphLoad() { + const data = sessionStorage.getItem('graph') || '{}'; + graphG6.value.read(JSON.parse(data)); + console.log(JSON.parse(JSON.stringify(graphG6.value.save()))); +}