ingenious-flow-designer
Version:
[演示地址](http://antd-vben5-pro.madong.tech/)
421 lines (417 loc) • 12.3 kB
text/typescript
import LogicFlow from '@logicflow/core';
import { DndPanel, Control, Menu } from '@logicflow/extension';
import StartSvg from './../assets/images/start.svg'
import UserTaskSvg from './../assets/images/user-task.svg'
import CustomSvg from './../assets/images/custom.svg'
import EndSvg from './../assets/images/end.svg'
import DecisionSvg from './../assets/images/decision.svg'
import ForkSvg from './../assets/images/fork.svg'
import JoinSvg from './../assets/images/join.svg'
import SubProcessSvg from './../assets/images/subProcess.svg'
import { FDControlItem, FDFormItemType, FDPatternItem } from '../types';
import * as schema from './schema'
import { NodeTypeEnum } from '../types/enums';
LogicFlow.use(DndPanel)
LogicFlow.use(Control)
LogicFlow.use(Menu)
const nodes = import.meta.glob('../nodes/*.ts',{
eager: true
})
const edges = import.meta.glob('../edges/*.ts',{
eager: true
})
class Flow {
dndPanel:Array<FDPatternItem> = []
static pluginName = 'flow'
static defaultEdgeType = 'ingenious:transition'
defaultDndPanel: Array<FDPatternItem>= [
{
type: 'start',
text: '开始',
// label: '开始节点',
icon: StartSvg,
},
{
type: 'task',
// label: '用户任务',
text: '用户任务',
icon: UserTaskSvg
},
{
type: 'custom',
// label: '自定义任务',
text: '自定义任务',
icon: CustomSvg
},
{
type: 'decision',
// label: '条件判断',
text: '条件判断',
icon: DecisionSvg
},
{
type: 'fork',
// label: '分支',
text: '分支',
icon: ForkSvg
},
{
type: 'join',
// label: '合并',
text: '合并',
icon: JoinSvg
},
{
type: 'sub_process',
// label: '子流程',
text: '子流程',
icon: SubProcessSvg
},
{
type: 'end',
text: '结束',
// label: '结束节点',
icon: EndSvg,
}
]
props: any;
defaultControl: Array<FDControlItem> = [
{
key: 'zoom-out',
iconClass: 'lf-control-zoomOut',
title: '缩小流程图',
text: '缩小',
sort: 10,
onClick: () => {
this.lf.zoom(false);
},
},
{
key: 'zoom-in',
iconClass: 'lf-control-zoomIn',
title: '放大流程图',
sort: 20,
text: '放大',
onClick: () => {
this.lf.zoom(true);
},
},
{
key: 'reset',
iconClass: 'lf-control-fit',
title: '恢复流程原有尺寸',
text: '适应',
sort: 30,
onClick: () => {
this.lf.resetZoom();
},
},
{
key: 'undo',
iconClass: 'lf-control-undo',
title: '回到上一步',
text: '上一步',
sort: 40,
onClick: () => {
this.lf.undo();
},
},
{
key: 'redo',
iconClass: 'lf-control-redo',
title: '移到下一步',
sort: 50,
text: '下一步',
onClick: () => {
this.lf.redo();
},
},
{
key: 'clear',
iconClass: 'lf-control-clear',
title: '清空画布',
sort: 60,
text: '清空',
onClick: () => {
this.lf.clearData();
(this.props.processForm || schema.process)?.formItems?.forEach((item: FDFormItemType)=>{
delete this.lf.graphModel[item.name]
})
}
},
{
key: 'see',
iconClass: 'lf-control-see',
title: '查看流程数据',
sort: 70,
text: '查看',
onClick: () => {
// 弹窗
this.props.modalRef.value?.show({
type: 'see',
graphData: this.lf.getGraphData(),
lf: this.lf
})
}
},
{
key: 'import',
iconClass: 'lf-control-import',
title: '导入流程数据',
sort: 80,
text: '导入',
onClick: () => {
// 弹窗
this.props.modalRef.value?.show({
type: 'import',
lf: this.lf
})
}
},
{
key: 'highlight',
iconClass: 'lf-control-highlight',
title: '设置高亮数据',
sort: 80,
text: '设置高亮',
onClick: () => {
// 弹窗
this.props.modalRef.value?.show({
type: 'highlight',
lf: this.lf
})
}
},
{
key: 'save',
iconClass: 'lf-control-save',
title: '保存流程数据',
sort: 90,
text: '保存',
onClick: () => {
const { eventCenter } = this.lf.graphModel;
eventCenter.emit("custom:save", this.lf.getGraphData());
}
}
];
lf: any;
constructor(data: any){
this.lf = data.lf;
const props = data.props
this.props = props
// 注册../node/*.ts下的所有节点
Object.keys(nodes).forEach((key) => {
const node = (nodes[key] as any).default
if(props.typePrefix && !node.type.startsWith(props.typePrefix)) {
node.type = `${props.typePrefix}${node.type}`
}
this.lf.register(node)
})
// 注册../edge/*.ts下的所有边
Object.keys(edges).forEach((key) => {
const edge = (edges[key] as any).default
if(props.typePrefix && !edge.type.startsWith(props.typePrefix)) {
edge.type = `${props.typePrefix}${edge.type}`
}
this.lf.register(edge)
})
// 预览模式时
if(props.viewer){
this.lf.extension.menu.setMenuConfig({
// nodeMenu: [],
// edgeMenu: []
})
} else {
// 设置右键菜单
this.lf.extension.menu.setMenuConfig({
nodeMenu: [
{
text: '删除',
callback: (node: any) => {
// node为该节点数据
this.lf.deleteNode(node.id)
}
}
]
})
}
// 拖拽面板追加类型前辍
this.defaultDndPanel = this.defaultDndPanel.map((item: FDPatternItem) => {
if(props.typePrefix && item.type && !item.type.startsWith(props.typePrefix)) {
item.type = `${props.typePrefix}${item.type}`
}
return item
})
let defaultEdgeType = props.defaultEdgeType || Flow.defaultEdgeType
if(props.typePrefix && !defaultEdgeType.startsWith(props.typePrefix)){
defaultEdgeType = `${props.typePrefix}${defaultEdgeType}`
}
this.lf.setDefaultEdgeType(defaultEdgeType)
if(props.initDndPanel !== false && props.viewer !==true) {
// 如果type相等,则替换属性,否则就追加
const newDndPanel: Array<FDPatternItem> = [...this.defaultDndPanel]
if(props.dndPanel && props.dndPanel.length > 0) {
props.dndPanel.forEach((item: FDPatternItem) => {
const index = newDndPanel.findIndex((i) => i.type === item.type)
if(index > -1) {
if(item.hide === true) {
// 隐藏,则删除
newDndPanel.splice(index, 1)
} else {
// 覆盖属性
newDndPanel[index] = {
...newDndPanel[index],
...item,
}
}
} else {
newDndPanel.push(item)
}
})
}
// 对newDndPanel进行排序,以sort属性正序排序
newDndPanel.sort((a:FDPatternItem, b: FDPatternItem) => {
return (a.sort === undefined ? 99: a.sort) - (b.sort === undefined ? 99: b.sort)
})
this.initDndPanel(newDndPanel)
}
if(props.initControl !== false) {
// 如果key相等,则替换属性,否则就追加
const newControl: Array<FDControlItem> = [...this.defaultControl]
if(props.control && props.control.length > 0) {
props.control.forEach((item: FDControlItem) => {
const index = newControl.findIndex((i) => i.key === item.key)
if(index > -1) {
if(item.hide === true) {
// 隐藏,则删除
newControl.splice(index, 1)
} else {
// 覆盖属性
newControl[index] = {
...newControl[index],
...item,
}
}
} else {
newControl.push(item)
}
})
}
// 对newDndPanel进行排序,以sort属性正序排序
newControl.sort((a:FDControlItem, b: FDControlItem) => {
return (a.sort === undefined ? 99: a.sort) - (b.sort === undefined ? 99: b.sort)
})
this.initControl(newControl.filter(item=>{
if(props.viewer === true) {
// 只读模式,隐藏部分操作
return !['undo','redo','clear','see','import','highlight','save'].includes(item.key as any)
}
return true
}))
} else {
this.lf.extension.control.controlItems = []
}
const lf = this.lf;
lf.adapterIn = (userData: any) =>{
// 绑定name和displayName到graphModel
// lf.graphModel.name = userData.name
// lf.graphModel.displayName = userData.displayName
(props.processForm || schema.process)?.formItems?.forEach((item: FDFormItemType)=>{
lf.graphModel[item.name] = userData[item.name]
})
return userData;
}
lf.adapterOut = (userData: any) =>{
// console.log('adapterOut', userData)
// 从graphModel取出name和displayName
const processData : any= {};
(props.processForm || schema.process)?.formItems?.forEach((item: FDFormItemType)=>{
processData[item.name] = lf.graphModel[item.name]
})
return {
...userData,
...processData
};
}
lf.graphModel.props = props;
// 事件处理优先级:dndPanel.nodeClick>props.nodeClick>defaultNodeClick
const { eventCenter } = lf.graphModel;
// 节点事件
eventCenter.on('node:click', (event: any) => {
if(props.viewer === true) {
return
}
const shapeList = this.dndPanel
const index = shapeList.findIndex((item: FDPatternItem)=>item.type === event.data.type)
let nodeClick = props.nodeClick
if(index>=-1) {
nodeClick = shapeList[index]?.nodeClick || props.nodeClick
}
if(nodeClick && typeof nodeClick === 'function') {
nodeClick(event)
} else {
props.drawerRef.value?.show({
...event,
patternItem: shapeList[index],
lf
})
}
})
// 边事件
eventCenter.on('edge:click', (event: any) => {
if(props.viewer === true) {
return
}
let edgeClick = props.edgeClick
if(edgeClick && typeof edgeClick === 'function') {
edgeClick(event)
} else {
props.drawerRef.value?.show({
...event,
patternItem: {
form: props.edgeForm || schema.edge
},
lf
})
}
})
// 画布事件
eventCenter.on('blank:contextmenu', (event: any) =>{
if(props.viewer === true) {
return
}
const processData : any= {};
(props.processForm || schema.process)?.formItems?.forEach((item: FDFormItemType)=>{
processData[item.name] = lf.graphModel[item.name]
})
props.drawerRef.value?.show({
...event,
data: {
type: 'process',
properties: processData
},
patternItem: {
form: props.processForm || schema.process
},
lf
})
})
}
initDndPanel(data: any){
this.lf.extension.dndPanel.setPatternItems(data)
this.dndPanel = data || []
this.dndPanel.forEach(item=>{
if(item.type && !item.form) {
let type: NodeTypeEnum = item.type as NodeTypeEnum
if(this.props.typePrefix) {
type = type.replace(this.props.typePrefix, '') as NodeTypeEnum
}
item.form = schema[type]
}
})
}
initControl(data: any){
this.lf.extension.control.controlItems = data || []
}
}
export default Flow