/** * 工作流 Store * 管理工作流状态、节点类型、字段映射等 */ import { defineStore } from 'pinia' import { ref, computed } from 'vue' import type { NodeTypeMetadata, WorkflowNode, WorkflowEdge, WorkflowDefinition, UpstreamNodeOutput, FieldPathNode, OutputParameter, HistoryItem, } from '#/types/workflow' import { MOCK_NODE_TYPES, getNodeTypeMetadata } from '#/views/workflow/vue-flow-design/mock/nodeTypes' export const useWorkflowStore = defineStore('workflow', () => { // ============== 状态 ============== /** 所有可用的节点类型 */ const nodeTypes = ref(MOCK_NODE_TYPES) /** 当前工作流的节点 */ const nodes = ref([]) /** 当前工作流的边 */ const edges = ref([]) /** 选中的节点 ID */ const selectedNodeId = ref(null) /** 选中的边 ID */ const selectedEdgeId = ref(null) /** 历史记录 */ const history = ref([]) /** 历史记录索引 */ const historyIndex = ref(-1) /** 最大历史记录数 */ const maxHistorySize = 50 /** 工作流元数据 */ const workflowMeta = ref({ name: '未命名工作流', description: '', id: '', }) // ============== 计算属性 ============== /** 选中的节点 */ const selectedNode = computed(() => { if (!selectedNodeId.value) return null return nodes.value.find((n) => n.id === selectedNodeId.value) }) /** 选中的边 */ const selectedEdge = computed(() => { if (!selectedEdgeId.value) return null return edges.value.find((e) => e.id === selectedEdgeId.value) }) /** 是否可以撤销 */ const canUndo = computed(() => historyIndex.value > 0) /** 是否可以重做 */ const canRedo = computed(() => historyIndex.value < history.value.length - 1) /** 节点统计 */ const statistics = computed(() => ({ nodeCount: nodes.value.length, edgeCount: edges.value.length, nodesByType: nodes.value.reduce((acc, node) => { acc[node.type] = (acc[node.type] || 0) + 1 return acc }, {} as Record), })) // ============== 节点操作 ============== /** * 添加节点 */ function addNode(node: WorkflowNode) { nodes.value.push(node) saveHistory() } /** * 删除节点 */ function removeNode(nodeId: string) { const index = nodes.value.findIndex((n) => n.id === nodeId) if (index > -1) { nodes.value.splice(index, 1) // 同时删除相关的边 edges.value = edges.value.filter( (e) => e.source !== nodeId && e.target !== nodeId ) saveHistory() } } /** * 更新节点数据 */ function updateNode(nodeId: string, data: Partial) { const node = nodes.value.find((n) => n.id === nodeId) if (node) { Object.assign(node, data) saveHistory() } } /** * 更新节点的 data 字段 */ function updateNodeData(nodeId: string, data: any) { const node = nodes.value.find((n) => n.id === nodeId) if (node) { node.data = { ...node.data, ...data } saveHistory() } } // ============== 边操作 ============== /** * 添加边 */ function addEdge(edge: WorkflowEdge) { edges.value.push(edge) saveHistory() } /** * 删除边 */ function removeEdge(edgeId: string) { const index = edges.value.findIndex((e) => e.id === edgeId) if (index > -1) { edges.value.splice(index, 1) saveHistory() } } // ============== 选择操作 ============== /** * 选择节点 */ function selectNode(nodeId: string | null) { selectedNodeId.value = nodeId selectedEdgeId.value = null } /** * 选择边 */ function selectEdge(edgeId: string | null) { selectedEdgeId.value = edgeId selectedNodeId.value = null } /** * 清除选择 */ function clearSelection() { selectedNodeId.value = null selectedEdgeId.value = null } // ============== 历史记录 ============== /** * 保存历史记录 */ function saveHistory() { const currentState: HistoryItem = { nodes: JSON.parse(JSON.stringify(nodes.value)), edges: JSON.parse(JSON.stringify(edges.value)), timestamp: Date.now(), } // 移除当前索引之后的历史记录 history.value = history.value.slice(0, historyIndex.value + 1) // 添加新的历史记录 history.value.push(currentState) // 限制历史记录大小 if (history.value.length > maxHistorySize) { history.value.shift() } else { historyIndex.value++ } } /** * 撤销 */ function undo() { if (canUndo.value) { historyIndex.value-- const state = history.value[historyIndex.value] nodes.value = JSON.parse(JSON.stringify(state.nodes)) edges.value = JSON.parse(JSON.stringify(state.edges)) } } /** * 重做 */ function redo() { if (canRedo.value) { historyIndex.value++ const state = history.value[historyIndex.value] nodes.value = JSON.parse(JSON.stringify(state.nodes)) edges.value = JSON.parse(JSON.stringify(state.edges)) } } // ============== 字段映射相关 ============== /** * 获取节点的上游节点输出(用于字段映射) */ function getUpstreamOutputs(nodeId: string): UpstreamNodeOutput[] { const upstreamNodes: UpstreamNodeOutput[] = [] // 找到所有连接到当前节点的边 const incomingEdges = edges.value.filter((e) => e.target === nodeId) incomingEdges.forEach((edge) => { const sourceNode = nodes.value.find((n) => n.id === edge.source) if (sourceNode) { const nodeType = getNodeTypeMetadata(sourceNode.type) if (nodeType) { const fieldTree = buildFieldTree(nodeType.outputs, edge.source) upstreamNodes.push({ nodeId: sourceNode.id, nodeName: sourceNode.data.label, outputs: nodeType.outputs, fieldTree, }) } } }) return upstreamNodes } /** * 从输出参数构建字段树 */ function buildFieldTree( outputs: OutputParameter[], nodeId: string ): FieldPathNode[] { const tree: FieldPathNode[] = [] outputs.forEach((output) => { const rootPath = `nodes.${nodeId}.output.${output.id}` if (output.schema) { // 有 schema,构建详细的字段树 const children = buildFieldTreeFromSchema(output.schema, rootPath) tree.push({ key: output.id, path: rootPath, type: output.type, description: output.description, children: children.length > 0 ? children : undefined, isLeaf: children.length === 0, }) } else { // 没有 schema,只显示顶级字段 tree.push({ key: output.id, path: rootPath, type: output.type, description: output.description, isLeaf: true, }) } }) return tree } /** * 从 Schema 递归构建字段树 */ function buildFieldTreeFromSchema( schema: any, basePath: string ): FieldPathNode[] { const nodes: FieldPathNode[] = [] if (schema.type === 'object' && schema.properties) { Object.entries(schema.properties).forEach(([key, propSchema]: [string, any]) => { const path = `${basePath}.${key}` const children = buildFieldTreeFromSchema(propSchema, path) nodes.push({ key, path, type: propSchema.type || 'any', description: propSchema.description, example: propSchema.example, children: children.length > 0 ? children : undefined, isLeaf: children.length === 0, }) }) } else if (schema.type === 'array' && schema.items) { // 数组类型,添加 [0] 示例 const path = `${basePath}[0]` const children = buildFieldTreeFromSchema(schema.items, path) if (children.length > 0) { nodes.push({ key: '[0]', path, type: 'object', description: '数组元素(示例)', children, isLeaf: false, }) } } return nodes } /** * 生成字段表达式 */ function generateFieldExpression(fieldPath: string): string { return `\${${fieldPath}}` } // ============== 工作流操作 ============== /** * 加载工作流 */ function loadWorkflow(workflow: WorkflowDefinition) { nodes.value = workflow.nodes edges.value = workflow.edges workflowMeta.value = { name: workflow.name, description: workflow.description || '', id: workflow.id || '', } // 重置历史记录 history.value = [{ nodes: JSON.parse(JSON.stringify(nodes.value)), edges: JSON.parse(JSON.stringify(edges.value)), timestamp: Date.now(), }] historyIndex.value = 0 } /** * 导出工作流定义 */ function exportWorkflow(): WorkflowDefinition { return { id: workflowMeta.value.id, name: workflowMeta.value.name, description: workflowMeta.value.description, nodes: nodes.value, edges: edges.value, version: '1.0', createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), } } /** * 清空工作流 */ function clearWorkflow() { nodes.value = [] edges.value = [] selectedNodeId.value = null selectedEdgeId.value = null history.value = [] historyIndex.value = -1 workflowMeta.value = { name: '未命名工作流', description: '', id: '', } } // ============== 节点类型查询 ============== /** * 根据类型获取节点元数据 */ function getNodeType(type: string): NodeTypeMetadata | undefined { return nodeTypes.value.find((t) => t.type === type) } /** * 创建节点实例 */ function createNodeInstance( type: string, position: { x: number; y: number } ): WorkflowNode | null { const nodeType = getNodeType(type) if (!nodeType) return null const nodeId = `${type}_${Date.now()}_${Math.random().toString(36).substr(2, 9)}` return { id: nodeId, type: nodeType.type, position, data: { label: nodeType.label, type: nodeType.type, description: nodeType.description, inputs: JSON.parse(JSON.stringify(nodeType.inputs)), outputs: JSON.parse(JSON.stringify(nodeType.outputs)), config: {}, }, } } return { // 状态 nodeTypes, nodes, edges, selectedNodeId, selectedEdgeId, workflowMeta, // 计算属性 selectedNode, selectedEdge, canUndo, canRedo, statistics, // 节点操作 addNode, removeNode, updateNode, updateNodeData, // 边操作 addEdge, removeEdge, // 选择操作 selectNode, selectEdge, clearSelection, // 历史记录 saveHistory, undo, redo, // 字段映射 getUpstreamOutputs, buildFieldTree, generateFieldExpression, // 工作流操作 loadWorkflow, exportWorkflow, clearWorkflow, // 节点类型 getNodeType, createNodeInstance, } })