项目经常会使用到流程图,现在流程图的样式多种多样,目前我用到的比较好的,全面的就是 LogicFlow
,这个流程图的种类比较多,适合二次开发。展示一下示例:
审批流
比赛图
编排器
树状流程图
1. 安装插件
npm install @logicflow/core --save
# 插件包(不使用插件时不需要引入)
npm install @logicflow/extension --save
2. 简单案例
<template>
<el-card header="Graph">
<div class="flex-wrapper">
<el-button key="arrow1" type="primary" @click="() => setLine('bezier')">连接线-贝塞尔曲线</el-button>
<el-button key="arrow2" type="primary" @click="() => setLine('polyline')">连接线-多段线</el-button>
<el-button key="arrow2" type="primary" @click="() => setLine('line')">连接线-直线</el-button>
<el-button key="focusOn" type="primary" @click="focusOn"> 定位到node-1 </el-button>
<el-button key="undo" type="primary" @click="() => lfRef.undo()">上一步</el-button>
<el-button key="redo" type="primary" @click="() => lfRef.redo()">下一步</el-button>
<el-button key="clearData" type="primary" @click="() => lfRef.clearData()">清空数据</el-button>
<el-button key="changeType" type="primary" @click="changeNodeType">切换节点为圆形</el-button>
<el-button key="cancelEdit" type="primary" @click="cancelEdit">禁止编辑</el-button>
<el-button key="cancelEdit" type="primary" @click="canEdit">允许编辑</el-button>
<el-button key="getData" type="primary" @click="() => getGraphData()">获取选中节点数据</el-button>
<el-button key="setZoom" type="primary" @click="() => lfRef.zoom(0.6, [400, 400])">设置大小</el-button>
<el-button key="selectElement" type="primary" @click="() => checkNode()">选中指定节点</el-button>
<el-button key=" translateCenter" type="primary" @click="() => lfRef.translateCenter()">居中</el-button>
<el-button key="fitView" type="primary" @click="() => lfRef.fitView()">适应屏幕</el-button>
<el-button key="deleteNode" type="primary" @click="() => delNode()">删除节点</el-button>
</div>
<el-divider content-position="left">节点面板</el-divider>
<div class="flex-wrapper">
<div className="dnd-item wrapper" @mousedown="handleDragRect">矩形</div>
<div className="dnd-item wrapper" @mousedown="handleDragCircle">圆形</div>
<div className="dnd-item wrapper" @mousedown="handleDragDiamond">菱形</div>
<div className="dnd-item wrapper" @mousedown="handleDragEllipse">椭圆</div>
<div className="dnd-item wrapper" @mousedown="handleDragPolygon">多边形</div>
<div className="dnd-item wrapper" @mousedown="handleDragText">文本</div>
</div>
<el-divider />
<div ref="containerRef" id="graph" class="viewport"></div>
</el-card>
</template>
<script setup>
import LogicFlow from '@logicflow/core'
import '@logicflow/core/es/index.css'
const data = {
nodes: [
{
id: 'custom-node-1',
text: 'node-1',
type: 'polygon',
x: 90,
y: 94,
},
]
}
const lfRef = ref(null)
const containerRef = ref(null)
const flowId = ref('')
onMounted(() => {
if (containerRef.value) {
const lf = new LogicFlow({
container: containerRef.value,
height: 400,
multipleSelectKey: 'ctrl',
disabledTools: ['multipleSelect'],
autoExpand: true,
adjustEdgeStartAndEnd: true,
allowRotate: true,
edgeTextEdit: true,
keyboard: {
enabled: true
},
partial: true,
background: {
color: '#FFFFFF'
},
grid: true,
edgeTextDraggable: true,
edgeType: 'bezier',
style: {
inputText: {
background: 'black',
color: 'white'
}
},
idGenerator(type) {
return type + '_' + Math.random()
}
})
lf.on('graph:rendered', ({ graphModel }) => {
flowId.value = graphModel?.flowId || ''
})
// 渲染数据
lf.render(data)
lfRef.value = lf
}
})
// 设置箭头
const setLine = (arrowName) => {
const lf = lfRef?.value
if (lf) {
const { edges } = lf.getSelectElements()
edges.forEach(({ id, properties }) => {
lf.changeEdgeType(id, arrowName)
})
}
}
// 定位到指定节点
const focusOn = () => {
lfRef?.value?.focusOn({
id: 'custom-node-1'
})
}
// 切换节点类型
const changeNodeType = () => {
const lf = lfRef?.value
if (lf) {
const { nodes } = lf.getSelectElements()
nodes.forEach(({ id, type }) => {
lf.changeNodeType(id, type === 'rect' ? 'circle' : 'rect')
})
}
}
// 取消编辑
const cancelEdit = () => {
const lf = lfRef?.value
if (lf) {
const { editConfigModel } = lf.graphModel
editConfigModel.updateEditConfig({
isSilentMode: true, // 是否为静默模式
stopZoomGraph: true, // 禁止缩放画布
stopScrollGraph: true, // 禁止鼠标滚动移动画布
stopMoveGraph: true // 禁止拖动画布
});
}
}
const canEdit = () => {
const lf = lfRef?.value
if (lf) {
const { editConfigModel } = lf.graphModel
editConfigModel.updateEditConfig({
isSilentMode: false,
stopZoomGraph: false,
stopScrollGraph: false,
stopMoveGraph: false
});
}
}
// 获取选中节点数据
const getGraphData = () => {
const lf = lfRef?.value
if (lf) {
const { nodes } = lf.getSelectElements()
console.log(nodes);
}
}
// 选中指定节点
const checkNode = () => {
const lf = lfRef?.value
if (lf) {
lf.selectElementById('custom-node-1')
}
}
// 删除节点
const delNode = () => {
const lf = lfRef?.value
if (lf) {
const { nodes } = lf.getSelectElements()
nodes.forEach(({ id }) => {
lf.deleteNode(id)
})
}
}
const handleDragRect = () => {
lfRef?.value?.dnd.startDrag({
type: 'rect',
text: '矩形'
})
}
const handleDragCircle = () => {
lfRef?.value?.dnd.startDrag({
type: 'circle',
text: '圆形',
r: 25
})
}
const handleDragDiamond = () => {
lfRef?.value?.dnd.startDrag({
type: 'diamond',
text: '菱形',
})
}
const handleDragEllipse = () => {
lfRef?.value?.dnd.startDrag({
type: 'ellipse',
text: '椭圆',
properties: {
rx: 40,
ry: 80,
},
})
}
const handleDragPolygon = () => {
let x = 50, y = 50
lfRef?.value?.dnd.startDrag({
type: 'polygon',
text: '多边形',
properties: {
points: [
[x - 0.205 * 100, y - 0.5 * 100],
[x + 0.205 * 100, y - 0.5 * 100],
[x + 0.5 * 100, y - 0.205 * 100],
[x + 0.5 * 100, y + 0.205 * 100],
[x + 0.205 * 100, y + 0.5 * 100],
[x - 0.205 * 100, y + 0.5 * 100],
[x - 0.5 * 100, y + 0.205 * 100],
[x - 0.5 * 100, y - 0.205 * 100],
],
}
})
}
const handleDragText = () => {
lfRef?.value?.dnd.startDrag({
type: 'text',
text: '文本'
})
}
const handleDragStar = () => {
lfRef?.value?.dnd.startDrag({
type: 'star',
text: '五角星'
})
}
</script>
<style scoped lang="scss">
.flex-wrapper {
display: flex;
gap: 8px;
flex-wrap: wrap;
align-items: center;
}
*:focus {
outline: none;
}
.rect {
width: 50px;
height: 50px;
background: #fff;
border: 2px solid #000;
}
.circle {
width: 50px;
height: 50px;
background: #fff;
border: 2px solid #000;
border-radius: 50%;
}
.dnd-item {
display: flex;
align-items: center;
justify-content: center;
cursor: grab;
user-select: none;
}
.wrapper {
width: 80px;
height: 50px;
background: #fff;
border: 2px solid #000;
}
</style>
以下为效果图
大部分功能都能实现,根据自己的需求去选择。