增加三方接口管理

This commit is contained in:
戚辰先生 2024-12-04 23:10:06 +08:00
parent dde2ad27ae
commit f52f1bed8b
17 changed files with 1977 additions and 10 deletions

362
frontend/frontend-guide.md Normal file
View File

@ -0,0 +1,362 @@
# 工作流平台前端开发指南
## 一、项目概述
### 1.1 技术栈
- 框架Ant Design Pro
- 语言TypeScript
- HTTP请求Umi Request
- 流程设计器LogicFlow
- 表单设计器FormRender
- 图表库ECharts
### 1.2 开发环境
- Node.js >= 16
- yarn >= 1.22
- TypeScript >= 4.x
## 二、接口定义
### 2.1 工作流定义管理
```typescript
// 工作流定义相关接口
interface WorkflowDefinitionAPI {
// 基础CRUD接口
list: '/api/v1/workflow-definitions' // GET 查询列表
create: '/api/v1/workflow-definitions' // POST 创建
update: '/api/v1/workflow-definitions/{id}' // PUT 更新
delete: '/api/v1/workflow-definitions/{id}' // DELETE 删除
get: '/api/v1/workflow-definitions/{id}' // GET 获取详情
// 特殊操作接口
publish: '/api/v1/workflow-definitions/{id}/publish' // POST 发布
disable: '/api/v1/workflow-definitions/{id}/disable' // POST 禁用
enable: '/api/v1/workflow-definitions/{id}/enable' // POST 启用
}
// 工作流定义数据结构
interface WorkflowDefinitionDTO {
id: number
code: string // 工作流编码
name: string // 工作流名称
description: string // 描述
status: 'DRAFT' | 'PUBLISHED' | 'DISABLED' // 状态
version: number // 版本号
nodeConfig: string // 节点配置(JSON)
transitionConfig: string // 流转配置(JSON)
formDefinition: string // 表单定义
graphDefinition: string // 图形信息
enabled: boolean // 是否启用
remark: string // 备注
nodes: NodeDefinitionDTO[] // 节点定义列表
}
// 节点定义数据结构
interface NodeDefinitionDTO {
id: number
nodeId: string // 节点ID
name: string // 节点名称
type: 'START' | 'END' | 'TASK' | 'GATEWAY' | 'SUB_PROCESS' | 'SHELL' // 节点类型
config: string // 节点配置(JSON)
description: string // 节点描述
workflowDefinitionId: number // 工作流定义ID
orderNum: number // 排序号
}
```
### 2.2 工作流实例管理
```typescript
// 工作流实例相关接口
interface WorkflowInstanceAPI {
// 基础CRUD接口
list: '/api/v1/workflow-instance' // GET 查询列表
get: '/api/v1/workflow-instance/{id}' // GET 获取详情
// 实例操作接口
create: '/api/v1/workflow-instance' // POST 创建实例
start: '/api/v1/workflow-instance/{id}/start' // POST 启动
cancel: '/api/v1/workflow-instance/{id}/cancel' // POST 取消
pause: '/api/v1/workflow-instance/{id}/pause' // POST 暂停
resume: '/api/v1/workflow-instance/{id}/resume' // POST 恢复
}
// 工作流实例数据结构
interface WorkflowInstanceDTO {
id: number
definitionId: number // 工作流定义ID
businessKey: string // 业务标识
status: 'RUNNING' | 'COMPLETED' | 'FAILED' | 'CANCELLED' | 'PAUSED' // 状态
startTime: string // 开始时间
endTime: string // 结束时间
variables: string // 工作流变量(JSON)
error: string // 错误信息
}
```
### 2.3 节点实例管理
```typescript
// 节点实例相关接口
interface NodeInstanceAPI {
// 查询接口
list: '/api/v1/node-instance' // 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 查询指定状态的节点
// 节点操作
updateStatus: '/api/v1/node-instance/{id}/status' // PUT 更新状态
}
// 节点实例数据结构
interface NodeInstanceDTO {
id: number
workflowInstanceId: number // 工作流实例ID
nodeId: string // 节点ID
nodeType: 'START' | 'END' | 'TASK' | 'GATEWAY' | 'SUB_PROCESS' | 'SHELL' // 节点类型
name: string // 节点名称
status: 'PENDING' | 'RUNNING' | 'COMPLETED' | 'FAILED' | 'CANCELLED' | 'PAUSED' | 'SKIPPED' // 状态
startTime: string // 开始时间
endTime: string // 结束时间
config: string // 节点配置(JSON)
input: string // 输入参数(JSON)
output: string // 输出结果(JSON)
error: string // 错误信息
preNodeId: string // 前置节点ID
}
```
### 2.4 工作流日志管理
```typescript
// 日志相关接口
interface WorkflowLogAPI {
// 基础CRUD接口
list: '/api/v1/workflow-logs' // GET 查询列表
create: '/api/v1/workflow-logs' // POST 创建
// 特殊查询接口
getWorkflowLogs: '/api/v1/workflow-logs/workflow/{workflowInstanceId}' // GET 查询工作流日志
getNodeLogs: '/api/v1/workflow-logs/node/{workflowInstanceId}/{nodeId}' // GET 查询节点日志
record: '/api/v1/workflow-logs/record' // POST 记录日志
}
// 日志数据结构
interface WorkflowLogDTO {
id: number
workflowInstanceId: number // 工作流实例ID
nodeId: string // 节点ID
message: string // 日志内容
level: 'INFO' | 'WARN' | 'ERROR' // 日志级别
detail: string // 详细信息
createTime: string // 创建时间
}
```
## 三、页面功能
### 3.1 工作流定义管理(/workflow/definition)
#### 3.1.1 列表页(/workflow/definition/list)
- 功能点:
- 工作流定义列表展示
- 支持按名称、编码、状态搜索
- 支持创建、编辑、删除操作
- 支持发布、禁用操作
- 支持查看历史版本
- 列表字段:
- 工作流编码
- 工作流名称
- 版本号
- 状态
- 创建时间
- 更新时间
- 操作按钮
#### 3.1.2 编辑页(/workflow/definition/edit)
- 功能点:
1. 基本信息编辑
- 工作流编码
- 工作流名称
- 描述
- 备注
2. 流程设计器
- 节点拖拽
- 连线绘制
- 节点配置
- 流程校验
3. 表单设计器
- 表单字段配置
- 字段验证规则
- 表单预览
4. 节点配置面板
- 节点基本信息
- 节点类型配置
- 执行条件配置
- 表单关联配置
### 3.2 工作流实例管理(/workflow/instance)
#### 3.2.1 列表页(/workflow/instance/list)
- 功能点:
- 工作流实例列表展示
- 支持按工作流名称、状态、时间搜索
- 支持启动、暂停、恢复、取消操作
- 列表字段:
- 实例ID
- 工作流名称
- 业务标识
- 状态
- 开始时间
- 结束时间
- 操作按钮
#### 3.2.2 详情页(/workflow/instance/detail)
- 功能点:
1. 基本信息展示
- 实例ID
- 工作流名称
- 状态
- 时间信息
2. 流程图展示
- 显示当前节点
- 节点状态标识
- 流程进度展示
3. 变量信息
- 变量列表
- 变量历史值
4. 日志信息
- 操作日志
- 执行日志
- 错误日志
### 3.3 监控大盘(/workflow/monitor)
- 功能点:
1. 统计信息
- 总工作流数
- 运行中实例数
- 完成实例数
- 失败实例数
2. 状态分布
- 饼图展示各状态占比
- 支持时间范围筛选
3. 执行时长统计
- 平均执行时长
- 最长执行时长
- 执行时长分布
4. 异常情况统计
- 异常类型分布
- 异常趋势图
- 异常节点TOP5
## 四、开发规范
### 4.1 项目结构
```
src/
├── components/ # 公共组件
│ ├── FlowDesigner/ # 流程设计器组件
│ ├── FormDesigner/ # 表单设计器组件
│ └── NodeConfig/ # 节点配置组件
├── pages/ # 页面组件
│ └── workflow/ # 工作流相关页面
├── services/ # API服务
│ └── workflow/ # 工作流相关API
├── models/ # 数据模型
├── utils/ # 工具函数
├── constants/ # 常量定义
├── types/ # TypeScript类型
└── locales/ # 国际化资源
```
### 4.2 命名规范
- 文件命名:使用 PascalCase
- 组件文件:`FlowDesigner.tsx`
- 类型文件:`WorkflowTypes.ts`
- 工具文件:`FlowUtils.ts`
- 变量命名:使用 camelCase
- 普通变量:`flowInstance`
- 布尔值:`isRunning`、`hasError`
- 常量命名:使用 UPPER_CASE
- `MAX_NODE_COUNT`
- `DEFAULT_CONFIG`
### 4.3 组件开发规范
1. 使用函数组件和 Hooks
2. 使用 TypeScript 类型声明
3. 添加必要的注释
4. 实现错误处理
5. 添加加载状态
6. 做好性能优化
### 4.4 代码提交规范
- feat: 新功能
- fix: 修复bug
- docs: 文档更新
- style: 代码格式
- 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. 支持国际化

View File

