UNPKG

ingenious-flow-designer

Version:

[演示地址](http://antd-vben5-pro.madong.tech/)

514 lines (491 loc) 15 kB
import LogicFlow from '@logicflow/core'; import { DndPanel, Control, Menu, MiniMap, Snapshot } from '@logicflow/extension'; import StartSvg from '../assets/images/panel-start.svg'; import EndSvg from '../assets/images/panel-end.svg'; import TaskSvg from '../assets/images/panel-task.svg'; import DecisionSvg from '../assets/images/panel-decision.svg'; import CustomSvg from '../assets/images/panel-custom.svg'; import ForkSvg from '../assets/images/panel-fork.svg'; import JoinSvg from '../assets/images/panel-join.svg'; import SubProcessSvg from '../assets/images/panel-subprocess.svg'; LogicFlow.use(DndPanel) LogicFlow.use(Control) LogicFlow.use(Menu) LogicFlow.use(MiniMap, { width: 200, height: 150, showEdge: true, isShowHeader: false, isShowCloseIcon: true, }) LogicFlow.use(Snapshot) const nodes = import.meta.glob('../nodes/*.ts',{ eager: true }) const edges = import.meta.glob('../edges/*.ts',{ eager: true }) class Flow { dndPanel: any[] = [] miniMapVisible: boolean = false static pluginName = 'flow' static defaultEdgeType = 'transition' defaultDndPanel: any[] = [ { type: 'start', text: '开始', icon: StartSvg, className: 'dnd-start', sort: 10 }, { type: 'task', text: '用户任务', icon: TaskSvg, className: 'dnd-task', sort: 20 }, { type: 'decision', text: '条件判断', icon: DecisionSvg, className: 'dnd-decision', sort: 30 }, { type: 'fork', text: '分支', icon: ForkSvg, className: 'dnd-fork', sort: 40 }, { type: 'join', text: '合并', icon: JoinSvg, className: 'dnd-join', sort: 50 }, { type: 'subProcess', text: '子流程', icon: SubProcessSvg, className: 'dnd-subprocess', sort: 60 }, { type: 'custom', text: '自定义', icon: CustomSvg, className: 'dnd-custom', sort: 70 }, { type: 'end', text: '结束', icon: EndSvg, className: 'dnd-end', sort: 80 } ] props: any; defaultControl: any[] = [ { 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(); } }, { 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: 85, 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()); } }, { key: 'minimap', iconClass: 'lf-control-minimap', title: '切换小地图', sort: 100, text: '小地图', onClick: () => { const miniMap = this.lf.extension.miniMap if (miniMap) { if (this.miniMapVisible) { miniMap.hide() this.miniMapVisible = false } else { miniMap.show() miniMap.updatePosition({ right: 15, top: 70 }) this.miniMapVisible = true } } } } ] lf: any; constructor(data: any){ this.lf = data.lf; const props = data.props this.props = props // 注册节点和边,并添加类型前缀 Object.keys(nodes).forEach((key) => { const node = (nodes[key] as any).default if (node && node.type) { if (props.typePrefix && !node.type.startsWith(props.typePrefix)) { node.type = `${props.typePrefix}${node.type}` } this.lf.register(node) console.log(`Registered node: ${node.type}`) } else { console.warn(`Node ${key} is invalid:`, node) } }) Object.keys(edges).forEach((key) => { const edge = (edges[key] as any).default if (edge && edge.type) { if (props.typePrefix && !edge.type.startsWith(props.typePrefix)) { edge.type = `${props.typePrefix}${edge.type}` } this.lf.register(edge) } else { console.warn(`Edge ${key} is invalid:`, edge) } }) // 设置默认边类型 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) { // 拖拽面板追加类型前辍 const defaultDndPanelWithPrefix = this.defaultDndPanel.map((item: any) => { if (props.typePrefix && item.type && !item.type.startsWith(props.typePrefix)) { return { ...item, type: `${props.typePrefix}${item.type}` } } return item }) const newDndPanel: any[] = [...defaultDndPanelWithPrefix] if(props.dndPanel && props.dndPanel.length > 0) { props.dndPanel.forEach((item: any) => { 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((a: any, b: any) => { return (a.sort === undefined ? 99: a.sort) - (b.sort === undefined ? 99: b.sort) }) this.initDndPanel(newDndPanel) } // 初始化控制面板 if (props.initControl !== false) { const newControl: any[] = [...this.defaultControl] if(props.control && props.control.length > 0) { props.control.forEach((item: any) => { 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) } }) } newControl.sort((a: any, b: any) => { 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','save','see','import','highlight'].includes(item.key) } return true })) } else { this.lf.extension.control.controlItems = [] } if(props.viewer) { this.lf.extension.menu.setMenuConfig({}) } else { this.lf.on('blank:contextmenu', (e: any) => { if (e.preventDefault) { e.preventDefault(); } // 优先调用用户传入的blankContextmenu方法 if (props.blankContextmenu && typeof props.blankContextmenu === 'function') { const processData: any = {}; const flowFields = ['name', 'display_name', 'key', 'version', 'expire_time', 'instance_url', 'instance_no_class', 'pre_interceptors', 'post_interceptors', 'formType', 'customFormConfig']; flowFields.forEach(field => { if (this.lf.graphModel[field] !== undefined) { processData[field] = this.lf.graphModel[field]; } }); // 调用用户传入的blankContextmenu方法 props.blankContextmenu({ data: { type: 'process', lf: this.lf, properties: processData } }); } else { // 如果用户没有传入blankContextmenu方法,检查dndPanel中是否有type为'process'的配置 const processData: any = {}; const flowFields = ['name', 'display_name', 'key', 'version', 'expire_time', 'instance_url', 'instance_no_class', 'pre_interceptors', 'post_interceptors', 'formType', 'customFormConfig']; flowFields.forEach(field => { if (this.lf.graphModel[field] !== undefined) { processData[field] = this.lf.graphModel[field]; } }); // 检查dndPanel中是否有type为'process'的配置 const dndPanel = props.dndPanel; const processConfig = dndPanel?.find((item: any) => item.type === 'process'); if (processConfig) { // 调用process配置的nodeClick方法 if (processConfig.nodeClick) { processConfig.nodeClick({ data: { type: 'process', lf: this.lf, properties: processData } }); } } else if (this.props.drawerRef?.value?.show) { // 使用内置的流程设置面板 this.props.drawerRef.value.show({ type: 'process', lf: this.lf, properties: processData }); } } }); this.lf.extension.menu.setMenuConfig({ nodeMenu: [ { text: '删除', callback: (node: any) => { this.lf.deleteNode(node.id) } } ], graphMenu: [] }) } const lf = this.lf; lf.adapterIn = (userData: any) => { const flowFields = ['name', 'display_name', 'key', 'version', 'expire_time', 'instance_url', 'instance_no_class', 'pre_interceptors', 'post_interceptors', 'formType', 'customFormConfig']; flowFields.forEach(field => { if (userData[field] !== undefined) { lf.graphModel[field] = userData[field]; } }); return userData; }; lf.adapterOut = (userData: any) => { const processData: any = {}; const flowFields = ['name', 'display_name', 'key', 'version', 'expire_time', 'instance_url', 'instance_no_class', 'pre_interceptors', 'post_interceptors', 'formType', 'customFormConfig']; flowFields.forEach(field => { if (lf.graphModel[field] !== undefined) { processData[field] = lf.graphModel[field]; } }); return { ...userData, ...processData }; }; lf.graphModel.props = props; const { eventCenter } = lf.graphModel; eventCenter.on('node:click', (event: any) => { if(props.viewer === true) { return } const shapeList = this.dndPanel const index = shapeList.findIndex((item: any)=>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 { const nodeData = { id: event.data.id, type: event.data.type, label: event.data.text?.value || event.data.text || '', properties: { ...event.data.properties } }; const nodeConfig = props.nodeConfig?.find((config: any) => config.type === event.data.type); let customPanel = props.customPanel; let useExternalPanel = false; if (nodeConfig) { if (nodeConfig.customPanel) { customPanel = nodeConfig.customPanel; } } const patternItem = shapeList[index]; if (patternItem && patternItem.form) { customPanel = patternItem.form; useExternalPanel = true; } if (this.props.drawerRef.value?.show) { this.props.drawerRef.value.show({ ...nodeData, patternItem: patternItem, lf }, customPanel, useExternalPanel ? 'external' : 'build_in'); } } }) eventCenter.on('edge:click', (event: any) => { if(props.viewer === true) { return } let edgeClick = props.edgeClick if(edgeClick && typeof edgeClick === 'function') { edgeClick(event) } else { this.props.drawerRef.value?.show({ ...event, patternItem: {}, lf }) } }) } initDndPanel(data: any){ this.lf.extension.dndPanel.setPatternItems(data) this.dndPanel = data || [] } initControl(data: any){ const control = this.lf?.extension?.control if (control && control.controlItems) { control.controlItems = data || [] } } } export default Flow