From 1207d730db92bb4a273648c6a7425c7ea2f4fa12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=88=9A=E8=BE=B0=E5=85=88=E7=94=9F?= Date: Thu, 5 Dec 2024 13:35:53 +0800 Subject: [PATCH] =?UTF-8?q?=E5=8F=AF=E6=AD=A3=E5=B8=B8=E4=BF=9D=E5=AD=98?= =?UTF-8?q?=E6=B5=81=E7=A8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/frontend-guide.md | 322 +++++-- frontend/package-lock.json | 831 +++++++++++++++++- frontend/package.json | 4 + .../components/FlowDesigner/index.module.css | 134 ++- .../Edit/components/FlowDesigner/index.tsx | 756 ++++++++++++---- .../Edit/components/FlowDesigner/nodes.ts | 118 --- .../Edit/components/FormDesigner/index.tsx | 6 +- .../Edit/components/NodeConfig/index.tsx | 160 ++-- frontend/src/pages/Workflow/service.ts | 11 +- frontend/src/pages/Workflow/types.ts | 23 + 10 files changed, 1820 insertions(+), 545 deletions(-) delete mode 100644 frontend/src/pages/Workflow/Definition/Edit/components/FlowDesigner/nodes.ts diff --git a/frontend/frontend-guide.md b/frontend/frontend-guide.md index 60f753c8..1c5cde77 100644 --- a/frontend/frontend-guide.md +++ b/frontend/frontend-guide.md @@ -22,7 +22,8 @@ // 工作流定义相关接口 interface WorkflowDefinitionAPI { // 基础CRUD接口 - list: '/api/v1/workflow-definitions' // GET 查询列表 + page: '/api/v1/workflow-definitions/page' // GET 分页查询,支持条件筛选 + list: '/api/v1/workflow-definitions/list' // GET 查询所有(不分页) create: '/api/v1/workflow-definitions' // POST 创建 update: '/api/v1/workflow-definitions/{id}' // PUT 更新 delete: '/api/v1/workflow-definitions/{id}' // DELETE 删除 @@ -32,6 +33,31 @@ interface WorkflowDefinitionAPI { publish: '/api/v1/workflow-definitions/{id}/publish' // POST 发布 disable: '/api/v1/workflow-definitions/{id}/disable' // POST 禁用 enable: '/api/v1/workflow-definitions/{id}/enable' // POST 启用 + versions: '/api/v1/workflow-definitions/{code}/versions' // GET 查询所有版本(不分页) +} + +// 节点类型相关接口 +interface NodeTypeAPI { + // 查询接口 + list: '/api/v1/node-types' // GET 获取所有可用的节点类型列表(不分页) + getExecutors: '/api/v1/node-types/{type}/executors' // GET 获取指定节点类型支持的执行器列表 +} + +// 分页请求参数 +interface PageQuery { + pageNum: number // 页码,从1开始 + pageSize: number // 每页大小 + sortField?: string // 排序字段 + sortOrder?: 'ascend' | 'descend' // 排序方向 +} + +// 分页响应结构 +interface PageResponse { + records: T[] // 数据列表 + total: number // 总记录数 + pages: number // 总页数 + pageNum: number // 当前页码 + pageSize: number // 每页大小 } // 工作流定义数据结构 @@ -42,10 +68,10 @@ interface WorkflowDefinitionDTO { description: string // 描述 status: 'DRAFT' | 'PUBLISHED' | 'DISABLED' // 状态 version: number // 版本号 - nodeConfig: string // 节点配置(JSON) - transitionConfig: string // 流转配置(JSON) - formDefinition: string // 表单定义 - graphDefinition: string // 图形信息 + nodeConfig: string // 节点配置(JSON),包含节点的基本信息和执行配置 + transitionConfig: string // 流转配置(JSON),定义节点间的连接和条件 + formDefinition: string // 表单定义(JSON),定义工作流的表单字段和验证规则 + graphDefinition: string // 图形信息(JSON),定义节点的位置和连线的样式 enabled: boolean // 是否启用 remark: string // 备注 nodes: NodeDefinitionDTO[] // 节点定义列表 @@ -62,6 +88,173 @@ interface NodeDefinitionDTO { workflowDefinitionId: number // 工作流定义ID orderNum: number // 排序号 } + +// 节点配置示例 +interface NodeConfig { + startNode: { + type: 'START' + name: string + } + endNode: { + type: 'END' + name: string + } + taskNodes: Array<{ + id: string + type: 'TASK' + name: string + executor?: string // 执行器类型 + config?: any // 执行器配置 + }> +} + +// 流转配置示例 +interface TransitionConfig { + transitions: Array<{ + from: string // 源节点ID + to: string // 目标节点ID + condition?: string // 流转条件 + }> +} + +// 表单定义示例 +interface FormDefinition { + fields: Array<{ + name: string // 字段名 + label: string // 字段标签 + type: 'input' | 'select' | 'date' | 'number' // 字段类型 + required?: boolean // 是否必填 + options?: Array<{ // 选项(用于select类型) + label: string + value: any + }> + rules?: Array<{ // 验证规则 + type: string + message: string + }> + }> +} + +// 图形定义示例 +interface GraphDefinition { + nodes: Array<{ + id: string + type: string + x: number // 节点X坐标 + y: number // 节点Y坐标 + properties?: any // 节点属性 + }> + edges: Array<{ + source: string // 源节点ID + target: string // 目标节点ID + properties?: any // 连线属性 + }> +} + +// 节点类型数据结构 +interface NodeTypeDTO { + code: string // 节点类型编码 + name: string // 节点类型名称 + description: string // 节点类型描述 + category: 'BASIC' | 'TASK' | 'GATEWAY' | 'EVENT' // 节点类型分类 + icon: string // 节点图标 + color: string // 节点颜色 + executors?: Array<{ // 支持的执行器列表 + code: string // 执行器编码 + name: string // 执行器名称 + description: string // 执行器描述 + configSchema: any // 执行器配置模式 + }> + configSchema?: any // 节点配置模式 + defaultConfig?: any // 默认配置 +} + +// 节点类型示例数据 +const nodeTypes = [ + { + code: 'START', + name: '开始节点', + description: '工作流的开始节点', + category: 'BASIC', + icon: 'play-circle', + color: '#52c41a' + }, + { + code: 'END', + name: '结束节点', + description: '工作流的结束节点', + category: 'BASIC', + icon: 'stop', + color: '#ff4d4f' + }, + { + code: 'TASK', + name: '任务节点', + description: '执行具体任务的节点', + category: 'TASK', + icon: 'code', + color: '#1890ff', + executors: [ + { + code: 'SHELL', + name: 'Shell脚本', + description: '执行Shell脚本', + configSchema: { + type: 'object', + properties: { + script: { + type: 'string', + title: '脚本内容', + format: 'shell' + }, + timeout: { + type: 'number', + title: '超时时间(秒)', + default: 300 + } + } + } + }, + { + code: 'JENKINS', + name: 'Jenkins任务', + description: '触发Jenkins构建任务', + configSchema: { + type: 'object', + properties: { + job: { + type: 'string', + title: 'Job名称' + }, + parameters: { + type: 'object', + title: '构建参数' + } + } + } + } + ] + }, + { + code: 'GATEWAY', + name: '网关节点', + description: '控制流程流转的节点', + category: 'GATEWAY', + icon: 'fork', + color: '#faad14', + configSchema: { + type: 'object', + properties: { + type: { + type: 'string', + title: '网关类型', + enum: ['PARALLEL', 'EXCLUSIVE', 'INCLUSIVE'], + enumNames: ['并行网关', '排他网关', '包容网关'] + } + } + } + } +]; ``` ### 2.2 工作流实例管理 @@ -69,7 +262,8 @@ interface NodeDefinitionDTO { // 工作流实例相关接口 interface WorkflowInstanceAPI { // 基础CRUD接口 - list: '/api/v1/workflow-instance' // GET 查询列表 + page: '/api/v1/workflow-instance/page' // GET 分页查询,支持条件筛选 + list: '/api/v1/workflow-instance/list' // GET 查询所有(不分页) get: '/api/v1/workflow-instance/{id}' // GET 获取详情 // 实例操作接口 @@ -98,10 +292,11 @@ interface WorkflowInstanceDTO { // 节点实例相关接口 interface NodeInstanceAPI { // 查询接口 - list: '/api/v1/node-instance' // GET 查询列表 + page: '/api/v1/node-instance/page' // GET 分页查询,支持条件筛选 + list: '/api/v1/node-instance/list' // GET 查询所有(不分页) get: '/api/v1/node-instance/{id}' // GET 获取详情 - findByWorkflow: '/api/v1/node-instance/workflow/{workflowInstanceId}' // GET 查询工作流下的节点 - findByStatus: '/api/v1/node-instance/workflow/{workflowInstanceId}/status/{status}' // GET 查询指定状态的节点 + findByWorkflow: '/api/v1/node-instance/workflow/{workflowInstanceId}' // GET 查询工作流下的节点(不分页) + findByStatus: '/api/v1/node-instance/workflow/{workflowInstanceId}/status/{status}' // GET 查询指定状态的节点(不分页) // 节点操作 updateStatus: '/api/v1/node-instance/{id}/status' // PUT 更新状态 @@ -130,12 +325,13 @@ interface NodeInstanceDTO { // 日志相关接口 interface WorkflowLogAPI { // 基础CRUD接口 - list: '/api/v1/workflow-logs' // GET 查询列表 + page: '/api/v1/workflow-logs/page' // GET 分页查询,支持条件筛选 + list: '/api/v1/workflow-logs/list' // GET 查询所有(不分页) create: '/api/v1/workflow-logs' // POST 创建 // 特殊查询接口 - getWorkflowLogs: '/api/v1/workflow-logs/workflow/{workflowInstanceId}' // GET 查询工作流日志 - getNodeLogs: '/api/v1/workflow-logs/node/{workflowInstanceId}/{nodeId}' // GET 查询节点日志 + getWorkflowLogs: '/api/v1/workflow-logs/workflow/{workflowInstanceId}' // GET 查询工作流日志(不分页) + getNodeLogs: '/api/v1/workflow-logs/node/{workflowInstanceId}/{nodeId}' // GET 查询节点日志(不分页) record: '/api/v1/workflow-logs/record' // POST 记录日志 } @@ -179,19 +375,38 @@ interface WorkflowLogDTO { - 描述 - 备注 2. 流程设计器 - - 节点拖拽 - - 连线绘制 - - 节点配置 + - 节点拖拽和布局 + - 连线绘制和调整 + - 节点配置(nodeConfig) + - 基本信息配置 + - 执行器类型选择 + - 执行器参数配置 + - 流转配置(transitionConfig) + - 连线条件配置 + - 优先级设置 + - 条件表达式编辑 - 流程校验 - 3. 表单设计器 + 3. 表单设计器(formDefinition) - 表单字段配置 + - 字段类型选择 + - 字段属性设置 + - 选项配置(针对select类型) - 字段验证规则 + - 必填验证 + - 格式验证 + - 自定义验证 + - 表单布局设计 - 表单预览 - 4. 节点配置面板 - - 节点基本信息 - - 节点类型配置 - - 执行条件配置 - - 表单关联配置 + 4. 图形布局(graphDefinition) + - 节点位置调整 + - 连线样式设置 + - 自动布局 + - 缩放和居中 + 5. 版本管理 + - 版本历史查看 + - 版本对比 + - 版本回滚 + - 创建新版本 ### 3.2 工作流实例管理(/workflow/instance) @@ -295,68 +510,3 @@ src/ - refactor: 重构 - test: 测试 - chore: 构建过程或辅助工具的变动 - -## 五、开发流程 - -### 5.1 环境搭建 -1. 创建项目 -```bash -yarn create umi -``` - -2. 安装依赖 -```bash -yarn add @ant-design/pro-components @logicflow/core @logicflow/extension form-render echarts -``` - -### 5.2 开发步骤 -1. 搭建基础框架和路由(2天) -2. 实现工作流定义CRUD(3天) -3. 开发流程设计器(5天) -4. 开发表单设计器(3天) -5. 实现工作流实例管理(3天) -6. 实现节点实例管理(2天) -7. 实现日志管理(2天) -8. 开发监控大盘(3天) -9. 测试和优化(2天) - -### 5.3 测试要求 -1. 单元测试覆盖率 > 80% -2. 必须包含组件测试 -3. 必须包含集成测试 -4. 必须进行性能测试 - -### 5.4 部署要求 -1. 使用 Docker 部署 -2. 配置 Nginx 代理 -3. 启用 GZIP 压缩 -4. 配置缓存策略 - -## 六、注意事项 - -### 6.1 性能优化 -1. 使用路由懒加载 -2. 组件按需加载 -3. 大数据列表虚拟化 -4. 合理使用缓存 -5. 避免不必要的渲染 - -### 6.2 安全性 -1. 添加权限控制 -2. 防止XSS攻击 -3. 添加数据验证 -4. 敏感信息加密 - -### 6.3 用户体验 -1. 添加适当的加载状态 -2. 提供操作反馈 -3. 添加错误处理 -4. 支持快捷键操作 -5. 添加操作确认 -6. 支持数据导出 - -### 6.4 兼容性 -1. 支持主流浏览器最新版本 -2. 支持响应式布局 -3. 支持暗黑模式 -4. 支持国际化 \ No newline at end of file diff --git a/frontend/package-lock.json b/frontend/package-lock.json index efe5d79b..a2550cb9 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -9,12 +9,15 @@ "version": "0.0.1", "dependencies": { "@ant-design/icons": "^5.2.6", + "@antv/layout": "^1.2.14-beta.8", "@antv/x6": "^2.18.1", + "@antv/x6-react-shape": "^2.2.3", "@logicflow/core": "^2.0.9", "@logicflow/extension": "^2.0.13", "@reduxjs/toolkit": "^2.0.1", "antd": "^5.22.2", "axios": "^1.6.2", + "dagre": "^0.8.5", "form-render": "^2.5.1", "react": "^18.2.0", "react-dom": "^18.2.0", @@ -22,6 +25,7 @@ "react-router-dom": "^6.21.0" }, "devDependencies": { + "@types/dagre": "^0.7.52", "@types/node": "^20.10.4", "@types/react": "^18.2.43", "@types/react-dom": "^18.2.17", @@ -138,16 +142,57 @@ "react": ">=16.9.0" } }, + "node_modules/@antv/event-emitter": { + "version": "0.1.3", + "resolved": "https://registry.npmmirror.com/@antv/event-emitter/-/event-emitter-0.1.3.tgz", + "integrity": "sha512-4ddpsiHN9Pd4UIlWuKVK1C4IiZIdbwQvy9i7DUSI3xNJ89FPUFt8lxDYj8GzzfdllV0NkJTRxnG+FvLk0llidg==" + }, + "node_modules/@antv/graphlib": { + "version": "2.0.4", + "resolved": "https://registry.npmmirror.com/@antv/graphlib/-/graphlib-2.0.4.tgz", + "integrity": "sha512-zc/5oQlsdk42Z0ib1mGklwzhJ5vczLFiPa1v7DgJkTbgJ2YxRh9xdarf86zI49sKVJmgbweRpJs7Nu5bIiwv4w==", + "dependencies": { + "@antv/event-emitter": "^0.1.3" + } + }, "node_modules/@antv/hierarchy": { "version": "0.6.14", "resolved": "https://registry.npmmirror.com/@antv/hierarchy/-/hierarchy-0.6.14.tgz", "integrity": "sha512-V3uknf7bhynOqQDw2sg+9r9DwZ9pc6k/EcqyTFdfXB1+ydr7urisP0MipIuimucvQKN+Qkd+d6w601r1UIroqQ==" }, + "node_modules/@antv/layout": { + "version": "1.2.14-beta.8", + "resolved": "https://registry.npmmirror.com/@antv/layout/-/layout-1.2.14-beta.8.tgz", + "integrity": "sha512-/zP8pRz28ahYSVk4fsfhl3T1X3FvCAi7Q3YQ8H2LZEvTojYYrJtO2AXFsojehU6HGVnIAq+QIjd3He6ot8+gvA==", + "dependencies": { + "@antv/event-emitter": "^0.1.3", + "@antv/graphlib": "^2.0.0", + "@antv/util": "^3.3.2", + "@naoak/workerize-transferable": "^0.1.0", + "comlink": "^4.4.1", + "d3-force": "^3.0.0", + "d3-force-3d": "^3.0.5", + "d3-octree": "^1.0.2", + "d3-quadtree": "^3.0.1", + "dagre": "^0.8.5", + "ml-matrix": "^6.10.4", + "tslib": "^2.5.0" + } + }, + "node_modules/@antv/util": { + "version": "3.3.10", + "resolved": "https://registry.npmmirror.com/@antv/util/-/util-3.3.10.tgz", + "integrity": "sha512-basGML3DFA3O87INnzvDStjzS+n0JLEhRnRsDzP9keiXz8gT1z/fTdmJAZFOzMMWxy+HKbi7NbSt0+8vz/OsBQ==", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "gl-matrix": "^3.3.0", + "tslib": "^2.3.1" + } + }, "node_modules/@antv/x6": { "version": "2.18.1", "resolved": "https://registry.npmmirror.com/@antv/x6/-/x6-2.18.1.tgz", "integrity": "sha512-FkWdbLOpN9J7dfJ+kiBxzowSx2N6syBily13NMVdMs+wqC6Eo5sLXWCZjQHateTFWgFw7ZGi2y9o3Pmdov1sXw==", - "license": "MIT", "dependencies": { "@antv/x6-common": "^2.0.16", "@antv/x6-geometry": "^2.0.5", @@ -170,6 +215,16 @@ "integrity": "sha512-MId6riEQkxphBpVeTcL4ZNXL4lScyvDEPLyIafvWMcWNTGK0jgkK7N20XSzqt8ltJb0mGUso5s56mrk8ysHu2A==", "license": "MIT" }, + "node_modules/@antv/x6-react-shape": { + "version": "2.2.3", + "resolved": "https://registry.npmmirror.com/@antv/x6-react-shape/-/x6-react-shape-2.2.3.tgz", + "integrity": "sha512-42PGxk3XLnx9bsHQiRPauai5UQCrsh0MbWI3MyHRpjQaC30FK1rqyjH/EJ8qH0p6/cAndszZzCaiPqBbs3FqLQ==", + "peerDependencies": { + "@antv/x6": "^2.x", + "react": ">=18.0.0", + "react-dom": ">= 18.0.0" + } + }, "node_modules/@babel/code-frame": { "version": "7.26.2", "resolved": "https://registry.npmmirror.com/@babel/code-frame/-/code-frame-7.26.2.tgz", @@ -996,7 +1051,6 @@ "version": "0.3.5", "resolved": "https://registry.npmmirror.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", - "dev": true, "dependencies": { "@jridgewell/set-array": "^1.2.1", "@jridgewell/sourcemap-codec": "^1.4.10", @@ -1010,7 +1064,6 @@ "version": "3.1.2", "resolved": "https://registry.npmmirror.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, "engines": { "node": ">=6.0.0" } @@ -1019,22 +1072,29 @@ "version": "1.2.1", "resolved": "https://registry.npmmirror.com/@jridgewell/set-array/-/set-array-1.2.1.tgz", "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", - "dev": true, "engines": { "node": ">=6.0.0" } }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.6", + "resolved": "https://registry.npmmirror.com/@jridgewell/source-map/-/source-map-0.3.6.tgz", + "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==", + "peer": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" + } + }, "node_modules/@jridgewell/sourcemap-codec": { "version": "1.5.0", "resolved": "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", - "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", - "dev": true + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==" }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.25", "resolved": "https://registry.npmmirror.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", - "dev": true, "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" @@ -1074,6 +1134,14 @@ "@logicflow/core": "2.0.9" } }, + "node_modules/@naoak/workerize-transferable": { + "version": "0.1.0", + "resolved": "https://registry.npmmirror.com/@naoak/workerize-transferable/-/workerize-transferable-0.1.0.tgz", + "integrity": "sha512-fDLfuP71IPNP5+zSfxFb52OHgtjZvauRJWbVnpzQ7G7BjcbLjTny0OW1d3ZO806XKpLWNKmeeW3MhE0sy8iwYQ==", + "peerDependencies": { + "workerize-loader": "*" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmmirror.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -1560,23 +1628,46 @@ "@babel/types": "^7.20.7" } }, + "node_modules/@types/dagre": { + "version": "0.7.52", + "resolved": "https://registry.npmmirror.com/@types/dagre/-/dagre-0.7.52.tgz", + "integrity": "sha512-XKJdy+OClLk3hketHi9Qg6gTfe1F3y+UFnHxKA2rn9Dw+oXa4Gb378Ztz9HlMgZKSxpPmn4BNVh9wgkpvrK1uw==", + "dev": true + }, + "node_modules/@types/eslint": { + "version": "9.6.1", + "resolved": "https://registry.npmmirror.com/@types/eslint/-/eslint-9.6.1.tgz", + "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==", + "peer": true, + "dependencies": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "node_modules/@types/eslint-scope": { + "version": "3.7.7", + "resolved": "https://registry.npmmirror.com/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", + "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", + "peer": true, + "dependencies": { + "@types/eslint": "*", + "@types/estree": "*" + } + }, "node_modules/@types/estree": { "version": "1.0.6", "resolved": "https://registry.npmmirror.com/@types/estree/-/estree-1.0.6.tgz", - "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", - "dev": true + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==" }, "node_modules/@types/json-schema": { "version": "7.0.15", "resolved": "https://registry.npmmirror.com/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "dev": true + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==" }, "node_modules/@types/node": { "version": "20.17.7", "resolved": "https://registry.npmmirror.com/@types/node/-/node-20.17.7.tgz", "integrity": "sha512-sZXXnpBFMKbao30dUAvzKbdwA2JM1fwUtVEq/kxKuPI5mMwZiRElCpTXb0Biq/LMEVpXDZL5G5V0RPnxKeyaYg==", - "dev": true, "dependencies": { "undici-types": "~6.19.2" } @@ -1832,11 +1923,168 @@ "vite": "^4.2.0 || ^5.0.0" } }, + "node_modules/@webassemblyjs/ast": { + "version": "1.14.1", + "resolved": "https://registry.npmmirror.com/@webassemblyjs/ast/-/ast-1.14.1.tgz", + "integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==", + "peer": true, + "dependencies": { + "@webassemblyjs/helper-numbers": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2" + } + }, + "node_modules/@webassemblyjs/floating-point-hex-parser": { + "version": "1.13.2", + "resolved": "https://registry.npmmirror.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz", + "integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==", + "peer": true + }, + "node_modules/@webassemblyjs/helper-api-error": { + "version": "1.13.2", + "resolved": "https://registry.npmmirror.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz", + "integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==", + "peer": true + }, + "node_modules/@webassemblyjs/helper-buffer": { + "version": "1.14.1", + "resolved": "https://registry.npmmirror.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz", + "integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==", + "peer": true + }, + "node_modules/@webassemblyjs/helper-numbers": { + "version": "1.13.2", + "resolved": "https://registry.npmmirror.com/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz", + "integrity": "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==", + "peer": true, + "dependencies": { + "@webassemblyjs/floating-point-hex-parser": "1.13.2", + "@webassemblyjs/helper-api-error": "1.13.2", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/helper-wasm-bytecode": { + "version": "1.13.2", + "resolved": "https://registry.npmmirror.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz", + "integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==", + "peer": true + }, + "node_modules/@webassemblyjs/helper-wasm-section": { + "version": "1.14.1", + "resolved": "https://registry.npmmirror.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz", + "integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==", + "peer": true, + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/wasm-gen": "1.14.1" + } + }, + "node_modules/@webassemblyjs/ieee754": { + "version": "1.13.2", + "resolved": "https://registry.npmmirror.com/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz", + "integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==", + "peer": true, + "dependencies": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "node_modules/@webassemblyjs/leb128": { + "version": "1.13.2", + "resolved": "https://registry.npmmirror.com/@webassemblyjs/leb128/-/leb128-1.13.2.tgz", + "integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==", + "peer": true, + "dependencies": { + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/utf8": { + "version": "1.13.2", + "resolved": "https://registry.npmmirror.com/@webassemblyjs/utf8/-/utf8-1.13.2.tgz", + "integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==", + "peer": true + }, + "node_modules/@webassemblyjs/wasm-edit": { + "version": "1.14.1", + "resolved": "https://registry.npmmirror.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz", + "integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==", + "peer": true, + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/helper-wasm-section": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-opt": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1", + "@webassemblyjs/wast-printer": "1.14.1" + } + }, + "node_modules/@webassemblyjs/wasm-gen": { + "version": "1.14.1", + "resolved": "https://registry.npmmirror.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz", + "integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==", + "peer": true, + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" + } + }, + "node_modules/@webassemblyjs/wasm-opt": { + "version": "1.14.1", + "resolved": "https://registry.npmmirror.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz", + "integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==", + "peer": true, + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1" + } + }, + "node_modules/@webassemblyjs/wasm-parser": { + "version": "1.14.1", + "resolved": "https://registry.npmmirror.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz", + "integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==", + "peer": true, + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-api-error": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" + } + }, + "node_modules/@webassemblyjs/wast-printer": { + "version": "1.14.1", + "resolved": "https://registry.npmmirror.com/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz", + "integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==", + "peer": true, + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "peer": true + }, + "node_modules/@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmmirror.com/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "peer": true + }, "node_modules/acorn": { "version": "8.14.0", "resolved": "https://registry.npmmirror.com/acorn/-/acorn-8.14.0.tgz", "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", - "dev": true, "bin": { "acorn": "bin/acorn" }, @@ -1887,7 +2135,6 @@ "version": "6.12.6", "resolved": "https://registry.npmmirror.com/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -1899,6 +2146,15 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmmirror.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "peer": true, + "peerDependencies": { + "ajv": "^6.9.1" + } + }, "node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-5.0.1.tgz", @@ -2042,6 +2298,15 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, + "node_modules/big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmmirror.com/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", + "peer": true, + "engines": { + "node": "*" + } + }, "node_modules/brace-expansion": { "version": "2.0.1", "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-2.0.1.tgz", @@ -2067,7 +2332,6 @@ "version": "4.24.2", "resolved": "https://registry.npmmirror.com/browserslist/-/browserslist-4.24.2.tgz", "integrity": "sha512-ZIc+Q62revdMcqC6aChtW4jz3My3klmCO1fEmINZY/8J3EpBg5/A/D0AKmBveUh6pgoeycoMkVMko84tuYS+Gg==", - "dev": true, "funding": [ { "type": "opencollective", @@ -2095,6 +2359,12 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmmirror.com/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "peer": true + }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmmirror.com/callsites/-/callsites-3.1.0.tgz", @@ -2108,7 +2378,6 @@ "version": "1.0.30001684", "resolved": "https://registry.npmmirror.com/caniuse-lite/-/caniuse-lite-1.0.30001684.tgz", "integrity": "sha512-G1LRwLIQjBQoyq0ZJGqGIJUXzJ8irpbjHLpVRXDvBEScFJ9b17sgK6vlx0GAJFE21okD7zXl08rRRUfq6HdoEQ==", - "dev": true, "funding": [ { "type": "opencollective", @@ -2140,6 +2409,15 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/chrome-trace-event": { + "version": "1.0.4", + "resolved": "https://registry.npmmirror.com/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz", + "integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==", + "peer": true, + "engines": { + "node": ">=6.0" + } + }, "node_modules/classnames": { "version": "2.5.1", "resolved": "https://registry.npmmirror.com/classnames/-/classnames-2.5.1.tgz", @@ -2204,6 +2482,17 @@ "node": ">= 0.8" } }, + "node_modules/comlink": { + "version": "4.4.2", + "resolved": "https://registry.npmmirror.com/comlink/-/comlink-4.4.2.tgz", + "integrity": "sha512-OxGdvBmJuNKSCMO4NTl1L47VRp6xn2wG4F/2hYzB6tiCb709otOxtEYCSvK80PtjODfXXZu8ds+Nw5kVCjqd2g==" + }, + "node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmmirror.com/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "peer": true + }, "node_modules/component-classes": { "version": "1.2.6", "resolved": "https://registry.npmmirror.com/component-classes/-/component-classes-1.2.6.tgz", @@ -2286,6 +2575,77 @@ "resolved": "https://registry.npmmirror.com/csstype/-/csstype-3.1.3.tgz", "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" }, + "node_modules/d3-binarytree": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/d3-binarytree/-/d3-binarytree-1.0.2.tgz", + "integrity": "sha512-cElUNH+sHu95L04m92pG73t2MEJXKu+GeKUN1TJkFsu93E5W8E9Sc3kHEGJKgenGvj19m6upSn2EunvMgMD2Yw==" + }, + "node_modules/d3-dispatch": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/d3-dispatch/-/d3-dispatch-3.0.1.tgz", + "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-force": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/d3-force/-/d3-force-3.0.0.tgz", + "integrity": "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-quadtree": "1 - 3", + "d3-timer": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-force-3d": { + "version": "3.0.5", + "resolved": "https://registry.npmmirror.com/d3-force-3d/-/d3-force-3d-3.0.5.tgz", + "integrity": "sha512-tdwhAhoTYZY/a6eo9nR7HP3xSW/C6XvJTbeRpR92nlPzH6OiE+4MliN9feuSFd0tPtEUo+191qOhCTWx3NYifg==", + "dependencies": { + "d3-binarytree": "1", + "d3-dispatch": "1 - 3", + "d3-octree": "1", + "d3-quadtree": "1 - 3", + "d3-timer": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-octree": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/d3-octree/-/d3-octree-1.0.2.tgz", + "integrity": "sha512-Qxg4oirJrNXauiuC94uKMbgxwnhdda9xRLl9ihq45srlJ4Ga3CSgqGcAL8iW7N5CIv4Oz8x3E734ulxyvHPvwA==" + }, + "node_modules/d3-quadtree": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/d3-quadtree/-/d3-quadtree-3.0.1.tgz", + "integrity": "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-timer": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/d3-timer/-/d3-timer-3.0.1.tgz", + "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", + "engines": { + "node": ">=12" + } + }, + "node_modules/dagre": { + "version": "0.8.5", + "resolved": "https://registry.npmmirror.com/dagre/-/dagre-0.8.5.tgz", + "integrity": "sha512-/aTqmnRta7x7MCCpExk7HQL2O4owCT2h8NT//9I1OQ9vt29Pa0BzSAkR5lwFUcQ7491yVi/3CXU9jQ5o0Mn2Sw==", + "dependencies": { + "graphlib": "^2.1.8", + "lodash": "^4.17.15" + } + }, "node_modules/dayjs": { "version": "1.11.13", "resolved": "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.13.tgz", @@ -2354,8 +2714,35 @@ "node_modules/electron-to-chromium": { "version": "1.5.64", "resolved": "https://registry.npmmirror.com/electron-to-chromium/-/electron-to-chromium-1.5.64.tgz", - "integrity": "sha512-IXEuxU+5ClW2IGEYFC2T7szbyVgehupCWQe5GNh+H065CD6U6IFN0s4KeAMFGNmQolRU4IV7zGBWSYMmZ8uuqQ==", - "dev": true + "integrity": "sha512-IXEuxU+5ClW2IGEYFC2T7szbyVgehupCWQe5GNh+H065CD6U6IFN0s4KeAMFGNmQolRU4IV7zGBWSYMmZ8uuqQ==" + }, + "node_modules/emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", + "peer": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/enhanced-resolve": { + "version": "5.17.1", + "resolved": "https://registry.npmmirror.com/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz", + "integrity": "sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==", + "peer": true, + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/es-module-lexer": { + "version": "1.5.4", + "resolved": "https://registry.npmmirror.com/es-module-lexer/-/es-module-lexer-1.5.4.tgz", + "integrity": "sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw==", + "peer": true }, "node_modules/esbuild": { "version": "0.21.5", @@ -2399,7 +2786,6 @@ "version": "3.2.0", "resolved": "https://registry.npmmirror.com/escalade/-/escalade-3.2.0.tgz", "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "dev": true, "engines": { "node": ">=6" } @@ -2591,7 +2977,6 @@ "version": "4.3.0", "resolved": "https://registry.npmmirror.com/esrecurse/-/esrecurse-4.3.0.tgz", "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, "dependencies": { "estraverse": "^5.2.0" }, @@ -2603,7 +2988,6 @@ "version": "5.3.0", "resolved": "https://registry.npmmirror.com/estraverse/-/estraverse-5.3.0.tgz", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, "engines": { "node": ">=4.0" } @@ -2617,11 +3001,19 @@ "node": ">=0.10.0" } }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmmirror.com/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "peer": true, + "engines": { + "node": ">=0.8.x" + } + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmmirror.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, "node_modules/fast-glob": { "version": "3.3.2", @@ -2654,8 +3046,7 @@ "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmmirror.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" }, "node_modules/fast-levenshtein": { "version": "2.0.6", @@ -2876,6 +3267,11 @@ "node": ">=6.9.0" } }, + "node_modules/gl-matrix": { + "version": "3.4.3", + "resolved": "https://registry.npmmirror.com/gl-matrix/-/gl-matrix-3.4.3.tgz", + "integrity": "sha512-wcCp8vu8FT22BnvKVPjXa/ICBWRq/zjFfdofZy1WSpQZpphblv12/bOQLBC1rMM7SGOFS9ltVmKOHil5+Ml7gA==" + }, "node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmmirror.com/glob/-/glob-7.2.3.tgz", @@ -2909,6 +3305,12 @@ "node": ">=10.13.0" } }, + "node_modules/glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmmirror.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", + "peer": true + }, "node_modules/glob/node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -2960,17 +3362,30 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmmirror.com/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "peer": true + }, "node_modules/graphemer": { "version": "1.4.0", "resolved": "https://registry.npmmirror.com/graphemer/-/graphemer-1.4.0.tgz", "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", "dev": true }, + "node_modules/graphlib": { + "version": "2.1.8", + "resolved": "https://registry.npmmirror.com/graphlib/-/graphlib-2.1.8.tgz", + "integrity": "sha512-jcLLfkpoVGmH7/InMC/1hIvOPSUh38oJtGhvrOFGzioE1DZ+0YW16RgmOJhHiuWTvGiJQ9Z1Ik43JvkRPRvE+A==", + "dependencies": { + "lodash": "^4.17.15" + } + }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, "engines": { "node": ">=8" } @@ -3045,6 +3460,11 @@ "resolved": "https://registry.npmmirror.com/intersection-observer/-/intersection-observer-0.12.2.tgz", "integrity": "sha512-7m1vEcPCxXYI8HqnL8CKI6siDyD+eIWSwgB3DZA+ZTogxk9I4CDnj4wilt9x/+/QbHI4YG5YZNmC6458/e9Ktg==" }, + "node_modules/is-any-array": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/is-any-array/-/is-any-array-2.0.1.tgz", + "integrity": "sha512-UtilS7hLRu++wb/WBAw9bNuP1Eg04Ivn1vERJck8zJthEvXCBEBpGR/33u/xLKWEQf95803oalHrVDptcAvFdQ==" + }, "node_modules/is-arrayish": { "version": "0.3.2", "resolved": "https://registry.npmmirror.com/is-arrayish/-/is-arrayish-0.3.2.tgz", @@ -3095,6 +3515,35 @@ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, + "node_modules/jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmmirror.com/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "peer": true, + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "peer": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, "node_modules/js-cookie": { "version": "3.0.5", "resolved": "https://registry.npmmirror.com/js-cookie/-/js-cookie-3.0.5.tgz", @@ -3138,11 +3587,16 @@ "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", "dev": true }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmmirror.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "peer": true + }, "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmmirror.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", @@ -3162,7 +3616,6 @@ "version": "2.2.3", "resolved": "https://registry.npmmirror.com/json5/-/json5-2.2.3.tgz", "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true, "bin": { "json5": "lib/cli.js" }, @@ -3192,6 +3645,29 @@ "node": ">= 0.8.0" } }, + "node_modules/loader-runner": { + "version": "4.3.0", + "resolved": "https://registry.npmmirror.com/loader-runner/-/loader-runner-4.3.0.tgz", + "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", + "peer": true, + "engines": { + "node": ">=6.11.5" + } + }, + "node_modules/loader-utils": { + "version": "2.0.4", + "resolved": "https://registry.npmmirror.com/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", + "peer": true, + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + }, + "engines": { + "node": ">=8.9.0" + } + }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmmirror.com/locate-path/-/locate-path-6.0.0.tgz", @@ -3249,6 +3725,12 @@ "resolved": "https://registry.npmmirror.com/medium-editor/-/medium-editor-5.23.3.tgz", "integrity": "sha512-he9/TdjX8f8MGdXGfCs8AllrYnqXJJvjNkDKmPg3aPW/uoIrlRqtkFthrwvmd+u4QyzEiadhCCM0EwTiRdUCJw==" }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "peer": true + }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmmirror.com/merge2/-/merge2-1.4.1.tgz", @@ -3305,6 +3787,41 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/ml-array-max": { + "version": "1.2.4", + "resolved": "https://registry.npmmirror.com/ml-array-max/-/ml-array-max-1.2.4.tgz", + "integrity": "sha512-BlEeg80jI0tW6WaPyGxf5Sa4sqvcyY6lbSn5Vcv44lp1I2GR6AWojfUvLnGTNsIXrZ8uqWmo8VcG1WpkI2ONMQ==", + "dependencies": { + "is-any-array": "^2.0.0" + } + }, + "node_modules/ml-array-min": { + "version": "1.2.3", + "resolved": "https://registry.npmmirror.com/ml-array-min/-/ml-array-min-1.2.3.tgz", + "integrity": "sha512-VcZ5f3VZ1iihtrGvgfh/q0XlMobG6GQ8FsNyQXD3T+IlstDv85g8kfV0xUG1QPRO/t21aukaJowDzMTc7j5V6Q==", + "dependencies": { + "is-any-array": "^2.0.0" + } + }, + "node_modules/ml-array-rescale": { + "version": "1.3.7", + "resolved": "https://registry.npmmirror.com/ml-array-rescale/-/ml-array-rescale-1.3.7.tgz", + "integrity": "sha512-48NGChTouvEo9KBctDfHC3udWnQKNKEWN0ziELvY3KG25GR5cA8K8wNVzracsqSW1QEkAXjTNx+ycgAv06/1mQ==", + "dependencies": { + "is-any-array": "^2.0.0", + "ml-array-max": "^1.2.4", + "ml-array-min": "^1.2.3" + } + }, + "node_modules/ml-matrix": { + "version": "6.12.0", + "resolved": "https://registry.npmmirror.com/ml-matrix/-/ml-matrix-6.12.0.tgz", + "integrity": "sha512-AGfR+pWaC0GmzjUnB6BfwhndPEUGz0i7QUYdqNuw1zhTov/vSRJ9pP2hs6BoGpaSbtXgrKjZz2zjD1M0xuur6A==", + "dependencies": { + "is-any-array": "^2.0.1", + "ml-array-rescale": "^1.3.7" + } + }, "node_modules/mobx": { "version": "5.15.7", "resolved": "https://registry.npmmirror.com/mobx/-/mobx-5.15.7.tgz", @@ -3369,11 +3886,16 @@ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmmirror.com/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "peer": true + }, "node_modules/node-releases": { "version": "2.0.18", "resolved": "https://registry.npmmirror.com/node-releases/-/node-releases-2.0.18.tgz", - "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==", - "dev": true + "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==" }, "node_modules/object-assign": { "version": "4.1.1", @@ -3495,8 +4017,7 @@ "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "dev": true + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==" }, "node_modules/picomatch": { "version": "2.3.1", @@ -3580,7 +4101,6 @@ "version": "2.3.1", "resolved": "https://registry.npmmirror.com/punycode/-/punycode-2.3.1.tgz", "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true, "engines": { "node": ">=6" } @@ -3613,6 +4133,15 @@ "performance-now": "^2.1.0" } }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "peer": true, + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, "node_modules/rangy": { "version": "1.3.2", "resolved": "https://registry.npmmirror.com/rangy/-/rangy-1.3.2.tgz", @@ -4500,6 +5029,26 @@ "queue-microtask": "^1.2.2" } }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "peer": true + }, "node_modules/scheduler": { "version": "0.23.2", "resolved": "https://registry.npmmirror.com/scheduler/-/scheduler-0.23.2.tgz", @@ -4508,6 +5057,24 @@ "loose-envify": "^1.1.0" } }, + "node_modules/schema-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmmirror.com/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "peer": true, + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, "node_modules/screenfull": { "version": "5.2.0", "resolved": "https://registry.npmmirror.com/screenfull/-/screenfull-5.2.0.tgz", @@ -4539,6 +5106,15 @@ "node": ">=10" } }, + "node_modules/serialize-javascript": { + "version": "6.0.2", + "resolved": "https://registry.npmmirror.com/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", + "peer": true, + "dependencies": { + "randombytes": "^2.1.0" + } + }, "node_modules/shallowequal": { "version": "1.1.0", "resolved": "https://registry.npmmirror.com/shallowequal/-/shallowequal-1.1.0.tgz", @@ -4582,6 +5158,15 @@ "node": ">=8" } }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/source-map-js": { "version": "1.2.1", "resolved": "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.2.1.tgz", @@ -4591,6 +5176,16 @@ "node": ">=0.10.0" } }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmmirror.com/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "peer": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, "node_modules/string-convert": { "version": "0.2.1", "resolved": "https://registry.npmmirror.com/string-convert/-/string-convert-0.2.1.tgz", @@ -4637,6 +5232,67 @@ "node": ">=8" } }, + "node_modules/tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmmirror.com/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "peer": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/terser": { + "version": "5.36.0", + "resolved": "https://registry.npmmirror.com/terser/-/terser-5.36.0.tgz", + "integrity": "sha512-IYV9eNMuFAV4THUspIRXkLakHnV6XO7FEdtKjf/mDyrnqUg9LnlOn6/RwRvM9SZjR4GUq8Nk8zj67FzVARr74w==", + "peer": true, + "dependencies": { + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.8.2", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/terser-webpack-plugin": { + "version": "5.3.10", + "resolved": "https://registry.npmmirror.com/terser-webpack-plugin/-/terser-webpack-plugin-5.3.10.tgz", + "integrity": "sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w==", + "peer": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.20", + "jest-worker": "^27.4.5", + "schema-utils": "^3.1.1", + "serialize-javascript": "^6.0.1", + "terser": "^5.26.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "uglify-js": { + "optional": true + } + } + }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmmirror.com/text-table/-/text-table-0.2.0.tgz", @@ -4730,14 +5386,12 @@ "node_modules/undici-types": { "version": "6.19.8", "resolved": "https://registry.npmmirror.com/undici-types/-/undici-types-6.19.8.tgz", - "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", - "dev": true + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==" }, "node_modules/update-browserslist-db": { "version": "1.1.1", "resolved": "https://registry.npmmirror.com/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz", "integrity": "sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==", - "dev": true, "funding": [ { "type": "opencollective", @@ -4767,7 +5421,6 @@ "version": "4.4.1", "resolved": "https://registry.npmmirror.com/uri-js/-/uri-js-4.4.1.tgz", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, "dependencies": { "punycode": "^2.1.0" } @@ -4878,6 +5531,96 @@ } } }, + "node_modules/watchpack": { + "version": "2.4.2", + "resolved": "https://registry.npmmirror.com/watchpack/-/watchpack-2.4.2.tgz", + "integrity": "sha512-TnbFSbcOCcDgjZ4piURLCbJ3nJhznVh9kw6F6iokjiFPl8ONxe9A6nMDVXDiNbrSfLILs6vB07F7wLBrwPYzJw==", + "peer": true, + "dependencies": { + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/webpack": { + "version": "5.97.0", + "resolved": "https://registry.npmmirror.com/webpack/-/webpack-5.97.0.tgz", + "integrity": "sha512-CWT8v7ShSfj7tGs4TLRtaOLmOCPWhoKEvp+eA7FVx8Xrjb3XfT0aXdxDItnRZmE8sHcH+a8ayDrJCOjXKxVFfQ==", + "peer": true, + "dependencies": { + "@types/eslint-scope": "^3.7.7", + "@types/estree": "^1.0.6", + "@webassemblyjs/ast": "^1.14.1", + "@webassemblyjs/wasm-edit": "^1.14.1", + "@webassemblyjs/wasm-parser": "^1.14.1", + "acorn": "^8.14.0", + "browserslist": "^4.24.0", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.17.1", + "es-module-lexer": "^1.2.1", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.11", + "json-parse-even-better-errors": "^2.3.1", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^3.2.0", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.3.10", + "watchpack": "^2.4.1", + "webpack-sources": "^3.2.3" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/webpack-sources": { + "version": "3.2.3", + "resolved": "https://registry.npmmirror.com/webpack-sources/-/webpack-sources-3.2.3.tgz", + "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", + "peer": true, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/webpack/node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmmirror.com/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "peer": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/webpack/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmmirror.com/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "peer": true, + "engines": { + "node": ">=4.0" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmmirror.com/which/-/which-2.0.2.tgz", @@ -4902,6 +5645,18 @@ "node": ">=0.10.0" } }, + "node_modules/workerize-loader": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/workerize-loader/-/workerize-loader-2.0.2.tgz", + "integrity": "sha512-HoZ6XY4sHWxA2w0WpzgBwUiR3dv1oo7bS+oCwIpb6n54MclQ/7KXdXsVIChTCygyuHtVuGBO1+i3HzTt699UJQ==", + "peer": true, + "dependencies": { + "loader-utils": "^2.0.0" + }, + "peerDependencies": { + "webpack": "*" + } + }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmmirror.com/wrappy/-/wrappy-1.0.2.tgz", diff --git a/frontend/package.json b/frontend/package.json index 171a15d1..1dbe8129 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -11,12 +11,15 @@ }, "dependencies": { "@ant-design/icons": "^5.2.6", + "@antv/layout": "^1.2.14-beta.8", "@antv/x6": "^2.18.1", + "@antv/x6-react-shape": "^2.2.3", "@logicflow/core": "^2.0.9", "@logicflow/extension": "^2.0.13", "@reduxjs/toolkit": "^2.0.1", "antd": "^5.22.2", "axios": "^1.6.2", + "dagre": "^0.8.5", "form-render": "^2.5.1", "react": "^18.2.0", "react-dom": "^18.2.0", @@ -24,6 +27,7 @@ "react-router-dom": "^6.21.0" }, "devDependencies": { + "@types/dagre": "^0.7.52", "@types/node": "^20.10.4", "@types/react": "^18.2.43", "@types/react-dom": "^18.2.17", diff --git a/frontend/src/pages/Workflow/Definition/Edit/components/FlowDesigner/index.module.css b/frontend/src/pages/Workflow/Definition/Edit/components/FlowDesigner/index.module.css index 4df1be3a..cc6c7eed 100644 --- a/frontend/src/pages/Workflow/Definition/Edit/components/FlowDesigner/index.module.css +++ b/frontend/src/pages/Workflow/Definition/Edit/components/FlowDesigner/index.module.css @@ -1,92 +1,86 @@ .container { width: 100%; - min-height: 600px; - position: relative; - background: #fff; + height: 600px; + background-color: #fff; border: 1px solid #e8e8e8; - border-radius: 2px; - display: flex; -} - -.canvas { - flex: 1; - min-height: 600px; - position: relative; + border-radius: 4px; overflow: hidden; - background: #fafafa; } -.canvas::before { - content: ''; - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - background-image: - linear-gradient(#e8e8e8 1px, transparent 1px), - linear-gradient(90deg, #e8e8e8 1px, transparent 1px); - background-size: 20px 20px; - opacity: 0.5; -} - -.configPanel { - width: 320px; +.dndNode { + width: 100%; height: 100%; - border-left: 1px solid #e8e8e8; - overflow-y: auto; - background: #fff; - box-shadow: -2px 0 8px rgba(0, 0, 0, 0.06); -} - -:global(.lf-control) { - position: absolute; - right: 330px; - top: 10px; display: flex; - gap: 8px; - background: #fff; - padding: 4px; + align-items: center; + justify-content: center; + background-color: #fff; + border: 1px solid #1890ff; border-radius: 4px; - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); - z-index: 2; + cursor: move; + user-select: none; } -:global(.lf-dnd-panel) { - position: absolute; - left: 10px; - top: 10px; - width: 160px; - background: #fff; - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15); - padding: 12px; - border-radius: 4px; - display: flex; - flex-direction: column; - gap: 8px; - z-index: 2; +.dndNode:hover { + background-color: #e6f7ff; + border-color: #1890ff; } -:global(.lf-dnd-panel .lf-dnd-item) { +:global(.lf-node) { display: flex; align-items: center; justify-content: center; width: 100%; - height: 35px; + height: 100%; +} + +:global(.lf-node-text) { + font-size: 12px; + color: #333; + text-align: center; + word-break: break-all; +} + +:global(.lf-edge-text) { + font-size: 12px; + color: #666; + background: #fff; + padding: 2px 4px; + border-radius: 2px; border: 1px solid #e8e8e8; - border-radius: 4px; - cursor: grab; +} + +:global(.lf-node-selected) { + box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.3); +} + +:global(.lf-edge-selected) { + stroke: #1890ff !important; + stroke-width: 2px !important; +} + +:global(.lf-anchor) { + stroke: #1890ff; + fill: #fff; + stroke-width: 1px; + cursor: crosshair; +} + +:global(.lf-anchor:hover) { + fill: #1890ff; +} + +:global(.lf-edge-label) { + background: #fff; + border: 1px solid #e8e8e8; + border-radius: 2px; + padding: 2px 4px; + font-size: 12px; + color: #666; user-select: none; - transition: all 0.3s; + cursor: pointer; } -:global(.lf-dnd-panel .lf-dnd-item:hover) { - border-color: #1890ff; - color: #1890ff; -} - -:global(.lf-dnd-panel .lf-dnd-item img) { - width: 20px; - height: 20px; - margin-right: 8px; +:global(.lf-edge-label:hover) { + background: #f5f5f5; + border-color: #d9d9d9; } \ No newline at end of file diff --git a/frontend/src/pages/Workflow/Definition/Edit/components/FlowDesigner/index.tsx b/frontend/src/pages/Workflow/Definition/Edit/components/FlowDesigner/index.tsx index 9640fdec..bea322e0 100644 --- a/frontend/src/pages/Workflow/Definition/Edit/components/FlowDesigner/index.tsx +++ b/frontend/src/pages/Workflow/Definition/Edit/components/FlowDesigner/index.tsx @@ -1,202 +1,626 @@ import React, { useEffect, useRef, useState } from 'react'; -import { Card } from 'antd'; -import LogicFlow from '@logicflow/core'; -import { DndPanel, SelectionSelect, Control, Menu } from '@logicflow/extension'; -import '@logicflow/core/es/index.css'; -import '@logicflow/extension/es/style/index.css'; +import { Graph, Node, Edge, Shape } from '@antv/x6'; +import dagre from 'dagre'; +import { getNodeTypes } from '@/pages/Workflow/service'; +import type { NodeTypeDTO } from '@/pages/Workflow/types'; import styles from './index.module.css'; -import { registerNodes } from './nodes'; -import NodeConfig from '../NodeConfig'; + +// 节点类型映射 +const NODE_TYPE_MAP: Record = { + 'TASK': 'SHELL' // 将 TASK 类型映射到 SHELL 类型 +}; + +// 特殊节点类型 +const SPECIAL_NODE_TYPES = { + START: 'START', + END: 'END' +}; + +// 记录已注册的节点类型 +const registeredNodes = new Set(); + +// 布局配置 +const LAYOUT_CONFIG = { + PADDING: 20, + NODE_MIN_DISTANCE: 100, + RANK_SEPARATION: 100, + NODE_SEPARATION: 80, + EDGE_SPACING: 20, +}; interface FlowDesignerProps { value?: string; onChange?: (value: string) => void; + workflowInstanceId?: number; + readOnly?: boolean; } const FlowDesigner: React.FC = ({ value, - onChange + onChange, + workflowInstanceId, + readOnly = false }) => { const containerRef = useRef(null); - const [lf, setLf] = useState(); - const [selectedNode, setSelectedNode] = useState(null); + const graphRef = useRef(); + const [nodeTypes, setNodeTypes] = useState([]); - // 初始化 LogicFlow - const initLogicFlow = () => { - if (!containerRef.current) { - console.error('Container ref is not ready'); + // 使用 dagre 布局算法 + const layout = (graph: Graph, force: boolean = false) => { + const nodes = graph.getNodes(); + const edges = graph.getEdges(); + + if (nodes.length === 0) return; + + // 如果不是强制布局,且所有节点都有位置信息,则不进行自动布局 + if (!force && nodes.every(node => { + const position = node.position(); + return position.x !== undefined && position.y !== undefined; + })) { return; } - try { - // 注册插件 - LogicFlow.use(DndPanel); - LogicFlow.use(SelectionSelect); - LogicFlow.use(Control); - LogicFlow.use(Menu); + const g = new dagre.graphlib.Graph(); + g.setGraph({ + rankdir: 'LR', + nodesep: LAYOUT_CONFIG.NODE_SEPARATION, + ranksep: LAYOUT_CONFIG.RANK_SEPARATION, + edgesep: LAYOUT_CONFIG.EDGE_SPACING, + marginx: LAYOUT_CONFIG.PADDING, + marginy: LAYOUT_CONFIG.PADDING, + }); - const logicflow = new LogicFlow({ - container: containerRef.current, - grid: true, - nodeTextEdit: true, - edgeTextEdit: true, - width: containerRef.current.offsetWidth || 800, - height: containerRef.current.offsetHeight || 600, - style: { - circle: { - r: 30, - stroke: '#000000', - strokeWidth: 1, - }, - rect: { - width: 100, - height: 50, - stroke: '#000000', - strokeWidth: 1, - }, - diamond: { - rx: 20, - ry: 20, - stroke: '#000000', - strokeWidth: 1, - }, - nodeText: { - fontSize: 12, - color: '#000000', - }, - edgeText: { - textWidth: 100, - fontSize: 12, - color: '#000000', - background: { - fill: '#FFFFFF', - }, - }, - }, + // 设置默认节点大小 + g.setDefaultEdgeLabel(() => ({})); + + // 添加节点 + nodes.forEach((node) => { + g.setNode(node.id, { + width: node.getSize().width, + height: node.getSize().height, }); + }); - console.log('LogicFlow instance created'); + // 添加边 + edges.forEach((edge) => { + const source = edge.getSourceCellId(); + const target = edge.getTargetCellId(); + g.setEdge(source, target); + }); - // 注册自定义节点 - registerNodes(logicflow); + // 执行布局 + dagre.layout(g); - // 配置拖拽面板 - logicflow.setPatternItems([ - { - type: 'start', - text: '开始节点', - label: '开始节点', - icon: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAADsSURBVDhP7ZQxDsIwDEV7AcZeggOwMXAFJg7BxMgBuAcrZ2DiGuwcgImNA3AFBgaWDhUlqhKnKWFh4EtWaif2s/OdAn8VY8wE13jEHR6wjUu0eIoL7ODBa0zQzlrswQWvcOcLSpzhFu2sxRN0wTZKLnGOT7jgHXZQcoYuOMKvkFZQcoMuKOdTkH4RpBWUdNEFU0idOsE7bnBYEL0gScvnkE5LgS44wA4+UHY5R3vMMtqZxB4+UQZkl+1M4hTf0K7Szjp4wQztrIMXlEFZQUkXL5jBf4KygpIuXjCDsoLyXwQzKCsoSWGwD3Q5p3qCzuvYAAAAAElFTkSuQmCC' - }, - { - type: 'task', - text: '任务节点', - label: '任务节点', - icon: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAB5SURBVDhP7ZTBCYAwDEU7QkfxZi/eHMGbo3Rwhe7hCB3Bo+kHIZgmTUXQgw8k5Cd5tE34VUSkhw3ucYUTrNBiggUecYsNWkzwCyvc4QMtJviFJZ7wCS0mGIQVnvEFLSb4hRVe8A0tJhiEFd7wAy0m+IUVZmHxPyLZAYNFt+rXusajAAAAAElFTkSuQmCC' - }, - { - type: 'gateway', - text: '网关节点', - label: '网关节点', - icon: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAACxSURBVDhP7ZQxDoMwDEU5AiM34AqMHXqFTj0EE2MP0L1bz8DENeicgYmNA3AFBgaWVrIiRyZRU1VIPPIT2Mb+NlYS8FeJMQ5wjkvc4QYnWKHFU5zjFtdeY4Z21mIHF7zAjS8ocYprtLMWj9AF2yi5wBk+4II32EHJKbrgAL9CWkHJFbqgnE9B+kWQVlDSRRdMIXXqCG+4wn5B9IIkLZ9DOi0FumAfW3hH2eUU7THLaGcSO/gBUb+nujmI/XsAAAAASUVORK5CYII=' - }, - { - type: 'end', - text: '结束节点', - label: '结束节点', - icon: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAADsSURBVDhP7ZQxDsIwDEV7AcZeggOwMXAFJg7BxMgBuAcrZ2DiGuwcgImNA3AFBgaWDhUlqhKnKWFh4EtWaif2s/OdAn8VY8wE13jEHR6wjUu0eIoL7ODBa0zQzlrswQWvcOcLSpzhFu2sxRN0wTZKLnGOT7jgHXZQcoYuOMKvkFZQcoMuKOdTkH4RpBWUdNEFU0idOsE7bnBYEL0gScvnkE5LgS44wA4+UHY5R3vMMtqZxB4+UQZkl+1M4hTf0K7Szjp4wQztrIMXlEFZQUkXL5jBf4KygpIuXjCDsoLyXwQzKCsoSWGwD3Q5p3qCzuvYAAAAAElFTkSuQmCC' - } - ]); - - // 注册事件 - logicflow.on('node:click', ({ data }) => { - console.log('Node clicked:', data); - const nodeModel = logicflow.getNodeModelById(data.id); - const nodeData = { - id: data.id, - type: data.type, - text: data.text, - properties: nodeModel.getProperties() + // 批量更新节点位置 + const updates = nodes.map((node) => { + const nodeWithPosition = g.node(node.id); + if (nodeWithPosition) { + return { + node, + position: { + x: nodeWithPosition.x - nodeWithPosition.width / 2, + y: nodeWithPosition.y - nodeWithPosition.height / 2, + }, }; - setSelectedNode(nodeData); - }); - - logicflow.on('blank:click', () => { - setSelectedNode(null); - }); - - logicflow.on('history:change', () => { - const graphData = logicflow.getGraphData(); - onChange?.(JSON.stringify(graphData)); - }); - - // 渲染初始数据 - if (value) { - try { - const graphData = JSON.parse(value); - logicflow.render(graphData); - console.log('Initial graph data rendered:', graphData); - } catch (error) { - console.error('Failed to parse graph data:', error); - logicflow.render({ nodes: [], edges: [] }); - } - } else { - logicflow.render({ nodes: [], edges: [] }); - console.log('Empty graph rendered'); } - - setLf(logicflow); - console.log('LogicFlow initialization completed'); - - return logicflow; - } catch (error) { - console.error('Failed to initialize LogicFlow:', error); return null; - } + }).filter(Boolean); + + // 使用批量更新 + graph.batchUpdate(() => { + updates.forEach((update) => { + if (update) { + update.node.position(update.position.x, update.position.y); + } + }); + }); + + graph.centerContent(); }; - useEffect(() => { - const logicflow = initLogicFlow(); - - return () => { - if (logicflow) { - console.log('Destroying LogicFlow instance'); - logicflow.destroy(); + // 注册节点类型 + const registerNodeTypes = (nodeTypes: NodeTypeDTO[]) => { + // 注册特殊节点类型 + if (!registeredNodes.has(SPECIAL_NODE_TYPES.START)) { + Graph.registerNode(SPECIAL_NODE_TYPES.START, { + inherit: 'circle', + width: 40, + height: 40, + attrs: { + body: { + fill: '#52c41a', + stroke: '#52c41a', + strokeWidth: 2, + }, + label: { + text: '开始', + fill: '#fff', + fontSize: 12, + fontFamily: 'Arial, helvetica, sans-serif', + }, + }, + ports: { + groups: { + right: { + position: { + name: 'right', + args: { + dx: 4, + }, + }, + attrs: { + circle: { + r: 4, + magnet: true, + stroke: '#52c41a', + strokeWidth: 2, + fill: '#fff', + }, + }, + }, + }, + items: [ + { + id: 'right', + group: 'right', + }, + ], + }, + }); + registeredNodes.add(SPECIAL_NODE_TYPES.START); + } + + if (!registeredNodes.has(SPECIAL_NODE_TYPES.END)) { + Graph.registerNode(SPECIAL_NODE_TYPES.END, { + inherit: 'circle', + width: 40, + height: 40, + attrs: { + body: { + fill: '#ff4d4f', + stroke: '#ff4d4f', + strokeWidth: 2, + }, + label: { + text: '结束', + fill: '#fff', + fontSize: 12, + fontFamily: 'Arial, helvetica, sans-serif', + }, + }, + ports: { + groups: { + left: { + position: { + name: 'left', + args: { + dx: -4, + }, + }, + attrs: { + circle: { + r: 4, + magnet: true, + stroke: '#ff4d4f', + strokeWidth: 2, + fill: '#fff', + }, + }, + }, + }, + items: [ + { + id: 'left', + group: 'left', + }, + ], + }, + }); + registeredNodes.add(SPECIAL_NODE_TYPES.END); + } + + // 注册其他节点类型 + nodeTypes.forEach(nodeType => { + if (registeredNodes.has(nodeType.code)) return; + + let shape: string; + let width = 120; + let height = 60; + let attrs: any = { + body: { + fill: nodeType.color || '#fff', + stroke: '#1890ff', + strokeWidth: 2, + }, + label: { + text: nodeType.name, + fill: '#000', + fontSize: 12, + fontFamily: 'Arial, helvetica, sans-serif', + textWrap: { + width: -10, + height: -10, + ellipsis: true, + }, + }, + }; + + switch (nodeType.category) { + case 'BASIC': + shape = 'circle'; + width = 60; + height = 60; + break; + case 'TASK': + shape = 'rect'; + attrs.body.rx = 4; + attrs.body.ry = 4; + break; + case 'GATEWAY': + shape = 'polygon'; + attrs.body.refPoints = '0,10 10,0 20,10 10,20'; + width = 80; + height = 80; + break; + case 'EVENT': + shape = 'circle'; + width = 60; + height = 60; + break; + default: + shape = 'rect'; } + + // 注册节点 + Graph.registerNode(nodeType.code, { + inherit: shape, + width, + height, + attrs, + ports: { + groups: { + left: { + position: { + name: 'left', + args: { + dx: -4, + }, + }, + attrs: { + circle: { + r: 4, + magnet: true, + stroke: '#1890ff', + strokeWidth: 2, + fill: '#fff', + }, + }, + label: { + position: 'left', + }, + }, + right: { + position: { + name: 'right', + args: { + dx: 4, + }, + }, + attrs: { + circle: { + r: 4, + magnet: true, + stroke: '#1890ff', + strokeWidth: 2, + fill: '#fff', + }, + }, + label: { + position: 'right', + }, + }, + }, + items: [ + { + id: 'left', + group: 'left', + }, + { + id: 'right', + group: 'right', + }, + ], + }, + markup: [ + { + tagName: 'rect', + selector: 'body', + }, + { + tagName: 'text', + selector: 'label', + }, + ], + }); + + registeredNodes.add(nodeType.code); + }); + }; + + // 加载节点类型 + useEffect(() => { + const loadNodeTypes = async () => { + try { + const response = await getNodeTypes(); + setNodeTypes(response); + } catch (error) { + console.error('加载节点类型失败:', error); + } + }; + loadNodeTypes(); + + return () => { + registeredNodes.clear(); }; }, []); - // 处理节点配置更新 - const handleNodeConfigSave = (config: any) => { - if (lf && selectedNode) { - const nodeModel = lf.getNodeModelById(selectedNode.id); - nodeModel.updateText(config.text.value); - nodeModel.setProperties(config); - setSelectedNode(null); - - // 触发 onChange - const graphData = lf.getGraphData(); - onChange?.(JSON.stringify(graphData)); + // 初始化流程设计器 + useEffect(() => { + if (!containerRef.current || !nodeTypes.length) { + return; } - }; + + registerNodeTypes(nodeTypes); + + const graph = new Graph({ + container: containerRef.current, + width: 800, + height: 600, + grid: { + size: 10, + visible: true, + type: 'doubleMesh', + args: [ + { + color: '#eee', + thickness: 1, + }, + { + color: '#ddd', + thickness: 1, + factor: 4, + }, + ], + }, + connecting: { + router: { + name: 'manhattan', + args: { + padding: LAYOUT_CONFIG.PADDING, + startDirections: ['right'], + endDirections: ['left'], + }, + }, + connector: { + name: 'rounded', + args: { + radius: 8, + }, + }, + anchor: 'center', + connectionPoint: 'anchor', + allowBlank: false, + allowLoop: true, + allowNode: false, + allowEdge: false, + snap: { + radius: 20, + }, + createEdge() { + return new Shape.Edge({ + attrs: { + line: { + stroke: '#1890ff', + strokeWidth: 2, + targetMarker: { + name: 'block', + width: 12, + height: 8, + }, + }, + }, + zIndex: -1, + router: { + name: 'manhattan', + args: { + padding: LAYOUT_CONFIG.PADDING, + startDirections: ['right'], + endDirections: ['left'], + }, + }, + connector: { + name: 'rounded', + args: { + radius: 8, + }, + }, + }); + }, + validateConnection({ targetMagnet, targetView, sourceView, sourceMagnet }) { + if (!targetMagnet || !sourceMagnet) return false; + if (sourceMagnet.getAttribute('port-group') !== 'right' || + targetMagnet.getAttribute('port-group') !== 'left') return false; + if (targetView === sourceView && targetMagnet === sourceMagnet) return false; + return true; + }, + }, + highlighting: { + magnetAvailable: { + name: 'stroke', + args: { + padding: 4, + attrs: { + strokeWidth: 4, + stroke: '#1890ff', + }, + }, + }, + }, + mousewheel: { + enabled: true, + modifiers: ['ctrl', 'meta'], + factor: 1.1, + maxScale: 1.5, + minScale: 0.5, + }, + interacting: { + nodeMovable: (view) => { + const node = view.cell; + // 开始和结束节点不允许移动 + if (node.shape === SPECIAL_NODE_TYPES.START || node.shape === SPECIAL_NODE_TYPES.END) { + return false; + } + return !readOnly; + }, + edgeMovable: !readOnly, + edgeLabelMovable: !readOnly, + vertexMovable: !readOnly, + vertexAddable: !readOnly, + vertexDeletable: !readOnly, + magnetConnectable: !readOnly, + }, + scaling: { + min: 0.5, + max: 1.5, + }, + background: { + color: '#f5f5f5', + }, + preventDefaultBlankAction: true, + preventDefaultContextMenu: true, + clickThreshold: 10, + magnetThreshold: 10, + translating: { + restrict: true, + }, + }); + + if (value) { + try { + const graphData = JSON.parse(value); + + // 批量添加节点和边 + const model = { + nodes: graphData.nodes.map((node: any) => { + const isSpecialNode = node.type === 'START' || node.type === 'END'; + return { + id: node.id, + shape: isSpecialNode ? node.type : (NODE_TYPE_MAP[node.type] || node.type), + x: node.position?.x, + y: node.position?.y, + label: isSpecialNode ? (node.type === 'START' ? '开始' : '结束') : (node.data?.name || '未命名节点'), + data: { + nodeType: isSpecialNode ? node.type : (NODE_TYPE_MAP[node.type] || node.type), + ...node.data, + config: node.data?.config || {} + } + }; + }), + edges: graphData.edges.map((edge: any) => ({ + id: edge.id, + source: { + cell: edge.source, + port: 'right', + }, + target: { + cell: edge.target, + port: 'left', + }, + label: edge.data?.condition || '', + data: { + condition: edge.data?.condition || null + }, + router: { + name: 'manhattan', + args: { + padding: LAYOUT_CONFIG.PADDING, + startDirections: ['right'], + endDirections: ['left'], + }, + }, + connector: { + name: 'rounded', + args: { + radius: 8, + }, + }, + })) + }; + + graph.fromJSON(model); + + // 只在初始加载时应用布局 + layout(graph, true); + } catch (error) { + console.error('加载流程数据失败:', error); + } + } + + if (!readOnly) { + let updateTimer: number | null = null; + + const updateGraph = () => { + if (updateTimer) { + window.clearTimeout(updateTimer); + } + + updateTimer = window.setTimeout(() => { + const nodes = graph.getNodes().map(node => ({ + id: node.id, + type: node.data?.nodeType || node.shape, + position: node.position(), + data: { + name: node.attr('label/text'), + description: node.data?.description, + config: node.data?.config || {} + } + })); + + const edges = graph.getEdges().map(edge => ({ + id: edge.id, + source: edge.getSourceCellId(), + target: edge.getTargetCellId(), + type: 'default', + data: { + condition: edge.data?.condition || null + } + })); + + onChange?.(JSON.stringify({ nodes, edges })); + }, 300); + }; + + // 监听节点变化 + graph.on('node:moved', updateGraph); + graph.on('cell:changed', updateGraph); + graph.on('edge:connected', () => { + // 只在新增连线时应用布局,且仅当节点数量大于1时 + if (graph.getNodes().length > 1) { + layout(graph, false); + } + updateGraph(); + }); + } + + graphRef.current = graph; + + return () => { + graph.dispose(); + }; + }, [nodeTypes, value, onChange, readOnly]); return ( -
-
- {selectedNode && ( -
- setSelectedNode(null)} - /> -
- )} -
+
); }; diff --git a/frontend/src/pages/Workflow/Definition/Edit/components/FlowDesigner/nodes.ts b/frontend/src/pages/Workflow/Definition/Edit/components/FlowDesigner/nodes.ts deleted file mode 100644 index 4a2c5dcd..00000000 --- a/frontend/src/pages/Workflow/Definition/Edit/components/FlowDesigner/nodes.ts +++ /dev/null @@ -1,118 +0,0 @@ -import LogicFlow from '@logicflow/core'; -import { CircleNode, CircleNodeModel, RectNode, RectNodeModel, DiamondNode, DiamondNodeModel } from '@logicflow/core'; - -export function registerNodes(lf: LogicFlow) { - // 开始节点 - class StartNodeModel extends CircleNodeModel { - initNodeData(data: any) { - super.initNodeData(data); - this.r = 30; - } - - getNodeStyle() { - const style = super.getNodeStyle(); - return { - ...style, - fill: '#C6E5FF', - stroke: '#1890FF' - }; - } - - setAttributes() { - this.text.editable = false; - } - } - - class StartNodeView extends CircleNode { } - - // 任务节点 - class TaskNodeModel extends RectNodeModel { - initNodeData(data: any) { - super.initNodeData(data); - this.width = 120; - this.height = 60; - } - - getNodeStyle() { - const style = super.getNodeStyle(); - return { - ...style, - fill: '#FFF', - stroke: '#1890FF' - }; - } - } - - class TaskNodeView extends RectNode { } - - // 网关节点 - class GatewayNodeModel extends DiamondNodeModel { - initNodeData(data: any) { - super.initNodeData(data); - this.rx = 40; - this.ry = 40; - } - - getNodeStyle() { - const style = super.getNodeStyle(); - return { - ...style, - fill: '#FFF', - stroke: '#1890FF' - }; - } - } - - class GatewayNodeView extends DiamondNode { } - - // 结束节点 - class EndNodeModel extends CircleNodeModel { - initNodeData(data: any) { - super.initNodeData(data); - this.r = 30; - } - - getNodeStyle() { - const style = super.getNodeStyle(); - return { - ...style, - fill: '#FFE7E7', - stroke: '#FF4D4F' - }; - } - - setAttributes() { - this.text.editable = false; - } - } - - class EndNodeView extends CircleNode { } - - // 注册节点 - lf.register({ - type: 'start', - view: StartNodeView, - model: StartNodeModel - }); - - lf.register({ - type: 'task', - view: TaskNodeView, - model: TaskNodeModel - }); - - lf.register({ - type: 'gateway', - view: GatewayNodeView, - model: GatewayNodeModel - }); - - lf.register({ - type: 'end', - view: EndNodeView, - model: EndNodeModel - }); - - // 设置默认连线类型 - lf.setDefaultEdgeType('polyline'); -} \ No newline at end of file diff --git a/frontend/src/pages/Workflow/Definition/Edit/components/FormDesigner/index.tsx b/frontend/src/pages/Workflow/Definition/Edit/components/FormDesigner/index.tsx index 7f6824de..c348e55a 100644 --- a/frontend/src/pages/Workflow/Definition/Edit/components/FormDesigner/index.tsx +++ b/frontend/src/pages/Workflow/Definition/Edit/components/FormDesigner/index.tsx @@ -37,8 +37,8 @@ const FormDesigner: React.FC = ({ console.log('Form data:', formData); }; - const handleValidate = (errors: any) => { - console.log('Form errors:', errors); + const handleFinishFailed = (errors: any) => { + console.log('Form validation errors:', errors); }; return ( @@ -48,7 +48,7 @@ const FormDesigner: React.FC = ({ form={form} schema={schema} onFinish={handleSubmit} - onValidate={handleValidate} + onFinishFailed={handleFinishFailed} disabled={readOnly} /> diff --git a/frontend/src/pages/Workflow/Definition/Edit/components/NodeConfig/index.tsx b/frontend/src/pages/Workflow/Definition/Edit/components/NodeConfig/index.tsx index b834e0fc..c3266b8c 100644 --- a/frontend/src/pages/Workflow/Definition/Edit/components/NodeConfig/index.tsx +++ b/frontend/src/pages/Workflow/Definition/Edit/components/NodeConfig/index.tsx @@ -1,27 +1,39 @@ -import React from 'react'; -import { Form, Input, Button, Space } from 'antd'; -import type { BaseNodeModel } from '@logicflow/core'; - -type NodeType = 'start' | 'end' | 'task' | 'gateway'; +import React, { useEffect } from 'react'; +import { Form, Button, Space, Input } from 'antd'; +import type { FormInstance } from 'antd'; +// @ts-ignore +import FormRender from 'form-render'; +import type { NodeTypeDTO } from '@/pages/Workflow/types'; interface NodeConfigProps { node: { id: string; - type: NodeType; + type: string; text?: { value: string; x: number; y: number }; properties?: Record; }; + nodeType?: NodeTypeDTO; onSave: (config: any) => void; onCancel: () => void; } const NodeConfig: React.FC = ({ node, + nodeType, onSave, onCancel }) => { const [form] = Form.useForm(); + useEffect(() => { + if (node.properties) { + form.setFieldsValue({ + ...node.properties, + text: node.text?.value + }); + } + }, [node, form]); + const handleFinish = (values: any) => { onSave({ ...values, @@ -29,65 +41,89 @@ const NodeConfig: React.FC = ({ }); }; + const handleFinishFailed = (errors: any) => { + console.log('Form validation errors:', errors); + }; + + // 如果没有节点类型信息,显示基础配置 + if (!nodeType) { + return ( +
+
+ + + + + + + + + + +
+
+ ); + } + + // 解析配置模式 + const configSchema = nodeType.configSchema ? JSON.parse(nodeType.configSchema) : {}; + const defaultConfig = nodeType.defaultConfig ? JSON.parse(nodeType.defaultConfig) : {}; + + // 合并节点类型的配置模式 + const schema = { + type: 'object', + properties: { + text: { + title: '节点名称', + type: 'string', + required: true + }, + ...configSchema.properties + }, + required: ['text', ...(configSchema.required || [])] + }; + return (
-
- - - - - {node.type === 'task' && ( - <> - - - - - - - - )} - - {node.type === 'gateway' && ( - - - - )} - - - - - - - -
+ onFinishFailed={handleFinishFailed} + defaultValue={{ + text: node.text?.value, + ...defaultConfig + }} + widgets={{ + // 这里可以添加自定义组件 + }} + watch={{ + // 这里可以添加表单联动逻辑 + }} + /> +
+ + + + +
); }; diff --git a/frontend/src/pages/Workflow/service.ts b/frontend/src/pages/Workflow/service.ts index 89047cee..2f5a90f0 100644 --- a/frontend/src/pages/Workflow/service.ts +++ b/frontend/src/pages/Workflow/service.ts @@ -9,13 +9,15 @@ import type { WorkflowInstanceQuery, NodeInstanceResponse, WorkflowLogResponse, - WorkflowDefinition + WorkflowDefinition, + NodeTypeDTO } from './types'; const DEFINITION_URL = '/api/v1/workflow-definitions'; const INSTANCE_URL = '/api/v1/workflow-instance'; const NODE_URL = '/api/v1/node-instance'; const LOG_URL = '/api/v1/workflow-logs'; +const NODE_TYPE_URL = '/api/v1/node-types'; // 工作流定义相关接口 export const getDefinitions = (params?: WorkflowDefinitionQuery) => @@ -94,4 +96,9 @@ export const saveDefinition = (data: WorkflowDefinition) => { return request.put(`/api/v1/workflow/definition/${data.id}`, data); } return request.post('/api/v1/workflow/definition', data); -}; \ No newline at end of file +}; + +// 获取节点类型列表 +export const getNodeTypes = () => + request.get(NODE_TYPE_URL); + \ No newline at end of file diff --git a/frontend/src/pages/Workflow/types.ts b/frontend/src/pages/Workflow/types.ts index fd529a62..eb199677 100644 --- a/frontend/src/pages/Workflow/types.ts +++ b/frontend/src/pages/Workflow/types.ts @@ -135,4 +135,27 @@ export interface WorkflowLogResponse extends BaseResponse { message: string; level: LogLevel; detail?: string; +} + +// 节点类型数据结构 +export interface NodeTypeDTO extends BaseResponse { + code: string; // 节点类型编码 + name: string; // 节点类型名称 + description: string; // 节点类型描述 + category: 'BASIC' | 'TASK' | 'GATEWAY' | 'EVENT'; // 节点类型分类 + icon: string; // 节点图标 + color: string; // 节点颜色 + executors?: Array<{ // 支持的执行器列表 + code: string; // 执行器编码 + name: string; // 执行器名称 + description: string; // 执行器描述 + configSchema: any; // 执行器配置模式 + }>; + configSchema?: any; // 节点配置模式 + defaultConfig?: any; // 默认配置 + enabled: boolean; // 是否启用 + deleted: boolean; // 是否删除 + version: number; // 版本号 + createBy: string; // 创建人 + updateBy: string; // 更新人 } \ No newline at end of file