feat: 拓扑编辑边
This commit is contained in:
@@ -1,12 +1,13 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { reactive, onMounted, ref } from 'vue';
|
import { reactive, onMounted, ref, watch } from 'vue';
|
||||||
import { PageContainer } from 'antdv-pro-layout';
|
import { PageContainer } from 'antdv-pro-layout';
|
||||||
import useI18n from '@/hooks/useI18n';
|
import useI18n from '@/hooks/useI18n';
|
||||||
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
||||||
import { listNe, stateNe } from '@/api/ne/ne';
|
import { listNe, stateNe } from '@/api/ne/ne';
|
||||||
import { parseDateToStr } from '@/utils/date-utils';
|
import { parseDateToStr } from '@/utils/date-utils';
|
||||||
import { Graph, Menu, Tooltip, Util, registerBehavior } from '@antv/g6';
|
import { Graph, Menu, Tooltip } from '@antv/g6';
|
||||||
import { message, Modal, Form, notification } from 'ant-design-vue/lib';
|
import { message, Modal, Form, notification } from 'ant-design-vue/lib';
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
/**图DOM节点实例对象 */
|
/**图DOM节点实例对象 */
|
||||||
@@ -352,6 +353,49 @@ function graphEvent(graph: Graph) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**图画布右击菜单 */
|
||||||
|
const graphCanvasMenu = new Menu({
|
||||||
|
offsetX: 6,
|
||||||
|
offseY: 10,
|
||||||
|
itemTypes: ['canvas'],
|
||||||
|
getContent(evt) {
|
||||||
|
return `
|
||||||
|
<div
|
||||||
|
style="
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
width: 140px;
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<div id="show" style="cursor: pointer; margin-bottom: 2px">
|
||||||
|
1. 显示所有隐藏项
|
||||||
|
</div>
|
||||||
|
</div>`;
|
||||||
|
},
|
||||||
|
handleMenuClick(target, item) {
|
||||||
|
console.log(target, item);
|
||||||
|
const targetId = target.id;
|
||||||
|
switch (targetId) {
|
||||||
|
case 'show':
|
||||||
|
// 显示节点
|
||||||
|
graphG6.value.getNodes().forEach((node: any) => {
|
||||||
|
if (!node.isVisible()) {
|
||||||
|
graphG6.value.showItem(node);
|
||||||
|
graphG6.value.refreshItem(node);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// 显示边
|
||||||
|
graphG6.value.getEdges().forEach((edge: any) => {
|
||||||
|
if (!edge.isVisible()) {
|
||||||
|
graphG6.value.showItem(edge);
|
||||||
|
graphG6.value.refreshItem(edge);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
/**图节点右击菜单 */
|
/**图节点右击菜单 */
|
||||||
const graphNodeMenu = new Menu({
|
const graphNodeMenu = new Menu({
|
||||||
offsetX: 6,
|
offsetX: 6,
|
||||||
@@ -397,6 +441,7 @@ const graphEdgeMenu = new Menu({
|
|||||||
offseY: 10,
|
offseY: 10,
|
||||||
itemTypes: ['edge'],
|
itemTypes: ['edge'],
|
||||||
getContent(evt) {
|
getContent(evt) {
|
||||||
|
console.log(evt);
|
||||||
return `
|
return `
|
||||||
<div
|
<div
|
||||||
style="
|
style="
|
||||||
@@ -420,11 +465,10 @@ const graphEdgeMenu = new Menu({
|
|||||||
const targetId = target.id;
|
const targetId = target.id;
|
||||||
switch (targetId) {
|
switch (targetId) {
|
||||||
case 'edit':
|
case 'edit':
|
||||||
const info = item.getModel();
|
const edge = item.getModel();
|
||||||
console.log(Object.assign({}, info));
|
|
||||||
|
|
||||||
modalState.title = '边信息编辑';
|
modalState.title = '边信息编辑';
|
||||||
modalState.formEdge = Object.assign(modalState.formEdge, info);
|
modalState.formEdgeOrigin = JSON.parse(JSON.stringify(edge));
|
||||||
|
modalState.formEdge = Object.assign(modalState.formEdge, edge);
|
||||||
modalState.visibleByEdge = true;
|
modalState.visibleByEdge = true;
|
||||||
break;
|
break;
|
||||||
case 'hide':
|
case 'hide':
|
||||||
@@ -518,8 +562,10 @@ function fnRanderGraph() {
|
|||||||
type: 'polyline',
|
type: 'polyline',
|
||||||
style: {
|
style: {
|
||||||
offset: 20, // 拐弯处距离节点最小距离
|
offset: 20, // 拐弯处距离节点最小距离
|
||||||
radius: 4, // 拐弯处的圆角弧度,若不设置则为直角
|
radius: 2, // 拐弯处的圆角弧度,若不设置则为直角
|
||||||
|
stroke: '#ffffff',
|
||||||
lineWidth: 1,
|
lineWidth: 1,
|
||||||
|
cursor: 'pointer',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
// defaultEdge: {
|
// defaultEdge: {
|
||||||
@@ -533,7 +579,13 @@ function fnRanderGraph() {
|
|||||||
fillOpacity: 0.1,
|
fillOpacity: 0.1,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
plugins: [graphNodeMenu, graphNodeTooltip, graphEdgeMenu, graphEdgeTooltip],
|
plugins: [
|
||||||
|
graphCanvasMenu,
|
||||||
|
graphNodeMenu,
|
||||||
|
graphNodeTooltip,
|
||||||
|
graphEdgeMenu,
|
||||||
|
graphEdgeTooltip,
|
||||||
|
],
|
||||||
});
|
});
|
||||||
graph.data(graphG6Data);
|
graph.data(graphG6Data);
|
||||||
graph.render();
|
graph.render();
|
||||||
@@ -685,6 +737,7 @@ type ModalStateType = {
|
|||||||
/**表单数据 */
|
/**表单数据 */
|
||||||
form: Record<string, any>;
|
form: Record<string, any>;
|
||||||
/**图边表单数据 */
|
/**图边表单数据 */
|
||||||
|
formEdgeOrigin: Record<string, any>;
|
||||||
formEdge: Record<string, any>;
|
formEdge: Record<string, any>;
|
||||||
/**确定按钮 loading */
|
/**确定按钮 loading */
|
||||||
confirmLoading: boolean;
|
confirmLoading: boolean;
|
||||||
@@ -698,19 +751,27 @@ let modalState: ModalStateType = reactive({
|
|||||||
id: '',
|
id: '',
|
||||||
msisdn: '',
|
msisdn: '',
|
||||||
},
|
},
|
||||||
|
formEdgeOrigin: {},
|
||||||
formEdge: {
|
formEdge: {
|
||||||
id: '',
|
id: '',
|
||||||
source: '',
|
source: '',
|
||||||
target: '',
|
target: '',
|
||||||
type: '',
|
type: '',
|
||||||
style: {},
|
style: {
|
||||||
|
stroke: '#ffffff',
|
||||||
|
lineWidth: 1,
|
||||||
|
},
|
||||||
label: '',
|
label: '',
|
||||||
labelCfg: {
|
labelCfg: {
|
||||||
refX: 0,
|
refX: 0,
|
||||||
refY: 0,
|
refY: 0,
|
||||||
position: 'middle',
|
position: 'middle',
|
||||||
autoRotate: false,
|
autoRotate: false,
|
||||||
style: {},
|
style: {
|
||||||
|
fill: '#ffffff',
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: 400,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
confirmLoading: false,
|
confirmLoading: false,
|
||||||
@@ -750,9 +811,18 @@ const modalStateFormEdge = Form.useForm(
|
|||||||
* 进行表达规则校验
|
* 进行表达规则校验
|
||||||
*/
|
*/
|
||||||
function fnModalCancel() {
|
function fnModalCancel() {
|
||||||
|
// 边编辑还原
|
||||||
modalState.visibleByEdge = false;
|
modalState.visibleByEdge = false;
|
||||||
|
const info = JSON.parse(JSON.stringify(modalState.formEdgeOrigin));
|
||||||
|
if (info.id) {
|
||||||
|
graphG6.value.removeItem(info.id);
|
||||||
|
console.log(info);
|
||||||
|
graphG6.value.addItem('edge', info);
|
||||||
|
modalStateFormEdge.resetFields();
|
||||||
|
modalState.formEdgeOrigin = {};
|
||||||
|
}
|
||||||
|
|
||||||
modalStateForm.resetFields();
|
modalStateForm.resetFields();
|
||||||
modalStateFormEdge.resetFields();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**内置边类型 */
|
/**内置边类型 */
|
||||||
@@ -791,25 +861,29 @@ const edgeType = [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
/**边编辑监听更新视图 */
|
||||||
|
watch(modalState.formEdge, edge => {
|
||||||
|
const info = JSON.parse(JSON.stringify(edge));
|
||||||
|
const edgeId = info.id;
|
||||||
|
if (edgeId) {
|
||||||
|
graphG6.value.clearItemStates(edgeId, 'selected');
|
||||||
|
graphG6.value.updateItem(edgeId, info);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
/**边新增 */
|
/**边新增 */
|
||||||
function fnModalOkEdge() {
|
function fnModalOkEdge() {
|
||||||
const model = {
|
const info = JSON.parse(JSON.stringify(modalState.formEdge));
|
||||||
id: '2-190',
|
const edgeId = info.id;
|
||||||
source: '2',
|
if (edgeId) {
|
||||||
target: '190',
|
graphG6.value.removeItem(edgeId);
|
||||||
type: 'line',
|
info.id = `${info.source}~${Date.now()}~${info.target}`;
|
||||||
style: {
|
console.log(info);
|
||||||
stroke: 'steelblue',
|
graphG6.value.addItem('edge', info);
|
||||||
lineWidth: 5,
|
modalState.visibleByEdge = false;
|
||||||
},
|
modalStateFormEdge.resetFields();
|
||||||
label: '边新增',
|
modalState.formEdgeOrigin = {};
|
||||||
labelCfg: {
|
}
|
||||||
position: 'end',
|
|
||||||
refY: 20,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
graphG6.value.addItem('edge', model);
|
|
||||||
|
|
||||||
console.log(JSON.parse(JSON.stringify(graphG6.value.save())));
|
console.log(JSON.parse(JSON.stringify(graphG6.value.save())));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -976,86 +1050,159 @@ function fnGraphLoad() {
|
|||||||
</a-col>
|
</a-col>
|
||||||
</a-row>
|
</a-row>
|
||||||
|
|
||||||
|
<a-row :gutter="16">
|
||||||
|
<a-col :lg="8" :md="8" :xs="24">
|
||||||
|
<a-form-item label="stroke" name="stroke" help="边的颜色">
|
||||||
|
<a-input
|
||||||
|
v-model:value="modalState.formEdge.style.stroke"
|
||||||
|
type="color"
|
||||||
|
:placeholder="t('common.ipnutPlease')"
|
||||||
|
size="small"
|
||||||
|
></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :lg="8" :md="8" :xs="24">
|
||||||
|
<a-form-item label="lineWidth" name="lineWidth" help="边宽度">
|
||||||
|
<a-input-number
|
||||||
|
v-model:value="modalState.formEdge.style.lineWidth"
|
||||||
|
style="width: 100%"
|
||||||
|
:min="1"
|
||||||
|
:max="100"
|
||||||
|
placeholder="<=100"
|
||||||
|
></a-input-number>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :lg="8" :md="8" :xs="24">
|
||||||
|
<a-form-item
|
||||||
|
label="endArrow"
|
||||||
|
name="endArrow"
|
||||||
|
help="边的结束端绘制默认箭头"
|
||||||
|
>
|
||||||
|
<a-switch
|
||||||
|
v-model:checked="modalState.formEdge.style.endArrow"
|
||||||
|
checked-children="是"
|
||||||
|
un-checked-children="否"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :lg="8" :md="8" :xs="24">
|
||||||
|
<a-form-item
|
||||||
|
label="startArrow"
|
||||||
|
name="startArrow"
|
||||||
|
help="边的开始端绘制默认箭头"
|
||||||
|
>
|
||||||
|
<a-switch
|
||||||
|
v-model:checked="modalState.formEdge.style.startArrow"
|
||||||
|
checked-children="是"
|
||||||
|
un-checked-children="否"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
|
||||||
|
<a-divider orientation="left">
|
||||||
|
标签文本 label 及其配置 labelCfg
|
||||||
|
</a-divider>
|
||||||
|
|
||||||
|
<div>{{ modalState.formEdge.labelCfg }}</div>
|
||||||
|
|
||||||
|
<a-form-item label="label" name="label" :label-col="{ span: 3 }">
|
||||||
|
<a-input v-model:value="modalState.formEdge.label" allow-clear>
|
||||||
|
<template #prefix>
|
||||||
|
<a-tooltip placement="topLeft">
|
||||||
|
<template #title> 文本文字,如果没有则不会显示 </template>
|
||||||
|
<InfoCircleOutlined style="color: rgba(0, 0, 0, 0.45)" />
|
||||||
|
</a-tooltip>
|
||||||
|
</template>
|
||||||
|
</a-input>
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
<a-row :gutter="16">
|
<a-row :gutter="16">
|
||||||
<a-col :lg="12" :md="12" :xs="24">
|
<a-col :lg="12" :md="12" :xs="24">
|
||||||
<a-form-item label="label" name="label">
|
<a-form-item
|
||||||
<a-input v-model:value="modalState.formEdge.label" allow-clear>
|
label="labelCfg.refX"
|
||||||
<template #prefix>
|
name="labelCfg.refX"
|
||||||
<a-tooltip placement="topLeft">
|
help="标签在 x 方向的偏移量"
|
||||||
<template #title> 文本文字,如果没有则不会显示 </template>
|
>
|
||||||
<InfoCircleOutlined style="color: rgba(0, 0, 0, 0.45)" />
|
<a-input-number
|
||||||
</a-tooltip>
|
v-model:value="modalState.formEdge.labelCfg.refX"
|
||||||
</template>
|
style="width: 100%"
|
||||||
</a-input>
|
:min="-100"
|
||||||
|
:max="100"
|
||||||
|
placeholder="<=100"
|
||||||
|
></a-input-number>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-col>
|
</a-col>
|
||||||
<a-col :lg="12" :md="12" :xs="24">
|
<a-col :lg="12" :md="12" :xs="24">
|
||||||
<a-form-item label="labelCfg" name="labelCfg">
|
<a-form-item
|
||||||
{{ modalState.form.labelCfg }}
|
label="labelCfg.refY"
|
||||||
|
name="labelCfg.refY"
|
||||||
|
help="标签在 y 方向的偏移量"
|
||||||
|
>
|
||||||
|
<a-input-number
|
||||||
|
v-model:value="modalState.formEdge.labelCfg.refY"
|
||||||
|
style="width: 100%"
|
||||||
|
:min="-100"
|
||||||
|
:max="100"
|
||||||
|
placeholder="<=100"
|
||||||
|
></a-input-number>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :lg="12" :md="12" :xs="24">
|
||||||
|
<a-form-item
|
||||||
|
label="labelCfg.position"
|
||||||
|
name="labelCfg.position"
|
||||||
|
help="文本相对于边的位置"
|
||||||
|
>
|
||||||
|
<a-select v-model:value="modalState.formEdge.labelCfg.position">
|
||||||
|
<a-select-option
|
||||||
|
v-for="opt in [
|
||||||
|
{ type: 'start' },
|
||||||
|
{ type: 'middle' },
|
||||||
|
{ type: 'end' },
|
||||||
|
]"
|
||||||
|
:key="opt.type"
|
||||||
|
:value="opt.type"
|
||||||
|
>
|
||||||
|
{{ opt.type }}
|
||||||
|
</a-select-option>
|
||||||
|
</a-select>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-col>
|
</a-col>
|
||||||
</a-row>
|
</a-row>
|
||||||
<a-row :gutter="16">
|
<a-row :gutter="16">
|
||||||
<a-col :lg="12" :md="12" :xs="24">
|
<a-col :lg="8" :md="8" :xs="24">
|
||||||
<a-form-item label="labelCfg.refX" name="labelCfg.refX">
|
<a-form-item
|
||||||
<a-input
|
label="labelCfg.autoRotate"
|
||||||
v-model:value="modalState.formEdge.labelCfg.refX"
|
name="labelCfg.autoRotate"
|
||||||
allow-clear
|
help="标签文字是否跟随边旋转"
|
||||||
>
|
>
|
||||||
<template #prefix>
|
<a-switch
|
||||||
<a-tooltip placement="topLeft">
|
v-model:checked="modalState.formEdge.labelCfg.autoRotate"
|
||||||
<template #title> 标签在 x 方向的偏移量 </template>
|
checked-children="是"
|
||||||
<InfoCircleOutlined style="color: rgba(0, 0, 0, 0.45)" />
|
un-checked-children="否"
|
||||||
</a-tooltip>
|
/>
|
||||||
</template>
|
|
||||||
</a-input>
|
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-col>
|
</a-col>
|
||||||
<a-col :lg="12" :md="12" :xs="24">
|
<a-col :lg="8" :md="8" :xs="24">
|
||||||
<a-form-item label="labelCfg.refY" name="labelCfg.refY">
|
<a-form-item label="fill" name="fill" help="文本颜色">
|
||||||
<a-input
|
<a-input
|
||||||
v-model:value="modalState.formEdge.labelCfg.refY"
|
v-model:value="modalState.formEdge.labelCfg.style.fill"
|
||||||
allow-clear
|
type="color"
|
||||||
>
|
:placeholder="t('common.ipnutPlease')"
|
||||||
<template #prefix>
|
size="small"
|
||||||
<a-tooltip placement="topLeft">
|
></a-input>
|
||||||
<template #title> 标签在 x 方向的偏移量 </template>
|
|
||||||
<InfoCircleOutlined style="color: rgba(0, 0, 0, 0.45)" />
|
|
||||||
</a-tooltip>
|
|
||||||
</template>
|
|
||||||
</a-input>
|
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-col>
|
</a-col>
|
||||||
</a-row>
|
<a-col :lg="8" :md="8" :xs="24">
|
||||||
<a-row :gutter="16">
|
<a-form-item label="fontSize" name="fontSize">
|
||||||
<a-col :lg="12" :md="12" :xs="24">
|
<a-input-number
|
||||||
<a-form-item label="labelCfg.refX" name="labelCfg.refX">
|
v-model:value="modalState.formEdge.labelCfg.style.fontSize"
|
||||||
<a-input
|
style="width: 100%"
|
||||||
v-model:value="modalState.formEdge.labelCfg.refX"
|
:min="10"
|
||||||
allow-clear
|
:max="100"
|
||||||
>
|
placeholder="<=100"
|
||||||
<template #prefix>
|
></a-input-number>
|
||||||
<a-tooltip placement="topLeft">
|
|
||||||
<template #title> 标签在 x 方向的偏移量 </template>
|
|
||||||
<InfoCircleOutlined style="color: rgba(0, 0, 0, 0.45)" />
|
|
||||||
</a-tooltip>
|
|
||||||
</template>
|
|
||||||
</a-input>
|
|
||||||
</a-form-item>
|
|
||||||
</a-col>
|
|
||||||
<a-col :lg="12" :md="12" :xs="24">
|
|
||||||
<a-form-item label="labelCfg.refY" name="labelCfg.refY">
|
|
||||||
<a-input
|
|
||||||
v-model:value="modalState.formEdge.labelCfg.refY"
|
|
||||||
allow-clear
|
|
||||||
>
|
|
||||||
<template #prefix>
|
|
||||||
<a-tooltip placement="topLeft">
|
|
||||||
<template #title> 标签在 x 方向的偏移量 </template>
|
|
||||||
<InfoCircleOutlined style="color: rgba(0, 0, 0, 0.45)" />
|
|
||||||
</a-tooltip>
|
|
||||||
</template>
|
|
||||||
</a-input>
|
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-col>
|
</a-col>
|
||||||
</a-row>
|
</a-row>
|
||||||
|
|||||||
Reference in New Issue
Block a user