@ -10,9 +10,12 @@
"dependencies": { "dependencies": {
"@ant-design/icons": "^5.2.6", "@ant-design/icons": "^5.2.6",
"@antv/x6": "^2.18.1", "@antv/x6": "^2.18.1",
"@logicflow/core": "^2.0.9",
"@logicflow/extension": "^2.0.13",
"@reduxjs/toolkit": "^2.0.1", "@reduxjs/toolkit": "^2.0.1",
"antd": "^5.22.2", "antd": "^5.22.2",
"axios": "^1.6.2", "axios": "^1.6.2",
"form-render": "^2.5.1",
"react": "^18.2.0", "react": "^18.2.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"react-redux": "^9.0.4", "react-redux": "^9.0.4",
@ -135,6 +138,11 @@
"react": ">=16.9.0" "react": ">=16.9.0"
} }
}, },
"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/x6": { "node_modules/@antv/x6": {
"version": "2.18.1", "version": "2.18.1",
"resolved": "https://registry.npmmirror.com/@antv/x6/-/x6-2.18.1.tgz", "resolved": "https://registry.npmmirror.com/@antv/x6/-/x6-2.18.1.tgz",
@ -1032,6 +1040,40 @@
"@jridgewell/sourcemap-codec": "^1.4.14" "@jridgewell/sourcemap-codec": "^1.4.14"
} }
}, },
"node_modules/@logicflow/core": {
"version": "2.0.9",
"resolved": "https://registry.npmmirror.com/@logicflow/core/-/core-2.0.9.tgz",
"integrity": "sha512-HRc3W+XbcXbq9E3wElFNkaxNBja7Ga+FK6LYbsoOGWDWzZ7gSBCaYTpKPUlQsfUK0EvztqzCuer2DRJQQ77Ylg==",
"dependencies": {
"classnames": "^2.3.2",
"lodash-es": "^4.17.21",
"mobx": "^5.15.7",
"mobx-preact": "^3.0.0",
"mobx-utils": "^5.6.1",
"mousetrap": "^1.6.5",
"preact": "^10.17.1",
"uuid": "^9.0.0"
}
},
"node_modules/@logicflow/extension": {
"version": "2.0.13",
"resolved": "https://registry.npmmirror.com/@logicflow/extension/-/extension-2.0.13.tgz",
"integrity": "sha512-1csZP2RYyGItvOMxVSpzrP7MguPrMRQYV+PUbm+8jZLrFbt3LcOQCNdHsEFU0cfuaiBF6Nx4qBdBeo0kog0eCw==",
"dependencies": {
"@antv/hierarchy": "^0.6.11",
"@logicflow/core": "2.0.9",
"classnames": "^2.3.2",
"lodash-es": "^4.17.21",
"medium-editor": "^5.23.3",
"mobx": "^5.15.7",
"preact": "^10.17.1",
"rangy": "^1.3.1",
"vanilla-picker": "^2.12.3"
},
"peerDependencies": {
"@logicflow/core": "2.0.9"
}
},
"node_modules/@nodelib/fs.scandir": { "node_modules/@nodelib/fs.scandir": {
"version": "2.1.5", "version": "2.1.5",
"resolved": "https://registry.npmmirror.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", "resolved": "https://registry.npmmirror.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
@ -1472,6 +1514,11 @@
"win32" "win32"
] ]
}, },
"node_modules/@sphinxxxx/color-conversion": {
"version": "2.2.2",
"resolved": "https://registry.npmmirror.com/@sphinxxxx/color-conversion/-/color-conversion-2.2.2.tgz",
"integrity": "sha512-XExJS3cLqgrmNBIP3bBw6+1oQ1ksGjFh0+oClDKFYpCCqx/hlqwWO5KO/S63fzUo67SxI9dMrF0y5T/Ey7h8Zw=="
},
"node_modules/@types/babel__core": { "node_modules/@types/babel__core": {
"version": "7.20.5", "version": "7.20.5",
"resolved": "https://registry.npmmirror.com/@types/babel__core/-/babel__core-7.20.5.tgz", "resolved": "https://registry.npmmirror.com/@types/babel__core/-/babel__core-7.20.5.tgz",
@ -1806,6 +1853,36 @@
"acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
} }
}, },
"node_modules/add-dom-event-listener": {
"version": "1.1.0",
"resolved": "https://registry.npmmirror.com/add-dom-event-listener/-/add-dom-event-listener-1.1.0.tgz",
"integrity": "sha512-WCxx1ixHT0GQU9hb0KI/mhgRQhnU+U3GvwY6ZvVjYq8rsihIGoaIOUbY0yMPBxLH5MDtr0kz3fisWGNcbWW7Jw==",
"dependencies": {
"object-assign": "4.x"
}
},
"node_modules/ahooks": {
"version": "3.8.1",
"resolved": "https://registry.npmmirror.com/ahooks/-/ahooks-3.8.1.tgz",
"integrity": "sha512-JoP9+/RWO7MnI/uSKdvQ8WB10Y3oo1PjLv+4Sv4Vpm19Z86VUMdXh+RhWvMGxZZs06sq2p0xVtFk8Oh5ZObsoA==",
"dependencies": {
"@babel/runtime": "^7.21.0",
"dayjs": "^1.9.1",
"intersection-observer": "^0.12.0",
"js-cookie": "^3.0.5",
"lodash": "^4.17.21",
"react-fast-compare": "^3.2.2",
"resize-observer-polyfill": "^1.5.1",
"screenfull": "^5.0.0",
"tslib": "^2.4.1"
},
"engines": {
"node": ">=8.0.0"
},
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
}
},
"node_modules/ajv": { "node_modules/ajv": {
"version": "6.12.6", "version": "6.12.6",
"resolved": "https://registry.npmmirror.com/ajv/-/ajv-6.12.6.tgz", "resolved": "https://registry.npmmirror.com/ajv/-/ajv-6.12.6.tgz",
@ -1925,6 +2002,11 @@
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/async-validator": {
"version": "3.5.2",
"resolved": "https://registry.npmmirror.com/async-validator/-/async-validator-3.5.2.tgz",
"integrity": "sha512-8eLCg00W9pIRZSB781UUX/H6Oskmm8xloZfr09lz5bikRpBVDlJ3hRVuxxP1SxcwsEYfJ4IU8Q19Y8/893r3rQ=="
},
"node_modules/asynckit": { "node_modules/asynckit": {
"version": "0.4.0", "version": "0.4.0",
"resolved": "https://registry.npmmirror.com/asynckit/-/asynckit-0.4.0.tgz", "resolved": "https://registry.npmmirror.com/asynckit/-/asynckit-0.4.0.tgz",
@ -1940,6 +2022,20 @@
"proxy-from-env": "^1.1.0" "proxy-from-env": "^1.1.0"
} }
}, },
"node_modules/babel-runtime": {
"version": "6.26.0",
"resolved": "https://registry.npmmirror.com/babel-runtime/-/babel-runtime-6.26.0.tgz",
"integrity": "sha512-ITKNuq2wKlW1fJg9sSW52eepoYgZBggvOAHC0u/CYu/qxQ9EVzThCgR69BnSXLHjy2f7SY5zaQ4yt7H9ZVxY2g==",
"dependencies": {
"core-js": "^2.4.0",
"regenerator-runtime": "^0.11.0"
}
},
"node_modules/babel-runtime/node_modules/regenerator-runtime": {
"version": "0.11.1",
"resolved": "https://registry.npmmirror.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz",
"integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg=="
},
"node_modules/balanced-match": { "node_modules/balanced-match": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz", "resolved": "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz",
@ -2049,6 +2145,15 @@
"resolved": "https://registry.npmmirror.com/classnames/-/classnames-2.5.1.tgz", "resolved": "https://registry.npmmirror.com/classnames/-/classnames-2.5.1.tgz",
"integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==" "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow=="
}, },
"node_modules/color": {
"version": "3.2.1",
"resolved": "https://registry.npmmirror.com/color/-/color-3.2.1.tgz",
"integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==",
"dependencies": {
"color-convert": "^1.9.3",
"color-string": "^1.6.0"
}
},
"node_modules/color-convert": { "node_modules/color-convert": {
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmmirror.com/color-convert/-/color-convert-2.0.1.tgz", "resolved": "https://registry.npmmirror.com/color-convert/-/color-convert-2.0.1.tgz",
@ -2064,8 +2169,29 @@
"node_modules/color-name": { "node_modules/color-name": {
"version": "1.1.4", "version": "1.1.4",
"resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz", "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
"dev": true },
"node_modules/color-string": {
"version": "1.9.1",
"resolved": "https://registry.npmmirror.com/color-string/-/color-string-1.9.1.tgz",
"integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==",
"dependencies": {
"color-name": "^1.0.0",
"simple-swizzle": "^0.2.2"
}
},
"node_modules/color/node_modules/color-convert": {
"version": "1.9.3",
"resolved": "https://registry.npmmirror.com/color-convert/-/color-convert-1.9.3.tgz",
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
"dependencies": {
"color-name": "1.1.3"
}
},
"node_modules/color/node_modules/color-name": {
"version": "1.1.3",
"resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.3.tgz",
"integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="
}, },
"node_modules/combined-stream": { "node_modules/combined-stream": {
"version": "1.0.8", "version": "1.0.8",
@ -2078,6 +2204,19 @@
"node": ">= 0.8" "node": ">= 0.8"
} }
}, },
"node_modules/component-classes": {
"version": "1.2.6",
"resolved": "https://registry.npmmirror.com/component-classes/-/component-classes-1.2.6.tgz",
"integrity": "sha512-hPFGULxdwugu1QWW3SvVOCUHLzO34+a2J6Wqy0c5ASQkfi9/8nZcBB0ZohaEbXOQlCflMAEMmEWk7u7BVs4koA==",
"dependencies": {
"component-indexof": "0.0.3"
}
},
"node_modules/component-indexof": {
"version": "0.0.3",
"resolved": "https://registry.npmmirror.com/component-indexof/-/component-indexof-0.0.3.tgz",
"integrity": "sha512-puDQKvx/64HZXb4hBwIcvQLaLgux8o1CbWl39s41hrIIZDl1lJiD5jc22gj3RBeGK0ovxALDYpIbyjqDUUl0rw=="
},
"node_modules/compute-scroll-into-view": { "node_modules/compute-scroll-into-view": {
"version": "3.1.0", "version": "3.1.0",
"resolved": "https://registry.npmmirror.com/compute-scroll-into-view/-/compute-scroll-into-view-3.1.0.tgz", "resolved": "https://registry.npmmirror.com/compute-scroll-into-view/-/compute-scroll-into-view-3.1.0.tgz",
@ -2103,6 +2242,22 @@
"toggle-selection": "^1.0.6" "toggle-selection": "^1.0.6"
} }
}, },
"node_modules/core-js": {
"version": "2.6.12",
"resolved": "https://registry.npmmirror.com/core-js/-/core-js-2.6.12.tgz",
"integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==",
"deprecated": "core-js@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js.",
"hasInstallScript": true
},
"node_modules/create-react-class": {
"version": "15.7.0",
"resolved": "https://registry.npmmirror.com/create-react-class/-/create-react-class-15.7.0.tgz",
"integrity": "sha512-QZv4sFWG9S5RUvkTYWbflxeZX+JG7Cz0Tn33rQBJ+WFQTqTfUTjMjiv9tnfXazjsO5r0KhPs+AqCjyrQX6h2ng==",
"dependencies": {
"loose-envify": "^1.3.1",
"object-assign": "^4.1.1"
}
},
"node_modules/cross-spawn": { "node_modules/cross-spawn": {
"version": "7.0.6", "version": "7.0.6",
"resolved": "https://registry.npmmirror.com/cross-spawn/-/cross-spawn-7.0.6.tgz", "resolved": "https://registry.npmmirror.com/cross-spawn/-/cross-spawn-7.0.6.tgz",
@ -2117,6 +2272,15 @@
"node": ">= 8" "node": ">= 8"
} }
}, },
"node_modules/css-animation": {
"version": "1.6.1",
"resolved": "https://registry.npmmirror.com/css-animation/-/css-animation-1.6.1.tgz",
"integrity": "sha512-/48+/BaEaHRY6kNQ2OIPzKf9A6g8WjZYjhiNDNuIVbsm5tXCGIAsHDjB4Xu1C4vXJtUWZo26O68OQkDpNBaPog==",
"dependencies": {
"babel-runtime": "6.x",
"component-classes": "^1.2.5"
}
},
"node_modules/csstype": { "node_modules/csstype": {
"version": "3.1.3", "version": "3.1.3",
"resolved": "https://registry.npmmirror.com/csstype/-/csstype-3.1.3.tgz", "resolved": "https://registry.npmmirror.com/csstype/-/csstype-3.1.3.tgz",
@ -2182,6 +2346,11 @@
"node": ">=6.0.0" "node": ">=6.0.0"
} }
}, },
"node_modules/dom-align": {
"version": "1.12.4",
"resolved": "https://registry.npmmirror.com/dom-align/-/dom-align-1.12.4.tgz",
"integrity": "sha512-R8LUSEay/68zE5c8/3BDxiTEvgb4xZTF0RKmAHfiEVN3klfIpXfi2/QCoiWPccVQ0J/ZGdz9OjzL4uJEP/MRAw=="
},
"node_modules/electron-to-chromium": { "node_modules/electron-to-chromium": {
"version": "1.5.64", "version": "1.5.64",
"resolved": "https://registry.npmmirror.com/electron-to-chromium/-/electron-to-chromium-1.5.64.tgz", "resolved": "https://registry.npmmirror.com/electron-to-chromium/-/electron-to-chromium-1.5.64.tgz",
@ -2595,6 +2764,89 @@
"node": ">= 6" "node": ">= 6"
} }
}, },
"node_modules/form-render": {
"version": "2.5.1",
"resolved": "https://registry.npmmirror.com/form-render/-/form-render-2.5.1.tgz",
"integrity": "sha512-oNbJ+McqB5h1yuyxYAT3ixJF8itmHlnKvqDgQhJT9Tw1c3yGwfRnVXboRxBV+Myz0dkf47zL6lyY1l74yQsWsg==",
"dependencies": {
"@ant-design/icons": "^4.0.2",
"ahooks": "^3.7.5",
"async-validator": "^3.5.1",
"classnames": "^2.3.1",
"color": "^3.1.2",
"dayjs": "^1.11.7",
"lodash-es": "^4.17.21",
"rc-color-picker": "^1.2.6",
"virtualizedtableforantd4": "^1.1.2",
"zustand": "^4.1.5"
},
"peerDependencies": {
"antd": "4.x || 5.x",
"react": ">=16.9.0",
"react-dom": ">=16.9.0"
}
},
"node_modules/form-render/node_modules/@ant-design/colors": {
"version": "6.0.0",
"resolved": "https://registry.npmmirror.com/@ant-design/colors/-/colors-6.0.0.tgz",
"integrity": "sha512-qAZRvPzfdWHtfameEGP2Qvuf838NhergR35o+EuVyB5XvSA98xod5r4utvi4TJ3ywmevm290g9nsCG5MryrdWQ==",
"dependencies": {
"@ctrl/tinycolor": "^3.4.0"
}
},
"node_modules/form-render/node_modules/@ant-design/icons": {
"version": "4.8.3",
"resolved": "https://registry.npmmirror.com/@ant-design/icons/-/icons-4.8.3.tgz",
"integrity": "sha512-HGlIQZzrEbAhpJR6+IGdzfbPym94Owr6JZkJ2QCCnOkPVIWMO2xgIVcOKnl8YcpijIo39V7l2qQL5fmtw56cMw==",
"dependencies": {
"@ant-design/colors": "^6.0.0",
"@ant-design/icons-svg": "^4.3.0",
"@babel/runtime": "^7.11.2",
"classnames": "^2.2.6",
"lodash": "^4.17.15",
"rc-util": "^5.9.4"
},
"engines": {
"node": ">=8"
},
"peerDependencies": {
"react": ">=16.0.0",
"react-dom": ">=16.0.0"
}
},
"node_modules/form-render/node_modules/rc-color-picker": {
"version": "1.2.6",
"resolved": "https://registry.npmmirror.com/rc-color-picker/-/rc-color-picker-1.2.6.tgz",
"integrity": "sha512-AaC9Pg7qCHSy5M4eVbqDIaNb2FC4SEw82GOHB2C4R/+vF2FVa/r5XA+Igg5+zLPmAvBLhz9tL4MAfkRA8yWNJw==",
"dependencies": {
"classnames": "^2.2.5",
"prop-types": "^15.5.8",
"rc-trigger": "1.x",
"rc-util": "^4.0.2",
"tinycolor2": "^1.4.1"
},
"peerDependencies": {
"react": "16.x",
"react-dom": "16.x"
}
},
"node_modules/form-render/node_modules/rc-color-picker/node_modules/rc-util": {
"version": "4.21.1",
"resolved": "https://registry.npmmirror.com/rc-util/-/rc-util-4.21.1.tgz",
"integrity": "sha512-Z+vlkSQVc1l8O2UjR3WQ+XdWlhj5q9BMQNLk2iOBch75CqPfrJyGtcWMcnhRlNuDu0Ndtt4kLVO8JI8BrABobg==",
"dependencies": {
"add-dom-event-listener": "^1.1.0",
"prop-types": "^15.5.10",
"react-is": "^16.12.0",
"react-lifecycles-compat": "^3.0.4",
"shallowequal": "^1.1.0"
}
},
"node_modules/form-render/node_modules/react-is": {
"version": "16.13.1",
"resolved": "https://registry.npmmirror.com/react-is/-/react-is-16.13.1.tgz",
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
},
"node_modules/fs.realpath": { "node_modules/fs.realpath": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmmirror.com/fs.realpath/-/fs.realpath-1.0.0.tgz", "resolved": "https://registry.npmmirror.com/fs.realpath/-/fs.realpath-1.0.0.tgz",
@ -2723,6 +2975,11 @@
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/hoist-non-react-statics": {
"version": "2.5.5",
"resolved": "https://registry.npmmirror.com/hoist-non-react-statics/-/hoist-non-react-statics-2.5.5.tgz",
"integrity": "sha512-rqcy4pJo55FTTLWt+bU8ukscqHeE/e9KWvsOW2b/a3afxQZhwkQdT1rPPCJ0rYXdj4vNcasY8zHTH+jF/qStxw=="
},
"node_modules/ignore": { "node_modules/ignore": {
"version": "5.3.2", "version": "5.3.2",
"resolved": "https://registry.npmmirror.com/ignore/-/ignore-5.3.2.tgz", "resolved": "https://registry.npmmirror.com/ignore/-/ignore-5.3.2.tgz",
@ -2783,6 +3040,16 @@
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
"dev": true "dev": true
}, },
"node_modules/intersection-observer": {
"version": "0.12.2",
"resolved": "https://registry.npmmirror.com/intersection-observer/-/intersection-observer-0.12.2.tgz",
"integrity": "sha512-7m1vEcPCxXYI8HqnL8CKI6siDyD+eIWSwgB3DZA+ZTogxk9I4CDnj4wilt9x/+/QbHI4YG5YZNmC6458/e9Ktg=="
},
"node_modules/is-arrayish": {
"version": "0.3.2",
"resolved": "https://registry.npmmirror.com/is-arrayish/-/is-arrayish-0.3.2.tgz",
"integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ=="
},
"node_modules/is-extglob": { "node_modules/is-extglob": {
"version": "2.1.1", "version": "2.1.1",
"resolved": "https://registry.npmmirror.com/is-extglob/-/is-extglob-2.1.1.tgz", "resolved": "https://registry.npmmirror.com/is-extglob/-/is-extglob-2.1.1.tgz",
@ -2828,6 +3095,14 @@
"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
"dev": true "dev": true
}, },
"node_modules/js-cookie": {
"version": "3.0.5",
"resolved": "https://registry.npmmirror.com/js-cookie/-/js-cookie-3.0.5.tgz",
"integrity": "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==",
"engines": {
"node": ">=14"
}
},
"node_modules/js-tokens": { "node_modules/js-tokens": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmmirror.com/js-tokens/-/js-tokens-4.0.0.tgz", "resolved": "https://registry.npmmirror.com/js-tokens/-/js-tokens-4.0.0.tgz",
@ -2932,6 +3207,11 @@
"url": "https://github.com/sponsors/sindresorhus" "url": "https://github.com/sponsors/sindresorhus"
} }
}, },
"node_modules/lodash": {
"version": "4.17.21",
"resolved": "https://registry.npmmirror.com/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
},
"node_modules/lodash-es": { "node_modules/lodash-es": {
"version": "4.17.21", "version": "4.17.21",
"resolved": "https://registry.npmmirror.com/lodash-es/-/lodash-es-4.17.21.tgz", "resolved": "https://registry.npmmirror.com/lodash-es/-/lodash-es-4.17.21.tgz",
@ -2964,6 +3244,11 @@
"yallist": "^3.0.2" "yallist": "^3.0.2"
} }
}, },
"node_modules/medium-editor": {
"version": "5.23.3",
"resolved": "https://registry.npmmirror.com/medium-editor/-/medium-editor-5.23.3.tgz",
"integrity": "sha512-he9/TdjX8f8MGdXGfCs8AllrYnqXJJvjNkDKmPg3aPW/uoIrlRqtkFthrwvmd+u4QyzEiadhCCM0EwTiRdUCJw=="
},
"node_modules/merge2": { "node_modules/merge2": {
"version": "1.4.1", "version": "1.4.1",
"resolved": "https://registry.npmmirror.com/merge2/-/merge2-1.4.1.tgz", "resolved": "https://registry.npmmirror.com/merge2/-/merge2-1.4.1.tgz",
@ -3020,6 +3305,40 @@
"url": "https://github.com/sponsors/isaacs" "url": "https://github.com/sponsors/isaacs"
} }
}, },
"node_modules/mobx": {
"version": "5.15.7",
"resolved": "https://registry.npmmirror.com/mobx/-/mobx-5.15.7.tgz",
"integrity": "sha512-wyM3FghTkhmC+hQjyPGGFdpehrcX1KOXsDuERhfK2YbJemkUhEB+6wzEN639T21onxlfYBmriA1PFnvxTUhcKw==",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/mobx"
}
},
"node_modules/mobx-preact": {
"version": "3.0.0",
"resolved": "https://registry.npmmirror.com/mobx-preact/-/mobx-preact-3.0.0.tgz",
"integrity": "sha512-ijan/cBs3WmRye87E5+3JmoFBB00KDAwNA3pm7bMwYLPHBAXlN86aC3gdrXw8aKzM5RI8V3a993PphzPv6P4FA==",
"dependencies": {
"hoist-non-react-statics": "^2.3.1"
},
"peerDependencies": {
"mobx": "5.x",
"preact": ">=8"
}
},
"node_modules/mobx-utils": {
"version": "5.6.2",
"resolved": "https://registry.npmmirror.com/mobx-utils/-/mobx-utils-5.6.2.tgz",
"integrity": "sha512-a/WlXyGkp6F12b01sTarENpxbmlRgPHFyR1Xv2bsSjQBm5dcOtd16ONb40/vOqck8L99NHpI+C9MXQ+SZ8f+yw==",
"peerDependencies": {
"mobx": "^4.13.1 || ^5.13.1"
}
},
"node_modules/mousetrap": {
"version": "1.6.5",
"resolved": "https://registry.npmmirror.com/mousetrap/-/mousetrap-1.6.5.tgz",
"integrity": "sha512-QNo4kEepaIBwiT8CDhP98umTetp+JNfQYBWvC1pc6/OAibuXtRcxZ58Qz8skvEHYvURne/7R8T5VoOI7rDsEUA=="
},
"node_modules/ms": { "node_modules/ms": {
"version": "2.1.3", "version": "2.1.3",
"resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz", "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz",
@ -3056,6 +3375,14 @@
"integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==", "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==",
"dev": true "dev": true
}, },
"node_modules/object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmmirror.com/object-assign/-/object-assign-4.1.1.tgz",
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/once": { "node_modules/once": {
"version": "1.4.0", "version": "1.4.0",
"resolved": "https://registry.npmmirror.com/once/-/once-1.4.0.tgz", "resolved": "https://registry.npmmirror.com/once/-/once-1.4.0.tgz",
@ -3160,6 +3487,11 @@
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/performance-now": {
"version": "2.1.0",
"resolved": "https://registry.npmmirror.com/performance-now/-/performance-now-2.1.0.tgz",
"integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow=="
},
"node_modules/picocolors": { "node_modules/picocolors": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-1.1.1.tgz", "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-1.1.1.tgz",
@ -3206,6 +3538,15 @@
"node": "^10 || ^12 || >=14" "node": "^10 || ^12 || >=14"
} }
}, },
"node_modules/preact": {
"version": "10.25.1",
"resolved": "https://registry.npmmirror.com/preact/-/preact-10.25.1.tgz",
"integrity": "sha512-frxeZV2vhQSohQwJ7FvlqC40ze89+8friponWUFeVEkaCfhC6Eu4V0iND5C9CXz8JLndV07QRDeXzH1+Anz5Og==",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/preact"
}
},
"node_modules/prelude-ls": { "node_modules/prelude-ls": {
"version": "1.2.1", "version": "1.2.1",
"resolved": "https://registry.npmmirror.com/prelude-ls/-/prelude-ls-1.2.1.tgz", "resolved": "https://registry.npmmirror.com/prelude-ls/-/prelude-ls-1.2.1.tgz",
@ -3215,6 +3556,21 @@
"node": ">= 0.8.0" "node": ">= 0.8.0"
} }
}, },
"node_modules/prop-types": {
"version": "15.8.1",
"resolved": "https://registry.npmmirror.com/prop-types/-/prop-types-15.8.1.tgz",
"integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
"dependencies": {
"loose-envify": "^1.4.0",
"object-assign": "^4.1.1",
"react-is": "^16.13.1"
}
},
"node_modules/prop-types/node_modules/react-is": {
"version": "16.13.1",
"resolved": "https://registry.npmmirror.com/react-is/-/react-is-16.13.1.tgz",
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
},
"node_modules/proxy-from-env": { "node_modules/proxy-from-env": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmmirror.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz", "resolved": "https://registry.npmmirror.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
@ -3249,6 +3605,78 @@
} }
] ]
}, },
"node_modules/raf": {
"version": "3.4.1",
"resolved": "https://registry.npmmirror.com/raf/-/raf-3.4.1.tgz",
"integrity": "sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==",
"dependencies": {
"performance-now": "^2.1.0"
}
},
"node_modules/rangy": {
"version": "1.3.2",
"resolved": "https://registry.npmmirror.com/rangy/-/rangy-1.3.2.tgz",
"integrity": "sha512-fS1C4MOyk8T+ZJZdLcgrukPWxkyDXa+Hd2Kj+Zg4wIK71yrWgmjzHubzPMY1G+WD9EgGxMp3fIL0zQ1ickmSWA=="
},
"node_modules/rc-align": {
"version": "2.4.5",
"resolved": "https://registry.npmmirror.com/rc-align/-/rc-align-2.4.5.tgz",
"integrity": "sha512-nv9wYUYdfyfK+qskThf4BQUSIadeI/dCsfaMZfNEoxm9HwOIioQ+LyqmMK6jWHAZQgOzMLaqawhuBXlF63vgjw==",
"dependencies": {
"babel-runtime": "^6.26.0",
"dom-align": "^1.7.0",
"prop-types": "^15.5.8",
"rc-util": "^4.0.4"
}
},
"node_modules/rc-align/node_modules/rc-util": {
"version": "4.21.1",
"resolved": "https://registry.npmmirror.com/rc-util/-/rc-util-4.21.1.tgz",
"integrity": "sha512-Z+vlkSQVc1l8O2UjR3WQ+XdWlhj5q9BMQNLk2iOBch75CqPfrJyGtcWMcnhRlNuDu0Ndtt4kLVO8JI8BrABobg==",
"dependencies": {
"add-dom-event-listener": "^1.1.0",
"prop-types": "^15.5.10",
"react-is": "^16.12.0",
"react-lifecycles-compat": "^3.0.4",
"shallowequal": "^1.1.0"
}
},
"node_modules/rc-align/node_modules/react-is": {
"version": "16.13.1",
"resolved": "https://registry.npmmirror.com/react-is/-/react-is-16.13.1.tgz",
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
},
"node_modules/rc-animate": {
"version": "2.11.1",
"resolved": "https://registry.npmmirror.com/rc-animate/-/rc-animate-2.11.1.tgz",
"integrity": "sha512-1NyuCGFJG/0Y+9RKh5y/i/AalUCA51opyyS/jO2seELpgymZm2u9QV3xwODwEuzkmeQ1BDPxMLmYLcTJedPlkQ==",
"dependencies": {
"babel-runtime": "6.x",
"classnames": "^2.2.6",
"css-animation": "^1.3.2",
"prop-types": "15.x",
"raf": "^3.4.0",
"rc-util": "^4.15.3",
"react-lifecycles-compat": "^3.0.4"
}
},
"node_modules/rc-animate/node_modules/rc-util": {
"version": "4.21.1",
"resolved": "https://registry.npmmirror.com/rc-util/-/rc-util-4.21.1.tgz",
"integrity": "sha512-Z+vlkSQVc1l8O2UjR3WQ+XdWlhj5q9BMQNLk2iOBch75CqPfrJyGtcWMcnhRlNuDu0Ndtt4kLVO8JI8BrABobg==",
"dependencies": {
"add-dom-event-listener": "^1.1.0",
"prop-types": "^15.5.10",
"react-is": "^16.12.0",
"react-lifecycles-compat": "^3.0.4",
"shallowequal": "^1.1.0"
}
},
"node_modules/rc-animate/node_modules/react-is": {
"version": "16.13.1",
"resolved": "https://registry.npmmirror.com/react-is/-/react-is-16.13.1.tgz",
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
},
"node_modules/rc-cascader": { "node_modules/rc-cascader": {
"version": "3.30.0", "version": "3.30.0",
"resolved": "https://registry.npmmirror.com/rc-cascader/-/rc-cascader-3.30.0.tgz", "resolved": "https://registry.npmmirror.com/rc-cascader/-/rc-cascader-3.30.0.tgz",
@ -3775,6 +4203,36 @@
"react-dom": "*" "react-dom": "*"
} }
}, },
"node_modules/rc-trigger": {
"version": "1.11.5",
"resolved": "https://registry.npmmirror.com/rc-trigger/-/rc-trigger-1.11.5.tgz",
"integrity": "sha512-MBuUPw1nFzA4K7jQOwb7uvFaZFjXGd00EofUYiZ+l/fgKVq8wnLC0lkv36kwqM7vfKyftRo2sh7cWVpdPuNnnw==",
"dependencies": {
"babel-runtime": "6.x",
"create-react-class": "15.x",
"prop-types": "15.x",
"rc-align": "2.x",
"rc-animate": "2.x",
"rc-util": "4.x"
}
},
"node_modules/rc-trigger/node_modules/rc-util": {
"version": "4.21.1",
"resolved": "https://registry.npmmirror.com/rc-util/-/rc-util-4.21.1.tgz",
"integrity": "sha512-Z+vlkSQVc1l8O2UjR3WQ+XdWlhj5q9BMQNLk2iOBch75CqPfrJyGtcWMcnhRlNuDu0Ndtt4kLVO8JI8BrABobg==",
"dependencies": {
"add-dom-event-listener": "^1.1.0",
"prop-types": "^15.5.10",
"react-is": "^16.12.0",
"react-lifecycles-compat": "^3.0.4",
"shallowequal": "^1.1.0"
}
},
"node_modules/rc-trigger/node_modules/react-is": {
"version": "16.13.1",
"resolved": "https://registry.npmmirror.com/react-is/-/react-is-16.13.1.tgz",
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
},
"node_modules/rc-upload": { "node_modules/rc-upload": {
"version": "4.8.1", "version": "4.8.1",
"resolved": "https://registry.npmmirror.com/rc-upload/-/rc-upload-4.8.1.tgz", "resolved": "https://registry.npmmirror.com/rc-upload/-/rc-upload-4.8.1.tgz",
@ -3843,11 +4301,21 @@
"react": "^18.3.1" "react": "^18.3.1"
} }
}, },
"node_modules/react-fast-compare": {
"version": "3.2.2",
"resolved": "https://registry.npmmirror.com/react-fast-compare/-/react-fast-compare-3.2.2.tgz",
"integrity": "sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ=="
},
"node_modules/react-is": { "node_modules/react-is": {
"version": "18.3.1", "version": "18.3.1",
"resolved": "https://registry.npmmirror.com/react-is/-/react-is-18.3.1.tgz", "resolved": "https://registry.npmmirror.com/react-is/-/react-is-18.3.1.tgz",
"integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==" "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="
}, },
"node_modules/react-lifecycles-compat": {
"version": "3.0.4",
"resolved": "https://registry.npmmirror.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz",
"integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA=="
},
"node_modules/react-redux": { "node_modules/react-redux": {
"version": "9.1.2", "version": "9.1.2",
"resolved": "https://registry.npmmirror.com/react-redux/-/react-redux-9.1.2.tgz", "resolved": "https://registry.npmmirror.com/react-redux/-/react-redux-9.1.2.tgz",
@ -4040,6 +4508,17 @@
"loose-envify": "^1.1.0" "loose-envify": "^1.1.0"
} }
}, },
"node_modules/screenfull": {
"version": "5.2.0",
"resolved": "https://registry.npmmirror.com/screenfull/-/screenfull-5.2.0.tgz",
"integrity": "sha512-9BakfsO2aUQN2K9Fdbj87RJIEZ82Q9IGim7FqM5OsebfoFC6ZHXgDq/KvniuLTPdeM8wY2o6Dj3WQ7KeQCj3cA==",
"engines": {
"node": ">=0.10.0"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/scroll-into-view-if-needed": { "node_modules/scroll-into-view-if-needed": {
"version": "3.1.0", "version": "3.1.0",
"resolved": "https://registry.npmmirror.com/scroll-into-view-if-needed/-/scroll-into-view-if-needed-3.1.0.tgz", "resolved": "https://registry.npmmirror.com/scroll-into-view-if-needed/-/scroll-into-view-if-needed-3.1.0.tgz",
@ -4060,6 +4539,11 @@
"node": ">=10" "node": ">=10"
} }
}, },
"node_modules/shallowequal": {
"version": "1.1.0",
"resolved": "https://registry.npmmirror.com/shallowequal/-/shallowequal-1.1.0.tgz",
"integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ=="
},
"node_modules/shebang-command": { "node_modules/shebang-command": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmmirror.com/shebang-command/-/shebang-command-2.0.0.tgz", "resolved": "https://registry.npmmirror.com/shebang-command/-/shebang-command-2.0.0.tgz",
@ -4081,6 +4565,14 @@
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/simple-swizzle": {
"version": "0.2.2",
"resolved": "https://registry.npmmirror.com/simple-swizzle/-/simple-swizzle-0.2.2.tgz",
"integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==",
"dependencies": {
"is-arrayish": "^0.3.1"
}
},
"node_modules/slash": { "node_modules/slash": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmmirror.com/slash/-/slash-3.0.0.tgz", "resolved": "https://registry.npmmirror.com/slash/-/slash-3.0.0.tgz",
@ -4159,6 +4651,11 @@
"node": ">=12.22" "node": ">=12.22"
} }
}, },
"node_modules/tinycolor2": {
"version": "1.6.0",
"resolved": "https://registry.npmmirror.com/tinycolor2/-/tinycolor2-1.6.0.tgz",
"integrity": "sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw=="
},
"node_modules/to-regex-range": { "node_modules/to-regex-range": {
"version": "5.0.1", "version": "5.0.1",
"resolved": "https://registry.npmmirror.com/to-regex-range/-/to-regex-range-5.0.1.tgz", "resolved": "https://registry.npmmirror.com/to-regex-range/-/to-regex-range-5.0.1.tgz",
@ -4188,6 +4685,11 @@
"typescript": ">=4.2.0" "typescript": ">=4.2.0"
} }
}, },
"node_modules/tslib": {
"version": "2.8.1",
"resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.8.1.tgz",
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="
},
"node_modules/type-check": { "node_modules/type-check": {
"version": "0.4.0", "version": "0.4.0",
"resolved": "https://registry.npmmirror.com/type-check/-/type-check-0.4.0.tgz", "resolved": "https://registry.npmmirror.com/type-check/-/type-check-0.4.0.tgz",
@ -4287,6 +4789,36 @@
"node": ">= 4" "node": ">= 4"
} }
}, },
"node_modules/uuid": {
"version": "9.0.1",
"resolved": "https://registry.npmmirror.com/uuid/-/uuid-9.0.1.tgz",
"integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==",
"funding": [
"https://github.com/sponsors/broofa",
"https://github.com/sponsors/ctavan"
],
"bin": {
"uuid": "dist/bin/uuid"
}
},
"node_modules/vanilla-picker": {
"version": "2.12.3",
"resolved": "https://registry.npmmirror.com/vanilla-picker/-/vanilla-picker-2.12.3.tgz",
"integrity": "sha512-qVkT1E7yMbUsB2mmJNFmaXMWE2hF8ffqzMMwe9zdAikd8u2VfnsVY2HQcOUi2F38bgbxzlJBEdS1UUhOXdF9GQ==",
"dependencies": {
"@sphinxxxx/color-conversion": "^2.2.2"
}
},
"node_modules/virtualizedtableforantd4": {
"version": "1.3.1",
"resolved": "https://registry.npmmirror.com/virtualizedtableforantd4/-/virtualizedtableforantd4-1.3.1.tgz",
"integrity": "sha512-rW8KoToI2nt1jNtweXIUIiygi74XMzKLzUrrtZbGsQc7m3v68AaedPuf4CZcte+nosgYuPEWnAgjuI/KR8BVbg==",
"peerDependencies": {
"antd": "^4.0.0 || ^5.0.0",
"react": "^16.8.0 || ^17.0.0 || ^18.0.0",
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0"
}
},
"node_modules/vite": { "node_modules/vite": {
"version": "5.4.11", "version": "5.4.11",
"resolved": "https://registry.npmmirror.com/vite/-/vite-5.4.11.tgz", "resolved": "https://registry.npmmirror.com/vite/-/vite-5.4.11.tgz",
@ -4393,6 +4925,33 @@
"funding": { "funding": {
"url": "https://github.com/sponsors/sindresorhus" "url": "https://github.com/sponsors/sindresorhus"
} }
},
"node_modules/zustand": {
"version": "4.5.5",
"resolved": "https://registry.npmmirror.com/zustand/-/zustand-4.5.5.tgz",
"integrity": "sha512-+0PALYNJNgK6hldkgDq2vLrw5f6g/jCInz52n9RTpropGgeAf/ioFUCdtsjCqu4gNhW9D01rUQBROoRjdzyn2Q==",
"dependencies": {
"use-sync-external-store": "1.2.2"
},
"engines": {
"node": ">=12.7.0"
},
"peerDependencies": {
"@types/react": ">=16.8",
"immer": ">=9.0.6",
"react": ">=16.8"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"immer": {
"optional": true
},
"react": {
"optional": true
}
}
} }
} }
} }

