UNPKG

jbxl-workflow

Version:

流程图

251 lines (246 loc) 8.39 kB
import {h, nextTick, render} from 'vue' import {ElMessage, ElMessageBox} from 'element-plus' import {generateRandomId} from "@dang_8899/xl-ui"; // components import Toolbar from '@/components/Toolbar.vue' // class import {LLMNode, ParameterNode, PluginNode} from '@/Node.class' class WorkflowToolbar { constructorFuncMap = new Map([ ['llm-node-shape', LLMNode], ['plugin-node-shape', PluginNode], ['parameter-node-shape', ParameterNode] ]) constructor(opts) { this.scaleValue = 0.8 // 缩放比例 this.container = opts.container this.clipboard = [] // 剪贴板 this.init() } // 初始化 init() { this.render() this.initEvent() } // 渲染工具栏到容器中 render() { const toolbarVNode = h(Toolbar, { scaleValue: this.scaleValue, onUndo: () => { this.undo() }, onRedo: () => { this.redo() }, onExpand: () => { this.toggleExpandAllAndRetractAll(true) }, onRetract: () => { this.toggleExpandAllAndRetractAll(false) }, onStartDrag: () => { this.startDrag() }, onStartSelect: () => { this.startSelect() }, onZoomOut: () => { globalThis._graph?.zoom(-0.2) }, onZoomIn: () => { globalThis._graph?.zoom(0.2) }, onChangeScaleValue: (factor) => { if (factor === 'auto') { globalThis._graph?.zoomToFit() } else { globalThis._graph?.zoomTo(factor) } } }) render(toolbarVNode, this.container) } // 初始化事件监听 initEvent() { globalThis._graph?.on('scale', ({ sx }) => { this.scaleValue = sx this.render() }) globalThis._graph?.zoomTo(this.scaleValue) } // 撤销 undo() { if (globalThis._graph?.canUndo()) { globalThis._graph?.undo() } } // 重做 redo() { if (globalThis._graph?.canRedo()) { globalThis._graph?.redo() } } // 全部展开/全部收起 toggleExpandAllAndRetractAll(isExpand) { const nodes = globalThis._graph?.getNodes() if (nodes.length) { nodes.forEach(node => { const data = node.getData() const { vueInstance } = data?.self || {} const { toggleExpandAllAndRetractAll } = vueInstance?.exposed || {} toggleExpandAllAndRetractAll?.(isExpand) }) } } // 拖动画布 startDrag() { globalThis._graph?.enablePanning() globalThis._graph?.disableSelection() } // 选中 startSelect() { globalThis._graph?.enableSelection() globalThis._graph?.disablePanning() } // 删除节点 removeNode() { const cells = globalThis._graph?.getSelectedCells() const nodes = cells.filter(cell => cell.isNode()) // 获取所有被选中的节点 if (nodes.some(item => ['start-node-shape', 'end-node-shape'].includes(item.shape))) { // return ElMessage.error('开始节点和结束节点不能删除') return ElMessage({ type: 'error', message: `开始节点和结束节点不能删除`, customClass: 'top-full-width-message' }); } if (globalThis.ISRUNNING) { ElMessage.closeAll() return ElMessage.warning('工作流正在运行中,无法进行删除操作') } ElMessageBox.confirm( '删除节点', '删除节点后将无法恢复,是否确认?', { customClass: 'delete-confirm' } ).then(() => { const nodePool = globalThis._graph.nodePool if (nodes.length) { nodes.forEach(node => { const data = node.getData() const prevNodeList = nodePool.getDirectUpstreamNodes(data?.self.id) const nextNodeList = nodePool.getDirectDownstreamNodes(data?.self.id) prevNodeList.forEach(prevNode => { // 断开所有直接上游节点与被删除节点之间连线 nodePool.disconnect(prevNode.id, data?.self.id) prevNode.removeNextNode(data?.self.id) }) nextNodeList.forEach(nextNode => { // 断开被删除节点与所有直接下游节点之间连线 nodePool.disconnect(data?.self.id, nextNode.id) }) nodePool.removeNode(data?.self) }) globalThis._graph?.removeCells(nodes) } }) } // 复制节点 copyNode() { // 获取当前焦点元素 const activeElement = document.activeElement // 检查当前焦点元素是否可编辑,用来判断是否复制文本,如果不是,则执行节点复制 const isEditable = activeElement?.getAttribute('contenteditable') === 'true' || ['INPUT', 'TEXTAREA'].includes(activeElement?.tagName) const cells = globalThis._graph?.getSelectedCells() const nodes = cells.filter(cell => cell.isNode()) // 获取所有被选中的节点 // 如果当前焦点在可编辑区域,则不执行节点复制 if (isEditable) { this.clipboard = [] return true } if (nodes.length) { this.clipboard = nodes.map(node => { const data = node.getData() return data?.self || {} }) } } // 粘贴节点 async pasteNode() { if (this.clipboard.length > 0) { if (this.clipboard.some(item => ['start-node-shape', 'end-node-shape'].includes(item.shapeType))) { // return ElMessage.error('开始节点和结束节点不能复制') return ElMessage({ type: 'error', message: `开始节点和结束节点不能复制`, customClass: 'top-full-width-message' }); } const newNodesIdList = [] let lastNodeId = null this.clipboard.forEach((self, index) => { const NodeInstance = this.constructorFuncMap.get(self.shapeType) const newNode = new NodeInstance(this.getConstructorOpts(self)) const params = newNode.getGraphPureParams() // params.x += 30 // params.y += 30 const [newNodeSelf, X6Node] = globalThis._graph?.addNode(params) // 生成节点 globalThis._graph?.nodePool?.addNode(newNodeSelf) // 添加到节点池 newNodesIdList.push(X6Node.id) if (index === this.clipboard.length - 1) { lastNodeId = X6Node.id } }) const lastNode = globalThis._graph?.getCellById(lastNodeId) if (lastNode) { lastNode.setZIndex(9999) } await globalThis._graph?.cleanSelection() await globalThis._graph?.select(newNodesIdList) } } getConstructorOpts(self) { let title = self.title || '' if (title.indexOf('_') > -1) { const [nodeTitle, nodeNumber] = title.split('_') title = `${nodeTitle}_${!isNaN(nodeNumber * 1) ? nodeNumber * 1 + 1 : 1}` } else { title = `${self.title}_1` } return { id: `${generateRandomId()}${self.shapeType}`, title, description: self.description, x: self.x + 30, y: self.y + 30, paramsDataList: self.paramsData.list.map(item => ({ ...item, id: '' })), outputParamsDataList: self.outputParamsData.list.map(item => ({ ...item, id: '' })), pluginId: self.pluginId, currentModel: self.currentModel, temperatureValue: self.temperatureValue, maxTokensValue: self.maxTokensValue, modelType: self.modelType, systemPrompt: self.systemPrompt, currRoadInstruction: self.currRoadInstruction, systemPromptRichText: self.systemPromptRichText, currRoadInstructionRichText: self.currRoadInstructionRichText, systemPromptWithId: self.systemPromptWithId, currRoadInstructionWithId: self.currRoadInstructionWithId, // ...self } } // 删除两节点间的连线 removeEdge(selectedEdgeSourceNode, selectedEdgeTargetNode) { if (globalThis.ISRUNNING) { ElMessage.closeAll() return ElMessage.warning('工作流正在运行中,无法进行删除连线操作') } if (selectedEdgeSourceNode && selectedEdgeTargetNode) { const sourceNode = selectedEdgeSourceNode.data?.self const targetNode = selectedEdgeTargetNode.data?.self globalThis._graph?.nodePool?.portConnectionCache.delete(`${selectedEdgeSourceNode.id}-${selectedEdgeTargetNode.id}`) globalThis._graph?.nodePool?.disconnect(sourceNode?.id, targetNode?.id) sourceNode.removeNextNode(targetNode?.id) } } } export default WorkflowToolbar