Toggle navigation
Toggle navigation
This project
Loading...
Sign in
Hooke
/
vue-flow-editor
Go to a project
Toggle navigation
Toggle navigation pinning
Projects
Groups
Snippets
Help
Project
Activity
Repository
Pipelines
Graphs
Issues
0
Merge Requests
0
Wiki
Snippets
Network
Create a new issue
Builds
Commits
Issue Boards
Authored by
hookehuyr
2023-11-14 14:17:03 +0800
Browse Files
Options
Browse Files
Download
Email Patches
Plain Diff
Commit
3f611d61173466efbeb85711c9bdfc58a5c66f2a
3f611d61
1 parent
1816b1ef
新增控制节点的自定义节点测试
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
213 additions
and
14 deletions
doc/App.vue
doc/data.js
src/editor/vue-flow-edit-menu.vue
src/editor/vue-flow-editor-canvas.tsx
src/editor/vue-flow-editor.tsx
src/shape/activity.ts
src/shape/control.ts
src/shape/index.ts
src/utils/utils.ts
doc/App.vue
View file @
3f611d6
...
...
@@ -13,9 +13,11 @@
:afterDelete="handleAfterDelete"
:beforeAdd="handleBeforeAdd"
:afterAdd="handleAfterAdd"
@dragend-node="onDragEndNode"
@dblclick-node="onDblclickNode"
@dblclick-edge="onDblClickEdge"
:activityConfig="state.activityConfig"
:controlConfig="state.controlConfig"
>
<!-- 左侧菜单 -->
<template v-slot:menu>
...
...
@@ -33,6 +35,21 @@
</template>
</vue-flow-edit-menu>
</vue-flow-edit-menu-group>
<vue-flow-edit-menu-group label="控制节点" value>
<!-- 注意 key 值的绑定 -->
<vue-flow-edit-menu
v-for="(value, key) in state.controlConfig"
:key="key"
:model="{ control: key, text: value.text, desc: value.desc }"
>
<template v-slot:content>
<div class="activity-menu">
<img :src="value.img" />
<span>{{ value.text }}</span>
</div>
</template>
</vue-flow-edit-menu>
</vue-flow-edit-menu-group>
<vue-flow-edit-menu-group
v-for="(group, groupIndex) in state.menuData"
:label="group.label"
...
...
@@ -297,6 +314,26 @@ export default {
img: "https://cdn.ipadbiz.cn/oa/crowd-node.svg"
}
},
controlConfig: {
start: {
text: "开始",
desc: "描述文字",
color: "#9283ed",
img: "https://cdn.ipadbiz.cn/oa/advertisement-node.svg"
},
stop: {
text: "中止",
desc: "描述文字",
color: "#ed8383",
img: "https://cdn.ipadbiz.cn/oa/coupon-node.svg"
},
end: {
text: "结束",
desc: "描述文字",
color: "#92dba8",
img: "https://cdn.ipadbiz.cn/oa/crowd-node.svg"
}
},
dialogUserFormVisible: false,
activeName: "node",
attr_radio: "基础属性",
...
...
@@ -412,6 +449,7 @@ export default {
model.style = model.style || {};
model.labelCfg = model.labelCfg || { style: {} };
model.data = model.data ? model.data : {};
state.detailModel = model;
editor.openModel();
}
...
...
@@ -526,12 +564,27 @@ export default {
* @param {type} type - The type of the event.
*/
function handleAfterAdd(model: myObj, type: string) {
// TODO: 因为resize会重新绘制,所以可能需要保存操作
if (type === 'node') {
console.log(`新增节点`);
}
if (type === "edge") {
console.log(`新增连接线`);
}
}
/**
* 拖动节点结束回调
*
* @param {myEvent} e - The event object containing information about the drag and drop.
*/
function onDragEndNode(e: myEvent) {
// TODO:可能需要接口保存相应位置,避免拖动窗口时数据丢失
const model = e.item.get("model");
console.log("onDragEndNode", model);
}
/**
* 打印流程图数据结构
*
* @return {void} No return value.
...
...
@@ -568,6 +621,7 @@ export default {
showMultipleSelect: true, // 编辑器是否可以多选
onDblclickNode,
onDragEndNode,
onDblClickEdge,
cancel,
save,
...
...
doc/data.js
View file @
3f611d6
...
...
@@ -17,6 +17,14 @@ export const AppData = {
"activity"
:
"coupon"
,
},
{
"id"
:
"23234567891"
,
"x"
:
130
,
"y"
:
130
,
"text"
:
"开始"
,
"desc"
:
"发布"
,
"control"
:
"start"
,
},
{
"data"
:
{},
"id"
:
"start-node"
,
"label"
:
"开始"
,
...
...
src/editor/vue-flow-edit-menu.vue
View file @
3f611d6
...
...
@@ -9,7 +9,7 @@
<script>
import {VueFlowEditorProvider} from "@/editor/editor";
import {formatNodeModel, formatPos} from "../utils/utils";
import {formatNodeModel, formatPos
,formatNodeModel_control
} from "../utils/utils";
export default {
name: "vue-flow-edit-menu",
...
...
@@ -34,8 +34,13 @@
},
drop: async e => {
let model = {...this.model}
// TAG: 自定义节点
formatNodeModel(model, this[VueFlowEditorProvider].props.activityConfig)
// TAG: 自定义节点 更新Model
if (model.activity) {
formatNodeModel(model, this[VueFlowEditorProvider].props.activityConfig)
}
if (model.control) {
formatNodeModel_control(model, this[VueFlowEditorProvider].props.controlConfig)
}
let {id, shape, size} = model
...
...
src/editor/vue-flow-editor-canvas.tsx
View file @
3f611d6
...
...
@@ -4,7 +4,7 @@ import {G6} from "@/g6/g6";
import {useBehavior} from "@/behavior";
import {GraphStyle} from "@/utils/styles";
import {registerShape} from "@/shape";
import {formatNodeModel} from "@/utils/utils";
import {formatNodeModel
,formatNodeModel_control
} from "@/utils/utils";
registerShape(G6)
...
...
@@ -59,8 +59,9 @@ export default {
let {nodes, edges} = data || {}
nodes = nodes || []
edges = edges || []
// TAG: 自定义节点
// TAG: 自定义节点
更新Model
nodes.forEach(node => formatNodeModel(node, editorProps.activityConfig))
nodes.forEach(node => formatNodeModel_control(node, editorProps.controlConfig))
data = {nodes, edges}
...
...
src/editor/vue-flow-editor.tsx
View file @
3f611d6
import {defineComponent,computed, getCurrentInstance, onBeforeUnmount, provide, reactive} from "vue";
import './vue-flow-editor.scss'
import {useCanvasProps, useEditorCommander, useEditorStyles, VueFlowEditorProvider} from "@/editor/editor";
import {formatNodeModel, suffixSize} from "@/utils/utils";
import {formatNodeModel, suffixSize
,formatNodeModel_control
} from "@/utils/utils";
export default {
name: 'vue-flow-editor',
...
...
@@ -28,8 +28,9 @@ export default {
afterDelete: {type: Function}, // 删除后动作
beforeAdd: {type: Function}, // 添加前校验
afterAdd: {type: Function}, // 添加后动作
// TAG: 自定义节点 - 前端注入类型
activityConfig: {type: Object}, // 注册活动节点
controlConfig: {type: Object}, // 注册活动节点
},
setup(props, context) {
...
...
@@ -58,6 +59,9 @@ export default {
graph.on('node:dblclick', (e) => {
context.emit('dblclick-node', e)
})
graph.on('node:dragend', (e) => {
context.emit('dragend-node', e)
})
graph.on('edge:dblclick', (e) => {
context.emit('dblclick-edge', e)
})
...
...
@@ -79,8 +83,10 @@ export default {
editorState.showModel = false
},
updateModel: (model) => {
// TAG: 自定义节点
// TAG: 自定义节点
更新Model
formatNodeModel(model, props.activityConfig)
formatNodeModel_control(model, props.controlConfig)
commander.commands.update(model)
},
openPreview: () => {
...
...
src/shape/activity.ts
View file @
3f611d6
/*
* @Date: 2023-10-27 09:29:59
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2023-11-1
3 17:25:41
* @LastEditTime: 2023-11-1
4 10:21:38
* @FilePath: /vue-flow-editor/src/shape/activity.ts
* @Description: 文件描述
*/
import
{
BASE_COLOR
}
from
"@/utils/styles"
;
// TAG: 自定义节点
// TAG: 自定义节点
- 定义节点
export
function
registerActivity
(
G6
)
{
G6
.
registerNode
(
'activity'
,
{
options
:
{
...
...
src/shape/control.ts
0 → 100644
View file @
3f611d6
/*
* @Date: 2023-10-27 09:29:59
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2023-11-14 11:16:04
* @FilePath: /vue-flow-editor/src/shape/control.ts
* @Description: 自定义控制节点
*/
import
{
BASE_COLOR
}
from
"@/utils/styles"
;
// TAG: 自定义节点 - 定义节点
export
function
registerControl
(
G6
)
{
G6
.
registerNode
(
'control'
,
{
options
:
{
style
:
{},
stateStyles
:
{},
},
setState
()
{},
drawShape
(
cfg
,
group
)
{
// 继承了基类,可以使用drawShape,如果没有继承,必须要有draw
let
{
text
,
desc
,
img
,
color
}
=
cfg
color
=
color
||
BASE_COLOR
desc
=
desc
||
'无描述'
const
[
width
,
height
]
=
(
cfg
.
size
||
[
200
,
80
])
as
[
number
,
number
]
// 定义节点里的图形,名字和配置可以自定义
const
shapes
=
{
keyShape
:
{
type
:
'rect'
,
attrs
:
{
fill
:
'white'
,
x
:
-
width
/
2
,
y
:
-
height
/
2
,
width
,
height
,
shadowColor
:
'#BFC5D2'
,
shadowBlur
:
50
},
},
sideRect
:
{
type
:
'rect'
,
attrs
:
{
x
:
-
width
/
2
,
y
:
-
height
/
2
,
width
:
6
,
height
,
fill
:
color
},
},
img
:
{
type
:
'image'
,
attrs
:
{
x
:
height
/
4
-
width
/
2
,
y
:
height
/
4
-
height
/
2
,
width
:
height
/
2
,
height
:
height
/
2
,
img
},
},
label
:
{
type
:
'text'
,
attrs
:
{
text
,
x
:
height
-
width
/
2
,
y
:
height
*
(
3
/
8
)
-
height
/
2
,
fontSize
:
16
,
textAlign
:
'left'
,
textBaseline
:
'middle'
,
fill
:
'black'
},
},
desc
:
{
type
:
'text'
,
attrs
:
{
text
:
desc
,
x
:
height
-
width
/
2
,
y
:
height
*
(
5
/
8
)
-
height
/
2
,
fontSize
:
12
,
textAlign
:
'left'
,
textBaseline
:
'middle'
,
fill
:
'#999'
},
},
}
const
addShapes
=
{}
let
keyShape
;
Object
.
keys
(
shapes
).
forEach
((
shapeName
)
=>
{
const
{
type
,
attrs
}
=
shapes
[
shapeName
]
addShapes
[
shapeName
]
=
group
.
addShape
(
type
,
{
attrs
,
name
:
shapeName
,
draggable
:
true
,
})
if
(
shapeName
===
'keyShape'
)
{
keyShape
=
addShapes
[
shapeName
]
}
})
group
.
shapes
=
addShapes
// 好像是必须返回一个名字叫keyShape,用于寻找节点
return
keyShape
},
update
(
cfg
,
group
)
{
group
=
group
.
getContainer
()
group
.
shapes
.
sideRect
.
attr
({
fill
:
cfg
.
color
})
group
.
shapes
.
img
.
attr
({
img
:
cfg
.
img
})
group
.
shapes
.
label
.
attr
({
text
:
cfg
.
text
})
group
.
shapes
.
desc
.
attr
({
text
:
cfg
.
desc
})
},
},
'single-shape'
)
// 继承了 single-shape 的基类
}
src/shape/index.ts
View file @
3f611d6
/*
* @Date: 2023-10-27 09:29:59
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2023-11-1
3 16:31:31
* @LastEditTime: 2023-11-1
4 11:17:20
* @FilePath: /vue-flow-editor/src/shape/index.ts
* @Description: 文件描述
*/
import
{
registerAnchor
}
from
"@/shape/anchor"
;
import
{
registerEdge
}
from
"@/shape/edge"
;
// TAG: 自定义节点
// TAG: 自定义节点
- 引入节点
import
{
registerActivity
}
from
"@/shape/activity"
;
import
{
registerControl
}
from
"@/shape/control"
;
export
function
registerShape
(
G6
)
{
registerAnchor
(
G6
)
registerEdge
(
G6
)
registerActivity
(
G6
)
registerControl
(
G6
)
}
...
...
src/utils/utils.ts
View file @
3f611d6
...
...
@@ -35,8 +35,9 @@ export const DEFAULT_SIZE = { // 不同类型的默认宽高
circle
:
[
80
,
80
],
triangle
:
[
80
,
80
],
star
:
[
80
,
80
],
// TAG: 自定义节点
// TAG: 自定义节点
- 自定义属性
activity
:
[
200
,
80
],
control
:
[
200
,
80
],
}
export
function
formatPos
(
option
:
{
x
:
number
,
y
:
number
,
size
:
[
number
,
number
],
shape
:
string
}):
{
x
:
number
,
y
:
number
,
size
:
[
number
,
number
],
shape
}
{
...
...
@@ -78,7 +79,7 @@ export function formatPos(option: { x: number, y: number, size: [number, number]
shape
:
option
.
shape
,
}
}
// TAG: 自定义节点
// TAG: 自定义节点
- 挂载额外属性到model上
export
function
formatNodeModel
(
model
,
activityConfig
)
{
let
{
shape
,
size
,
activity
}
=
model
...
...
@@ -113,5 +114,50 @@ export function formatNodeModel(model, activityConfig) {
model
.
shape
=
'activity'
model
.
img
=
activityConfig
[
activity
].
img
model
.
color
=
activityConfig
[
activity
].
color
}
}
/**
* 自定义节点 Control - 挂载额外属性到model上
*
* @param {any} model - The node model to format.
* @param {object} controlConfig - The configuration for the control.
*/
export
function
formatNodeModel_control
(
model
:
any
,
controlConfig
:
object
)
{
let
{
shape
,
size
,
control
}
=
model
/**
* {
"id": "123456",
"x": 590,
"y": 100,
"text": "广告宣传",
"desc": "通过广告短频宣传",
"control": "advertisement", // 自定义节点的类型
}
*/
if
(
!!
control
)
{
shape
=
'control'
}
model
.
size
=
size
||
DEFAULT_SIZE
[
shape
]
/**
* advertisement: {
text: "广告宣传1",
desc: "通过广告宣传新品",
color: "#9283ed",
img: "https://cdn.ipadbiz.cn/oa/advertisement-node.svg"
}
*/
if
(
!!
control
&&
!!
controlConfig
[
control
])
{
// 把 control 的配置挂到model上
model
.
shape
=
'control'
model
.
img
=
controlConfig
[
control
].
img
model
.
color
=
controlConfig
[
control
].
color
}
}
...
...
Please
register
or
login
to post a comment