View File

@ -12,9 +12,12 @@
"dependencies": { "dependencies": {
"@ant-design/icons": "^5.2.6", "@ant-design/icons": "^5.2.6",
"@antv/x6": "^2.18.1", "@antv/x6": "^2.18.1",
"@logicflow/core": "^2.0.9",
"@logicflow/extension": "^2.0.13",
"@reduxjs/toolkit": "^2.0.1", "@reduxjs/toolkit": "^2.0.1",
"antd": "^5.22.2", "antd": "^5.22.2",
"axios": "^1.6.2", "axios": "^1.6.2",
"form-render": "^2.5.1",
"react": "^18.2.0", "react": "^18.2.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"react-redux": "^9.0.4", "react-redux": "^9.0.4",

View File

@ -0,0 +1,12 @@
import React from 'react';
import { Spin } from 'antd';
const LoadingComponent: React.FC = () => {
return (
<div style={{ padding: 24, textAlign: 'center' }}>
<Spin size="large" />
</div>
);
};
export default LoadingComponent;

View File

@ -1,6 +1,7 @@
import request from '@/utils/request'; import request from '@/utils/request';
import type { Page } from '@/types/base/page'; import type { Page } from '@/types/base/page';
import type { MenuResponse, MenuRequest } from './types'; import type { MenuResponse, MenuRequest } from './types';
import { MenuTypeEnum } from './types';
const BASE_URL = '/api/v1/menu'; const BASE_URL = '/api/v1/menu';
@ -15,6 +16,8 @@ export const getMenuTree = () =>
// 获取当前用户菜单 // 获取当前用户菜单
export const getCurrentUserMenus = async () => { export const getCurrentUserMenus = async () => {
const menus = await request.get<MenuResponse[]>(`${BASE_URL}/current`); const menus = await request.get<MenuResponse[]>(`${BASE_URL}/current`);
console.log('Backend menus:', menus);
// 添加首页路由 // 添加首页路由
const dashboard: MenuResponse = { const dashboard: MenuResponse = {
id: 0, id: 0,
@ -25,7 +28,7 @@ export const getCurrentUserMenus = async () => {
path: "/dashboard", path: "/dashboard",
component: "/Dashboard/index", component: "/Dashboard/index",
icon: "dashboard", icon: "dashboard",
type: 2, type: MenuTypeEnum.MENU,
parentId: 0, parentId: 0,
sort: 0, sort: 0,
hidden: false, hidden: false,
@ -34,6 +37,77 @@ export const getCurrentUserMenus = async () => {
updateBy: "system" updateBy: "system"
}; };
// 添加工作流菜单
const workflow: MenuResponse = {
id: -2,
createTime: new Date().toISOString(),
updateTime: new Date().toISOString(),
version: 0,
name: "工作流管理",
path: "/workflow",
icon: "apartment",
type: MenuTypeEnum.DIRECTORY,
parentId: 0,
sort: 2,
hidden: false,
enabled: true,
createBy: "system",
updateBy: "system",
children: [
{
id: -21,
createTime: new Date().toISOString(),
updateTime: new Date().toISOString(),
version: 0,
name: "流程定义",
path: "/workflow/definition",
component: "/pages/Workflow/Definition/index",
icon: "apartment",
type: MenuTypeEnum.MENU,
parentId: -2,
sort: 0,
hidden: false,
enabled: true,
createBy: "system",
updateBy: "system"
},
{
id: -22,
createTime: new Date().toISOString(),
updateTime: new Date().toISOString(),
version: 0,
name: "流程实例",
path: "/workflow/instance",
component: "/pages/Workflow/Instance/index",
icon: "apartment",
type: MenuTypeEnum.MENU,
parentId: -2,
sort: 1,
hidden: false,
enabled: true,
createBy: "system",
updateBy: "system"
},
{
id: -23,
createTime: new Date().toISOString(),
updateTime: new Date().toISOString(),
version: 0,
name: "流程监控",
path: "/workflow/monitor",
component: "/pages/Workflow/Monitor/index",
icon: "dashboard",
type: MenuTypeEnum.MENU,
parentId: -2,
sort: 2,
hidden: false,
enabled: true,
createBy: "system",
updateBy: "system"
}
]
};
// 添加X6测试菜单 // 添加X6测试菜单
const x6Test: MenuResponse = { const x6Test: MenuResponse = {
id: -1, id: -1,
@ -44,7 +118,7 @@ export const getCurrentUserMenus = async () => {
path: "/x6-test", path: "/x6-test",
component: "/X6Test/index", component: "/X6Test/index",
icon: "experiment", icon: "experiment",
type: 2, type: MenuTypeEnum.MENU,
parentId: 0, parentId: 0,
sort: 1, sort: 1,
hidden: false, hidden: false,
@ -71,7 +145,9 @@ export const getCurrentUserMenus = async () => {
}; };
const processedMenus = menus.map(processMenu); const processedMenus = menus.map(processMenu);
return [dashboard, x6Test, ...processedMenus]; const allMenus = [dashboard, x6Test, workflow, ...processedMenus];
console.log('All menus:', allMenus);
return allMenus;
}; };
// 创建菜单 // 创建菜单

View File

@ -0,0 +1,27 @@
.container {
width: 100%;
height: 600px;
position: relative;
background: #fff;
border: 1px solid #e8e8e8;
border-radius: 2px;
display: flex;
}
.canvas {
flex: 1;
height: 100%;
}
.configPanel {
width: 320px;
height: 100%;
border-left: 1px solid #e8e8e8;
overflow-y: auto;
}
:global(.lf-control) {
position: absolute;
right: 330px;
top: 10px;
}

View File

@ -0,0 +1,91 @@
import React, { useEffect, useRef, useState } from 'react';
import { Card, Space } from 'antd';
import LogicFlow from '@logicflow/core';
import { DndPanel, SelectionSelect, Control } from '@logicflow/extension';
import '@logicflow/core/es/index.css';
import '@logicflow/extension/es/style/index.css';
import styles from './index.module.css';
import { registerNodes } from './nodes';
import NodeConfig from '../NodeConfig';
interface FlowDesignerProps {
value?: string;
onChange?: (value: string) => void;
}
const FlowDesigner: React.FC<FlowDesignerProps> = ({
value,
onChange
}) => {
const containerRef = useRef<HTMLDivElement>(null);
const [lf, setLf] = useState<LogicFlow>();
const [selectedNode, setSelectedNode] = useState<any>(null);
useEffect(() => {
if (containerRef.current) {
// 初始化 LogicFlow
const logicflow = new LogicFlow({
container: containerRef.current,
grid: true,
plugins: [DndPanel, SelectionSelect, Control]
});
// 注册自定义节点
registerNodes(logicflow);
// 注册事件
logicflow.on('node:click', ({ data }) => {
setSelectedNode(data);
});
logicflow.on('blank:click', () => {
setSelectedNode(null);
});
// 如果有初始值,渲染流程图
if (value) {
try {
const graphData = JSON.parse(value);
logicflow.render(graphData);
} catch (error) {
console.error('Failed to parse graph data:', error);
}
}
setLf(logicflow);
return () => {
logicflow.destroy();
};
}
}, []);
// 处理节点配置更新
const handleNodeConfigSave = (config: any) => {
if (lf && selectedNode) {
lf.setProperties(selectedNode.id, config);
setSelectedNode(null);
// 触发 onChange
const graphData = lf.getGraphData();
onChange?.(JSON.stringify(graphData));
}
};
return (
<div className={styles.container}>
<div className={styles.canvas} ref={containerRef} />
{selectedNode && (
<div className={styles.configPanel}>
<NodeConfig
node={selectedNode}
onSave={handleNodeConfigSave}
onCancel={() => setSelectedNode(null)}
/>
</div>
)}
</div>
);
};
export default FlowDesigner;

View File

@ -0,0 +1,81 @@
import LogicFlow from '@logicflow/core';
import { CircleNode, RectNode, DiamondNode } from '@logicflow/core';
export function registerNodes(lf: LogicFlow) {
// 开始节点
class StartNode extends CircleNode {
static extendKey = 'StartNode';
getNodeStyle() {
const style = super.getNodeStyle();
return {
...style,
fill: '#C6E5FF',
stroke: '#1890FF'
};
}
}
// 任务节点
class TaskNode extends RectNode {
static extendKey = 'TaskNode';
getNodeStyle() {
const style = super.getNodeStyle();
return {
...style,
fill: '#FFF',
stroke: '#1890FF'
};
}
}
// 网关节点
class GatewayNode extends DiamondNode {
static extendKey = 'GatewayNode';
getNodeStyle() {
const style = super.getNodeStyle();
return {
...style,
fill: '#FFF',
stroke: '#1890FF'
};
}
}
// 结束节点
class EndNode extends CircleNode {
static extendKey = 'EndNode';
getNodeStyle() {
const style = super.getNodeStyle();
return {
...style,
fill: '#FFE7E7',
stroke: '#FF4D4F'
};
}
}
// 注册节点
lf.register({
type: 'start',
view: StartNode,
model: StartNode
});
lf.register({
type: 'task',
view: TaskNode,
model: TaskNode
});
lf.register({
type: 'gateway',
view: GatewayNode,
model: GatewayNode
});
lf.register({
type: 'end',
view: EndNode,
model: EndNode
});
}

View File

@ -0,0 +1,57 @@
import React, { useState } from 'react';
import { Card, Tabs } from 'antd';
import type { TabsProps } from 'antd';
import FormRender from 'form-render';
interface FormDesignerProps {
value?: string;
onChange?: (value: string) => void;
readOnly?: boolean;
}
const defaultSchema = {
type: 'object',
properties: {
input1: {
title: '输入框',
type: 'string'
}
}
};
const FormDesigner: React.FC<FormDesignerProps> = ({
value,
onChange,
readOnly = false
}) => {
const [schema, setSchema] = useState<any>(() => {
try {
return value ? JSON.parse(value) : defaultSchema;
} catch (error) {
return defaultSchema;
}
});
const handleSubmit = (formData: any) => {
console.log('Form data:', formData);
};
const handleValidate = (errors: any) => {
console.log('Form errors:', errors);
};
return (
<div style={{ padding: 24 }}>
<Card title="表单预览">
<FormRender
schema={schema}
onFinish={handleSubmit}
onValidate={handleValidate}
disabled={readOnly}
/>
</Card>
</div>
);
};
export default FormDesigner;

View File

@ -0,0 +1,90 @@
import React from 'react';
import { Form, Input, Button, Space } from 'antd';
import type { BaseNodeModel } from '@logicflow/core';
type NodeType = 'start' | 'end' | 'task' | 'gateway';
interface NodeConfigProps {
node: BaseNodeModel & { type: NodeType };
onSave: (config: any) => void;
onCancel: () => void;
}
const NodeConfig: React.FC<NodeConfigProps> = ({
node,
onSave,
onCancel
}) => {
const [form] = Form.useForm();
const handleFinish = (values: any) => {
onSave({
...values,
text: { value: values.text, x: node.text?.x, y: node.text?.y }
});
};
return (
<div style={{ padding: 16 }}>
<Form
form={form}
layout="vertical"
initialValues={{
text: node.text?.value,
...node.getProperties()
}}
onFinish={handleFinish}
>
<Form.Item
label="节点名称"
name="text"
rules={[{ required: true, message: '请输入节点名称' }]}
>
<Input />
</Form.Item>
{node.type === 'task' && (
<>
<Form.Item
label="表单Key"
name="formKey"
rules={[{ required: true, message: '请输入表单Key' }]}
>
<Input />
</Form.Item>
<Form.Item
label="处理人"
name="assignee"
rules={[{ required: true, message: '请输入处理人' }]}
>
<Input />
</Form.Item>
</>
)}
{node.type === 'gateway' && (
<Form.Item
label="条件表达式"
name="condition"
rules={[{ required: true, message: '请输入条件表达式' }]}
>
<Input.TextArea rows={4} />
</Form.Item>
)}
<Form.Item>
<Space>
<Button type="primary" htmlType="submit">
</Button>
<Button onClick={onCancel}>
</Button>
</Space>
</Form.Item>
</Form>
</div>
);
};
export default NodeConfig;

View File

@ -0,0 +1,131 @@
import React, { useEffect, useState } from 'react';
import { Card, Tabs, Button, Form, Input, message } from 'antd';
import type { TabsProps } from 'antd';
import { useNavigate, useParams } from 'react-router-dom';
import FlowDesigner from './components/FlowDesigner';
import FormDesigner from './components/FormDesigner';
import { getDefinition, createDefinition, updateDefinition } from '../../service';
import type { WorkflowDefinitionRequest } from '../../types';
const Edit: React.FC = () => {
const navigate = useNavigate();
const { id } = useParams<{ id: string }>();
const [form] = Form.useForm();
const [loading, setLoading] = useState(false);
const [graphData, setGraphData] = useState('');
const [formData, setFormData] = useState('');
useEffect(() => {
if (id) {
setLoading(true);
getDefinition(parseInt(id))
.then(data => {
form.setFieldsValue({
name: data.name,
code: data.code,
description: data.description
});
setGraphData(data.graphDefinition || '');
setFormData(data.formDefinition || '');
})
.finally(() => {
setLoading(false);
});
}
}, [id, form]);
const handleSave = async () => {
try {
const values = await form.validateFields();
const data: WorkflowDefinitionRequest = {
...values,
graphDefinition: graphData,
formDefinition: formData,
nodeConfig: '{}',
transitionConfig: '{}',
enabled: true
};
if (id) {
await updateDefinition(parseInt(id), data);
} else {
await createDefinition(data);
}
message.success('保存成功');
navigate('/workflow/definition');
} catch (error) {
console.error('Save failed:', error);
}
};
const items: TabsProps['items'] = [
{
key: 'basic',
label: '基本信息',
children: (
<Form
form={form}
layout="vertical"
style={{ maxWidth: 600, margin: '24px auto' }}
>
<Form.Item
label="流程名称"
name="name"
rules={[{ required: true, message: '请输入流程名称' }]}
>
<Input />
</Form.Item>
<Form.Item
label="流程编码"
name="code"
rules={[{ required: true, message: '请输入流程编码' }]}
>
<Input />
</Form.Item>
<Form.Item
label="描述"
name="description"
>
<Input.TextArea rows={4} />
</Form.Item>
</Form>
)
},
{
key: 'flow',
label: '流程设计',
children: (
<FlowDesigner
value={graphData}
onChange={setGraphData}
/>
)
},
{
key: 'form',
label: '表单设计',
children: (
<FormDesigner
value={formData}
onChange={setFormData}
/>
)
}
];
return (
<Card
title={id ? '编辑流程' : '新建流程'}
extra={
<Button type="primary" onClick={handleSave}>
</Button>
}
>
<Tabs items={items} />
</Card>
);
};
export default Edit;

View File

@ -0,0 +1,165 @@
import React from 'react';
import { Card, Table, Button, Space, Tag, Popconfirm, message } from 'antd';
import { useNavigate } from 'react-router-dom';
import type { ColumnsType } from 'antd/es/table';
import { useTableData } from '@/hooks/useTableData';
import { getDefinitions, deleteDefinition, publishDefinition, disableDefinition, enableDefinition } from '../service';
import { WorkflowDefinitionResponse, WorkflowStatus } from '../types';
const Definition: React.FC = () => {
const navigate = useNavigate();
const { list, loading, pagination, handleTableChange, refresh } = useTableData({
service: {
list: getDefinitions
}
});
const handlePublish = async (id: number) => {
try {
await publishDefinition(id);
message.success('发布成功');
refresh();
} catch (error) {
message.error('发布失败');
}
};
const handleDisable = async (id: number) => {
try {
await disableDefinition(id);
message.success('禁用成功');
refresh();
} catch (error) {
message.error('禁用失败');
}
};
const handleEnable = async (id: number) => {
try {
await enableDefinition(id);
message.success('启用成功');
refresh();
} catch (error) {
message.error('启用失败');
}
};
const handleDelete = async (id: number) => {
try {
await deleteDefinition(id);
message.success('删除成功');
refresh();
} catch (error) {
message.error('删除失败');
}
};
const columns: ColumnsType<WorkflowDefinitionResponse> = [
{
title: '流程名称',
dataIndex: 'name',
width: 200,
},
{
title: '流程编码',
dataIndex: 'code',
width: 200,
},
{
title: '状态',
dataIndex: 'status',
width: 120,
render: (status: WorkflowStatus) => {
const statusMap = {
'DRAFT': { text: '草稿', color: 'default' },
'PUBLISHED': { text: '已发布', color: 'success' },
'DISABLED': { text: '已禁用', color: 'error' },
};
const { text, color } = statusMap[status];
return <Tag color={color}>{text}</Tag>;
},
},
{
title: '描述',
dataIndex: 'description',
ellipsis: true,
},
{
title: '创建时间',
dataIndex: 'createTime',
width: 180,
},
{
title: '操作',
width: 280,
render: (_, record) => (
<Space>
<Button
type="link"
onClick={() => navigate(`/workflow/definition/edit/${record.id}`)}
>
</Button>
{record.status === 'DRAFT' && (
<Button
type="link"
onClick={() => handlePublish(record.id)}
>
</Button>
)}
{record.status === 'PUBLISHED' && (
<Button
type="link"
danger
onClick={() => handleDisable(record.id)}
>
</Button>
)}
{record.status === 'DISABLED' && (
<Button
type="link"
onClick={() => handleEnable(record.id)}
>
</Button>
)}
<Popconfirm
title="确定要删除吗?"
onConfirm={() => handleDelete(record.id)}
>
<Button type="link" danger>
</Button>
</Popconfirm>
</Space>
),
},
];
return (
<Card
title="流程定义"
extra={
<Button
type="primary"
onClick={() => navigate('/workflow/definition/edit')}
>
</Button>
}
>
<Table
columns={columns}
dataSource={list}
loading={loading}
pagination={pagination}
onChange={handleTableChange}
rowKey="id"
/>
</Card>
);
};
export default Definition;

View File

@ -0,0 +1,12 @@
import React from 'react';
import { Card } from 'antd';
const WorkflowInstance: React.FC = () => {
return (
<Card>
<div></div>
</Card>
);
};
export default WorkflowInstance;

View File

@ -0,0 +1,12 @@
import React from 'react';
import { Card } from 'antd';
const WorkflowMonitor: React.FC = () => {
return (
<Card>
<div></div>
</Card>
);
};
export default WorkflowMonitor;

View File

@ -0,0 +1,97 @@
import request from '@/utils/request';
import type { Page } from '@/types/base/page';
import type { BaseResponse } from '@/types/base/response';
import type {
WorkflowDefinitionResponse,
WorkflowDefinitionRequest,
WorkflowDefinitionQuery,
WorkflowInstanceResponse,
WorkflowInstanceQuery,
NodeInstanceResponse,
WorkflowLogResponse,
WorkflowDefinition
} 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';
// 工作流定义相关接口
export const getDefinitions = (params?: WorkflowDefinitionQuery) =>
request.get<Page<WorkflowDefinitionResponse>>(`${DEFINITION_URL}`, { params });
export const getDefinition = (id: number) =>
request.get<WorkflowDefinitionResponse>(`${DEFINITION_URL}/${id}`);
export const createDefinition = (data: WorkflowDefinitionRequest) =>
request.post<WorkflowDefinitionResponse>(DEFINITION_URL, data);
export const updateDefinition = (id: number, data: WorkflowDefinitionRequest) =>
request.put<WorkflowDefinitionResponse>(`${DEFINITION_URL}/${id}`, data);
export const deleteDefinition = (id: number) =>
request.delete(`${DEFINITION_URL}/${id}`);
export const publishDefinition = (id: number) =>
request.post(`${DEFINITION_URL}/${id}/publish`);
export const disableDefinition = (id: number) =>
request.post(`${DEFINITION_URL}/${id}/disable`);
export const enableDefinition = (id: number) =>
request.post(`${DEFINITION_URL}/${id}/enable`);
// 工作流实例相关接口
export const getInstances = (params?: WorkflowInstanceQuery) =>
request.get<Page<WorkflowInstanceResponse>>(`${INSTANCE_URL}`, { params });
export const getInstance = (id: number) =>
request.get<WorkflowInstanceResponse>(`${INSTANCE_URL}/${id}`);
export const createInstance = (definitionId: number, businessKey: string, variables?: Record<string, any>) =>
request.post<WorkflowInstanceResponse>(INSTANCE_URL, { definitionId, businessKey, variables });
export const startInstance = (id: number) =>
request.post(`${INSTANCE_URL}/${id}/start`);
export const cancelInstance = (id: number) =>
request.post(`${INSTANCE_URL}/${id}/cancel`);
export const pauseInstance = (id: number) =>
request.post(`${INSTANCE_URL}/${id}/pause`);
export const resumeInstance = (id: number) =>
request.post(`${INSTANCE_URL}/${id}/resume`);
// 节点实例相关接口
export const getNodeInstances = (workflowInstanceId: number) =>
request.get<NodeInstanceResponse[]>(`${NODE_URL}/workflow/${workflowInstanceId}`);
export const getNodeInstancesByStatus = (workflowInstanceId: number, status: string) =>
request.get<NodeInstanceResponse[]>(`${NODE_URL}/workflow/${workflowInstanceId}/status/${status}`);
export const updateNodeStatus = (id: number, status: string) =>
request.put(`${NODE_URL}/${id}/status`, { status });
// 日志相关接口
export const getWorkflowLogs = (workflowInstanceId: number) =>
request.get<WorkflowLogResponse[]>(`${LOG_URL}/workflow/${workflowInstanceId}`);
export const getNodeLogs = (workflowInstanceId: number, nodeId: string) =>
request.get<WorkflowLogResponse[]>(`${LOG_URL}/node/${workflowInstanceId}/${nodeId}`);
export const recordLog = (data: Omit<WorkflowLogResponse, keyof BaseResponse>) =>
request.post<WorkflowLogResponse>(`${LOG_URL}/record`, data);
// 获取工作流定义列表
export const getDefinitionList = (params: WorkflowDefinitionQuery) =>
request.get<WorkflowDefinitionResponse[]>('/api/v1/workflow/definition', { params });
// 保存工作流定义
export const saveDefinition = (data: WorkflowDefinition) => {
if (data.id) {
return request.put<WorkflowDefinitionResponse>(`/api/v1/workflow/definition/${data.id}`, data);
}
return request.post<WorkflowDefinitionResponse>('/api/v1/workflow/definition', data);
};

View File

@ -0,0 +1,146 @@
import { BaseResponse } from '@/types/base/response';
import { BaseQuery } from '@/types/base/query';
// 工作流状态枚举
export enum WorkflowStatus {
DRAFT = 'DRAFT',
PUBLISHED = 'PUBLISHED',
DISABLED = 'DISABLED'
}
// 节点类型枚举
export enum NodeType {
START = 'START',
END = 'END',
TASK = 'TASK',
GATEWAY = 'GATEWAY',
SUB_PROCESS = 'SUB_PROCESS',
SHELL = 'SHELL'
}
// 节点状态枚举
export enum NodeStatus {
PENDING = 'PENDING',
RUNNING = 'RUNNING',
COMPLETED = 'COMPLETED',
FAILED = 'FAILED',
CANCELLED = 'CANCELLED',
PAUSED = 'PAUSED',
SKIPPED = 'SKIPPED'
}
// 工作流实例状态枚举
export enum InstanceStatus {
RUNNING = 'RUNNING',
COMPLETED = 'COMPLETED',
FAILED = 'FAILED',
CANCELLED = 'CANCELLED',
PAUSED = 'PAUSED'
}
// 日志级别枚举
export enum LogLevel {
INFO = 'INFO',
WARN = 'WARN',
ERROR = 'ERROR'
}
// 工作流定义基础接口
export interface WorkflowDefinition {
id?: number;
name: string;
code: string;
description?: string;
nodeConfig: string;
transitionConfig: string;
formDefinition: string;
graphDefinition: string;
enabled: boolean;
remark?: string;
status?: WorkflowStatus;
createTime?: string;
updateTime?: string;
}
// 工作流定义响应数据
export interface WorkflowDefinitionResponse extends BaseResponse, Omit<WorkflowDefinition, 'id' | 'status' | 'createTime' | 'updateTime'> {
id: number;
status: WorkflowStatus;
createTime: string;
updateTime: string;
}
// 工作流定义查询参数
export interface WorkflowDefinitionQuery extends BaseQuery {
name?: string;
code?: string;
status?: WorkflowStatus;
}
// 工作流定义请求数据
export interface WorkflowDefinitionRequest {
code: string;
name: string;
description?: string;
nodeConfig: string;
transitionConfig: string;
formDefinition: string;
graphDefinition: string;
enabled: boolean;
remark?: string;
}
// 节点定义响应数据
export interface NodeDefinitionResponse extends BaseResponse {
nodeId: string;
name: string;
type: NodeType;
config: string;
description?: string;
workflowDefinitionId: number;
orderNum: number;
}
// 工作流实例查询参数
export interface WorkflowInstanceQuery extends BaseQuery {
definitionId?: number;
status?: InstanceStatus;
startTime?: string;
endTime?: string;
}
// 工作流实例响应数据
export interface WorkflowInstanceResponse extends BaseResponse {
definitionId: number;
businessKey: string;
status: InstanceStatus;
startTime: string;
endTime?: string;
variables: string;
error?: string;
}
// 节点实例响应数据
export interface NodeInstanceResponse extends BaseResponse {
workflowInstanceId: number;
nodeId: string;
nodeType: NodeType;
name: string;
status: NodeStatus;
startTime: string;
endTime?: string;
config: string;
input: string;
output: string;
error?: string;
preNodeId: string;
}
// 工作流日志响应数据
export interface WorkflowLogResponse extends BaseResponse {
workflowInstanceId: number;
nodeId: string;
message: string;
level: LogLevel;
detail?: string;
}

View File

@ -6,7 +6,7 @@ import BasicLayout from '../layouts/BasicLayout';
import {useSelector} from 'react-redux'; import {useSelector} from 'react-redux';
import {RootState} from '../store'; import {RootState} from '../store';
// 加中组件 // 加中组件
const LoadingComponent = () => ( const LoadingComponent = () => (
<div style={{padding: 24, textAlign: 'center'}}> <div style={{padding: 24, textAlign: 'center'}}>
<Spin size="large"/> <Spin size="large"/>
@ -32,9 +32,17 @@ const Menu = lazy(() => import('../pages/System/Menu'));
const Department = lazy(() => import('../pages/System/Department')); const Department = lazy(() => import('../pages/System/Department'));
const External = lazy(() => import('../pages/System/External')); const External = lazy(() => import('../pages/System/External'));
const X6Test = lazy(() => import('../pages/X6Test')); const X6Test = lazy(() => import('../pages/X6Test'));
const WorkflowDefinition = lazy(() => import('../pages/Workflow/Definition'));
const WorkflowDefinitionEdit = lazy(() => import('../pages/Workflow/Definition/Edit'));
const WorkflowInstance = lazy(() => import('../pages/Workflow/Instance'));
const WorkflowMonitor = lazy(() => import('../pages/Workflow/Monitor'));
// 创建路由 // 创建路由
const router = createBrowserRouter([ const router = createBrowserRouter([
{
path: '/login',
element: <Login/>
},
{ {
path: '/', path: '/',
element: ( element: (
@ -109,14 +117,52 @@ const router = createBrowserRouter([
) )
}, },
{ {
path: '*', path: 'workflow',
element: <Navigate to="/dashboard"/> children: [
{
path: 'definition',
children: [
{
path: '',
element: (
<Suspense fallback={<LoadingComponent/>}>
<WorkflowDefinition/>
</Suspense>
)
},
{
path: 'edit/:id?',
element: (
<Suspense fallback={<LoadingComponent/>}>
<WorkflowDefinitionEdit/>
</Suspense>
)
} }
] ]
}, },
{ {
path: '/login', path: 'instance',
element: <Login/> element: (
<Suspense fallback={<LoadingComponent/>}>
<WorkflowInstance/>
</Suspense>
)
},
{
path: 'monitor',
element: (
<Suspense fallback={<LoadingComponent/>}>
<WorkflowMonitor/>
</Suspense>
)
}
]
},
{
path: '*',
element: <Navigate to="/dashboard"/>
}
]
} }
]); ]);