feat: 拓扑编辑边

This commit is contained in:
TsMask
2023-12-26 16:24:54 +08:00
parent 6441a1efaf
commit 45b04975eb

View File

@@ -1,12 +1,13 @@
<script setup lang="ts">
import { reactive, onMounted, ref } from 'vue';
import { reactive, onMounted, ref, watch } from 'vue';
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 { 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';
const { t } = useI18n();
/**图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({
offsetX: 6,
@@ -397,6 +441,7 @@ const graphEdgeMenu = new Menu({
offseY: 10,
itemTypes: ['edge'],
getContent(evt) {
console.log(evt);
return `
<div
style="
@@ -420,11 +465,10 @@ const graphEdgeMenu = new Menu({
const targetId = target.id;
switch (targetId) {
case 'edit':
const info = item.getModel();
console.log(Object.assign({}, info));
const edge = item.getModel();
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;
break;
case 'hide':
@@ -518,8 +562,10 @@ function fnRanderGraph() {
type: 'polyline',
style: {
offset: 20, // 拐弯处距离节点最小距离
radius: 4, // 拐弯处的圆角弧度,若不设置则为直角
radius: 2, // 拐弯处的圆角弧度,若不设置则为直角
stroke: '#ffffff',
lineWidth: 1,
cursor: 'pointer',
},
},
// defaultEdge: {
@@ -533,7 +579,13 @@ function fnRanderGraph() {
fillOpacity: 0.1,
},
},
plugins: [graphNodeMenu, graphNodeTooltip, graphEdgeMenu, graphEdgeTooltip],
plugins: [
graphCanvasMenu,
graphNodeMenu,
graphNodeTooltip,
graphEdgeMenu,
graphEdgeTooltip,
],
});
graph.data(graphG6Data);
graph.render();
@@ -685,6 +737,7 @@ type ModalStateType = {
/**表单数据 */
form: Record<string, any>;
/**图边表单数据 */
formEdgeOrigin: Record<string, any>;
formEdge: Record<string, any>;
/**确定按钮 loading */
confirmLoading: boolean;
@@ -698,19 +751,27 @@ let modalState: ModalStateType = reactive({
id: '',
msisdn: '',
},
formEdgeOrigin: {},
formEdge: {
id: '',
source: '',
target: '',
type: '',
style: {},
style: {
stroke: '#ffffff',
lineWidth: 1,
},
label: '',
labelCfg: {
refX: 0,
refY: 0,
position: 'middle',
autoRotate: false,
style: {},
style: {
fill: '#ffffff',
fontSize: 14,
fontWeight: 400,
},
},
},
confirmLoading: false,
@@ -750,9 +811,18 @@ const modalStateFormEdge = Form.useForm(
* 进行表达规则校验
*/
function fnModalCancel() {
// 边编辑还原
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();
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() {
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);
const info = JSON.parse(JSON.stringify(modalState.formEdge));
const edgeId = info.id;
if (edgeId) {
graphG6.value.removeItem(edgeId);
info.id = `${info.source}~${Date.now()}~${info.target}`;
console.log(info);
graphG6.value.addItem('edge', info);
modalState.visibleByEdge = false;
modalStateFormEdge.resetFields();
modalState.formEdgeOrigin = {};
}
console.log(JSON.parse(JSON.stringify(graphG6.value.save())));
}
@@ -976,86 +1050,159 @@ function fnGraphLoad() {
</a-col>
</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-col :lg="12" :md="12" :xs="24">
<a-form-item label="label" name="label">
<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
label="labelCfg.refX"
name="labelCfg.refX"
help="标签在 x 方向的偏移量"
>
<a-input-number
v-model:value="modalState.formEdge.labelCfg.refX"
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" name="labelCfg">
{{ modalState.form.labelCfg }}
<a-form-item
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-col>
</a-row>
<a-row :gutter="16">
<a-col :lg="12" :md="12" :xs="24">
<a-form-item label="labelCfg.refX" name="labelCfg.refX">
<a-input
v-model:value="modalState.formEdge.labelCfg.refX"
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-col :lg="8" :md="8" :xs="24">
<a-form-item
label="labelCfg.autoRotate"
name="labelCfg.autoRotate"
help="标签文字是否跟随边旋转"
>
<a-switch
v-model:checked="modalState.formEdge.labelCfg.autoRotate"
checked-children="是"
un-checked-children="否"
/>
</a-form-item>
</a-col>
<a-col :lg="12" :md="12" :xs="24">
<a-form-item label="labelCfg.refY" name="labelCfg.refY">
<a-col :lg="8" :md="8" :xs="24">
<a-form-item label="fill" name="fill" help="文本颜色">
<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>
v-model:value="modalState.formEdge.labelCfg.style.fill"
type="color"
:placeholder="t('common.ipnutPlease')"
size="small"
></a-input>
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="16">
<a-col :lg="12" :md="12" :xs="24">
<a-form-item label="labelCfg.refX" name="labelCfg.refX">
<a-input
v-model:value="modalState.formEdge.labelCfg.refX"
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-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-col :lg="8" :md="8" :xs="24">
<a-form-item label="fontSize" name="fontSize">
<a-input-number
v-model:value="modalState.formEdge.labelCfg.style.fontSize"
style="width: 100%"
:min="10"
:max="100"
placeholder="<=100"
></a-input-number>
</a-form-item>
</a-col>
</a-row>