mirror of
https://gitee.com/myxzgzs/boyue-ui-admin-vue3
synced 2025-08-08 08:22:41 +08:00
Merge branch 'master' of https://gitee.com/yudaocode/yudao-ui-admin-vue3 into feature/iot
# Conflicts: # pnpm-lock.yaml # src/utils/index.ts
This commit is contained in:
commit
e29d6f910a
Binary file not shown.
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 25 KiB |
112
README.md
112
README.md
@ -81,16 +81,13 @@
|
|||||||
|
|
||||||
系统内置多种多种业务功能,可以用于快速你的业务系统:
|
系统内置多种多种业务功能,可以用于快速你的业务系统:
|
||||||
|
|
||||||
* 系统功能
|
系统内置多种多种业务功能,可以用于快速你的业务系统:
|
||||||
* 基础设施
|
|
||||||
* 工作流程
|

|
||||||
* 支付系统
|
|
||||||
* 会员中心
|
* 通用模块(必选):系统功能、基础设施
|
||||||
* 数据报表
|
* 通用模块(可选):工作流程、支付系统、数据报表、会员中心
|
||||||
* 商城系统
|
* 业务系统(按需):ERP 系统、CRM 系统、商城系统、微信公众号、AI 大模型
|
||||||
* 微信公众号
|
|
||||||
* ERP 系统
|
|
||||||
* CRM 系统
|
|
||||||
|
|
||||||
### 系统功能
|
### 系统功能
|
||||||
|
|
||||||
@ -120,54 +117,77 @@
|
|||||||
|
|
||||||
### 工作流程
|
### 工作流程
|
||||||
|
|
||||||
| | 功能 | 描述 |
|
|
||||||
|----|-------|-----------------------------------------|
|
|
||||||
| 🚀 | 流程模型 | 配置工作流的流程模型,支持 BPMN 和仿钉钉/飞书设计器 |
|
|
||||||
| 🚀 | 流程表单 | 拖动表单元素生成相应的工作流表单,覆盖 Element UI 所有的表单组件 |
|
|
||||||
| 🚀 | 用户分组 | 自定义用户分组,可用于工作流的审批分组 |
|
|
||||||
| 🚀 | 我的流程 | 查看我发起的工作流程,支持新建、取消流程等操作,高亮流程图、审批时间线 |
|
|
||||||
| 🚀 | 待办任务 | 查看自己【未】审批的工作任务,支持通过、不通过、转派、委派、退回、加减签等操作 |
|
|
||||||
| 🚀 | 已办任务 | 查看自己【已】审批的工作任务,支持流程预测,展示未来审批人信息 |
|
|
||||||
| 🚀 | OA 请假 | 作为业务自定义接入工作流的使用示例,只需创建请求对应的工作流程,即可进行审批 |
|
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
基于 Flowable 构建,可支持信创(国产)数据库,满足中国特色流程操作:
|
||||||
|
|
||||||
| BPMN 设计器 | 钉钉/飞书设计器 |
|
| BPMN 设计器 | 钉钉/飞书设计器 |
|
||||||
|------------------------------|--------------------------------|
|
|------------------------------|--------------------------------|
|
||||||
|  |  |
|
|  |  |
|
||||||
|
|
||||||
|
> 历经头部企业生产验证,工作流引擎须标配仿钉钉/飞书 + BPMN 双设计器!!!
|
||||||
|
>
|
||||||
|
> 前者支持轻量配置简单流程,后者实现复杂场景深度编排
|
||||||
|
|
||||||
|
| 功能列表 | 功能描述 | 是否完成 |
|
||||||
|
|------------|-------------------------------------------------------------------------------------|------|
|
||||||
|
| SIMPLE 设计器 | 仿钉钉/飞书设计器,支持拖拽搭建表单流程,10 分钟快速完成审批流程配置 | ✅ |
|
||||||
|
| BPMN 设计器 | 基于 BPMN 标准开发,适配复杂业务场景,满足多层级审批及流程自动化需求 | ✅ |
|
||||||
|
| 会签 | 同一个审批节点设置多个人(如 A、B、C 三人,三人会同时收到待办任务),需全部同意之后,审批才可到下一审批节点 | ✅ |
|
||||||
|
| 或签 | 同一个审批节点设置多个人,任意一个人处理后,就能进入下一个节点 | ✅ |
|
||||||
|
| 依次审批 | (顺序会签)同一个审批节点设置多个人(如 A、B、C 三人),三人按顺序依次收到待办,即 A 先审批,A 提交后 B 才能审批,需全部同意之后,审批才可到下一审批节点 | ✅ |
|
||||||
|
| 抄送 | 将审批结果通知给抄送人,同一个审批默认排重,不重复抄送给同一人 | ✅ |
|
||||||
|
| 驳回 | (退回)将审批重置发送给某节点,重新审批。可驳回至发起人、上一节点、任意节点 | ✅ |
|
||||||
|
| 转办 | A 转给其 B 审批,B 审批后,进入下一节点 | ✅ |
|
||||||
|
| 委派 | A 转给其 B 审批,B 审批后,转给 A,A 继续审批后进入下一节点 | ✅ |
|
||||||
|
| 加签 | 允许当前审批人根据需要,自行增加当前节点的审批人,支持向前、向后加签 | ✅ |
|
||||||
|
| 减签 | (取消加签)在当前审批人操作之前,减少审批人 | ✅ |
|
||||||
|
| 撤销 | (取消流程)流程发起人,可以对流程进行撤销处理 | ✅ |
|
||||||
|
| 终止 | 系统管理员,在任意节点终止流程实例 | ✅ |
|
||||||
|
| 表单权限 | 支持拖拉拽配置表单,每个审批节点可配置只读、编辑、隐藏权限 | ✅ |
|
||||||
|
| 超时审批 | 配置超时审批时间,超时后自动触发审批通过、不通过、驳回等操作 | ✅ |
|
||||||
|
| 自动提醒 | 配置提醒时间,到达时间后自动触发短信、邮箱、站内信等通知提醒,支持自定义重复提醒频次 | ✅ |
|
||||||
|
| 父子流程 | 主流程设置子流程节点,子流程节点会自动触发子流程。子流程结束后,主流程才会执行(继续往下下执行),支持同步子流程、异步子流程 | ✅ |
|
||||||
|
| 条件分支 | (排它分支)用于在流程中实现决策,即根据条件选择一个分支执行 | ✅ |
|
||||||
|
| 并行分支 | 允许将流程分成多条分支,不进行条件判断,所有分支都会执行 | ✅ |
|
||||||
|
| 包容分支 | (条件分支 + 并行分支的结合体)允许基于条件选择多条分支执行,但如果没有任何一个分支满足条件,则可以选择默认分支 | ✅ |
|
||||||
|
| 路由分支 | 根据条件选择一个分支执行(重定向到指定配置节点),也可以选择默认分支执行(继续往下执行) | ✅ |
|
||||||
|
| 触发节点 | 执行到该节点,触发 HTTP 请求、HTTP 回调、更新数据、删除数据等 | ✅ |
|
||||||
|
| 延迟节点 | 执行到该节点,审批等待一段时间再执行,支持固定时长、固定日期等 | ✅ |
|
||||||
|
| 拓展设置 | 流程前置/后置通知,节点(任务)前置、后置通知,流程报表,自动审批去重,自定流程编号、标题、摘要,流程报表等 | ✅ |
|
||||||
|
|
||||||
### 支付系统
|
### 支付系统
|
||||||
|
|
||||||
| | 功能 | 描述 |
|
| | 功能 | 描述 |
|
||||||
|-----|------|---------------------------|
|
|-----|------|---------------------------|
|
||||||
| 🚀 | 商户信息 | 管理商户信息,支持 Saas 场景下的多商户功能 |
|
|
||||||
| 🚀 | 应用信息 | 配置商户的应用信息,对接支付宝、微信等多个支付渠道 |
|
| 🚀 | 应用信息 | 配置商户的应用信息,对接支付宝、微信等多个支付渠道 |
|
||||||
| 🚀 | 支付订单 | 查看用户发起的支付宝、微信等的【支付】订单 |
|
| 🚀 | 支付订单 | 查看用户发起的支付宝、微信等的【支付】订单 |
|
||||||
| 🚀 | 退款订单 | 查看用户发起的支付宝、微信等的【退款】订单 |
|
| 🚀 | 退款订单 | 查看用户发起的支付宝、微信等的【退款】订单 |
|
||||||
|
| 🚀 | 回调通知 | 查看支付回调业务的【支付】【退款】的通知结果 |
|
||||||
ps:核心功能已经实现,正在对接微信小程序中...
|
| 🚀 | 接入示例 | 提供接入支付系统的【支付】【退款】的功能实战 |
|
||||||
|
|
||||||
### 基础设施
|
### 基础设施
|
||||||
|
|
||||||
| | 功能 | 描述 |
|
| | 功能 | 描述 |
|
||||||
|----|----------|----------------------------------------------|
|
|-----|-----------|----------------------------------------------|
|
||||||
| 🚀 | 代码生成 | 前后端代码的生成(Java、Vue、SQL、单元测试),支持 CRUD 下载 |
|
| 🚀 | 代码生成 | 前后端代码的生成(Java、Vue、SQL、单元测试),支持 CRUD 下载 |
|
||||||
| 🚀 | 系统接口 | 基于 Swagger 自动生成相关的 RESTful API 接口文档 |
|
| 🚀 | 系统接口 | 基于 Swagger 自动生成相关的 RESTful API 接口文档 |
|
||||||
| 🚀 | 数据库文档 | 基于 Screw 自动生成数据库文档,支持导出 Word、HTML、MD 格式 |
|
| 🚀 | 数据库文档 | 基于 Screw 自动生成数据库文档,支持导出 Word、HTML、MD 格式 |
|
||||||
| | 表单构建 | 拖动表单元素生成相应的 HTML 代码,支持导出 JSON、Vue 文件 |
|
| | 表单构建 | 拖动表单元素生成相应的 HTML 代码,支持导出 JSON、Vue 文件 |
|
||||||
| 🚀 | 配置管理 | 对系统动态配置常用参数,支持 SpringBoot 加载 |
|
| 🚀 | 配置管理 | 对系统动态配置常用参数,支持 SpringBoot 加载 |
|
||||||
| ⭐️ | 定时任务 | 在线(添加、修改、删除)任务调度包含执行结果日志 |
|
| ⭐️ | 定时任务 | 在线(添加、修改、删除)任务调度包含执行结果日志 |
|
||||||
| 🚀 | 文件服务 | 支持将文件存储到 S3(MinIO、阿里云、腾讯云、七牛云)、本地、FTP、数据库等 |
|
| 🚀 | 文件服务 | 支持将文件存储到 S3(MinIO、阿里云、腾讯云、七牛云)、本地、FTP、数据库等 |
|
||||||
| 🚀 | API 日志 | 包括 RESTful API 访问日志、异常日志两部分,方便排查 API 相关的问题 |
|
| 🚀 | WebSocket | 提供 WebSocket 接入示例,支持一对一、一对多发送方式 |
|
||||||
| | MySQL 监控 | 监视当前系统数据库连接池状态,可进行分析SQL找出系统性能瓶颈 |
|
| 🚀 | API 日志 | 包括 RESTful API 访问日志、异常日志两部分,方便排查 API 相关的问题 |
|
||||||
| | Redis 监控 | 监控 Redis 数据库的使用情况,使用的 Redis Key 管理 |
|
| | MySQL 监控 | 监视当前系统数据库连接池状态,可进行分析SQL找出系统性能瓶颈 |
|
||||||
| 🚀 | 消息队列 | 基于 Redis 实现消息队列,Stream 提供集群消费,Pub/Sub 提供广播消费 |
|
| | Redis 监控 | 监控 Redis 数据库的使用情况,使用的 Redis Key 管理 |
|
||||||
| 🚀 | Java 监控 | 基于 Spring Boot Admin 实现 Java 应用的监控 |
|
| 🚀 | 消息队列 | 基于 Redis 实现消息队列,Stream 提供集群消费,Pub/Sub 提供广播消费 |
|
||||||
| 🚀 | 链路追踪 | 接入 SkyWalking 组件,实现链路追踪 |
|
| 🚀 | Java 监控 | 基于 Spring Boot Admin 实现 Java 应用的监控 |
|
||||||
| 🚀 | 日志中心 | 接入 SkyWalking 组件,实现日志中心 |
|
| 🚀 | 链路追踪 | 接入 SkyWalking 组件,实现链路追踪 |
|
||||||
| 🚀 | 服务保障 | 基于 Redis 实现分布式锁、幂等、限流功能,满足高并发场景 |
|
| 🚀 | 日志中心 | 接入 SkyWalking 组件,实现日志中心 |
|
||||||
| 🚀 | 日志服务 | 轻量级日志中心,查看远程服务器的日志 |
|
| 🚀 | 服务保障 | 基于 Redis 实现分布式锁、幂等、限流功能,满足高并发场景 |
|
||||||
| 🚀 | 单元测试 | 基于 JUnit + Mockito 实现单元测试,保证功能的正确性、代码的质量等 |
|
| 🚀 | 日志服务 | 轻量级日志中心,查看远程服务器的日志 |
|
||||||
|
| 🚀 | 单元测试 | 基于 JUnit + Mockito 实现单元测试,保证功能的正确性、代码的质量等 |
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
@ -213,6 +233,14 @@ ps:核心功能已经实现,正在对接微信小程序中...
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
### AI 大模型
|
||||||
|
|
||||||
|
演示地址:<https://doc.iocoder.cn/ai-preview/>
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
## 🐷 演示图
|
## 🐷 演示图
|
||||||
|
|
||||||
### 系统功能
|
### 系统功能
|
||||||
|
@ -114,7 +114,8 @@ const include = [
|
|||||||
'element-plus/es/components/segmented/style/css',
|
'element-plus/es/components/segmented/style/css',
|
||||||
'@element-plus/icons-vue',
|
'@element-plus/icons-vue',
|
||||||
'element-plus/es/components/footer/style/css',
|
'element-plus/es/components/footer/style/css',
|
||||||
'element-plus/es/components/empty/style/css'
|
'element-plus/es/components/empty/style/css',
|
||||||
|
'element-plus/es/components/mention/style/css'
|
||||||
]
|
]
|
||||||
|
|
||||||
const exclude = ['@iconify/json']
|
const exclude = ['@iconify/json']
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "yudao-ui-admin-vue3",
|
"name": "yudao-ui-admin-vue3",
|
||||||
"version": "2.4.0-snapshot",
|
"version": "2.4.1-snapshot",
|
||||||
"description": "基于vue3、vite4、element-plus、typesScript",
|
"description": "基于vue3、vite4、element-plus、typesScript",
|
||||||
"author": "xingyu",
|
"author": "xingyu",
|
||||||
"private": false,
|
"private": false,
|
||||||
@ -38,7 +38,7 @@
|
|||||||
"animate.css": "^4.1.1",
|
"animate.css": "^4.1.1",
|
||||||
"axios": "^1.6.8",
|
"axios": "^1.6.8",
|
||||||
"benz-amr-recorder": "^1.1.5",
|
"benz-amr-recorder": "^1.1.5",
|
||||||
"bpmn-js-token-simulation": "^0.10.0",
|
"bpmn-js-token-simulation": "^0.36.0",
|
||||||
"camunda-bpmn-moddle": "^7.0.1",
|
"camunda-bpmn-moddle": "^7.0.1",
|
||||||
"cropperjs": "^1.6.1",
|
"cropperjs": "^1.6.1",
|
||||||
"crypto-js": "^4.2.0",
|
"crypto-js": "^4.2.0",
|
||||||
@ -74,6 +74,7 @@
|
|||||||
"vue-i18n": "9.10.2",
|
"vue-i18n": "9.10.2",
|
||||||
"vue-router": "4.4.5",
|
"vue-router": "4.4.5",
|
||||||
"vue-types": "^5.1.1",
|
"vue-types": "^5.1.1",
|
||||||
|
"vue3-signature": "^0.2.4",
|
||||||
"vuedraggable": "^4.1.0",
|
"vuedraggable": "^4.1.0",
|
||||||
"web-storage-cache": "^1.1.1",
|
"web-storage-cache": "^1.1.1",
|
||||||
"xml-js": "^1.6.11"
|
"xml-js": "^1.6.11"
|
||||||
@ -92,6 +93,7 @@
|
|||||||
"@typescript-eslint/eslint-plugin": "^7.1.0",
|
"@typescript-eslint/eslint-plugin": "^7.1.0",
|
||||||
"@typescript-eslint/parser": "^7.1.0",
|
"@typescript-eslint/parser": "^7.1.0",
|
||||||
"@unocss/eslint-config": "^0.57.4",
|
"@unocss/eslint-config": "^0.57.4",
|
||||||
|
"@unocss/eslint-plugin": "66.1.0-beta.5",
|
||||||
"@unocss/transformer-variant-group": "^0.58.5",
|
"@unocss/transformer-variant-group": "^0.58.5",
|
||||||
"@vitejs/plugin-legacy": "^5.3.1",
|
"@vitejs/plugin-legacy": "^5.3.1",
|
||||||
"@vitejs/plugin-vue": "^5.0.4",
|
"@vitejs/plugin-vue": "^5.0.4",
|
||||||
|
2933
pnpm-lock.yaml
generated
2933
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@ -14,9 +14,16 @@ export interface ChatMessageVO {
|
|||||||
modelId: number // 模型编号
|
modelId: number // 模型编号
|
||||||
content: string // 聊天内容
|
content: string // 聊天内容
|
||||||
tokens: number // 消耗 Token 数量
|
tokens: number // 消耗 Token 数量
|
||||||
|
segmentIds?: number[] // 段落编号
|
||||||
|
segments?: {
|
||||||
|
id: number // 段落编号
|
||||||
|
content: string // 段落内容
|
||||||
|
documentId: number // 文档编号
|
||||||
|
documentName: string // 文档名称
|
||||||
|
}[]
|
||||||
createTime: Date // 创建时间
|
createTime: Date // 创建时间
|
||||||
roleAvatar: string // 角色头像
|
roleAvatar: string // 角色头像
|
||||||
userAvatar: string // 创建时间
|
userAvatar: string // 用户头像
|
||||||
}
|
}
|
||||||
|
|
||||||
// AI chat 聊天
|
// AI chat 聊天
|
||||||
|
@ -20,9 +20,8 @@ export interface ImageVO {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface ImageDrawReqVO {
|
export interface ImageDrawReqVO {
|
||||||
platform: string // 平台
|
|
||||||
prompt: string // 提示词
|
prompt: string // 提示词
|
||||||
model: string // 模型
|
modelId: number // 模型
|
||||||
style: string // 图像生成的风格
|
style: string // 图像生成的风格
|
||||||
width: string // 图片宽度
|
width: string // 图片宽度
|
||||||
height: string // 图片高度
|
height: string // 图片高度
|
||||||
@ -31,7 +30,7 @@ export interface ImageDrawReqVO {
|
|||||||
|
|
||||||
export interface ImageMidjourneyImagineReqVO {
|
export interface ImageMidjourneyImagineReqVO {
|
||||||
prompt: string // 提示词
|
prompt: string // 提示词
|
||||||
model: string // 模型 mj nijj
|
modelId: number // 模型
|
||||||
base64Array: string[] // size不能为空
|
base64Array: string[] // size不能为空
|
||||||
width: string // 图片宽度
|
width: string // 图片宽度
|
||||||
height: string // 图片高度
|
height: string // 图片高度
|
||||||
|
54
src/api/ai/knowledge/document/index.ts
Normal file
54
src/api/ai/knowledge/document/index.ts
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
import request from '@/config/axios'
|
||||||
|
|
||||||
|
// AI 知识库文档 VO
|
||||||
|
export interface KnowledgeDocumentVO {
|
||||||
|
id: number // 编号
|
||||||
|
knowledgeId: number // 知识库编号
|
||||||
|
name: string // 文档名称
|
||||||
|
contentLength: number // 字符数
|
||||||
|
tokens: number // token 数
|
||||||
|
segmentMaxTokens: number // 分片最大 token 数
|
||||||
|
retrievalCount: number // 召回次数
|
||||||
|
status: number // 是否启用
|
||||||
|
}
|
||||||
|
|
||||||
|
// AI 知识库文档 API
|
||||||
|
export const KnowledgeDocumentApi = {
|
||||||
|
// 查询知识库文档分页
|
||||||
|
getKnowledgeDocumentPage: async (params: any) => {
|
||||||
|
return await request.get({ url: `/ai/knowledge/document/page`, params })
|
||||||
|
},
|
||||||
|
|
||||||
|
// 查询知识库文档详情
|
||||||
|
getKnowledgeDocument: async (id: number) => {
|
||||||
|
return await request.get({ url: `/ai/knowledge/document/get?id=` + id })
|
||||||
|
},
|
||||||
|
|
||||||
|
// 新增知识库文档(单个)
|
||||||
|
createKnowledgeDocument: async (data: any) => {
|
||||||
|
return await request.post({ url: `/ai/knowledge/document/create`, data })
|
||||||
|
},
|
||||||
|
|
||||||
|
// 新增知识库文档(多个)
|
||||||
|
createKnowledgeDocumentList: async (data: any) => {
|
||||||
|
return await request.post({ url: `/ai/knowledge/document/create-list`, data })
|
||||||
|
},
|
||||||
|
|
||||||
|
// 修改知识库文档
|
||||||
|
updateKnowledgeDocument: async (data: any) => {
|
||||||
|
return await request.put({ url: `/ai/knowledge/document/update`, data })
|
||||||
|
},
|
||||||
|
|
||||||
|
// 修改知识库文档状态
|
||||||
|
updateKnowledgeDocumentStatus: async (data: any) => {
|
||||||
|
return await request.put({
|
||||||
|
url: `/ai/knowledge/document/update-status`,
|
||||||
|
data
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
// 删除知识库文档
|
||||||
|
deleteKnowledgeDocument: async (id: number) => {
|
||||||
|
return await request.delete({ url: `/ai/knowledge/document/delete?id=` + id })
|
||||||
|
}
|
||||||
|
}
|
44
src/api/ai/knowledge/knowledge/index.ts
Normal file
44
src/api/ai/knowledge/knowledge/index.ts
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
import request from '@/config/axios'
|
||||||
|
|
||||||
|
// AI 知识库 VO
|
||||||
|
export interface KnowledgeVO {
|
||||||
|
id: number // 编号
|
||||||
|
name: string // 知识库名称
|
||||||
|
description: string // 知识库描述
|
||||||
|
embeddingModelId: number // 嵌入模型编号,高质量模式时维护
|
||||||
|
topK: number // topK
|
||||||
|
similarityThreshold: number // 相似度阈值
|
||||||
|
}
|
||||||
|
|
||||||
|
// AI 知识库 API
|
||||||
|
export const KnowledgeApi = {
|
||||||
|
// 查询知识库分页
|
||||||
|
getKnowledgePage: async (params: any) => {
|
||||||
|
return await request.get({ url: `/ai/knowledge/page`, params })
|
||||||
|
},
|
||||||
|
|
||||||
|
// 查询知识库详情
|
||||||
|
getKnowledge: async (id: number) => {
|
||||||
|
return await request.get({ url: `/ai/knowledge/get?id=` + id })
|
||||||
|
},
|
||||||
|
|
||||||
|
// 新增知识库
|
||||||
|
createKnowledge: async (data: KnowledgeVO) => {
|
||||||
|
return await request.post({ url: `/ai/knowledge/create`, data })
|
||||||
|
},
|
||||||
|
|
||||||
|
// 修改知识库
|
||||||
|
updateKnowledge: async (data: KnowledgeVO) => {
|
||||||
|
return await request.put({ url: `/ai/knowledge/update`, data })
|
||||||
|
},
|
||||||
|
|
||||||
|
// 删除知识库
|
||||||
|
deleteKnowledge: async (id: number) => {
|
||||||
|
return await request.delete({ url: `/ai/knowledge/delete?id=` + id })
|
||||||
|
},
|
||||||
|
|
||||||
|
// 获取知识库简单列表
|
||||||
|
getSimpleKnowledgeList: async () => {
|
||||||
|
return await request.get({ url: `/ai/knowledge/simple-list` })
|
||||||
|
}
|
||||||
|
}
|
75
src/api/ai/knowledge/segment/index.ts
Normal file
75
src/api/ai/knowledge/segment/index.ts
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
import request from '@/config/axios'
|
||||||
|
|
||||||
|
// AI 知识库分段 VO
|
||||||
|
export interface KnowledgeSegmentVO {
|
||||||
|
id: number // 编号
|
||||||
|
documentId: number // 文档编号
|
||||||
|
knowledgeId: number // 知识库编号
|
||||||
|
vectorId: string // 向量库编号
|
||||||
|
content: string // 切片内容
|
||||||
|
contentLength: number // 切片内容长度
|
||||||
|
tokens: number // token 数量
|
||||||
|
retrievalCount: number // 召回次数
|
||||||
|
status: number // 文档状态
|
||||||
|
createTime: number // 创建时间
|
||||||
|
}
|
||||||
|
|
||||||
|
// AI 知识库分段 API
|
||||||
|
export const KnowledgeSegmentApi = {
|
||||||
|
// 查询知识库分段分页
|
||||||
|
getKnowledgeSegmentPage: async (params: any) => {
|
||||||
|
return await request.get({ url: `/ai/knowledge/segment/page`, params })
|
||||||
|
},
|
||||||
|
|
||||||
|
// 查询知识库分段详情
|
||||||
|
getKnowledgeSegment: async (id: number) => {
|
||||||
|
return await request.get({ url: `/ai/knowledge/segment/get?id=` + id })
|
||||||
|
},
|
||||||
|
|
||||||
|
// 删除知识库分段
|
||||||
|
deleteKnowledgeSegment: async (id: number) => {
|
||||||
|
return await request.delete({ url: `/ai/knowledge/segment/delete?id=` + id })
|
||||||
|
},
|
||||||
|
|
||||||
|
// 新增知识库分段
|
||||||
|
createKnowledgeSegment: async (data: KnowledgeSegmentVO) => {
|
||||||
|
return await request.post({ url: `/ai/knowledge/segment/create`, data })
|
||||||
|
},
|
||||||
|
|
||||||
|
// 修改知识库分段
|
||||||
|
updateKnowledgeSegment: async (data: KnowledgeSegmentVO) => {
|
||||||
|
return await request.put({ url: `/ai/knowledge/segment/update`, data })
|
||||||
|
},
|
||||||
|
|
||||||
|
// 修改知识库分段状态
|
||||||
|
updateKnowledgeSegmentStatus: async (data: any) => {
|
||||||
|
return await request.put({
|
||||||
|
url: `/ai/knowledge/segment/update-status`,
|
||||||
|
data
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
// 切片内容
|
||||||
|
splitContent: async (url: string, segmentMaxTokens: number) => {
|
||||||
|
return await request.get({
|
||||||
|
url: `/ai/knowledge/segment/split`,
|
||||||
|
params: { url, segmentMaxTokens }
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
// 获取文档处理列表
|
||||||
|
getKnowledgeSegmentProcessList: async (documentIds: number[]) => {
|
||||||
|
return await request.get({
|
||||||
|
url: `/ai/knowledge/segment/get-process-list`,
|
||||||
|
params: { documentIds: documentIds.join(',') }
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
// 搜索知识库分段
|
||||||
|
searchKnowledgeSegment: async (params: any) => {
|
||||||
|
return await request.get({
|
||||||
|
url: `/ai/knowledge/segment/search`,
|
||||||
|
params
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
@ -1,53 +0,0 @@
|
|||||||
import request from '@/config/axios'
|
|
||||||
|
|
||||||
// AI 聊天模型 VO
|
|
||||||
export interface ChatModelVO {
|
|
||||||
id: number // 编号
|
|
||||||
keyId: number // API 秘钥编号
|
|
||||||
name: string // 模型名字
|
|
||||||
model: string // 模型标识
|
|
||||||
platform: string // 模型平台
|
|
||||||
sort: number // 排序
|
|
||||||
status: number // 状态
|
|
||||||
temperature: number // 温度参数
|
|
||||||
maxTokens: number // 单条回复的最大 Token 数量
|
|
||||||
maxContexts: number // 上下文的最大 Message 数量
|
|
||||||
}
|
|
||||||
|
|
||||||
// AI 聊天模型 API
|
|
||||||
export const ChatModelApi = {
|
|
||||||
// 查询聊天模型分页
|
|
||||||
getChatModelPage: async (params: any) => {
|
|
||||||
return await request.get({ url: `/ai/chat-model/page`, params })
|
|
||||||
},
|
|
||||||
|
|
||||||
// 获得聊天模型列表
|
|
||||||
getChatModelSimpleList: async (status?: number) => {
|
|
||||||
return await request.get({
|
|
||||||
url: `/ai/chat-model/simple-list`,
|
|
||||||
params: {
|
|
||||||
status
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
// 查询聊天模型详情
|
|
||||||
getChatModel: async (id: number) => {
|
|
||||||
return await request.get({ url: `/ai/chat-model/get?id=` + id })
|
|
||||||
},
|
|
||||||
|
|
||||||
// 新增聊天模型
|
|
||||||
createChatModel: async (data: ChatModelVO) => {
|
|
||||||
return await request.post({ url: `/ai/chat-model/create`, data })
|
|
||||||
},
|
|
||||||
|
|
||||||
// 修改聊天模型
|
|
||||||
updateChatModel: async (data: ChatModelVO) => {
|
|
||||||
return await request.put({ url: `/ai/chat-model/update`, data })
|
|
||||||
},
|
|
||||||
|
|
||||||
// 删除聊天模型
|
|
||||||
deleteChatModel: async (id: number) => {
|
|
||||||
return await request.delete({ url: `/ai/chat-model/delete?id=` + id })
|
|
||||||
}
|
|
||||||
}
|
|
@ -13,6 +13,8 @@ export interface ChatRoleVO {
|
|||||||
welcomeMessage: string // 角色设定
|
welcomeMessage: string // 角色设定
|
||||||
publicStatus: boolean // 是否公开
|
publicStatus: boolean // 是否公开
|
||||||
status: number // 状态
|
status: number // 状态
|
||||||
|
knowledgeIds?: number[] // 引用的知识库 ID 列表
|
||||||
|
toolIds?: number[] // 引用的工具 ID 列表
|
||||||
}
|
}
|
||||||
|
|
||||||
// AI 聊天角色 分页请求 vo
|
// AI 聊天角色 分页请求 vo
|
||||||
|
54
src/api/ai/model/model/index.ts
Normal file
54
src/api/ai/model/model/index.ts
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
import request from '@/config/axios'
|
||||||
|
|
||||||
|
// AI 模型 VO
|
||||||
|
export interface ModelVO {
|
||||||
|
id: number // 编号
|
||||||
|
keyId: number // API 秘钥编号
|
||||||
|
name: string // 模型名字
|
||||||
|
model: string // 模型标识
|
||||||
|
platform: string // 模型平台
|
||||||
|
type: number // 模型类型
|
||||||
|
sort: number // 排序
|
||||||
|
status: number // 状态
|
||||||
|
temperature?: number // 温度参数
|
||||||
|
maxTokens?: number // 单条回复的最大 Token 数量
|
||||||
|
maxContexts?: number // 上下文的最大 Message 数量
|
||||||
|
}
|
||||||
|
|
||||||
|
// AI 模型 API
|
||||||
|
export const ModelApi = {
|
||||||
|
// 查询模型分页
|
||||||
|
getModelPage: async (params: any) => {
|
||||||
|
return await request.get({ url: `/ai/model/page`, params })
|
||||||
|
},
|
||||||
|
|
||||||
|
// 获得模型列表
|
||||||
|
getModelSimpleList: async (type?: number) => {
|
||||||
|
return await request.get({
|
||||||
|
url: `/ai/model/simple-list`,
|
||||||
|
params: {
|
||||||
|
type
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
// 查询模型详情
|
||||||
|
getModel: async (id: number) => {
|
||||||
|
return await request.get({ url: `/ai/model/get?id=` + id })
|
||||||
|
},
|
||||||
|
|
||||||
|
// 新增模型
|
||||||
|
createModel: async (data: ModelVO) => {
|
||||||
|
return await request.post({ url: `/ai/model/create`, data })
|
||||||
|
},
|
||||||
|
|
||||||
|
// 修改模型
|
||||||
|
updateModel: async (data: ModelVO) => {
|
||||||
|
return await request.put({ url: `/ai/model/update`, data })
|
||||||
|
},
|
||||||
|
|
||||||
|
// 删除模型
|
||||||
|
deleteModel: async (id: number) => {
|
||||||
|
return await request.delete({ url: `/ai/model/delete?id=` + id })
|
||||||
|
}
|
||||||
|
}
|
42
src/api/ai/model/tool/index.ts
Normal file
42
src/api/ai/model/tool/index.ts
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
import request from '@/config/axios'
|
||||||
|
|
||||||
|
// AI 工具 VO
|
||||||
|
export interface ToolVO {
|
||||||
|
id: number // 工具编号
|
||||||
|
name: string // 工具名称
|
||||||
|
description: string // 工具描述
|
||||||
|
status: number // 状态
|
||||||
|
}
|
||||||
|
|
||||||
|
// AI 工具 API
|
||||||
|
export const ToolApi = {
|
||||||
|
// 查询工具分页
|
||||||
|
getToolPage: async (params: any) => {
|
||||||
|
return await request.get({ url: `/ai/tool/page`, params })
|
||||||
|
},
|
||||||
|
|
||||||
|
// 查询工具详情
|
||||||
|
getTool: async (id: number) => {
|
||||||
|
return await request.get({ url: `/ai/tool/get?id=` + id })
|
||||||
|
},
|
||||||
|
|
||||||
|
// 新增工具
|
||||||
|
createTool: async (data: ToolVO) => {
|
||||||
|
return await request.post({ url: `/ai/tool/create`, data })
|
||||||
|
},
|
||||||
|
|
||||||
|
// 修改工具
|
||||||
|
updateTool: async (data: ToolVO) => {
|
||||||
|
return await request.put({ url: `/ai/tool/update`, data })
|
||||||
|
},
|
||||||
|
|
||||||
|
// 删除工具
|
||||||
|
deleteTool: async (id: number) => {
|
||||||
|
return await request.delete({ url: `/ai/tool/delete?id=` + id })
|
||||||
|
},
|
||||||
|
|
||||||
|
// 获取工具简单列表
|
||||||
|
getToolSimpleList: async () => {
|
||||||
|
return await request.get({ url: `/ai/tool/simple-list` })
|
||||||
|
}
|
||||||
|
}
|
@ -20,3 +20,9 @@ export const getProcessDefinitionList = async (params) => {
|
|||||||
params
|
params
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const getSimpleProcessDefinitionList = async () => {
|
||||||
|
return await request.get({
|
||||||
|
url: '/bpm/process-definition/simple-list'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
@ -72,3 +72,7 @@ export const deleteModel = async (id: number) => {
|
|||||||
export const deployModel = async (id: number) => {
|
export const deployModel = async (id: number) => {
|
||||||
return await request.post({ url: '/bpm/model/deploy?id=' + id })
|
return await request.post({ url: '/bpm/model/deploy?id=' + id })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const cleanModel = async (id: number) => {
|
||||||
|
return await request.delete({ url: '/bpm/model/clean?id=' + id })
|
||||||
|
}
|
||||||
|
@ -36,6 +36,7 @@ export type ApprovalTaskInfo = {
|
|||||||
assigneeUser: User
|
assigneeUser: User
|
||||||
status: number
|
status: number
|
||||||
reason: string
|
reason: string
|
||||||
|
signPicUrl: string
|
||||||
}
|
}
|
||||||
|
|
||||||
// 审批节点信息
|
// 审批节点信息
|
||||||
@ -89,7 +90,12 @@ export const getProcessInstanceCopyPage = async (params: any) => {
|
|||||||
|
|
||||||
// 获取审批详情
|
// 获取审批详情
|
||||||
export const getApprovalDetail = async (params: any) => {
|
export const getApprovalDetail = async (params: any) => {
|
||||||
return await request.get({ url: 'bpm/process-instance/get-approval-detail' , params })
|
return await request.get({ url: '/bpm/process-instance/get-approval-detail', params })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取下一个执行的流程节点
|
||||||
|
export const getNextApprovalNodes = async (params: any) => {
|
||||||
|
return await request.get({ url: '/bpm/process-instance/get-next-approval-nodes', params })
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取表单字段权限
|
// 获取表单字段权限
|
||||||
|
@ -83,5 +83,5 @@ export const reqCheck = (data: any) => {
|
|||||||
|
|
||||||
// 通过短信重置密码
|
// 通过短信重置密码
|
||||||
export const smsResetPassword = (data: any) => {
|
export const smsResetPassword = (data: any) => {
|
||||||
return request.post({ url: '/system/auth/sms-reset-password', data })
|
return request.post({ url: '/system/auth/reset-password', data })
|
||||||
}
|
}
|
||||||
|
@ -101,7 +101,7 @@ export const deleteSpu = (id: number) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 导出商品 Spu Excel
|
// 导出商品 Spu Excel
|
||||||
export const exportSpu = async (params) => {
|
export const exportSpu = async (params: any) => {
|
||||||
return await request.download({ url: '/product/spu/export', params })
|
return await request.download({ url: '/product/spu/export', params })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
1
src/assets/svgs/bpm/child-process.svg
Normal file
1
src/assets/svgs/bpm/child-process.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg t="1740116949537" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1153" width="200" height="200"><path d="M440.32 296.96h283.30496v145.92h66.56V230.4H440.32V17.92H17.92v424.96H440.32V296.96zM373.76 376.32H84.48v-291.84H373.76v291.84zM586.24 588.8v143.36512H298.66496V586.24h-66.56v212.48512H586.24V1013.76H1008.64v-424.96h-422.4z m355.84 358.4h-289.28v-291.84H942.08v291.84z" p-id="1154" fill="#ffffff"></path></svg>
|
After Width: | Height: | Size: 465 B |
1
src/assets/svgs/bpm/transactor.svg
Normal file
1
src/assets/svgs/bpm/transactor.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg t="1739406626368" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1300" width="200" height="200"><path d="M803.221 925.573H224.356c-68.568 0-124.352-55.784-124.352-124.353V222.356c0-68.568 55.784-124.352 124.352-124.352h355.311v64H224.356c-33.278 0-60.352 27.074-60.352 60.352V801.22c0 33.278 27.074 60.353 60.352 60.353H803.22c33.278 0 60.353-27.074 60.353-60.353V448.208h64V801.22c0 68.569-55.784 124.353-124.352 124.353z" fill="#ffffff" p-id="1301"></path><path d="M300.357 756.916l35.024-195.867L770.117 84.404c10.05-11.02 25.015-18.052 41.058-19.293 16.017-1.247 31.987 3.379 43.841 12.667l83.662 65.549c21.643 16.956 24.254 45.964 5.942 66.038l-437.613 479.8-206.65 67.751z m104.994-170.751l-13.14 73.487 69.671-22.842 415.465-455.517-59.909-46.939-412.087 451.811z" fill="#ffffff" p-id="1302"></path><path d="M732.25 220.897l41.144-49.023 81.151 68.11-41.145 49.023z" fill="#ffffff" p-id="1303"></path></svg>
|
After Width: | Height: | Size: 964 B |
@ -51,7 +51,8 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ComponentStyle, usePropertyForm } from '@/components/DiyEditor/util'
|
import { ComponentStyle } from '@/components/DiyEditor/util'
|
||||||
|
import { useVModel } from '@vueuse/core'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 组件容器属性:目前右边部分
|
* 组件容器属性:目前右边部分
|
||||||
@ -61,7 +62,7 @@ defineOptions({ name: 'ComponentContainer' })
|
|||||||
|
|
||||||
const props = defineProps<{ modelValue: ComponentStyle }>()
|
const props = defineProps<{ modelValue: ComponentStyle }>()
|
||||||
const emit = defineEmits(['update:modelValue'])
|
const emit = defineEmits(['update:modelValue'])
|
||||||
const { formData } = usePropertyForm(props.modelValue, emit)
|
const formData = useVModel(props, 'modelValue', emit)
|
||||||
|
|
||||||
const treeData = [
|
const treeData = [
|
||||||
{
|
{
|
||||||
|
@ -93,14 +93,14 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { CarouselProperty } from './config'
|
import { CarouselProperty } from './config'
|
||||||
import { usePropertyForm } from '@/components/DiyEditor/util'
|
import { useVModel } from '@vueuse/core'
|
||||||
|
|
||||||
// 轮播图属性面板
|
// 轮播图属性面板
|
||||||
defineOptions({ name: 'CarouselProperty' })
|
defineOptions({ name: 'CarouselProperty' })
|
||||||
|
|
||||||
const props = defineProps<{ modelValue: CarouselProperty }>()
|
const props = defineProps<{ modelValue: CarouselProperty }>()
|
||||||
const emit = defineEmits(['update:modelValue'])
|
const emit = defineEmits(['update:modelValue'])
|
||||||
const { formData } = usePropertyForm(props.modelValue, emit)
|
const formData = useVModel(props, 'modelValue', emit)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss"></style>
|
<style scoped lang="scss"></style>
|
||||||
|
@ -68,15 +68,20 @@
|
|||||||
</el-form>
|
</el-form>
|
||||||
</ComponentContainerProperty>
|
</ComponentContainerProperty>
|
||||||
<!-- 优惠券选择 -->
|
<!-- 优惠券选择 -->
|
||||||
<CouponSelect ref="couponSelectDialog" v-model:multiple-selection="couponList" />
|
<CouponSelect
|
||||||
|
ref="couponSelectDialog"
|
||||||
|
v-model:multiple-selection="couponList"
|
||||||
|
:take-type="CouponTemplateTakeTypeEnum.USER.type"
|
||||||
|
@change="handleCouponSelect"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { CouponCardProperty } from './config'
|
import { CouponCardProperty } from './config'
|
||||||
import { usePropertyForm } from '@/components/DiyEditor/util'
|
import { useVModel } from '@vueuse/core'
|
||||||
import * as CouponTemplateApi from '@/api/mall/promotion/coupon/couponTemplate'
|
import * as CouponTemplateApi from '@/api/mall/promotion/coupon/couponTemplate'
|
||||||
import { floatToFixed2 } from '@/utils'
|
import { floatToFixed2 } from '@/utils'
|
||||||
import { PromotionDiscountTypeEnum } from '@/utils/constants'
|
import { CouponTemplateTakeTypeEnum, PromotionDiscountTypeEnum } from '@/utils/constants'
|
||||||
import CouponSelect from '@/views/mall/promotion/coupon/components/CouponSelect.vue'
|
import CouponSelect from '@/views/mall/promotion/coupon/components/CouponSelect.vue'
|
||||||
|
|
||||||
// 优惠券卡片属性面板
|
// 优惠券卡片属性面板
|
||||||
@ -84,7 +89,7 @@ defineOptions({ name: 'CouponCardProperty' })
|
|||||||
|
|
||||||
const props = defineProps<{ modelValue: CouponCardProperty }>()
|
const props = defineProps<{ modelValue: CouponCardProperty }>()
|
||||||
const emit = defineEmits(['update:modelValue'])
|
const emit = defineEmits(['update:modelValue'])
|
||||||
const { formData } = usePropertyForm(props.modelValue, emit)
|
const formData = useVModel(props, 'modelValue', emit)
|
||||||
|
|
||||||
// 优惠券列表
|
// 优惠券列表
|
||||||
const couponList = ref<CouponTemplateApi.CouponTemplateVO[]>([])
|
const couponList = ref<CouponTemplateApi.CouponTemplateVO[]>([])
|
||||||
@ -93,10 +98,20 @@ const couponSelectDialog = ref()
|
|||||||
const handleAddCoupon = () => {
|
const handleAddCoupon = () => {
|
||||||
couponSelectDialog.value.open()
|
couponSelectDialog.value.open()
|
||||||
}
|
}
|
||||||
|
const handleCouponSelect = () => {
|
||||||
|
formData.value.couponIds = couponList.value.map((coupon) => coupon.id)
|
||||||
|
}
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => couponList.value,
|
() => formData.value.couponIds,
|
||||||
() => {
|
async () => {
|
||||||
formData.value.couponIds = couponList.value.map((coupon) => coupon.id)
|
if (formData.value.couponIds?.length > 0) {
|
||||||
|
couponList.value = await CouponTemplateApi.getCouponTemplateList(formData.value.couponIds)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
immediate: true,
|
||||||
|
deep: true
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
</script>
|
</script>
|
||||||
|
@ -45,12 +45,12 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { DividerProperty } from './config'
|
import { DividerProperty } from './config'
|
||||||
import { usePropertyForm } from '@/components/DiyEditor/util'
|
import { useVModel } from '@vueuse/core'
|
||||||
// 导航栏属性面板
|
// 导航栏属性面板
|
||||||
defineOptions({ name: 'DividerProperty' })
|
defineOptions({ name: 'DividerProperty' })
|
||||||
const props = defineProps<{ modelValue: DividerProperty }>()
|
const props = defineProps<{ modelValue: DividerProperty }>()
|
||||||
const emit = defineEmits(['update:modelValue'])
|
const emit = defineEmits(['update:modelValue'])
|
||||||
const { formData } = usePropertyForm(props.modelValue, emit)
|
const formData = useVModel(props, 'modelValue', emit)
|
||||||
|
|
||||||
//线类型
|
//线类型
|
||||||
const BORDER_TYPES = [
|
const BORDER_TYPES = [
|
||||||
|
@ -31,14 +31,14 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { FloatingActionButtonProperty } from './config'
|
import { FloatingActionButtonProperty } from './config'
|
||||||
import { usePropertyForm } from '@/components/DiyEditor/util'
|
import { useVModel } from '@vueuse/core'
|
||||||
|
|
||||||
// 悬浮按钮属性面板
|
// 悬浮按钮属性面板
|
||||||
defineOptions({ name: 'FloatingActionButtonProperty' })
|
defineOptions({ name: 'FloatingActionButtonProperty' })
|
||||||
|
|
||||||
const props = defineProps<{ modelValue: FloatingActionButtonProperty }>()
|
const props = defineProps<{ modelValue: FloatingActionButtonProperty }>()
|
||||||
const emit = defineEmits(['update:modelValue'])
|
const emit = defineEmits(['update:modelValue'])
|
||||||
const { formData } = usePropertyForm(props.modelValue, emit)
|
const formData = useVModel(props, 'modelValue', emit)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss"></style>
|
<style scoped lang="scss"></style>
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { usePropertyForm } from '@/components/DiyEditor/util'
|
import { useVModel } from '@vueuse/core'
|
||||||
import { HotZoneProperty } from '@/components/DiyEditor/components/mobile/HotZone/config'
|
import { HotZoneProperty } from '@/components/DiyEditor/components/mobile/HotZone/config'
|
||||||
import HotZoneEditDialog from './components/HotZoneEditDialog/index.vue'
|
import HotZoneEditDialog from './components/HotZoneEditDialog/index.vue'
|
||||||
|
|
||||||
@ -29,7 +29,7 @@ defineOptions({ name: 'HotZoneProperty' })
|
|||||||
|
|
||||||
const props = defineProps<{ modelValue: HotZoneProperty }>()
|
const props = defineProps<{ modelValue: HotZoneProperty }>()
|
||||||
const emit = defineEmits(['update:modelValue'])
|
const emit = defineEmits(['update:modelValue'])
|
||||||
const { formData } = usePropertyForm(props.modelValue, emit)
|
const formData = useVModel(props, 'modelValue', emit)
|
||||||
|
|
||||||
// 热区编辑对话框
|
// 热区编辑对话框
|
||||||
const editDialogRef = ref()
|
const editDialogRef = ref()
|
||||||
|
@ -21,14 +21,14 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ImageBarProperty } from './config'
|
import { ImageBarProperty } from './config'
|
||||||
import { usePropertyForm } from '@/components/DiyEditor/util'
|
import { useVModel } from '@vueuse/core'
|
||||||
|
|
||||||
// 图片展示属性面板
|
// 图片展示属性面板
|
||||||
defineOptions({ name: 'ImageBarProperty' })
|
defineOptions({ name: 'ImageBarProperty' })
|
||||||
|
|
||||||
const props = defineProps<{ modelValue: ImageBarProperty }>()
|
const props = defineProps<{ modelValue: ImageBarProperty }>()
|
||||||
const emit = defineEmits(['update:modelValue'])
|
const emit = defineEmits(['update:modelValue'])
|
||||||
const { formData } = usePropertyForm(props.modelValue, emit)
|
const formData = useVModel(props, 'modelValue', emit)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss"></style>
|
<style scoped lang="scss"></style>
|
||||||
|
@ -56,7 +56,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { usePropertyForm } from '@/components/DiyEditor/util'
|
import { useVModel } from '@vueuse/core'
|
||||||
import { MagicCubeProperty } from '@/components/DiyEditor/components/mobile/MagicCube/config'
|
import { MagicCubeProperty } from '@/components/DiyEditor/components/mobile/MagicCube/config'
|
||||||
|
|
||||||
/** 广告魔方属性面板 */
|
/** 广告魔方属性面板 */
|
||||||
@ -64,7 +64,7 @@ defineOptions({ name: 'MagicCubeProperty' })
|
|||||||
|
|
||||||
const props = defineProps<{ modelValue: MagicCubeProperty }>()
|
const props = defineProps<{ modelValue: MagicCubeProperty }>()
|
||||||
const emit = defineEmits(['update:modelValue'])
|
const emit = defineEmits(['update:modelValue'])
|
||||||
const { formData } = usePropertyForm(props.modelValue, emit)
|
const formData = useVModel(props, 'modelValue', emit)
|
||||||
|
|
||||||
// 选中的热区
|
// 选中的热区
|
||||||
const selectedHotAreaIndex = ref(-1)
|
const selectedHotAreaIndex = ref(-1)
|
||||||
|
@ -48,7 +48,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { usePropertyForm } from '@/components/DiyEditor/util'
|
import { useVModel } from '@vueuse/core'
|
||||||
import {
|
import {
|
||||||
EMPTY_MENU_GRID_ITEM_PROPERTY,
|
EMPTY_MENU_GRID_ITEM_PROPERTY,
|
||||||
MenuGridProperty
|
MenuGridProperty
|
||||||
@ -59,7 +59,7 @@ defineOptions({ name: 'MenuGridProperty' })
|
|||||||
|
|
||||||
const props = defineProps<{ modelValue: MenuGridProperty }>()
|
const props = defineProps<{ modelValue: MenuGridProperty }>()
|
||||||
const emit = defineEmits(['update:modelValue'])
|
const emit = defineEmits(['update:modelValue'])
|
||||||
const { formData } = usePropertyForm(props.modelValue, emit)
|
const formData = useVModel(props, 'modelValue', emit)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss"></style>
|
<style scoped lang="scss"></style>
|
||||||
|
@ -28,7 +28,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { usePropertyForm } from '@/components/DiyEditor/util'
|
import { useVModel } from '@vueuse/core'
|
||||||
import {
|
import {
|
||||||
EMPTY_MENU_LIST_ITEM_PROPERTY,
|
EMPTY_MENU_LIST_ITEM_PROPERTY,
|
||||||
MenuListProperty
|
MenuListProperty
|
||||||
@ -39,7 +39,7 @@ defineOptions({ name: 'MenuListProperty' })
|
|||||||
|
|
||||||
const props = defineProps<{ modelValue: MenuListProperty }>()
|
const props = defineProps<{ modelValue: MenuListProperty }>()
|
||||||
const emit = defineEmits(['update:modelValue'])
|
const emit = defineEmits(['update:modelValue'])
|
||||||
const { formData } = usePropertyForm(props.modelValue, emit)
|
const formData = useVModel(props, 'modelValue', emit)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss"></style>
|
<style scoped lang="scss"></style>
|
||||||
|
@ -58,7 +58,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { usePropertyForm } from '@/components/DiyEditor/util'
|
import { useVModel } from '@vueuse/core'
|
||||||
import {
|
import {
|
||||||
EMPTY_MENU_SWIPER_ITEM_PROPERTY,
|
EMPTY_MENU_SWIPER_ITEM_PROPERTY,
|
||||||
MenuSwiperProperty
|
MenuSwiperProperty
|
||||||
@ -70,7 +70,7 @@ defineOptions({ name: 'MenuSwiperProperty' })
|
|||||||
|
|
||||||
const props = defineProps<{ modelValue: MenuSwiperProperty }>()
|
const props = defineProps<{ modelValue: MenuSwiperProperty }>()
|
||||||
const emit = defineEmits(['update:modelValue'])
|
const emit = defineEmits(['update:modelValue'])
|
||||||
const { formData } = usePropertyForm(props.modelValue, emit)
|
const formData = useVModel(props, 'modelValue', emit)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss"></style>
|
<style scoped lang="scss"></style>
|
||||||
|
@ -64,17 +64,22 @@
|
|||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { NavigationBarCellProperty } from '../config'
|
import { NavigationBarCellProperty } from '../config'
|
||||||
import { usePropertyForm } from '@/components/DiyEditor/util'
|
import { useVModel } from '@vueuse/core'
|
||||||
// 导航栏属性面板
|
// 导航栏属性面板
|
||||||
defineOptions({ name: 'NavigationBarCellProperty' })
|
defineOptions({ name: 'NavigationBarCellProperty' })
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = withDefaults(
|
||||||
modelValue: NavigationBarCellProperty[]
|
defineProps<{
|
||||||
isMp: boolean
|
modelValue: NavigationBarCellProperty[]
|
||||||
}>()
|
isMp: boolean
|
||||||
|
}>(),
|
||||||
|
{
|
||||||
|
modelValue: () => [],
|
||||||
|
isMp: true
|
||||||
|
}
|
||||||
|
)
|
||||||
const emit = defineEmits(['update:modelValue'])
|
const emit = defineEmits(['update:modelValue'])
|
||||||
const { formData: cellList } = usePropertyForm(props.modelValue, emit)
|
const cellList = useVModel(props, 'modelValue', emit)
|
||||||
if (!cellList.value) cellList.value = []
|
|
||||||
|
|
||||||
// 单元格数量:小程序6个(右侧胶囊按钮占了2个),其它平台8个
|
// 单元格数量:小程序6个(右侧胶囊按钮占了2个),其它平台8个
|
||||||
const cellCount = computed(() => (props.isMp ? 6 : 8))
|
const cellCount = computed(() => (props.isMp ? 6 : 8))
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
<div v-for="(cell, cellIndex) in cellList" :key="cellIndex" :style="getCellStyle(cell)">
|
<div v-for="(cell, cellIndex) in cellList" :key="cellIndex" :style="getCellStyle(cell)">
|
||||||
<span v-if="cell.type === 'text'">{{ cell.text }}</span>
|
<span v-if="cell.type === 'text'">{{ cell.text }}</span>
|
||||||
<img v-else-if="cell.type === 'image'" :src="cell.imgUrl" alt="" class="h-full w-full" />
|
<img v-else-if="cell.type === 'image'" :src="cell.imgUrl" alt="" class="h-full w-full" />
|
||||||
<SearchBar v-else :property="getSearchProp" />
|
<SearchBar v-else :property="getSearchProp(cell)" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<img
|
<img
|
||||||
@ -51,14 +51,14 @@ const getCellStyle = (cell: NavigationBarCellProperty) => {
|
|||||||
} as StyleValue
|
} as StyleValue
|
||||||
}
|
}
|
||||||
// 获得搜索框属性
|
// 获得搜索框属性
|
||||||
const getSearchProp = (cell: NavigationBarCellProperty) => {
|
const getSearchProp = computed(() => (cell: NavigationBarCellProperty) => {
|
||||||
return {
|
return {
|
||||||
height: 30,
|
height: 30,
|
||||||
showScan: false,
|
showScan: false,
|
||||||
placeholder: cell.placeholder,
|
placeholder: cell.placeholder,
|
||||||
borderRadius: cell.borderRadius
|
borderRadius: cell.borderRadius
|
||||||
} as SearchProperty
|
} as SearchProperty
|
||||||
}
|
})
|
||||||
</script>
|
</script>
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.navigation-bar {
|
.navigation-bar {
|
||||||
|
@ -66,7 +66,7 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { NavigationBarProperty } from './config'
|
import { NavigationBarProperty } from './config'
|
||||||
import { usePropertyForm } from '@/components/DiyEditor/util'
|
import { useVModel } from '@vueuse/core'
|
||||||
import NavigationBarCellProperty from '@/components/DiyEditor/components/mobile/NavigationBar/components/CellProperty.vue'
|
import NavigationBarCellProperty from '@/components/DiyEditor/components/mobile/NavigationBar/components/CellProperty.vue'
|
||||||
// 导航栏属性面板
|
// 导航栏属性面板
|
||||||
defineOptions({ name: 'NavigationBarProperty' })
|
defineOptions({ name: 'NavigationBarProperty' })
|
||||||
@ -77,7 +77,7 @@ const rules = {
|
|||||||
|
|
||||||
const props = defineProps<{ modelValue: NavigationBarProperty }>()
|
const props = defineProps<{ modelValue: NavigationBarProperty }>()
|
||||||
const emit = defineEmits(['update:modelValue'])
|
const emit = defineEmits(['update:modelValue'])
|
||||||
const { formData } = usePropertyForm(props.modelValue, emit)
|
const formData = useVModel(props, 'modelValue', emit)
|
||||||
if (!formData.value._local) {
|
if (!formData.value._local) {
|
||||||
formData.value._local = { previewMp: true, previewOther: false }
|
formData.value._local = { previewMp: true, previewOther: false }
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,7 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { NoticeBarProperty } from './config'
|
import { NoticeBarProperty } from './config'
|
||||||
import { usePropertyForm } from '@/components/DiyEditor/util'
|
import { useVModel } from '@vueuse/core'
|
||||||
// 通知栏属性面板
|
// 通知栏属性面板
|
||||||
defineOptions({ name: 'NoticeBarProperty' })
|
defineOptions({ name: 'NoticeBarProperty' })
|
||||||
// 表单校验
|
// 表单校验
|
||||||
@ -40,7 +40,7 @@ const rules = {
|
|||||||
|
|
||||||
const props = defineProps<{ modelValue: NoticeBarProperty }>()
|
const props = defineProps<{ modelValue: NoticeBarProperty }>()
|
||||||
const emit = defineEmits(['update:modelValue'])
|
const emit = defineEmits(['update:modelValue'])
|
||||||
const { formData } = usePropertyForm(props.modelValue, emit)
|
const formData = useVModel(props, 'modelValue', emit)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss"></style>
|
<style scoped lang="scss"></style>
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { PageConfigProperty } from './config'
|
import { PageConfigProperty } from './config'
|
||||||
import { usePropertyForm } from '@/components/DiyEditor/util'
|
import { useVModel } from '@vueuse/core'
|
||||||
// 导航栏属性面板
|
// 导航栏属性面板
|
||||||
defineOptions({ name: 'PageConfigProperty' })
|
defineOptions({ name: 'PageConfigProperty' })
|
||||||
// 表单校验
|
// 表单校验
|
||||||
@ -28,7 +28,7 @@ const rules = {}
|
|||||||
|
|
||||||
const props = defineProps<{ modelValue: PageConfigProperty }>()
|
const props = defineProps<{ modelValue: PageConfigProperty }>()
|
||||||
const emit = defineEmits(['update:modelValue'])
|
const emit = defineEmits(['update:modelValue'])
|
||||||
const { formData } = usePropertyForm(props.modelValue, emit)
|
const formData = useVModel(props, 'modelValue', emit)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss"></style>
|
<style scoped lang="scss"></style>
|
||||||
|
@ -25,14 +25,14 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { PopoverProperty } from './config'
|
import { PopoverProperty } from './config'
|
||||||
import { usePropertyForm } from '@/components/DiyEditor/util'
|
import { useVModel } from '@vueuse/core'
|
||||||
|
|
||||||
// 弹窗广告属性面板
|
// 弹窗广告属性面板
|
||||||
defineOptions({ name: 'PopoverProperty' })
|
defineOptions({ name: 'PopoverProperty' })
|
||||||
|
|
||||||
const props = defineProps<{ modelValue: PopoverProperty }>()
|
const props = defineProps<{ modelValue: PopoverProperty }>()
|
||||||
const emit = defineEmits(['update:modelValue'])
|
const emit = defineEmits(['update:modelValue'])
|
||||||
const { formData } = usePropertyForm(props.modelValue, emit)
|
const formData = useVModel(props, 'modelValue', emit)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss"></style>
|
<style scoped lang="scss"></style>
|
||||||
|
@ -135,7 +135,7 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ProductCardProperty } from './config'
|
import { ProductCardProperty } from './config'
|
||||||
import { usePropertyForm } from '@/components/DiyEditor/util'
|
import { useVModel } from '@vueuse/core'
|
||||||
import SpuShowcase from '@/views/mall/product/spu/components/SpuShowcase.vue'
|
import SpuShowcase from '@/views/mall/product/spu/components/SpuShowcase.vue'
|
||||||
|
|
||||||
// 商品卡片属性面板
|
// 商品卡片属性面板
|
||||||
@ -143,7 +143,7 @@ defineOptions({ name: 'ProductCardProperty' })
|
|||||||
|
|
||||||
const props = defineProps<{ modelValue: ProductCardProperty }>()
|
const props = defineProps<{ modelValue: ProductCardProperty }>()
|
||||||
const emit = defineEmits(['update:modelValue'])
|
const emit = defineEmits(['update:modelValue'])
|
||||||
const { formData } = usePropertyForm(props.modelValue, emit)
|
const formData = useVModel(props, 'modelValue', emit)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss"></style>
|
<style scoped lang="scss"></style>
|
||||||
|
@ -85,7 +85,7 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ProductListProperty } from './config'
|
import { ProductListProperty } from './config'
|
||||||
import { usePropertyForm } from '@/components/DiyEditor/util'
|
import { useVModel } from '@vueuse/core'
|
||||||
import SpuShowcase from '@/views/mall/product/spu/components/SpuShowcase.vue'
|
import SpuShowcase from '@/views/mall/product/spu/components/SpuShowcase.vue'
|
||||||
|
|
||||||
// 商品栏属性面板
|
// 商品栏属性面板
|
||||||
@ -93,7 +93,7 @@ defineOptions({ name: 'ProductListProperty' })
|
|||||||
|
|
||||||
const props = defineProps<{ modelValue: ProductListProperty }>()
|
const props = defineProps<{ modelValue: ProductListProperty }>()
|
||||||
const emit = defineEmits(['update:modelValue'])
|
const emit = defineEmits(['update:modelValue'])
|
||||||
const { formData } = usePropertyForm(props.modelValue, emit)
|
const formData = useVModel(props, 'modelValue', emit)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss"></style>
|
<style scoped lang="scss"></style>
|
||||||
|
@ -25,7 +25,7 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { PromotionArticleProperty } from './config'
|
import { PromotionArticleProperty } from './config'
|
||||||
import { usePropertyForm } from '@/components/DiyEditor/util'
|
import { useVModel } from '@vueuse/core'
|
||||||
import * as ArticleApi from '@/api/mall/promotion/article/index'
|
import * as ArticleApi from '@/api/mall/promotion/article/index'
|
||||||
|
|
||||||
// 营销文章属性面板
|
// 营销文章属性面板
|
||||||
@ -33,7 +33,7 @@ defineOptions({ name: 'PromotionArticleProperty' })
|
|||||||
|
|
||||||
const props = defineProps<{ modelValue: PromotionArticleProperty }>()
|
const props = defineProps<{ modelValue: PromotionArticleProperty }>()
|
||||||
const emit = defineEmits(['update:modelValue'])
|
const emit = defineEmits(['update:modelValue'])
|
||||||
const { formData } = usePropertyForm(props.modelValue, emit)
|
const formData = useVModel(props, 'modelValue', emit)
|
||||||
// 文章列表
|
// 文章列表
|
||||||
const articles = ref<ArticleApi.ArticleVO>([])
|
const articles = ref<ArticleApi.ArticleVO>([])
|
||||||
|
|
||||||
|
@ -140,7 +140,7 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { PromotionCombinationProperty } from './config'
|
import { PromotionCombinationProperty } from './config'
|
||||||
import { usePropertyForm } from '@/components/DiyEditor/util'
|
import { useVModel } from '@vueuse/core'
|
||||||
import * as CombinationActivityApi from '@/api/mall/promotion/combination/combinationActivity'
|
import * as CombinationActivityApi from '@/api/mall/promotion/combination/combinationActivity'
|
||||||
import { CommonStatusEnum } from '@/utils/constants'
|
import { CommonStatusEnum } from '@/utils/constants'
|
||||||
import CombinationShowcase from '@/views/mall/promotion/combination/components/CombinationShowcase.vue'
|
import CombinationShowcase from '@/views/mall/promotion/combination/components/CombinationShowcase.vue'
|
||||||
@ -150,7 +150,7 @@ defineOptions({ name: 'PromotionCombinationProperty' })
|
|||||||
|
|
||||||
const props = defineProps<{ modelValue: PromotionCombinationProperty }>()
|
const props = defineProps<{ modelValue: PromotionCombinationProperty }>()
|
||||||
const emit = defineEmits(['update:modelValue'])
|
const emit = defineEmits(['update:modelValue'])
|
||||||
const { formData } = usePropertyForm(props.modelValue, emit)
|
const formData = useVModel(props, 'modelValue', emit)
|
||||||
// 活动列表
|
// 活动列表
|
||||||
const activityList = ref<CombinationActivityApi.CombinationActivityVO[]>([])
|
const activityList = ref<CombinationActivityApi.CombinationActivityVO[]>([])
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
|
@ -140,7 +140,7 @@
|
|||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { PromotionPointProperty } from './config'
|
import { PromotionPointProperty } from './config'
|
||||||
import { usePropertyForm } from '@/components/DiyEditor/util'
|
import { useVModel } from '@vueuse/core'
|
||||||
import PointShowcase from '@/views/mall/promotion/point/components/PointShowcase.vue'
|
import PointShowcase from '@/views/mall/promotion/point/components/PointShowcase.vue'
|
||||||
|
|
||||||
// 秒杀属性面板
|
// 秒杀属性面板
|
||||||
@ -148,7 +148,7 @@ defineOptions({ name: 'PromotionPointProperty' })
|
|||||||
|
|
||||||
const props = defineProps<{ modelValue: PromotionPointProperty }>()
|
const props = defineProps<{ modelValue: PromotionPointProperty }>()
|
||||||
const emit = defineEmits(['update:modelValue'])
|
const emit = defineEmits(['update:modelValue'])
|
||||||
const { formData } = usePropertyForm(props.modelValue, emit)
|
const formData = useVModel(props, 'modelValue', emit)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped></style>
|
<style lang="scss" scoped></style>
|
||||||
|
@ -140,7 +140,7 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { PromotionSeckillProperty } from './config'
|
import { PromotionSeckillProperty } from './config'
|
||||||
import { usePropertyForm } from '@/components/DiyEditor/util'
|
import { useVModel } from '@vueuse/core'
|
||||||
import * as SeckillActivityApi from '@/api/mall/promotion/seckill/seckillActivity'
|
import * as SeckillActivityApi from '@/api/mall/promotion/seckill/seckillActivity'
|
||||||
import { CommonStatusEnum } from '@/utils/constants'
|
import { CommonStatusEnum } from '@/utils/constants'
|
||||||
import SeckillShowcase from '@/views/mall/promotion/seckill/components/SeckillShowcase.vue'
|
import SeckillShowcase from '@/views/mall/promotion/seckill/components/SeckillShowcase.vue'
|
||||||
@ -150,7 +150,7 @@ defineOptions({ name: 'PromotionSeckillProperty' })
|
|||||||
|
|
||||||
const props = defineProps<{ modelValue: PromotionSeckillProperty }>()
|
const props = defineProps<{ modelValue: PromotionSeckillProperty }>()
|
||||||
const emit = defineEmits(['update:modelValue'])
|
const emit = defineEmits(['update:modelValue'])
|
||||||
const { formData } = usePropertyForm(props.modelValue, emit)
|
const formData = useVModel(props, 'modelValue', emit)
|
||||||
// 活动列表
|
// 活动列表
|
||||||
const activityList = ref<SeckillActivityApi.SeckillActivityVO[]>([])
|
const activityList = ref<SeckillActivityApi.SeckillActivityVO[]>([])
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
|
@ -59,7 +59,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { usePropertyForm } from '@/components/DiyEditor/util'
|
import { useVModel } from '@vueuse/core'
|
||||||
import { SearchProperty } from '@/components/DiyEditor/components/mobile/SearchBar/config'
|
import { SearchProperty } from '@/components/DiyEditor/components/mobile/SearchBar/config'
|
||||||
|
|
||||||
/** 搜索框属性面板 */
|
/** 搜索框属性面板 */
|
||||||
@ -67,7 +67,7 @@ defineOptions({ name: 'SearchProperty' })
|
|||||||
|
|
||||||
const props = defineProps<{ modelValue: SearchProperty }>()
|
const props = defineProps<{ modelValue: SearchProperty }>()
|
||||||
const emit = defineEmits(['update:modelValue'])
|
const emit = defineEmits(['update:modelValue'])
|
||||||
const { formData } = usePropertyForm(props.modelValue, emit)
|
const formData = useVModel(props, 'modelValue', emit)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss"></style>
|
<style scoped lang="scss"></style>
|
||||||
|
@ -80,13 +80,13 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { TabBarProperty, component, THEME_LIST } from './config'
|
import { TabBarProperty, component, THEME_LIST } from './config'
|
||||||
import { usePropertyForm } from '@/components/DiyEditor/util'
|
import { useVModel } from '@vueuse/core'
|
||||||
// 底部导航栏
|
// 底部导航栏
|
||||||
defineOptions({ name: 'TabBarProperty' })
|
defineOptions({ name: 'TabBarProperty' })
|
||||||
|
|
||||||
const props = defineProps<{ modelValue: TabBarProperty }>()
|
const props = defineProps<{ modelValue: TabBarProperty }>()
|
||||||
const emit = defineEmits(['update:modelValue'])
|
const emit = defineEmits(['update:modelValue'])
|
||||||
const { formData } = usePropertyForm(props.modelValue, emit)
|
const formData = useVModel(props, 'modelValue', emit)
|
||||||
|
|
||||||
// 将数据库的值更新到右侧属性栏
|
// 将数据库的值更新到右侧属性栏
|
||||||
component.property.items = formData.value.items
|
component.property.items = formData.value.items
|
||||||
|
@ -101,13 +101,13 @@
|
|||||||
</template>
|
</template>
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { TitleBarProperty } from './config'
|
import { TitleBarProperty } from './config'
|
||||||
import { usePropertyForm } from '@/components/DiyEditor/util'
|
import { useVModel } from '@vueuse/core'
|
||||||
// 导航栏属性面板
|
// 导航栏属性面板
|
||||||
defineOptions({ name: 'TitleBarProperty' })
|
defineOptions({ name: 'TitleBarProperty' })
|
||||||
|
|
||||||
const props = defineProps<{ modelValue: TitleBarProperty }>()
|
const props = defineProps<{ modelValue: TitleBarProperty }>()
|
||||||
const emit = defineEmits(['update:modelValue'])
|
const emit = defineEmits(['update:modelValue'])
|
||||||
const { formData } = usePropertyForm(props.modelValue, emit)
|
const formData = useVModel(props, 'modelValue', emit)
|
||||||
|
|
||||||
// 表单校验
|
// 表单校验
|
||||||
const rules = {}
|
const rules = {}
|
||||||
|
@ -4,14 +4,14 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { UserCardProperty } from './config'
|
import { UserCardProperty } from './config'
|
||||||
import { usePropertyForm } from '@/components/DiyEditor/util'
|
import { useVModel } from '@vueuse/core'
|
||||||
|
|
||||||
// 用户卡片属性面板
|
// 用户卡片属性面板
|
||||||
defineOptions({ name: 'UserCardProperty' })
|
defineOptions({ name: 'UserCardProperty' })
|
||||||
|
|
||||||
const props = defineProps<{ modelValue: UserCardProperty }>()
|
const props = defineProps<{ modelValue: UserCardProperty }>()
|
||||||
const emit = defineEmits(['update:modelValue'])
|
const emit = defineEmits(['update:modelValue'])
|
||||||
const { formData } = usePropertyForm(props.modelValue, emit)
|
const formData = useVModel(props, 'modelValue', emit)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss"></style>
|
<style scoped lang="scss"></style>
|
||||||
|
@ -4,14 +4,14 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { UserCouponProperty } from './config'
|
import { UserCouponProperty } from './config'
|
||||||
import { usePropertyForm } from '@/components/DiyEditor/util'
|
import { useVModel } from '@vueuse/core'
|
||||||
|
|
||||||
// 用户卡券属性面板
|
// 用户卡券属性面板
|
||||||
defineOptions({ name: 'UserCouponProperty' })
|
defineOptions({ name: 'UserCouponProperty' })
|
||||||
|
|
||||||
const props = defineProps<{ modelValue: UserCouponProperty }>()
|
const props = defineProps<{ modelValue: UserCouponProperty }>()
|
||||||
const emit = defineEmits(['update:modelValue'])
|
const emit = defineEmits(['update:modelValue'])
|
||||||
const { formData } = usePropertyForm(props.modelValue, emit)
|
const formData = useVModel(props, 'modelValue', emit)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss"></style>
|
<style scoped lang="scss"></style>
|
||||||
|
@ -4,14 +4,14 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { UserOrderProperty } from './config'
|
import { UserOrderProperty } from './config'
|
||||||
import { usePropertyForm } from '@/components/DiyEditor/util'
|
import { useVModel } from '@vueuse/core'
|
||||||
|
|
||||||
// 用户订单属性面板
|
// 用户订单属性面板
|
||||||
defineOptions({ name: 'UserOrderProperty' })
|
defineOptions({ name: 'UserOrderProperty' })
|
||||||
|
|
||||||
const props = defineProps<{ modelValue: UserOrderProperty }>()
|
const props = defineProps<{ modelValue: UserOrderProperty }>()
|
||||||
const emit = defineEmits(['update:modelValue'])
|
const emit = defineEmits(['update:modelValue'])
|
||||||
const { formData } = usePropertyForm(props.modelValue, emit)
|
const formData = useVModel(props, 'modelValue', emit)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss"></style>
|
<style scoped lang="scss"></style>
|
||||||
|
@ -4,14 +4,14 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { UserWalletProperty } from './config'
|
import { UserWalletProperty } from './config'
|
||||||
import { usePropertyForm } from '@/components/DiyEditor/util'
|
import { useVModel } from '@vueuse/core'
|
||||||
|
|
||||||
// 用户资产属性面板
|
// 用户资产属性面板
|
||||||
defineOptions({ name: 'UserWalletProperty' })
|
defineOptions({ name: 'UserWalletProperty' })
|
||||||
|
|
||||||
const props = defineProps<{ modelValue: UserWalletProperty }>()
|
const props = defineProps<{ modelValue: UserWalletProperty }>()
|
||||||
const emit = defineEmits(['update:modelValue'])
|
const emit = defineEmits(['update:modelValue'])
|
||||||
const { formData } = usePropertyForm(props.modelValue, emit)
|
const formData = useVModel(props, 'modelValue', emit)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss"></style>
|
<style scoped lang="scss"></style>
|
||||||
|
@ -42,14 +42,14 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { VideoPlayerProperty } from './config'
|
import { VideoPlayerProperty } from './config'
|
||||||
import { usePropertyForm } from '@/components/DiyEditor/util'
|
import { useVModel } from '@vueuse/core'
|
||||||
|
|
||||||
// 视频播放属性面板
|
// 视频播放属性面板
|
||||||
defineOptions({ name: 'VideoPlayerProperty' })
|
defineOptions({ name: 'VideoPlayerProperty' })
|
||||||
|
|
||||||
const props = defineProps<{ modelValue: VideoPlayerProperty }>()
|
const props = defineProps<{ modelValue: VideoPlayerProperty }>()
|
||||||
const emit = defineEmits(['update:modelValue'])
|
const emit = defineEmits(['update:modelValue'])
|
||||||
const { formData } = usePropertyForm(props.modelValue, emit)
|
const formData = useVModel(props, 'modelValue', emit)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss"></style>
|
<style scoped lang="scss"></style>
|
||||||
|
@ -110,7 +110,7 @@
|
|||||||
<el-tag
|
<el-tag
|
||||||
v-if="showPageConfig"
|
v-if="showPageConfig"
|
||||||
:effect="selectedComponent?.uid === pageConfigComponent.uid ? 'dark' : 'plain'"
|
:effect="selectedComponent?.uid === pageConfigComponent.uid ? 'dark' : 'plain'"
|
||||||
:type="selectedComponent?.uid === pageConfigComponent.uid ? '' : 'info'"
|
:type="selectedComponent?.uid === pageConfigComponent.uid ? 'primary' : 'info'"
|
||||||
size="large"
|
size="large"
|
||||||
@click="handleComponentSelected(pageConfigComponent)"
|
@click="handleComponentSelected(pageConfigComponent)"
|
||||||
>
|
>
|
||||||
@ -121,7 +121,7 @@
|
|||||||
<el-tag
|
<el-tag
|
||||||
v-if="component.position === 'fixed'"
|
v-if="component.position === 'fixed'"
|
||||||
:effect="selectedComponent?.uid === component.uid ? 'dark' : 'plain'"
|
:effect="selectedComponent?.uid === component.uid ? 'dark' : 'plain'"
|
||||||
:type="selectedComponent?.uid === component.uid ? '' : 'info'"
|
:type="selectedComponent?.uid === component.uid ? 'primary' : 'info'"
|
||||||
closable
|
closable
|
||||||
size="large"
|
size="large"
|
||||||
@click="handleComponentSelected(component)"
|
@click="handleComponentSelected(component)"
|
||||||
@ -191,7 +191,7 @@ import { cloneDeep, includes } from 'lodash-es'
|
|||||||
import { component as PAGE_CONFIG_COMPONENT } from '@/components/DiyEditor/components/mobile/PageConfig/config'
|
import { component as PAGE_CONFIG_COMPONENT } from '@/components/DiyEditor/components/mobile/PageConfig/config'
|
||||||
import { component as NAVIGATION_BAR_COMPONENT } from './components/mobile/NavigationBar/config'
|
import { component as NAVIGATION_BAR_COMPONENT } from './components/mobile/NavigationBar/config'
|
||||||
import { component as TAB_BAR_COMPONENT } from './components/mobile/TabBar/config'
|
import { component as TAB_BAR_COMPONENT } from './components/mobile/TabBar/config'
|
||||||
import { isString } from '@/utils/is'
|
import { isEmpty, isString } from '@/utils/is'
|
||||||
import { DiyComponent, DiyComponentLibrary, PageConfig } from '@/components/DiyEditor/util'
|
import { DiyComponent, DiyComponentLibrary, PageConfig } from '@/components/DiyEditor/util'
|
||||||
import { componentConfigs } from '@/components/DiyEditor/components/mobile'
|
import { componentConfigs } from '@/components/DiyEditor/components/mobile'
|
||||||
import { array, oneOfType } from 'vue-types'
|
import { array, oneOfType } from 'vue-types'
|
||||||
@ -238,24 +238,42 @@ const props = defineProps({
|
|||||||
watch(
|
watch(
|
||||||
() => props.modelValue,
|
() => props.modelValue,
|
||||||
() => {
|
() => {
|
||||||
const modelValue = isString(props.modelValue)
|
const modelValue =
|
||||||
? (JSON.parse(props.modelValue) as PageConfig)
|
isString(props.modelValue) && !isEmpty(props.modelValue)
|
||||||
: props.modelValue
|
? (JSON.parse(props.modelValue) as PageConfig)
|
||||||
pageConfigComponent.value.property = modelValue?.page || PAGE_CONFIG_COMPONENT.property
|
: props.modelValue
|
||||||
|
pageConfigComponent.value.property =
|
||||||
|
(typeof modelValue !== 'string' && modelValue?.page) || PAGE_CONFIG_COMPONENT.property
|
||||||
navigationBarComponent.value.property =
|
navigationBarComponent.value.property =
|
||||||
modelValue?.navigationBar || NAVIGATION_BAR_COMPONENT.property
|
(typeof modelValue !== 'string' && modelValue?.navigationBar) ||
|
||||||
tabBarComponent.value.property = modelValue?.tabBar || TAB_BAR_COMPONENT.property
|
NAVIGATION_BAR_COMPONENT.property
|
||||||
|
tabBarComponent.value.property =
|
||||||
|
(typeof modelValue !== 'string' && modelValue?.tabBar) || TAB_BAR_COMPONENT.property
|
||||||
// 查找对应的页面组件
|
// 查找对应的页面组件
|
||||||
pageComponents.value = (modelValue?.components || []).map((item) => {
|
pageComponents.value = ((typeof modelValue !== 'string' && modelValue?.components) || []).map(
|
||||||
const component = componentConfigs[item.id]
|
(item) => {
|
||||||
return { ...component, property: item.property }
|
const component = componentConfigs[item.id]
|
||||||
})
|
return { ...component, property: item.property }
|
||||||
|
}
|
||||||
|
)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
immediate: true
|
immediate: true
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
/** 选择组件修改其属性后更新它的配置 */
|
||||||
|
watch(
|
||||||
|
selectedComponent,
|
||||||
|
(val: any) => {
|
||||||
|
if (!val || selectedComponentIndex.value === -1) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
pageComponents.value[selectedComponentIndex.value] = selectedComponent.value!
|
||||||
|
},
|
||||||
|
{ deep: true }
|
||||||
|
)
|
||||||
|
|
||||||
// 保存
|
// 保存
|
||||||
const handleSave = () => {
|
const handleSave = () => {
|
||||||
// 发送保存通知
|
// 发送保存通知
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import { ref, Ref } from 'vue'
|
|
||||||
import { PageConfigProperty } from '@/components/DiyEditor/components/mobile/PageConfig/config'
|
import { PageConfigProperty } from '@/components/DiyEditor/components/mobile/PageConfig/config'
|
||||||
import { NavigationBarProperty } from '@/components/DiyEditor/components/mobile/NavigationBar/config'
|
import { NavigationBarProperty } from '@/components/DiyEditor/components/mobile/NavigationBar/config'
|
||||||
import { TabBarProperty } from '@/components/DiyEditor/components/mobile/TabBar/config'
|
import { TabBarProperty } from '@/components/DiyEditor/components/mobile/TabBar/config'
|
||||||
@ -78,34 +77,6 @@ export interface PageConfig {
|
|||||||
// 页面组件,只保留组件ID,组件属性
|
// 页面组件,只保留组件ID,组件属性
|
||||||
export interface PageComponent extends Pick<DiyComponent<any>, 'id' | 'property'> {}
|
export interface PageComponent extends Pick<DiyComponent<any>, 'id' | 'property'> {}
|
||||||
|
|
||||||
// 属性表单监听
|
|
||||||
export function usePropertyForm<T>(modelValue: T, emit: Function): { formData: Ref<T> } {
|
|
||||||
const formData = ref<T>()
|
|
||||||
// 监听属性数据变动
|
|
||||||
watch(
|
|
||||||
() => modelValue,
|
|
||||||
() => {
|
|
||||||
formData.value = modelValue
|
|
||||||
},
|
|
||||||
{
|
|
||||||
deep: true,
|
|
||||||
immediate: true
|
|
||||||
}
|
|
||||||
)
|
|
||||||
// 监听表单数据变动
|
|
||||||
watch(
|
|
||||||
() => formData.value,
|
|
||||||
() => {
|
|
||||||
emit('update:modelValue', formData.value)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
deep: true
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
return { formData } as { formData: Ref<T> }
|
|
||||||
}
|
|
||||||
|
|
||||||
// 页面组件库
|
// 页面组件库
|
||||||
export const PAGE_LIBS = [
|
export const PAGE_LIBS = [
|
||||||
{
|
{
|
||||||
|
@ -13,9 +13,16 @@
|
|||||||
class="mb-4px flex flex-col gap-4px border border-gray-2 border-rounded rounded border-solid p-8px"
|
class="mb-4px flex flex-col gap-4px border border-gray-2 border-rounded rounded border-solid p-8px"
|
||||||
>
|
>
|
||||||
<!-- 操作按钮区 -->
|
<!-- 操作按钮区 -->
|
||||||
<div class="m--8px m-b-4px flex flex-row items-center justify-between p-8px" style="background-color: var(--app-content-bg-color);">
|
<div
|
||||||
|
class="m--8px m-b-4px flex flex-row items-center justify-between p-8px"
|
||||||
|
style="background-color: var(--app-content-bg-color)"
|
||||||
|
>
|
||||||
<el-tooltip content="拖动排序">
|
<el-tooltip content="拖动排序">
|
||||||
<Icon icon="ic:round-drag-indicator" class="drag-icon cursor-move" style="color: #8a909c;" />
|
<Icon
|
||||||
|
icon="ic:round-drag-indicator"
|
||||||
|
class="drag-icon cursor-move"
|
||||||
|
style="color: #8a909c"
|
||||||
|
/>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
<el-tooltip content="删除">
|
<el-tooltip content="删除">
|
||||||
<Icon
|
<Icon
|
||||||
@ -47,7 +54,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
// 拖拽组件
|
// 拖拽组件
|
||||||
import VueDraggable from 'vuedraggable'
|
import VueDraggable from 'vuedraggable'
|
||||||
import { usePropertyForm } from '@/components/DiyEditor/util'
|
import { useVModel } from '@vueuse/core'
|
||||||
import { any, array } from 'vue-types'
|
import { any, array } from 'vue-types'
|
||||||
import { propTypes } from '@/utils/propTypes'
|
import { propTypes } from '@/utils/propTypes'
|
||||||
import { cloneDeep } from 'lodash-es'
|
import { cloneDeep } from 'lodash-es'
|
||||||
@ -66,7 +73,7 @@ const props = defineProps({
|
|||||||
})
|
})
|
||||||
// 定义事件
|
// 定义事件
|
||||||
const emit = defineEmits(['update:modelValue'])
|
const emit = defineEmits(['update:modelValue'])
|
||||||
const { formData } = usePropertyForm(props.modelValue, emit)
|
const formData = useVModel(props, 'modelValue', emit)
|
||||||
|
|
||||||
// 处理添加
|
// 处理添加
|
||||||
const handleAdd = () => formData.value.push(cloneDeep(props.emptyItem || {}))
|
const handleAdd = () => formData.value.push(cloneDeep(props.emptyItem || {}))
|
||||||
|
@ -105,7 +105,7 @@ const editorConfig = computed((): IEditorConfig => {
|
|||||||
},
|
},
|
||||||
|
|
||||||
// 超时时间,默认为 10 秒
|
// 超时时间,默认为 10 秒
|
||||||
timeout: 5 * 1000, // 5 秒
|
timeout: 15 * 1000, // 15 秒
|
||||||
|
|
||||||
// form-data fieldName,后端接口参数名称,默认值wangeditor-uploaded-image
|
// form-data fieldName,后端接口参数名称,默认值wangeditor-uploaded-image
|
||||||
fieldName: 'file',
|
fieldName: 'file',
|
||||||
|
@ -75,7 +75,7 @@ watch(
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<ElIcon :class="prefixCls" :color="color" :size="size">
|
<ElIcon :class="prefixCls" :color="color" :size="size">
|
||||||
<svg v-if="isLocal" :class="getSvgClass" aria-hidden="true">
|
<svg v-if="isLocal" :class="getSvgClass">
|
||||||
<use :xlink:href="symbolId" />
|
<use :xlink:href="symbolId" />
|
||||||
</svg>
|
</svg>
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<el-input v-model="valueRef" v-bind="$attrs">
|
<el-input v-model="modelValue" v-bind="$attrs">
|
||||||
<template #append>
|
<template #append>
|
||||||
<el-color-picker v-model="colorRef" :predefine="PREDEFINE_COLORS" />
|
<el-color-picker v-model="color" :predefine="PREDEFINE_COLORS" />
|
||||||
</template>
|
</template>
|
||||||
</el-input>
|
</el-input>
|
||||||
</template>
|
</template>
|
||||||
@ -9,6 +9,7 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { propTypes } from '@/utils/propTypes'
|
import { propTypes } from '@/utils/propTypes'
|
||||||
import { PREDEFINE_COLORS } from '@/utils/color'
|
import { PREDEFINE_COLORS } from '@/utils/color'
|
||||||
|
import { useVModels } from '@vueuse/core'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 带颜色选择器输入框
|
* 带颜色选择器输入框
|
||||||
@ -19,33 +20,8 @@ const props = defineProps({
|
|||||||
modelValue: propTypes.string.def('').isRequired,
|
modelValue: propTypes.string.def('').isRequired,
|
||||||
color: propTypes.string.def('').isRequired
|
color: propTypes.string.def('').isRequired
|
||||||
})
|
})
|
||||||
|
|
||||||
watch(
|
|
||||||
() => props.modelValue,
|
|
||||||
(val: string) => {
|
|
||||||
if (val === unref(valueRef)) return
|
|
||||||
valueRef.value = val
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
const emit = defineEmits(['update:modelValue', 'update:color'])
|
const emit = defineEmits(['update:modelValue', 'update:color'])
|
||||||
|
const { modelValue, color } = useVModels(props, emit)
|
||||||
// 输入框的值
|
|
||||||
const valueRef = ref(props.modelValue)
|
|
||||||
watch(
|
|
||||||
() => valueRef.value,
|
|
||||||
(val: string) => {
|
|
||||||
emit('update:modelValue', val)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
// 颜色
|
|
||||||
const colorRef = ref(props.color)
|
|
||||||
watch(
|
|
||||||
() => colorRef.value,
|
|
||||||
(val: string) => {
|
|
||||||
emit('update:color', val)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
</script>
|
</script>
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
:deep(.el-input-group__append) {
|
:deep(.el-input-group__append) {
|
||||||
|
@ -15,6 +15,12 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="handler-item-text">审批人</div>
|
<div class="handler-item-text">审批人</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="handler-item" @click="addNode(NodeType.TRANSACTOR_NODE)">
|
||||||
|
<div class="transactor handler-item-icon">
|
||||||
|
<span class="iconfont icon-transactor icon-size"></span>
|
||||||
|
</div>
|
||||||
|
<div class="handler-item-text">办理人</div>
|
||||||
|
</div>
|
||||||
<div class="handler-item" @click="addNode(NodeType.COPY_TASK_NODE)">
|
<div class="handler-item" @click="addNode(NodeType.COPY_TASK_NODE)">
|
||||||
<div class="handler-item-icon copy">
|
<div class="handler-item-icon copy">
|
||||||
<span class="iconfont icon-size icon-copy"></span>
|
<span class="iconfont icon-size icon-copy"></span>
|
||||||
@ -40,12 +46,29 @@
|
|||||||
<div class="handler-item-text">包容分支</div>
|
<div class="handler-item-text">包容分支</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="handler-item" @click="addNode(NodeType.DELAY_TIMER_NODE)">
|
<div class="handler-item" @click="addNode(NodeType.DELAY_TIMER_NODE)">
|
||||||
<!-- TODO @芋艿 需要更换一下iconfont的图标 -->
|
<div class="handler-item-icon delay">
|
||||||
<div class="handler-item-icon copy">
|
<span class="iconfont icon-size icon-delay"></span>
|
||||||
<span class="iconfont icon-size icon-copy"></span>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="handler-item-text">延迟器</div>
|
<div class="handler-item-text">延迟器</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="handler-item" @click="addNode(NodeType.ROUTER_BRANCH_NODE)">
|
||||||
|
<div class="handler-item-icon router">
|
||||||
|
<span class="iconfont icon-size icon-router"></span>
|
||||||
|
</div>
|
||||||
|
<div class="handler-item-text">路由分支</div>
|
||||||
|
</div>
|
||||||
|
<div class="handler-item" @click="addNode(NodeType.TRIGGER_NODE)">
|
||||||
|
<div class="handler-item-icon trigger">
|
||||||
|
<span class="iconfont icon-size icon-trigger"></span>
|
||||||
|
</div>
|
||||||
|
<div class="handler-item-text">触发器</div>
|
||||||
|
</div>
|
||||||
|
<div class="handler-item" @click="addNode(NodeType.CHILD_PROCESS_NODE)">
|
||||||
|
<div class="handler-item-icon child-process">
|
||||||
|
<span class="iconfont icon-size icon-child-process"></span>
|
||||||
|
</div>
|
||||||
|
<div class="handler-item-text">子流程</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<template #reference>
|
<template #reference>
|
||||||
<div class="add-icon"><Icon icon="ep:plus" /></div>
|
<div class="add-icon"><Icon icon="ep:plus" /></div>
|
||||||
@ -60,10 +83,12 @@ import {
|
|||||||
ApproveMethodType,
|
ApproveMethodType,
|
||||||
AssignEmptyHandlerType,
|
AssignEmptyHandlerType,
|
||||||
AssignStartUserHandlerType,
|
AssignStartUserHandlerType,
|
||||||
|
ConditionType,
|
||||||
NODE_DEFAULT_NAME,
|
NODE_DEFAULT_NAME,
|
||||||
NodeType,
|
NodeType,
|
||||||
RejectHandlerType,
|
RejectHandlerType,
|
||||||
SimpleFlowNode
|
SimpleFlowNode,
|
||||||
|
DEFAULT_CONDITION_GROUP_VALUE
|
||||||
} from './consts'
|
} from './consts'
|
||||||
import { generateUUID } from '@/utils'
|
import { generateUUID } from '@/utils'
|
||||||
|
|
||||||
@ -101,13 +126,13 @@ const addNode = (type: number) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
popoverShow.value = false
|
popoverShow.value = false
|
||||||
if (type === NodeType.USER_TASK_NODE) {
|
if (type === NodeType.USER_TASK_NODE || type === NodeType.TRANSACTOR_NODE) {
|
||||||
const id = 'Activity_' + generateUUID()
|
const id = 'Activity_' + generateUUID()
|
||||||
const data: SimpleFlowNode = {
|
const data: SimpleFlowNode = {
|
||||||
id: id,
|
id: id,
|
||||||
name: NODE_DEFAULT_NAME.get(NodeType.USER_TASK_NODE) as string,
|
name: NODE_DEFAULT_NAME.get(type) as string,
|
||||||
showText: '',
|
showText: '',
|
||||||
type: NodeType.USER_TASK_NODE,
|
type: type,
|
||||||
approveMethod: ApproveMethodType.SEQUENTIAL_APPROVE,
|
approveMethod: ApproveMethodType.SEQUENTIAL_APPROVE,
|
||||||
// 超时处理
|
// 超时处理
|
||||||
rejectHandler: {
|
rejectHandler: {
|
||||||
@ -120,7 +145,16 @@ const addNode = (type: number) => {
|
|||||||
type: AssignEmptyHandlerType.APPROVE
|
type: AssignEmptyHandlerType.APPROVE
|
||||||
},
|
},
|
||||||
assignStartUserHandlerType: AssignStartUserHandlerType.START_USER_AUDIT,
|
assignStartUserHandlerType: AssignStartUserHandlerType.START_USER_AUDIT,
|
||||||
childNode: props.childNode
|
childNode: props.childNode,
|
||||||
|
taskCreateListener: {
|
||||||
|
enable: false
|
||||||
|
},
|
||||||
|
taskAssignListener: {
|
||||||
|
enable: false
|
||||||
|
},
|
||||||
|
taskCompleteListener: {
|
||||||
|
enable: false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
emits('update:childNode', data)
|
emits('update:childNode', data)
|
||||||
}
|
}
|
||||||
@ -147,8 +181,11 @@ const addNode = (type: number) => {
|
|||||||
showText: '',
|
showText: '',
|
||||||
type: NodeType.CONDITION_NODE,
|
type: NodeType.CONDITION_NODE,
|
||||||
childNode: undefined,
|
childNode: undefined,
|
||||||
conditionType: 1,
|
conditionSetting: {
|
||||||
defaultFlow: false
|
defaultFlow: false,
|
||||||
|
conditionType: ConditionType.RULE,
|
||||||
|
conditionGroups: DEFAULT_CONDITION_GROUP_VALUE
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'Flow_' + generateUUID(),
|
id: 'Flow_' + generateUUID(),
|
||||||
@ -156,8 +193,9 @@ const addNode = (type: number) => {
|
|||||||
showText: '未满足其它条件时,将进入此分支',
|
showText: '未满足其它条件时,将进入此分支',
|
||||||
type: NodeType.CONDITION_NODE,
|
type: NodeType.CONDITION_NODE,
|
||||||
childNode: undefined,
|
childNode: undefined,
|
||||||
conditionType: undefined,
|
conditionSetting: {
|
||||||
defaultFlow: true
|
defaultFlow: true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -201,7 +239,11 @@ const addNode = (type: number) => {
|
|||||||
showText: '',
|
showText: '',
|
||||||
type: NodeType.CONDITION_NODE,
|
type: NodeType.CONDITION_NODE,
|
||||||
childNode: undefined,
|
childNode: undefined,
|
||||||
defaultFlow: false
|
conditionSetting: {
|
||||||
|
defaultFlow: false,
|
||||||
|
conditionType: ConditionType.RULE,
|
||||||
|
conditionGroups: DEFAULT_CONDITION_GROUP_VALUE
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'Flow_' + generateUUID(),
|
id: 'Flow_' + generateUUID(),
|
||||||
@ -209,7 +251,9 @@ const addNode = (type: number) => {
|
|||||||
showText: '未满足其它条件时,将进入此分支',
|
showText: '未满足其它条件时,将进入此分支',
|
||||||
type: NodeType.CONDITION_NODE,
|
type: NodeType.CONDITION_NODE,
|
||||||
childNode: undefined,
|
childNode: undefined,
|
||||||
defaultFlow: true
|
conditionSetting: {
|
||||||
|
defaultFlow: true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -225,6 +269,51 @@ const addNode = (type: number) => {
|
|||||||
}
|
}
|
||||||
emits('update:childNode', data)
|
emits('update:childNode', data)
|
||||||
}
|
}
|
||||||
|
if (type === NodeType.ROUTER_BRANCH_NODE) {
|
||||||
|
const data: SimpleFlowNode = {
|
||||||
|
id: 'GateWay_' + generateUUID(),
|
||||||
|
name: NODE_DEFAULT_NAME.get(NodeType.ROUTER_BRANCH_NODE) as string,
|
||||||
|
showText: '',
|
||||||
|
type: NodeType.ROUTER_BRANCH_NODE,
|
||||||
|
childNode: props.childNode
|
||||||
|
}
|
||||||
|
emits('update:childNode', data)
|
||||||
|
}
|
||||||
|
if (type === NodeType.TRIGGER_NODE) {
|
||||||
|
const data: SimpleFlowNode = {
|
||||||
|
id: 'Activity_' + generateUUID(),
|
||||||
|
name: NODE_DEFAULT_NAME.get(NodeType.TRIGGER_NODE) as string,
|
||||||
|
showText: '',
|
||||||
|
type: NodeType.TRIGGER_NODE,
|
||||||
|
childNode: props.childNode
|
||||||
|
}
|
||||||
|
emits('update:childNode', data)
|
||||||
|
}
|
||||||
|
if (type === NodeType.CHILD_PROCESS_NODE) {
|
||||||
|
const data: SimpleFlowNode = {
|
||||||
|
id: 'Activity_' + generateUUID(),
|
||||||
|
name: NODE_DEFAULT_NAME.get(NodeType.CHILD_PROCESS_NODE) as string,
|
||||||
|
showText: '',
|
||||||
|
type: NodeType.CHILD_PROCESS_NODE,
|
||||||
|
childNode: props.childNode,
|
||||||
|
childProcessSetting: {
|
||||||
|
calledProcessDefinitionKey: '',
|
||||||
|
calledProcessDefinitionName: '',
|
||||||
|
async: false,
|
||||||
|
skipStartUserNode: false,
|
||||||
|
startUserSetting: {
|
||||||
|
type: 1
|
||||||
|
},
|
||||||
|
timeoutSetting: {
|
||||||
|
enable: false
|
||||||
|
},
|
||||||
|
multiInstanceSetting: {
|
||||||
|
enable: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
emits('update:childNode', data)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -6,7 +6,11 @@
|
|||||||
/>
|
/>
|
||||||
<!-- 审批节点 -->
|
<!-- 审批节点 -->
|
||||||
<UserTaskNode
|
<UserTaskNode
|
||||||
v-if="currentNode && currentNode.type === NodeType.USER_TASK_NODE"
|
v-if="
|
||||||
|
currentNode &&
|
||||||
|
(currentNode.type === NodeType.USER_TASK_NODE ||
|
||||||
|
currentNode.type === NodeType.TRANSACTOR_NODE)
|
||||||
|
"
|
||||||
:flow-node="currentNode"
|
:flow-node="currentNode"
|
||||||
@update:flow-node="handleModelValueUpdate"
|
@update:flow-node="handleModelValueUpdate"
|
||||||
@find:parent-node="findFromParentNode"
|
@find:parent-node="findFromParentNode"
|
||||||
@ -44,6 +48,24 @@
|
|||||||
:flow-node="currentNode"
|
:flow-node="currentNode"
|
||||||
@update:flow-node="handleModelValueUpdate"
|
@update:flow-node="handleModelValueUpdate"
|
||||||
/>
|
/>
|
||||||
|
<!-- 路由分支节点 -->
|
||||||
|
<RouterNode
|
||||||
|
v-if="currentNode && currentNode.type === NodeType.ROUTER_BRANCH_NODE"
|
||||||
|
:flow-node="currentNode"
|
||||||
|
@update:flow-node="handleModelValueUpdate"
|
||||||
|
/>
|
||||||
|
<!-- 触发器节点 -->
|
||||||
|
<TriggerNode
|
||||||
|
v-if="currentNode && currentNode.type === NodeType.TRIGGER_NODE"
|
||||||
|
:flow-node="currentNode"
|
||||||
|
@update:flow-node="handleModelValueUpdate"
|
||||||
|
/>
|
||||||
|
<!-- 子流程节点 -->
|
||||||
|
<ChildProcessNode
|
||||||
|
v-if="currentNode && currentNode.type === NodeType.CHILD_PROCESS_NODE"
|
||||||
|
:flow-node="currentNode"
|
||||||
|
@update:flow-node="handleModelValueUpdate"
|
||||||
|
/>
|
||||||
<!-- 递归显示孩子节点 -->
|
<!-- 递归显示孩子节点 -->
|
||||||
<ProcessNodeTree
|
<ProcessNodeTree
|
||||||
v-if="currentNode && currentNode.childNode"
|
v-if="currentNode && currentNode.childNode"
|
||||||
@ -67,6 +89,9 @@ import ExclusiveNode from './nodes/ExclusiveNode.vue'
|
|||||||
import ParallelNode from './nodes/ParallelNode.vue'
|
import ParallelNode from './nodes/ParallelNode.vue'
|
||||||
import InclusiveNode from './nodes/InclusiveNode.vue'
|
import InclusiveNode from './nodes/InclusiveNode.vue'
|
||||||
import DelayTimerNode from './nodes/DelayTimerNode.vue'
|
import DelayTimerNode from './nodes/DelayTimerNode.vue'
|
||||||
|
import RouterNode from './nodes/RouterNode.vue'
|
||||||
|
import TriggerNode from './nodes/TriggerNode.vue'
|
||||||
|
import ChildProcessNode from './nodes/ChildProcessNode.vue'
|
||||||
import { SimpleFlowNode, NodeType } from './consts'
|
import { SimpleFlowNode, NodeType } from './consts'
|
||||||
import { useWatchNode } from './node'
|
import { useWatchNode } from './node'
|
||||||
defineOptions({
|
defineOptions({
|
||||||
|
@ -25,7 +25,6 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import SimpleProcessModel from './SimpleProcessModel.vue'
|
import SimpleProcessModel from './SimpleProcessModel.vue'
|
||||||
import { updateBpmSimpleModel, getBpmSimpleModel } from '@/api/bpm/simple'
|
|
||||||
import { SimpleFlowNode, NodeType, NodeId, NODE_DEFAULT_TEXT } from './consts'
|
import { SimpleFlowNode, NodeType, NodeId, NODE_DEFAULT_TEXT } from './consts'
|
||||||
import { getModel } from '@/api/bpm/model'
|
import { getModel } from '@/api/bpm/model'
|
||||||
import { getForm, FormVO } from '@/api/bpm/form'
|
import { getForm, FormVO } from '@/api/bpm/form'
|
||||||
@ -35,12 +34,13 @@ import * as DeptApi from '@/api/system/dept'
|
|||||||
import * as PostApi from '@/api/system/post'
|
import * as PostApi from '@/api/system/post'
|
||||||
import * as UserApi from '@/api/system/user'
|
import * as UserApi from '@/api/system/user'
|
||||||
import * as UserGroupApi from '@/api/bpm/userGroup'
|
import * as UserGroupApi from '@/api/bpm/userGroup'
|
||||||
|
import { BpmModelFormType } from '@/utils/constants'
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: 'SimpleProcessDesigner'
|
name: 'SimpleProcessDesigner'
|
||||||
})
|
})
|
||||||
|
|
||||||
const emits = defineEmits(['success', 'init-finished']) // 保存成功事件
|
const emits = defineEmits(['success']) // 保存成功事件
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
modelId: {
|
modelId: {
|
||||||
@ -56,16 +56,13 @@ const props = defineProps({
|
|||||||
required: false
|
required: false
|
||||||
},
|
},
|
||||||
// 可发起流程的人员编号
|
// 可发起流程的人员编号
|
||||||
startUserIds : {
|
startUserIds: {
|
||||||
type: Array,
|
type: Array,
|
||||||
required: false
|
required: false
|
||||||
},
|
|
||||||
value: {
|
|
||||||
type: [String, Object],
|
|
||||||
required: false
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const processData = inject('processData') as Ref
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
const formFields = ref<string[]>([])
|
const formFields = ref<string[]>([])
|
||||||
const formType = ref(20)
|
const formType = ref(20)
|
||||||
@ -76,9 +73,6 @@ const deptOptions = ref<DeptApi.DeptVO[]>([]) // 部门列表
|
|||||||
const deptTreeOptions = ref()
|
const deptTreeOptions = ref()
|
||||||
const userGroupOptions = ref<UserGroupApi.UserGroupVO[]>([]) // 用户组列表
|
const userGroupOptions = ref<UserGroupApi.UserGroupVO[]>([]) // 用户组列表
|
||||||
|
|
||||||
// 添加当前值的引用
|
|
||||||
const currentValue = ref<SimpleFlowNode | undefined>()
|
|
||||||
|
|
||||||
provide('formFields', formFields)
|
provide('formFields', formFields)
|
||||||
provide('formType', formType)
|
provide('formType', formType)
|
||||||
provide('roleList', roleOptions)
|
provide('roleList', roleOptions)
|
||||||
@ -88,9 +82,11 @@ provide('deptList', deptOptions)
|
|||||||
provide('userGroupList', userGroupOptions)
|
provide('userGroupList', userGroupOptions)
|
||||||
provide('deptTree', deptTreeOptions)
|
provide('deptTree', deptTreeOptions)
|
||||||
provide('startUserIds', props.startUserIds)
|
provide('startUserIds', props.startUserIds)
|
||||||
|
provide('tasks', [])
|
||||||
|
provide('processInstance', {})
|
||||||
const message = useMessage() // 国际化
|
const message = useMessage() // 国际化
|
||||||
const processNodeTree = ref<SimpleFlowNode | undefined>()
|
const processNodeTree = ref<SimpleFlowNode | undefined>()
|
||||||
|
provide('processNodeTree', processNodeTree)
|
||||||
const errorDialogVisible = ref(false)
|
const errorDialogVisible = ref(false)
|
||||||
let errorNodes: SimpleFlowNode[] = []
|
let errorNodes: SimpleFlowNode[] = []
|
||||||
|
|
||||||
@ -112,70 +108,13 @@ const updateModel = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 加载流程数据
|
|
||||||
const loadProcessData = async (data: any) => {
|
|
||||||
try {
|
|
||||||
if (data) {
|
|
||||||
const parsedData = typeof data === 'string' ? JSON.parse(data) : data
|
|
||||||
processNodeTree.value = parsedData
|
|
||||||
currentValue.value = parsedData
|
|
||||||
// 确保数据加载后刷新视图
|
|
||||||
await nextTick()
|
|
||||||
if (simpleProcessModelRef.value?.refresh) {
|
|
||||||
await simpleProcessModelRef.value.refresh()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('加载流程数据失败:', error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 监听属性变化
|
|
||||||
watch(
|
|
||||||
() => props.value,
|
|
||||||
async (newValue, oldValue) => {
|
|
||||||
if (newValue && newValue !== oldValue) {
|
|
||||||
await loadProcessData(newValue)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{ immediate: true, deep: true }
|
|
||||||
)
|
|
||||||
|
|
||||||
// 监听流程节点树变化,自动保存
|
|
||||||
watch(
|
|
||||||
() => processNodeTree.value,
|
|
||||||
async (newValue, oldValue) => {
|
|
||||||
if (newValue && oldValue && JSON.stringify(newValue) !== JSON.stringify(oldValue)) {
|
|
||||||
await saveSimpleFlowModel(newValue)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{ deep: true }
|
|
||||||
)
|
|
||||||
|
|
||||||
const saveSimpleFlowModel = async (simpleModelNode: SimpleFlowNode) => {
|
const saveSimpleFlowModel = async (simpleModelNode: SimpleFlowNode) => {
|
||||||
if (!simpleModelNode) {
|
if (!simpleModelNode) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 校验节点
|
|
||||||
errorNodes = []
|
|
||||||
validateNode(simpleModelNode, errorNodes)
|
|
||||||
if (errorNodes.length > 0) {
|
|
||||||
errorDialogVisible.value = true
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (props.modelId) {
|
processData.value = simpleModelNode
|
||||||
// 编辑模式
|
|
||||||
const data = {
|
|
||||||
id: props.modelId,
|
|
||||||
simpleModel: simpleModelNode
|
|
||||||
}
|
|
||||||
await updateBpmSimpleModel(data)
|
|
||||||
}
|
|
||||||
// 无论是编辑还是新建模式,都更新当前值并触发事件
|
|
||||||
currentValue.value = simpleModelNode
|
|
||||||
emits('success', simpleModelNode)
|
emits('success', simpleModelNode)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('保存失败:', error)
|
console.error('保存失败:', error)
|
||||||
@ -229,7 +168,7 @@ onMounted(async () => {
|
|||||||
const bpmnModel = await getModel(props.modelId)
|
const bpmnModel = await getModel(props.modelId)
|
||||||
if (bpmnModel) {
|
if (bpmnModel) {
|
||||||
formType.value = bpmnModel.formType
|
formType.value = bpmnModel.formType
|
||||||
if (formType.value === 10) {
|
if (formType.value === BpmModelFormType.NORMAL && bpmnModel.formId) {
|
||||||
const bpmnForm = (await getForm(bpmnModel.formId)) as unknown as FormVO
|
const bpmnForm = (await getForm(bpmnModel.formId)) as unknown as FormVO
|
||||||
formFields.value = bpmnForm?.fields
|
formFields.value = bpmnForm?.fields
|
||||||
}
|
}
|
||||||
@ -246,61 +185,18 @@ onMounted(async () => {
|
|||||||
deptTreeOptions.value = handleTree(deptOptions.value as DeptApi.DeptVO[], 'id')
|
deptTreeOptions.value = handleTree(deptOptions.value as DeptApi.DeptVO[], 'id')
|
||||||
// 获取用户组列表
|
// 获取用户组列表
|
||||||
userGroupOptions.value = await UserGroupApi.getUserGroupSimpleList()
|
userGroupOptions.value = await UserGroupApi.getUserGroupSimpleList()
|
||||||
|
|
||||||
// 加载流程数据
|
// 加载流程数据
|
||||||
if (props.modelId) {
|
if (processData.value) {
|
||||||
// 获取 SIMPLE 设计器模型
|
processNodeTree.value = processData?.value
|
||||||
const result = await getBpmSimpleModel(props.modelId)
|
|
||||||
if (result) {
|
|
||||||
await loadProcessData(result)
|
|
||||||
} else {
|
|
||||||
updateModel()
|
|
||||||
}
|
|
||||||
} else if (props.value) {
|
|
||||||
await loadProcessData(props.value)
|
|
||||||
} else {
|
} else {
|
||||||
updateModel()
|
updateModel()
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
loading.value = false
|
loading.value = false
|
||||||
emits('init-finished')
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const simpleProcessModelRef = ref()
|
const simpleProcessModelRef = ref()
|
||||||
|
|
||||||
/** 获取当前流程数据 */
|
defineExpose({})
|
||||||
const getCurrentFlowData = async () => {
|
|
||||||
try {
|
|
||||||
if (simpleProcessModelRef.value) {
|
|
||||||
const data = await simpleProcessModelRef.value.getCurrentFlowData()
|
|
||||||
if (data) {
|
|
||||||
currentValue.value = data
|
|
||||||
return data
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return currentValue.value
|
|
||||||
} catch (error) {
|
|
||||||
console.error('获取流程数据失败:', error)
|
|
||||||
return currentValue.value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 刷新方法
|
|
||||||
const refresh = async () => {
|
|
||||||
try {
|
|
||||||
if (currentValue.value) {
|
|
||||||
await loadProcessData(currentValue.value)
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('刷新失败:', error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
defineExpose({
|
|
||||||
getCurrentFlowData,
|
|
||||||
updateModel,
|
|
||||||
loadProcessData,
|
|
||||||
refresh
|
|
||||||
})
|
|
||||||
</script>
|
</script>
|
||||||
|
@ -1,16 +1,41 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="simple-process-model-container position-relative">
|
<div class="simple-process-model-container position-relative">
|
||||||
<div class="position-absolute top-0px right-0px bg-#fff">
|
<div class="position-absolute top-0px right-0px bg-#fff z-index-button-group">
|
||||||
<el-row type="flex" justify="end">
|
<el-row type="flex" justify="end">
|
||||||
<el-button-group key="scale-control" size="default">
|
<el-button-group key="scale-control" size="default">
|
||||||
|
<el-button v-if="!readonly" size="default" @click="exportJson">
|
||||||
|
<Icon icon="ep:download" /> 导出
|
||||||
|
</el-button>
|
||||||
|
<el-button v-if="!readonly" size="default" @click="importJson">
|
||||||
|
<Icon icon="ep:upload" />导入
|
||||||
|
</el-button>
|
||||||
|
<!-- 用于打开本地文件-->
|
||||||
|
<input
|
||||||
|
v-if="!readonly"
|
||||||
|
type="file"
|
||||||
|
id="files"
|
||||||
|
ref="refFile"
|
||||||
|
style="display: none"
|
||||||
|
accept=".json"
|
||||||
|
@change="importLocalFile"
|
||||||
|
/>
|
||||||
<el-button size="default" :icon="ScaleToOriginal" @click="processReZoom()" />
|
<el-button size="default" :icon="ScaleToOriginal" @click="processReZoom()" />
|
||||||
<el-button size="default" :plain="true" :icon="ZoomOut" @click="zoomOut()" />
|
<el-button size="default" :plain="true" :icon="ZoomOut" @click="zoomOut()" />
|
||||||
<el-button size="default" class="w-80px"> {{ scaleValue }}% </el-button>
|
<el-button size="default" class="w-80px"> {{ scaleValue }}% </el-button>
|
||||||
<el-button size="default" :plain="true" :icon="ZoomIn" @click="zoomIn()" />
|
<el-button size="default" :plain="true" :icon="ZoomIn" @click="zoomIn()" />
|
||||||
|
<el-button size="default" @click="resetPosition">重置</el-button>
|
||||||
</el-button-group>
|
</el-button-group>
|
||||||
</el-row>
|
</el-row>
|
||||||
</div>
|
</div>
|
||||||
<div class="simple-process-model" :style="`transform: scale(${scaleValue / 100});`">
|
<div
|
||||||
|
class="simple-process-model"
|
||||||
|
:style="`transform: translate(${currentX}px, ${currentY}px) scale(${scaleValue / 100});`"
|
||||||
|
@mousedown="startDrag"
|
||||||
|
@mousemove="onDrag"
|
||||||
|
@mouseup="stopDrag"
|
||||||
|
@mouseleave="stopDrag"
|
||||||
|
@mouseenter="setGrabCursor"
|
||||||
|
>
|
||||||
<ProcessNodeTree v-if="processNodeTree" v-model:flow-node="processNodeTree" />
|
<ProcessNodeTree v-if="processNodeTree" v-model:flow-node="processNodeTree" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -34,6 +59,8 @@ import ProcessNodeTree from './ProcessNodeTree.vue'
|
|||||||
import { SimpleFlowNode, NodeType, NODE_DEFAULT_TEXT } from './consts'
|
import { SimpleFlowNode, NodeType, NODE_DEFAULT_TEXT } from './consts'
|
||||||
import { useWatchNode } from './node'
|
import { useWatchNode } from './node'
|
||||||
import { ZoomOut, ZoomIn, ScaleToOriginal } from '@element-plus/icons-vue'
|
import { ZoomOut, ZoomIn, ScaleToOriginal } from '@element-plus/icons-vue'
|
||||||
|
import { isString } from '@/utils/is'
|
||||||
|
import download from '@/utils/download'
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: 'SimpleProcessModel'
|
name: 'SimpleProcessModel'
|
||||||
@ -52,17 +79,57 @@ const props = defineProps({
|
|||||||
})
|
})
|
||||||
|
|
||||||
const emits = defineEmits<{
|
const emits = defineEmits<{
|
||||||
'save': [node: SimpleFlowNode | undefined]
|
save: [node: SimpleFlowNode | undefined]
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const processNodeTree = useWatchNode(props)
|
const processNodeTree = useWatchNode(props)
|
||||||
|
|
||||||
provide('readonly', props.readonly)
|
provide('readonly', props.readonly)
|
||||||
|
|
||||||
|
// TODO 可优化:拖拽有点卡顿
|
||||||
|
/** 拖拽、放大缩小等操作 */
|
||||||
let scaleValue = ref(100)
|
let scaleValue = ref(100)
|
||||||
const MAX_SCALE_VALUE = 200
|
const MAX_SCALE_VALUE = 200
|
||||||
const MIN_SCALE_VALUE = 50
|
const MIN_SCALE_VALUE = 50
|
||||||
|
const isDragging = ref(false)
|
||||||
|
const startX = ref(0)
|
||||||
|
const startY = ref(0)
|
||||||
|
const currentX = ref(0)
|
||||||
|
const currentY = ref(0)
|
||||||
|
const initialX = ref(0)
|
||||||
|
const initialY = ref(0)
|
||||||
|
|
||||||
|
const setGrabCursor = () => {
|
||||||
|
document.body.style.cursor = 'grab'
|
||||||
|
}
|
||||||
|
|
||||||
|
const resetCursor = () => {
|
||||||
|
document.body.style.cursor = 'default'
|
||||||
|
}
|
||||||
|
|
||||||
|
const startDrag = (e: MouseEvent) => {
|
||||||
|
isDragging.value = true
|
||||||
|
startX.value = e.clientX - currentX.value
|
||||||
|
startY.value = e.clientY - currentY.value
|
||||||
|
setGrabCursor() // 设置小手光标
|
||||||
|
}
|
||||||
|
|
||||||
|
const onDrag = (e: MouseEvent) => {
|
||||||
|
if (!isDragging.value) return
|
||||||
|
e.preventDefault() // 禁用文本选择
|
||||||
|
|
||||||
|
// 使用 requestAnimationFrame 优化性能
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
currentX.value = e.clientX - startX.value
|
||||||
|
currentY.value = e.clientY - startY.value
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const stopDrag = () => {
|
||||||
|
isDragging.value = false
|
||||||
|
resetCursor() // 重置光标
|
||||||
|
}
|
||||||
|
|
||||||
// 放大
|
|
||||||
const zoomIn = () => {
|
const zoomIn = () => {
|
||||||
if (scaleValue.value == MAX_SCALE_VALUE) {
|
if (scaleValue.value == MAX_SCALE_VALUE) {
|
||||||
return
|
return
|
||||||
@ -70,7 +137,6 @@ const zoomIn = () => {
|
|||||||
scaleValue.value += 10
|
scaleValue.value += 10
|
||||||
}
|
}
|
||||||
|
|
||||||
// 缩小
|
|
||||||
const zoomOut = () => {
|
const zoomOut = () => {
|
||||||
if (scaleValue.value == MIN_SCALE_VALUE) {
|
if (scaleValue.value == MIN_SCALE_VALUE) {
|
||||||
return
|
return
|
||||||
@ -82,10 +148,15 @@ const processReZoom = () => {
|
|||||||
scaleValue.value = 100
|
scaleValue.value = 100
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const resetPosition = () => {
|
||||||
|
currentX.value = initialX.value
|
||||||
|
currentY.value = initialY.value
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 校验节点设置 */
|
||||||
const errorDialogVisible = ref(false)
|
const errorDialogVisible = ref(false)
|
||||||
let errorNodes: SimpleFlowNode[] = []
|
let errorNodes: SimpleFlowNode[] = []
|
||||||
|
|
||||||
// 校验节点设置。 暂时以 showText 为空 未节点错误配置
|
|
||||||
const validateNode = (node: SimpleFlowNode | undefined, errorNodes: SimpleFlowNode[]) => {
|
const validateNode = (node: SimpleFlowNode | undefined, errorNodes: SimpleFlowNode[]) => {
|
||||||
if (node) {
|
if (node) {
|
||||||
const { type, showText, conditionNodes } = node
|
const { type, showText, conditionNodes } = node
|
||||||
@ -143,6 +214,52 @@ const getCurrentFlowData = async () => {
|
|||||||
defineExpose({
|
defineExpose({
|
||||||
getCurrentFlowData
|
getCurrentFlowData
|
||||||
})
|
})
|
||||||
|
|
||||||
|
/** 导出 JSON */
|
||||||
|
const exportJson = () => {
|
||||||
|
download.json(new Blob([JSON.stringify(processNodeTree.value)]), 'model.json')
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 导入 JSON */
|
||||||
|
const refFile = ref()
|
||||||
|
const importJson = () => {
|
||||||
|
refFile.value.click()
|
||||||
|
}
|
||||||
|
const importLocalFile = () => {
|
||||||
|
const file = refFile.value.files[0]
|
||||||
|
const reader = new FileReader()
|
||||||
|
reader.readAsText(file)
|
||||||
|
reader.onload = function () {
|
||||||
|
if (isString(this.result)) {
|
||||||
|
processNodeTree.value = JSON.parse(this.result)
|
||||||
|
emits('save', processNodeTree.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 在组件初始化时记录初始位置
|
||||||
|
onMounted(() => {
|
||||||
|
initialX.value = currentX.value
|
||||||
|
initialY.value = currentY.value
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped></style>
|
<style lang="scss" scoped>
|
||||||
|
.simple-process-model-container {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
user-select: none; // 禁用文本选择
|
||||||
|
}
|
||||||
|
|
||||||
|
.simple-process-model {
|
||||||
|
position: relative; // 确保相对定位
|
||||||
|
min-width: 100%; // 确保宽度为100%
|
||||||
|
min-height: 100%; // 确保高度为100%
|
||||||
|
}
|
||||||
|
|
||||||
|
.z-index-button-group {
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
@ -45,4 +45,3 @@ watch(
|
|||||||
provide('tasks', approveTasks)
|
provide('tasks', approveTasks)
|
||||||
provide('processInstance', currentProcessInstance)
|
provide('processInstance', currentProcessInstance)
|
||||||
</script>
|
</script>
|
||||||
p
|
|
||||||
|
@ -23,11 +23,26 @@ export enum NodeType {
|
|||||||
*/
|
*/
|
||||||
COPY_TASK_NODE = 12,
|
COPY_TASK_NODE = 12,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 办理人节点
|
||||||
|
*/
|
||||||
|
TRANSACTOR_NODE = 13,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 延迟器节点
|
* 延迟器节点
|
||||||
*/
|
*/
|
||||||
DELAY_TIMER_NODE = 14,
|
DELAY_TIMER_NODE = 14,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 触发器节点
|
||||||
|
*/
|
||||||
|
TRIGGER_NODE = 15,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 子流程节点
|
||||||
|
*/
|
||||||
|
CHILD_PROCESS_NODE = 20,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 条件节点
|
* 条件节点
|
||||||
*/
|
*/
|
||||||
@ -44,7 +59,11 @@ export enum NodeType {
|
|||||||
/**
|
/**
|
||||||
* 包容分支节点 (对应包容网关)
|
* 包容分支节点 (对应包容网关)
|
||||||
*/
|
*/
|
||||||
INCLUSIVE_BRANCH_NODE = 53
|
INCLUSIVE_BRANCH_NODE = 53,
|
||||||
|
/**
|
||||||
|
* 路由分支节点
|
||||||
|
*/
|
||||||
|
ROUTER_BRANCH_NODE = 54
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum NodeId {
|
export enum NodeId {
|
||||||
@ -93,18 +112,29 @@ export interface SimpleFlowNode {
|
|||||||
assignEmptyHandler?: AssignEmptyHandler
|
assignEmptyHandler?: AssignEmptyHandler
|
||||||
// 审批节点的审批人与发起人相同时,对应的处理类型
|
// 审批节点的审批人与发起人相同时,对应的处理类型
|
||||||
assignStartUserHandlerType?: number
|
assignStartUserHandlerType?: number
|
||||||
// 条件类型
|
// 创建任务监听器
|
||||||
conditionType?: ConditionType
|
taskCreateListener?: ListenerHandler
|
||||||
// 条件表达式
|
// 创建任务监听器
|
||||||
conditionExpression?: string
|
taskAssignListener?: ListenerHandler
|
||||||
// 条件组
|
// 创建任务监听器
|
||||||
conditionGroups?: ConditionGroup
|
taskCompleteListener?: ListenerHandler
|
||||||
// 是否默认的条件
|
// 条件设置
|
||||||
defaultFlow?: boolean
|
conditionSetting?: ConditionSetting
|
||||||
// 活动的状态,用于前端节点状态展示
|
// 活动的状态,用于前端节点状态展示
|
||||||
activityStatus?: TaskStatusEnum
|
activityStatus?: TaskStatusEnum
|
||||||
// 延迟设置
|
// 延迟设置
|
||||||
delaySetting?: DelaySetting
|
delaySetting?: DelaySetting
|
||||||
|
// 路由分支
|
||||||
|
routerGroups?: RouterSetting[]
|
||||||
|
defaultFlowId?: string
|
||||||
|
// 签名
|
||||||
|
signEnable?: boolean
|
||||||
|
// 审批意见
|
||||||
|
reasonRequire?: boolean
|
||||||
|
// 触发器设置
|
||||||
|
triggerSetting?: TriggerSetting
|
||||||
|
// 子流程
|
||||||
|
childProcessSetting?: ChildProcessSetting
|
||||||
}
|
}
|
||||||
// 候选人策略枚举 ( 用于审批节点。抄送节点 )
|
// 候选人策略枚举 ( 用于审批节点。抄送节点 )
|
||||||
export enum CandidateStrategy {
|
export enum CandidateStrategy {
|
||||||
@ -132,6 +162,10 @@ export enum CandidateStrategy {
|
|||||||
* 指定用户
|
* 指定用户
|
||||||
*/
|
*/
|
||||||
USER = 30,
|
USER = 30,
|
||||||
|
/**
|
||||||
|
* 审批人自选
|
||||||
|
*/
|
||||||
|
APPROVE_USER_SELECT = 34,
|
||||||
/**
|
/**
|
||||||
* 发起人自选
|
* 发起人自选
|
||||||
*/
|
*/
|
||||||
@ -222,6 +256,41 @@ export type AssignEmptyHandler = {
|
|||||||
userIds?: number[]
|
userIds?: number[]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 监听器的结构定义
|
||||||
|
*/
|
||||||
|
export type ListenerHandler = {
|
||||||
|
enable: boolean
|
||||||
|
path?: string
|
||||||
|
header?: HttpRequestParam[]
|
||||||
|
body?: HttpRequestParam[]
|
||||||
|
}
|
||||||
|
export type HttpRequestParam = {
|
||||||
|
key: string
|
||||||
|
type: number
|
||||||
|
value: string
|
||||||
|
}
|
||||||
|
export enum BpmHttpRequestParamTypeEnum {
|
||||||
|
/**
|
||||||
|
* 固定值
|
||||||
|
*/
|
||||||
|
FIXED_VALUE = 1,
|
||||||
|
/**
|
||||||
|
* 表单
|
||||||
|
*/
|
||||||
|
FROM_FORM = 2
|
||||||
|
}
|
||||||
|
export const BPM_HTTP_REQUEST_PARAM_TYPES = [
|
||||||
|
{
|
||||||
|
value: 1,
|
||||||
|
label: '固定值'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 2,
|
||||||
|
label: '表单'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
// 审批拒绝类型枚举
|
// 审批拒绝类型枚举
|
||||||
export enum RejectHandlerType {
|
export enum RejectHandlerType {
|
||||||
/**
|
/**
|
||||||
@ -315,6 +384,20 @@ export enum TimeUnitType {
|
|||||||
DAY = 3
|
DAY = 3
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 条件节点设置结构定义,用于条件节点
|
||||||
|
*/
|
||||||
|
export type ConditionSetting = {
|
||||||
|
// 条件类型
|
||||||
|
conditionType?: ConditionType
|
||||||
|
// 条件表达式
|
||||||
|
conditionExpression?: string
|
||||||
|
// 条件组
|
||||||
|
conditionGroups?: ConditionGroup
|
||||||
|
// 是否默认的条件
|
||||||
|
defaultFlow?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
// 条件配置类型 ( 用于条件节点配置 )
|
// 条件配置类型 ( 用于条件节点配置 )
|
||||||
export enum ConditionType {
|
export enum ConditionType {
|
||||||
/**
|
/**
|
||||||
@ -389,8 +472,6 @@ export enum OperationButtonType {
|
|||||||
* 条件规则结构定义
|
* 条件规则结构定义
|
||||||
*/
|
*/
|
||||||
export type ConditionRule = {
|
export type ConditionRule = {
|
||||||
type: number
|
|
||||||
opName: string
|
|
||||||
opCode: string
|
opCode: string
|
||||||
leftSide: string
|
leftSide: string
|
||||||
rightSide: string
|
rightSide: string
|
||||||
@ -405,6 +486,24 @@ export type ConditionGroup = {
|
|||||||
// 条件数组
|
// 条件数组
|
||||||
conditions: Condition[]
|
conditions: Condition[]
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* 条件组默认值
|
||||||
|
*/
|
||||||
|
export const DEFAULT_CONDITION_GROUP_VALUE = {
|
||||||
|
and: true,
|
||||||
|
conditions: [
|
||||||
|
{
|
||||||
|
and: true,
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
opCode: '==',
|
||||||
|
leftSide: '',
|
||||||
|
rightSide: ''
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 条件结构定义
|
* 条件结构定义
|
||||||
@ -421,6 +520,10 @@ NODE_DEFAULT_TEXT.set(NodeType.COPY_TASK_NODE, '请配置抄送人')
|
|||||||
NODE_DEFAULT_TEXT.set(NodeType.CONDITION_NODE, '请设置条件')
|
NODE_DEFAULT_TEXT.set(NodeType.CONDITION_NODE, '请设置条件')
|
||||||
NODE_DEFAULT_TEXT.set(NodeType.START_USER_NODE, '请设置发起人')
|
NODE_DEFAULT_TEXT.set(NodeType.START_USER_NODE, '请设置发起人')
|
||||||
NODE_DEFAULT_TEXT.set(NodeType.DELAY_TIMER_NODE, '请设置延迟器')
|
NODE_DEFAULT_TEXT.set(NodeType.DELAY_TIMER_NODE, '请设置延迟器')
|
||||||
|
NODE_DEFAULT_TEXT.set(NodeType.ROUTER_BRANCH_NODE, '请设置路由节点')
|
||||||
|
NODE_DEFAULT_TEXT.set(NodeType.TRIGGER_NODE, '请设置触发器')
|
||||||
|
NODE_DEFAULT_TEXT.set(NodeType.TRANSACTOR_NODE, '请设置办理人')
|
||||||
|
NODE_DEFAULT_TEXT.set(NodeType.CHILD_PROCESS_NODE, '请设置子流程')
|
||||||
|
|
||||||
export const NODE_DEFAULT_NAME = new Map<number, string>()
|
export const NODE_DEFAULT_NAME = new Map<number, string>()
|
||||||
NODE_DEFAULT_NAME.set(NodeType.USER_TASK_NODE, '审批人')
|
NODE_DEFAULT_NAME.set(NodeType.USER_TASK_NODE, '审批人')
|
||||||
@ -428,15 +531,21 @@ NODE_DEFAULT_NAME.set(NodeType.COPY_TASK_NODE, '抄送人')
|
|||||||
NODE_DEFAULT_NAME.set(NodeType.CONDITION_NODE, '条件')
|
NODE_DEFAULT_NAME.set(NodeType.CONDITION_NODE, '条件')
|
||||||
NODE_DEFAULT_NAME.set(NodeType.START_USER_NODE, '发起人')
|
NODE_DEFAULT_NAME.set(NodeType.START_USER_NODE, '发起人')
|
||||||
NODE_DEFAULT_NAME.set(NodeType.DELAY_TIMER_NODE, '延迟器')
|
NODE_DEFAULT_NAME.set(NodeType.DELAY_TIMER_NODE, '延迟器')
|
||||||
|
NODE_DEFAULT_NAME.set(NodeType.ROUTER_BRANCH_NODE, '路由分支')
|
||||||
|
NODE_DEFAULT_NAME.set(NodeType.TRIGGER_NODE, '触发器')
|
||||||
|
NODE_DEFAULT_NAME.set(NodeType.TRANSACTOR_NODE, '办理人')
|
||||||
|
NODE_DEFAULT_NAME.set(NodeType.CHILD_PROCESS_NODE, '子流程')
|
||||||
|
|
||||||
// 候选人策略。暂时不从字典中取。 后续可能调整。控制显示顺序
|
// 候选人策略。暂时不从字典中取。 后续可能调整。控制显示顺序
|
||||||
export const CANDIDATE_STRATEGY: DictDataVO[] = [
|
export const CANDIDATE_STRATEGY: DictDataVO[] = [
|
||||||
{ label: '指定成员', value: CandidateStrategy.USER },
|
{ label: '指定成员', value: CandidateStrategy.USER },
|
||||||
{ label: '指定角色', value: CandidateStrategy.ROLE },
|
{ label: '指定角色', value: CandidateStrategy.ROLE },
|
||||||
|
{ label: '指定岗位', value: CandidateStrategy.POST },
|
||||||
{ label: '部门成员', value: CandidateStrategy.DEPT_MEMBER },
|
{ label: '部门成员', value: CandidateStrategy.DEPT_MEMBER },
|
||||||
{ label: '部门负责人', value: CandidateStrategy.DEPT_LEADER },
|
{ label: '部门负责人', value: CandidateStrategy.DEPT_LEADER },
|
||||||
{ label: '连续多级部门负责人', value: CandidateStrategy.MULTI_LEVEL_DEPT_LEADER },
|
{ label: '连续多级部门负责人', value: CandidateStrategy.MULTI_LEVEL_DEPT_LEADER },
|
||||||
{ label: '发起人自选', value: CandidateStrategy.START_USER_SELECT },
|
{ label: '发起人自选', value: CandidateStrategy.START_USER_SELECT },
|
||||||
|
{ label: '审批人自选', value: CandidateStrategy.APPROVE_USER_SELECT },
|
||||||
{ label: '发起人本人', value: CandidateStrategy.START_USER },
|
{ label: '发起人本人', value: CandidateStrategy.START_USER },
|
||||||
{ label: '发起人部门负责人', value: CandidateStrategy.START_USER_DEPT_LEADER },
|
{ label: '发起人部门负责人', value: CandidateStrategy.START_USER_DEPT_LEADER },
|
||||||
{ label: '发起人连续部门负责人', value: CandidateStrategy.START_USER_MULTI_LEVEL_DEPT_LEADER },
|
{ label: '发起人连续部门负责人', value: CandidateStrategy.START_USER_MULTI_LEVEL_DEPT_LEADER },
|
||||||
@ -460,8 +569,8 @@ export const APPROVE_METHODS: DictDataVO[] = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
export const CONDITION_CONFIG_TYPES: DictDataVO[] = [
|
export const CONDITION_CONFIG_TYPES: DictDataVO[] = [
|
||||||
{ label: '条件表达式', value: ConditionType.EXPRESSION },
|
{ label: '条件规则', value: ConditionType.RULE },
|
||||||
{ label: '条件规则', value: ConditionType.RULE }
|
{ label: '条件表达式', value: ConditionType.EXPRESSION }
|
||||||
]
|
]
|
||||||
|
|
||||||
// 时间单位类型
|
// 时间单位类型
|
||||||
@ -540,6 +649,16 @@ export const DEFAULT_BUTTON_SETTING: ButtonSetting[] = [
|
|||||||
{ id: OperationButtonType.RETURN, displayName: '退回', enable: true }
|
{ id: OperationButtonType.RETURN, displayName: '退回', enable: true }
|
||||||
]
|
]
|
||||||
|
|
||||||
|
// 办理人默认的按钮权限设置
|
||||||
|
export const TRANSACTOR_DEFAULT_BUTTON_SETTING: ButtonSetting[] = [
|
||||||
|
{ id: OperationButtonType.APPROVE, displayName: '办理', enable: true },
|
||||||
|
{ id: OperationButtonType.REJECT, displayName: '拒绝', enable: false },
|
||||||
|
{ id: OperationButtonType.TRANSFER, displayName: '转办', enable: false },
|
||||||
|
{ id: OperationButtonType.DELEGATE, displayName: '委派', enable: false },
|
||||||
|
{ id: OperationButtonType.ADD_SIGN, displayName: '加签', enable: false },
|
||||||
|
{ id: OperationButtonType.RETURN, displayName: '退回', enable: false }
|
||||||
|
]
|
||||||
|
|
||||||
// 发起人的按钮权限。暂时定死,不可以编辑
|
// 发起人的按钮权限。暂时定死,不可以编辑
|
||||||
export const START_USER_BUTTON_SETTING: ButtonSetting[] = [
|
export const START_USER_BUTTON_SETTING: ButtonSetting[] = [
|
||||||
{ id: OperationButtonType.APPROVE, displayName: '提交', enable: true },
|
{ id: OperationButtonType.APPROVE, displayName: '提交', enable: true },
|
||||||
@ -575,7 +694,15 @@ export enum ProcessVariableEnum {
|
|||||||
/**
|
/**
|
||||||
* 发起用户 ID
|
* 发起用户 ID
|
||||||
*/
|
*/
|
||||||
START_USER_ID = 'PROCESS_START_USER_ID'
|
START_USER_ID = 'PROCESS_START_USER_ID',
|
||||||
|
/**
|
||||||
|
* 发起时间
|
||||||
|
*/
|
||||||
|
START_TIME = 'PROCESS_START_TIME',
|
||||||
|
/**
|
||||||
|
* 流程定义名称
|
||||||
|
*/
|
||||||
|
PROCESS_DEFINITION_NAME = 'PROCESS_DEFINITION_NAME'
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -604,3 +731,170 @@ export const DELAY_TYPE = [
|
|||||||
{ label: '固定时长', value: DelayTypeEnum.FIXED_TIME_DURATION },
|
{ label: '固定时长', value: DelayTypeEnum.FIXED_TIME_DURATION },
|
||||||
{ label: '固定日期', value: DelayTypeEnum.FIXED_DATE_TIME }
|
{ label: '固定日期', value: DelayTypeEnum.FIXED_DATE_TIME }
|
||||||
]
|
]
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 路由分支结构定义
|
||||||
|
*/
|
||||||
|
export type RouterSetting = {
|
||||||
|
nodeId: string
|
||||||
|
conditionType: ConditionType
|
||||||
|
conditionExpression: string
|
||||||
|
conditionGroups: ConditionGroup
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== 触发器相关定义 ====================
|
||||||
|
/**
|
||||||
|
* 触发器节点结构定义
|
||||||
|
*/
|
||||||
|
export type TriggerSetting = {
|
||||||
|
type: TriggerTypeEnum
|
||||||
|
httpRequestSetting?: HttpRequestTriggerSetting
|
||||||
|
formSettings?: FormTriggerSetting[]
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 触发器类型枚举
|
||||||
|
*/
|
||||||
|
export enum TriggerTypeEnum {
|
||||||
|
/**
|
||||||
|
* 发送 HTTP 请求触发器
|
||||||
|
*/
|
||||||
|
HTTP_REQUEST = 1,
|
||||||
|
/**
|
||||||
|
* 接收 HTTP 回调请求触发器
|
||||||
|
*/
|
||||||
|
HTTP_CALLBACK = 2,
|
||||||
|
/**
|
||||||
|
* 表单数据更新触发器
|
||||||
|
*/
|
||||||
|
FORM_UPDATE = 10,
|
||||||
|
/**
|
||||||
|
* 表单数据删除触发器
|
||||||
|
*/
|
||||||
|
FORM_DELETE = 11
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP 请求触发器结构定义
|
||||||
|
*/
|
||||||
|
export type HttpRequestTriggerSetting = {
|
||||||
|
// 请求 URL
|
||||||
|
url: string
|
||||||
|
// 请求头参数设置
|
||||||
|
header?: HttpRequestParam[]
|
||||||
|
// 请求体参数设置
|
||||||
|
body?: HttpRequestParam[]
|
||||||
|
// 请求响应设置
|
||||||
|
response?: Record<string, string>[]
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 流程表单触发器配置结构定义
|
||||||
|
*/
|
||||||
|
export type FormTriggerSetting = {
|
||||||
|
// 条件类型
|
||||||
|
conditionType?: ConditionType
|
||||||
|
// 条件表达式
|
||||||
|
conditionExpression?: string
|
||||||
|
// 条件组
|
||||||
|
conditionGroups?: ConditionGroup
|
||||||
|
// 更新表单字段配置
|
||||||
|
updateFormFields?: Record<string, any>
|
||||||
|
// 删除表单字段配置
|
||||||
|
deleteFields?: string[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export const TRIGGER_TYPES: DictDataVO[] = [
|
||||||
|
{ label: '发送 HTTP 请求', value: TriggerTypeEnum.HTTP_REQUEST },
|
||||||
|
{ label: '接收 HTTP 回调', value: TriggerTypeEnum.HTTP_CALLBACK },
|
||||||
|
{ label: '修改表单数据', value: TriggerTypeEnum.FORM_UPDATE },
|
||||||
|
{ label: '删除表单数据', value: TriggerTypeEnum.FORM_DELETE }
|
||||||
|
]
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 子流程节点结构定义
|
||||||
|
*/
|
||||||
|
export type ChildProcessSetting = {
|
||||||
|
calledProcessDefinitionKey: string
|
||||||
|
calledProcessDefinitionName: string
|
||||||
|
async: boolean
|
||||||
|
inVariables?: IOParameter[]
|
||||||
|
outVariables?: IOParameter[]
|
||||||
|
skipStartUserNode: boolean
|
||||||
|
startUserSetting: StartUserSetting
|
||||||
|
timeoutSetting: TimeoutSetting
|
||||||
|
multiInstanceSetting: MultiInstanceSetting
|
||||||
|
}
|
||||||
|
export type IOParameter = {
|
||||||
|
source: string
|
||||||
|
target: string
|
||||||
|
}
|
||||||
|
export type StartUserSetting = {
|
||||||
|
type: ChildProcessStartUserTypeEnum
|
||||||
|
formField?: string
|
||||||
|
emptyType?: ChildProcessStartUserEmptyTypeEnum
|
||||||
|
}
|
||||||
|
export type TimeoutSetting = {
|
||||||
|
enable: boolean
|
||||||
|
type?: DelayTypeEnum
|
||||||
|
timeExpression?: string
|
||||||
|
}
|
||||||
|
export type MultiInstanceSetting = {
|
||||||
|
enable: boolean
|
||||||
|
sequential?: boolean
|
||||||
|
approveRatio?: number
|
||||||
|
sourceType?: ChildProcessMultiInstanceSourceTypeEnum
|
||||||
|
source?: string
|
||||||
|
}
|
||||||
|
export enum ChildProcessStartUserTypeEnum {
|
||||||
|
/**
|
||||||
|
* 同主流程发起人
|
||||||
|
*/
|
||||||
|
MAIN_PROCESS_START_USER = 1,
|
||||||
|
/**
|
||||||
|
* 表单
|
||||||
|
*/
|
||||||
|
FROM_FORM = 2
|
||||||
|
}
|
||||||
|
export const CHILD_PROCESS_START_USER_TYPE = [
|
||||||
|
{ label: '同主流程发起人', value: ChildProcessStartUserTypeEnum.MAIN_PROCESS_START_USER },
|
||||||
|
{ label: '表单', value: ChildProcessStartUserTypeEnum.FROM_FORM }
|
||||||
|
]
|
||||||
|
export enum ChildProcessStartUserEmptyTypeEnum {
|
||||||
|
/**
|
||||||
|
* 同主流程发起人
|
||||||
|
*/
|
||||||
|
MAIN_PROCESS_START_USER = 1,
|
||||||
|
/**
|
||||||
|
* 子流程管理员
|
||||||
|
*/
|
||||||
|
CHILD_PROCESS_ADMIN = 2,
|
||||||
|
/**
|
||||||
|
* 主流程管理员
|
||||||
|
*/
|
||||||
|
MAIN_PROCESS_ADMIN = 3
|
||||||
|
}
|
||||||
|
export const CHILD_PROCESS_START_USER_EMPTY_TYPE = [
|
||||||
|
{ label: '同主流程发起人', value: ChildProcessStartUserEmptyTypeEnum.MAIN_PROCESS_START_USER },
|
||||||
|
{ label: '子流程管理员', value: ChildProcessStartUserEmptyTypeEnum.CHILD_PROCESS_ADMIN },
|
||||||
|
{ label: '主流程管理员', value: ChildProcessStartUserEmptyTypeEnum.MAIN_PROCESS_ADMIN }
|
||||||
|
]
|
||||||
|
export enum ChildProcessMultiInstanceSourceTypeEnum {
|
||||||
|
/**
|
||||||
|
* 固定数量
|
||||||
|
*/
|
||||||
|
FIXED_QUANTITY = 1,
|
||||||
|
/**
|
||||||
|
* 数字表单
|
||||||
|
*/
|
||||||
|
NUMBER_FORM = 2,
|
||||||
|
/**
|
||||||
|
* 多选表单
|
||||||
|
*/
|
||||||
|
MULTIPLE_FORM = 3
|
||||||
|
}
|
||||||
|
export const CHILD_PROCESS_MULTI_INSTANCE_SOURCE_TYPE = [
|
||||||
|
{ label: '固定数量', value: ChildProcessMultiInstanceSourceTypeEnum.FIXED_QUANTITY },
|
||||||
|
{ label: '数字表单', value: ChildProcessMultiInstanceSourceTypeEnum.NUMBER_FORM },
|
||||||
|
{ label: '多选表单', value: ChildProcessMultiInstanceSourceTypeEnum.MULTIPLE_FORM }
|
||||||
|
]
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import { cloneDeep } from 'lodash-es'
|
|
||||||
import { TaskStatusEnum } from '@/api/bpm/task'
|
import { TaskStatusEnum } from '@/api/bpm/task'
|
||||||
import * as RoleApi from '@/api/system/role'
|
import * as RoleApi from '@/api/system/role'
|
||||||
import * as DeptApi from '@/api/system/dept'
|
import * as DeptApi from '@/api/system/dept'
|
||||||
@ -14,9 +13,15 @@ import {
|
|||||||
NODE_DEFAULT_NAME,
|
NODE_DEFAULT_NAME,
|
||||||
AssignStartUserHandlerType,
|
AssignStartUserHandlerType,
|
||||||
AssignEmptyHandlerType,
|
AssignEmptyHandlerType,
|
||||||
FieldPermissionType
|
FieldPermissionType,
|
||||||
|
HttpRequestParam,
|
||||||
|
ProcessVariableEnum,
|
||||||
|
ConditionType,
|
||||||
|
ConditionGroup,
|
||||||
|
COMPARISON_OPERATORS
|
||||||
} from './consts'
|
} from './consts'
|
||||||
import { parseFormFields } from '@/components/FormCreate/src/utils/index'
|
import { parseFormFields } from '@/components/FormCreate/src/utils'
|
||||||
|
|
||||||
export function useWatchNode(props: { flowNode: SimpleFlowNode }): Ref<SimpleFlowNode> {
|
export function useWatchNode(props: { flowNode: SimpleFlowNode }): Ref<SimpleFlowNode> {
|
||||||
const node = ref<SimpleFlowNode>(props.flowNode)
|
const node = ref<SimpleFlowNode>(props.flowNode)
|
||||||
watch(
|
watch(
|
||||||
@ -46,9 +51,9 @@ export function useFormFieldsPermission(defaultPermission: FieldPermissionType)
|
|||||||
// 字段权限配置. 需要有 field, title, permissioin 属性
|
// 字段权限配置. 需要有 field, title, permissioin 属性
|
||||||
const fieldsPermissionConfig = ref<Array<Record<string, any>>>([])
|
const fieldsPermissionConfig = ref<Array<Record<string, any>>>([])
|
||||||
|
|
||||||
const formType = inject<Ref<number>>('formType') // 表单类型
|
const formType = inject<Ref<number | undefined>>('formType', ref()) // 表单类型
|
||||||
|
|
||||||
const formFields = inject<Ref<string[]>>('formFields') // 流程表单字段
|
const formFields = inject<Ref<string[]>>('formFields', ref([])) // 流程表单字段
|
||||||
|
|
||||||
const getNodeConfigFormFields = (nodeFormFields?: Array<Record<string, string>>) => {
|
const getNodeConfigFormFields = (nodeFormFields?: Array<Record<string, string>>) => {
|
||||||
nodeFormFields = toRaw(nodeFormFields)
|
nodeFormFields = toRaw(nodeFormFields)
|
||||||
@ -104,16 +109,32 @@ export function useFormFieldsPermission(defaultPermission: FieldPermissionType)
|
|||||||
getNodeConfigFormFields
|
getNodeConfigFormFields
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description 获取表单的字段
|
* @description 获取流程表单的字段
|
||||||
*/
|
*/
|
||||||
export function useFormFields() {
|
export function useFormFields() {
|
||||||
const formFields = inject<Ref<string[]>>('formFields') // 流程表单字段
|
const formFields = inject<Ref<string[]>>('formFields', ref([])) // 流程表单字段
|
||||||
return parseFormCreateFields(unref(formFields))
|
return parseFormCreateFields(unref(formFields))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO @芋艿:后续需要把各种类似 useFormFieldsPermission 的逻辑,抽成一个通用方法。
|
||||||
|
/**
|
||||||
|
* @description 获取流程表单的字段和发起人字段
|
||||||
|
*/
|
||||||
|
export function useFormFieldsAndStartUser() {
|
||||||
|
const injectFormFields = inject<Ref<string[]>>('formFields', ref([])) // 流程表单字段
|
||||||
|
const formFields = parseFormCreateFields(unref(injectFormFields))
|
||||||
|
// 添加发起人
|
||||||
|
formFields.unshift({
|
||||||
|
field: ProcessVariableEnum.START_USER_ID,
|
||||||
|
title: '发起人',
|
||||||
|
required: true
|
||||||
|
})
|
||||||
|
return formFields
|
||||||
|
}
|
||||||
|
|
||||||
export type UserTaskFormType = {
|
export type UserTaskFormType = {
|
||||||
//candidateParamArray: any[]
|
|
||||||
candidateStrategy: CandidateStrategy
|
candidateStrategy: CandidateStrategy
|
||||||
approveMethod: ApproveMethodType
|
approveMethod: ApproveMethodType
|
||||||
roleIds?: number[] // 角色
|
roleIds?: number[] // 角色
|
||||||
@ -136,10 +157,29 @@ export type UserTaskFormType = {
|
|||||||
timeDuration?: number
|
timeDuration?: number
|
||||||
maxRemindCount?: number
|
maxRemindCount?: number
|
||||||
buttonsSetting: any[]
|
buttonsSetting: any[]
|
||||||
|
taskCreateListenerEnable?: boolean
|
||||||
|
taskCreateListenerPath?: string
|
||||||
|
taskCreateListener?: {
|
||||||
|
header: HttpRequestParam[]
|
||||||
|
body: HttpRequestParam[]
|
||||||
|
}
|
||||||
|
taskAssignListenerEnable?: boolean
|
||||||
|
taskAssignListenerPath?: string
|
||||||
|
taskAssignListener?: {
|
||||||
|
header: HttpRequestParam[]
|
||||||
|
body: HttpRequestParam[]
|
||||||
|
}
|
||||||
|
taskCompleteListenerEnable?: boolean
|
||||||
|
taskCompleteListenerPath?: string
|
||||||
|
taskCompleteListener?: {
|
||||||
|
header: HttpRequestParam[]
|
||||||
|
body: HttpRequestParam[]
|
||||||
|
}
|
||||||
|
signEnable: boolean
|
||||||
|
reasonRequire: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export type CopyTaskFormType = {
|
export type CopyTaskFormType = {
|
||||||
// candidateParamArray: any[]
|
|
||||||
candidateStrategy: CandidateStrategy
|
candidateStrategy: CandidateStrategy
|
||||||
roleIds?: number[] // 角色
|
roleIds?: number[] // 角色
|
||||||
deptIds?: number[] // 部门
|
deptIds?: number[] // 部门
|
||||||
@ -156,15 +196,15 @@ export type CopyTaskFormType = {
|
|||||||
* @description 节点表单数据。 用于审批节点、抄送节点
|
* @description 节点表单数据。 用于审批节点、抄送节点
|
||||||
*/
|
*/
|
||||||
export function useNodeForm(nodeType: NodeType) {
|
export function useNodeForm(nodeType: NodeType) {
|
||||||
const roleOptions = inject<Ref<RoleApi.RoleVO[]>>('roleList') // 角色列表
|
const roleOptions = inject<Ref<RoleApi.RoleVO[]>>('roleList', ref([])) // 角色列表
|
||||||
const postOptions = inject<Ref<PostApi.PostVO[]>>('postList') // 岗位列表
|
const postOptions = inject<Ref<PostApi.PostVO[]>>('postList', ref([])) // 岗位列表
|
||||||
const userOptions = inject<Ref<UserApi.UserVO[]>>('userList') // 用户列表
|
const userOptions = inject<Ref<UserApi.UserVO[]>>('userList', ref([])) // 用户列表
|
||||||
const deptOptions = inject<Ref<DeptApi.DeptVO[]>>('deptList') // 部门列表
|
const deptOptions = inject<Ref<DeptApi.DeptVO[]>>('deptList', ref([])) // 部门列表
|
||||||
const userGroupOptions = inject<Ref<UserGroupApi.UserGroupVO[]>>('userGroupList') // 用户组列表
|
const userGroupOptions = inject<Ref<UserGroupApi.UserGroupVO[]>>('userGroupList', ref([])) // 用户组列表
|
||||||
const deptTreeOptions = inject('deptTree') // 部门树
|
const deptTreeOptions = inject('deptTree', ref()) // 部门树
|
||||||
const formFields = inject<Ref<string[]>>('formFields') // 流程表单字段
|
const formFields = inject<Ref<string[]>>('formFields', ref([])) // 流程表单字段
|
||||||
const configForm = ref<UserTaskFormType | CopyTaskFormType>()
|
const configForm = ref<UserTaskFormType | CopyTaskFormType>()
|
||||||
if (nodeType === NodeType.USER_TASK_NODE) {
|
if (nodeType === NodeType.USER_TASK_NODE || nodeType === NodeType.TRANSACTOR_NODE) {
|
||||||
configForm.value = {
|
configForm.value = {
|
||||||
candidateStrategy: CandidateStrategy.USER,
|
candidateStrategy: CandidateStrategy.USER,
|
||||||
approveMethod: ApproveMethodType.SEQUENTIAL_APPROVE,
|
approveMethod: ApproveMethodType.SEQUENTIAL_APPROVE,
|
||||||
@ -270,6 +310,11 @@ export function useNodeForm(nodeType: NodeType) {
|
|||||||
showText = `表单内部门负责人`
|
showText = `表单内部门负责人`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 审批人自选
|
||||||
|
if (configForm.value?.candidateStrategy === CandidateStrategy.APPROVE_USER_SELECT) {
|
||||||
|
showText = `审批人自选`
|
||||||
|
}
|
||||||
|
|
||||||
// 发起人自选
|
// 发起人自选
|
||||||
if (configForm.value?.candidateStrategy === CandidateStrategy.START_USER_SELECT) {
|
if (configForm.value?.candidateStrategy === CandidateStrategy.START_USER_SELECT) {
|
||||||
showText = `发起人自选`
|
showText = `发起人自选`
|
||||||
@ -506,6 +551,66 @@ export function useTaskStatusClass(taskStatus: TaskStatusEnum | undefined): stri
|
|||||||
if (taskStatus === TaskStatusEnum.CANCEL) {
|
if (taskStatus === TaskStatusEnum.CANCEL) {
|
||||||
return 'status-cancel'
|
return 'status-cancel'
|
||||||
}
|
}
|
||||||
|
|
||||||
return ''
|
return ''
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 条件组件文字展示 */
|
||||||
|
export function getConditionShowText(
|
||||||
|
conditionType: ConditionType | undefined,
|
||||||
|
conditionExpression: string | undefined,
|
||||||
|
conditionGroups: ConditionGroup | undefined,
|
||||||
|
fieldOptions: Array<Record<string, any>>
|
||||||
|
) {
|
||||||
|
let showText = ''
|
||||||
|
if (conditionType === ConditionType.EXPRESSION) {
|
||||||
|
if (conditionExpression) {
|
||||||
|
showText = `表达式:${conditionExpression}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (conditionType === ConditionType.RULE) {
|
||||||
|
// 条件组是否为与关系
|
||||||
|
const groupAnd = conditionGroups?.and
|
||||||
|
let warningMessage: undefined | string = undefined
|
||||||
|
const conditionGroup = conditionGroups?.conditions.map((item) => {
|
||||||
|
return (
|
||||||
|
'(' +
|
||||||
|
item.rules
|
||||||
|
.map((rule) => {
|
||||||
|
if (rule.leftSide && rule.rightSide) {
|
||||||
|
return (
|
||||||
|
getFormFieldTitle(fieldOptions, rule.leftSide) +
|
||||||
|
' ' +
|
||||||
|
getOpName(rule.opCode) +
|
||||||
|
' ' +
|
||||||
|
rule.rightSide
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
// 有一条规则不完善。提示错误
|
||||||
|
warningMessage = '请完善条件规则'
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.join(item.and ? ' 且 ' : ' 或 ') +
|
||||||
|
' ) '
|
||||||
|
)
|
||||||
|
})
|
||||||
|
if (warningMessage) {
|
||||||
|
showText = ''
|
||||||
|
} else {
|
||||||
|
showText = conditionGroup!.join(groupAnd ? ' 且 ' : ' 或 ')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return showText
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 获取表单字段名称*/
|
||||||
|
const getFormFieldTitle = (fieldOptions: Array<Record<string, any>>, field: string) => {
|
||||||
|
const item = fieldOptions.find((item) => item.field === field)
|
||||||
|
return item?.title
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 获取操作符名称 */
|
||||||
|
const getOpName = (opCode: string): string => {
|
||||||
|
const opName = COMPARISON_OPERATORS.find((item: any) => item.value === opCode)
|
||||||
|
return opName?.label
|
||||||
|
}
|
||||||
|
@ -0,0 +1,610 @@
|
|||||||
|
<template>
|
||||||
|
<el-drawer
|
||||||
|
:append-to-body="true"
|
||||||
|
v-model="settingVisible"
|
||||||
|
:show-close="false"
|
||||||
|
:size="550"
|
||||||
|
:before-close="saveConfig"
|
||||||
|
>
|
||||||
|
<template #header>
|
||||||
|
<div class="config-header">
|
||||||
|
<input
|
||||||
|
v-if="showInput"
|
||||||
|
type="text"
|
||||||
|
class="config-editable-input"
|
||||||
|
@blur="blurEvent()"
|
||||||
|
v-mountedFocus
|
||||||
|
v-model="nodeName"
|
||||||
|
:placeholder="nodeName"
|
||||||
|
/>
|
||||||
|
<div v-else class="node-name">
|
||||||
|
{{ nodeName }} <Icon class="ml-1" icon="ep:edit-pen" :size="16" @click="clickIcon()" />
|
||||||
|
</div>
|
||||||
|
<div class="divide-line"></div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<el-tabs type="border-card" v-model="activeTabName">
|
||||||
|
<el-tab-pane label="子流程" name="child">
|
||||||
|
<div>
|
||||||
|
<el-form ref="formRef" :model="configForm" label-position="top" :rules="formRules">
|
||||||
|
<el-form-item label="是否异步" prop="async">
|
||||||
|
<el-switch v-model="configForm.async" active-text="异步" inactive-text="不异步" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="选择子流程" prop="calledProcessDefinitionKey">
|
||||||
|
<el-select
|
||||||
|
v-model="configForm.calledProcessDefinitionKey"
|
||||||
|
clearable
|
||||||
|
@change="handleCalledElementChange"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="(item, index) in childProcessOptions"
|
||||||
|
:key="index"
|
||||||
|
:label="item.name"
|
||||||
|
:value="item.key"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="是否自动跳过子流程发起节点" prop="skipStartUserNode">
|
||||||
|
<el-switch
|
||||||
|
v-model="configForm.skipStartUserNode"
|
||||||
|
active-text="跳过"
|
||||||
|
inactive-text="不跳过"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="主→子变量传递" prop="inVariables">
|
||||||
|
<div class="flex pt-2" v-for="(item, index) in configForm.inVariables" :key="index">
|
||||||
|
<div class="mr-2">
|
||||||
|
<el-form-item
|
||||||
|
:prop="`inVariables.${index}.source`"
|
||||||
|
:rules="{
|
||||||
|
required: true,
|
||||||
|
message: '变量不能为空',
|
||||||
|
trigger: 'blur'
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<el-select class="w-200px!" v-model="item.source">
|
||||||
|
<el-option
|
||||||
|
v-for="(field, fIdx) in formFieldOptions"
|
||||||
|
:key="fIdx"
|
||||||
|
:label="field.title"
|
||||||
|
:value="field.field"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</div>
|
||||||
|
<div class="mr-2">
|
||||||
|
<el-form-item
|
||||||
|
:prop="`inVariables.${index}.target`"
|
||||||
|
:rules="{
|
||||||
|
required: true,
|
||||||
|
message: '变量不能为空',
|
||||||
|
trigger: 'blur'
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<el-select class="w-200px!" v-model="item.target">
|
||||||
|
<el-option
|
||||||
|
v-for="(field, fIdx) in childFormFieldOptions"
|
||||||
|
:key="fIdx"
|
||||||
|
:label="field.title"
|
||||||
|
:value="field.field"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</div>
|
||||||
|
<div class="mr-1 flex items-center">
|
||||||
|
<Icon
|
||||||
|
icon="ep:delete"
|
||||||
|
:size="18"
|
||||||
|
@click="deleteVariable(index, configForm.inVariables)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<el-button type="primary" text @click="addVariable(configForm.inVariables)">
|
||||||
|
<Icon icon="ep:plus" class="mr-5px" />添加一行
|
||||||
|
</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
v-if="configForm.async === false"
|
||||||
|
label="子→主变量传递"
|
||||||
|
prop="outVariables"
|
||||||
|
>
|
||||||
|
<div class="flex pt-2" v-for="(item, index) in configForm.outVariables" :key="index">
|
||||||
|
<div class="mr-2">
|
||||||
|
<el-form-item
|
||||||
|
:prop="`outVariables.${index}.source`"
|
||||||
|
:rules="{
|
||||||
|
required: true,
|
||||||
|
message: '变量不能为空',
|
||||||
|
trigger: 'blur'
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<el-select class="w-200px!" v-model="item.source">
|
||||||
|
<el-option
|
||||||
|
v-for="(field, fIdx) in childFormFieldOptions"
|
||||||
|
:key="fIdx"
|
||||||
|
:label="field.title"
|
||||||
|
:value="field.field"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</div>
|
||||||
|
<div class="mr-2">
|
||||||
|
<el-form-item
|
||||||
|
:prop="`outVariables.${index}.target`"
|
||||||
|
:rules="{
|
||||||
|
required: true,
|
||||||
|
message: '变量不能为空',
|
||||||
|
trigger: 'blur'
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<el-select class="w-200px!" v-model="item.target">
|
||||||
|
<el-option
|
||||||
|
v-for="(field, fIdx) in formFieldOptions"
|
||||||
|
:key="fIdx"
|
||||||
|
:label="field.title"
|
||||||
|
:value="field.field"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</div>
|
||||||
|
<div class="mr-1 flex items-center">
|
||||||
|
<Icon
|
||||||
|
icon="ep:delete"
|
||||||
|
:size="18"
|
||||||
|
@click="deleteVariable(index, configForm.outVariables)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<el-button type="primary" text @click="addVariable(configForm.outVariables)">
|
||||||
|
<Icon icon="ep:plus" class="mr-5px" />添加一行
|
||||||
|
</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="子流程发起人" prop="startUserType">
|
||||||
|
<el-radio-group v-model="configForm.startUserType">
|
||||||
|
<el-radio
|
||||||
|
v-for="item in CHILD_PROCESS_START_USER_TYPE"
|
||||||
|
:key="item.value"
|
||||||
|
:value="item.value"
|
||||||
|
>
|
||||||
|
{{ item.label }}
|
||||||
|
</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
v-if="configForm.startUserType === ChildProcessStartUserTypeEnum.FROM_FORM"
|
||||||
|
label="当子流程发起人为空时"
|
||||||
|
prop="startUserType"
|
||||||
|
>
|
||||||
|
<el-radio-group v-model="configForm.startUserEmptyType">
|
||||||
|
<el-radio
|
||||||
|
v-for="item in CHILD_PROCESS_START_USER_EMPTY_TYPE"
|
||||||
|
:key="item.value"
|
||||||
|
:value="item.value"
|
||||||
|
>
|
||||||
|
{{ item.label }}
|
||||||
|
</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
v-if="configForm.startUserType === 2"
|
||||||
|
label="发起人表单"
|
||||||
|
prop="startUserFormField"
|
||||||
|
>
|
||||||
|
<el-select class="w-200px!" v-model="configForm.startUserFormField">
|
||||||
|
<el-option
|
||||||
|
v-for="(field, fIdx) in formFieldOptions"
|
||||||
|
:key="fIdx"
|
||||||
|
:label="field.title"
|
||||||
|
:value="field.field"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-divider content-position="left">超时设置</el-divider>
|
||||||
|
<el-form-item label="启用开关" prop="timeoutEnable">
|
||||||
|
<el-switch
|
||||||
|
v-model="configForm.timeoutEnable"
|
||||||
|
active-text="开启"
|
||||||
|
inactive-text="关闭"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<div v-if="configForm.timeoutEnable">
|
||||||
|
<el-form-item prop="timeoutType">
|
||||||
|
<el-radio-group v-model="configForm.timeoutType">
|
||||||
|
<el-radio-button
|
||||||
|
v-for="item in DELAY_TYPE"
|
||||||
|
:key="item.value"
|
||||||
|
:label="item.label"
|
||||||
|
:value="item.value"
|
||||||
|
/>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item v-if="configForm.timeoutType === DelayTypeEnum.FIXED_TIME_DURATION">
|
||||||
|
<el-form-item prop="timeDuration">
|
||||||
|
<el-input-number
|
||||||
|
class="mr-2"
|
||||||
|
:style="{ width: '100px' }"
|
||||||
|
v-model="configForm.timeDuration"
|
||||||
|
:min="1"
|
||||||
|
controls-position="right"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-select v-model="configForm.timeUnit" class="mr-2" :style="{ width: '100px' }">
|
||||||
|
<el-option
|
||||||
|
v-for="item in TIME_UNIT_TYPES"
|
||||||
|
:key="item.value"
|
||||||
|
:label="item.label"
|
||||||
|
:value="item.value"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
<el-text>后进入下一节点</el-text>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
v-if="configForm.timeoutType === DelayTypeEnum.FIXED_DATE_TIME"
|
||||||
|
prop="dateTime"
|
||||||
|
>
|
||||||
|
<el-date-picker
|
||||||
|
class="mr-2"
|
||||||
|
v-model="configForm.dateTime"
|
||||||
|
type="datetime"
|
||||||
|
placeholder="请选择日期和时间"
|
||||||
|
value-format="YYYY-MM-DDTHH:mm:ss"
|
||||||
|
/>
|
||||||
|
<el-text>后进入下一节点</el-text>
|
||||||
|
</el-form-item>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<el-divider content-position="left">多实例设置</el-divider>
|
||||||
|
<el-form-item label="启用开关" prop="multiInstanceEnable">
|
||||||
|
<el-switch
|
||||||
|
v-model="configForm.multiInstanceEnable"
|
||||||
|
active-text="开启"
|
||||||
|
inactive-text="关闭"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<div v-if="configForm.multiInstanceEnable">
|
||||||
|
<el-form-item prop="sequential">
|
||||||
|
<el-switch
|
||||||
|
v-model="configForm.sequential"
|
||||||
|
active-text="串行"
|
||||||
|
inactive-text="并行"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item prop="approveRatio">
|
||||||
|
<el-text>完成比例(%)</el-text>
|
||||||
|
<el-input-number
|
||||||
|
class="ml-10px"
|
||||||
|
v-model="configForm.approveRatio"
|
||||||
|
:min="10"
|
||||||
|
:max="100"
|
||||||
|
:step="10"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item prop="multiInstanceSourceType">
|
||||||
|
<el-text>多实例来源</el-text>
|
||||||
|
<el-select
|
||||||
|
class="ml-10px w-200px!"
|
||||||
|
v-model="configForm.multiInstanceSourceType"
|
||||||
|
@change="handleMultiInstanceSourceTypeChange"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="item in CHILD_PROCESS_MULTI_INSTANCE_SOURCE_TYPE"
|
||||||
|
:key="item.value"
|
||||||
|
:label="item.label"
|
||||||
|
:value="item.value"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item v-if="configForm.multiInstanceSourceType === ChildProcessMultiInstanceSourceTypeEnum.FIXED_QUANTITY">
|
||||||
|
<el-input-number v-model="configForm.multiInstanceSource" :min="1" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item v-if="configForm.multiInstanceSourceType === ChildProcessMultiInstanceSourceTypeEnum.NUMBER_FORM">
|
||||||
|
<el-select class="w-200px!" v-model="configForm.multiInstanceSource">
|
||||||
|
<el-option
|
||||||
|
v-for="(field, fIdx) in digitalFormFieldOptions"
|
||||||
|
:key="fIdx"
|
||||||
|
:label="field.title"
|
||||||
|
:value="field.field"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item v-if="configForm.multiInstanceSourceType === ChildProcessMultiInstanceSourceTypeEnum.MULTIPLE_FORM">
|
||||||
|
<el-select class="w-200px!" v-model="configForm.multiInstanceSource">
|
||||||
|
<el-option
|
||||||
|
v-for="(field, fIdx) in multiFormFieldOptions"
|
||||||
|
:key="fIdx"
|
||||||
|
:label="field.title"
|
||||||
|
:value="field.field"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</div>
|
||||||
|
</el-form>
|
||||||
|
</div>
|
||||||
|
</el-tab-pane>
|
||||||
|
</el-tabs>
|
||||||
|
<template #footer>
|
||||||
|
<el-divider />
|
||||||
|
<div>
|
||||||
|
<el-button type="primary" @click="saveConfig">确 定</el-button>
|
||||||
|
<el-button @click="closeDrawer">取 消</el-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-drawer>
|
||||||
|
</template>
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { getModelList } from '@/api/bpm/model'
|
||||||
|
import { getForm } from '@/api/bpm/form'
|
||||||
|
import {
|
||||||
|
SimpleFlowNode,
|
||||||
|
NodeType,
|
||||||
|
TIME_UNIT_TYPES,
|
||||||
|
TimeUnitType,
|
||||||
|
DelayTypeEnum,
|
||||||
|
DELAY_TYPE,
|
||||||
|
IOParameter,
|
||||||
|
ChildProcessStartUserTypeEnum,
|
||||||
|
CHILD_PROCESS_START_USER_TYPE,
|
||||||
|
ChildProcessStartUserEmptyTypeEnum,
|
||||||
|
CHILD_PROCESS_START_USER_EMPTY_TYPE,
|
||||||
|
CHILD_PROCESS_MULTI_INSTANCE_SOURCE_TYPE,
|
||||||
|
ChildProcessMultiInstanceSourceTypeEnum
|
||||||
|
} from '../consts'
|
||||||
|
import { useWatchNode, useDrawer, useNodeName, useFormFieldsAndStartUser } from '../node'
|
||||||
|
import { parseFormFields } from '@/components/FormCreate/src/utils'
|
||||||
|
import { convertTimeUnit } from '../utils'
|
||||||
|
defineOptions({
|
||||||
|
name: 'ChildProcessNodeConfig'
|
||||||
|
})
|
||||||
|
const props = defineProps({
|
||||||
|
flowNode: {
|
||||||
|
type: Object as () => SimpleFlowNode,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
// 抽屉配置
|
||||||
|
const { settingVisible, closeDrawer, openDrawer } = useDrawer()
|
||||||
|
// 当前节点
|
||||||
|
const currentNode = useWatchNode(props)
|
||||||
|
// 节点名称
|
||||||
|
const { nodeName, showInput, clickIcon, blurEvent } = useNodeName(NodeType.CHILD_PROCESS_NODE)
|
||||||
|
// 激活的 Tab 标签页
|
||||||
|
const activeTabName = ref('child')
|
||||||
|
// 子流程表单配置
|
||||||
|
const formRef = ref() // 表单 Ref
|
||||||
|
// 表单校验规则
|
||||||
|
const formRules = reactive({
|
||||||
|
async: [{ required: true, message: '是否异步不能为空', trigger: 'change' }],
|
||||||
|
calledProcessDefinitionKey: [{ required: true, message: '子流程不能为空', trigger: 'change' }],
|
||||||
|
skipStartUserNode: [
|
||||||
|
{ required: true, message: '是否自动跳过子流程发起节点不能为空', trigger: 'change' }
|
||||||
|
],
|
||||||
|
startUserType: [{ required: true, message: '子流程发起人不能为空', trigger: 'change' }],
|
||||||
|
startUserEmptyType: [
|
||||||
|
{ required: true, message: '当子流程发起人为空时不能为空', trigger: 'change' }
|
||||||
|
],
|
||||||
|
startUserFormField: [{ required: true, message: '发起人表单不能为空', trigger: 'change' }],
|
||||||
|
timeoutEnable: [{ required: true, message: '超时设置是否开启不能为空', trigger: 'change' }],
|
||||||
|
timeoutType: [{ required: true, message: '超时设置时间不能为空', trigger: 'change' }],
|
||||||
|
timeDuration: [{ required: true, message: '超时设置时间不能为空', trigger: 'change' }],
|
||||||
|
dateTime: [{ required: true, message: '超时设置时间不能为空', trigger: 'change' }],
|
||||||
|
multiInstanceEnable: [{ required: true, message: '多实例设置不能为空', trigger: 'change' }]
|
||||||
|
})
|
||||||
|
type ChildProcessFormType = {
|
||||||
|
async: boolean
|
||||||
|
calledProcessDefinitionKey: string
|
||||||
|
skipStartUserNode: boolean
|
||||||
|
inVariables?: IOParameter[]
|
||||||
|
outVariables?: IOParameter[]
|
||||||
|
startUserType: ChildProcessStartUserTypeEnum
|
||||||
|
startUserEmptyType: ChildProcessStartUserEmptyTypeEnum
|
||||||
|
startUserFormField: string
|
||||||
|
timeoutEnable: boolean
|
||||||
|
timeoutType: DelayTypeEnum
|
||||||
|
timeDuration: number
|
||||||
|
timeUnit: TimeUnitType
|
||||||
|
dateTime: string
|
||||||
|
multiInstanceEnable: boolean
|
||||||
|
sequential: boolean
|
||||||
|
approveRatio: number
|
||||||
|
multiInstanceSourceType: ChildProcessMultiInstanceSourceTypeEnum
|
||||||
|
multiInstanceSource: string
|
||||||
|
}
|
||||||
|
const configForm = ref<ChildProcessFormType>({
|
||||||
|
async: false,
|
||||||
|
calledProcessDefinitionKey: '',
|
||||||
|
skipStartUserNode: false,
|
||||||
|
inVariables: [],
|
||||||
|
outVariables: [],
|
||||||
|
startUserType: ChildProcessStartUserTypeEnum.MAIN_PROCESS_START_USER,
|
||||||
|
startUserEmptyType: ChildProcessStartUserEmptyTypeEnum.MAIN_PROCESS_START_USER,
|
||||||
|
startUserFormField: '',
|
||||||
|
timeoutEnable: false,
|
||||||
|
timeoutType: DelayTypeEnum.FIXED_TIME_DURATION,
|
||||||
|
timeDuration: 1,
|
||||||
|
timeUnit: TimeUnitType.HOUR,
|
||||||
|
dateTime: '',
|
||||||
|
multiInstanceEnable: false,
|
||||||
|
sequential: false,
|
||||||
|
approveRatio: 100,
|
||||||
|
multiInstanceSourceType: ChildProcessMultiInstanceSourceTypeEnum.FIXED_QUANTITY,
|
||||||
|
multiInstanceSource: ''
|
||||||
|
})
|
||||||
|
const childProcessOptions = ref()
|
||||||
|
const formFieldOptions = useFormFieldsAndStartUser()
|
||||||
|
const digitalFormFieldOptions = computed(() => {
|
||||||
|
return formFieldOptions.filter((item) => item.type === 'inputNumber')
|
||||||
|
})
|
||||||
|
const multiFormFieldOptions = computed(() => {
|
||||||
|
return formFieldOptions.filter((item) => item.type === 'select' || item.type === 'checkbox')
|
||||||
|
})
|
||||||
|
const childFormFieldOptions = ref()
|
||||||
|
|
||||||
|
// 保存配置
|
||||||
|
const saveConfig = async () => {
|
||||||
|
activeTabName.value = 'child'
|
||||||
|
if (!formRef) return false
|
||||||
|
const valid = await formRef.value.validate()
|
||||||
|
if (!valid) return false
|
||||||
|
const childInfo = childProcessOptions.value.find(
|
||||||
|
(option: any) => option.key === configForm.value.calledProcessDefinitionKey
|
||||||
|
)
|
||||||
|
currentNode.value.name = nodeName.value!
|
||||||
|
if (currentNode.value.childProcessSetting) {
|
||||||
|
// 1. 是否异步
|
||||||
|
currentNode.value.childProcessSetting.async = configForm.value.async
|
||||||
|
// 2. 调用流程
|
||||||
|
currentNode.value.childProcessSetting.calledProcessDefinitionKey = childInfo.key
|
||||||
|
currentNode.value.childProcessSetting.calledProcessDefinitionName = childInfo.name
|
||||||
|
// 3. 是否跳过发起人
|
||||||
|
currentNode.value.childProcessSetting.skipStartUserNode = configForm.value.skipStartUserNode
|
||||||
|
// 4. 主->子变量
|
||||||
|
currentNode.value.childProcessSetting.inVariables = configForm.value.inVariables
|
||||||
|
// 5. 子->主变量
|
||||||
|
currentNode.value.childProcessSetting.outVariables = configForm.value.outVariables
|
||||||
|
// 6. 发起人设置
|
||||||
|
currentNode.value.childProcessSetting.startUserSetting.type = configForm.value.startUserType
|
||||||
|
currentNode.value.childProcessSetting.startUserSetting.emptyType =
|
||||||
|
configForm.value.startUserEmptyType
|
||||||
|
currentNode.value.childProcessSetting.startUserSetting.formField =
|
||||||
|
configForm.value.startUserFormField
|
||||||
|
// 7. 超时设置
|
||||||
|
currentNode.value.childProcessSetting.timeoutSetting = {
|
||||||
|
enable: configForm.value.timeoutEnable
|
||||||
|
}
|
||||||
|
if (configForm.value.timeoutEnable) {
|
||||||
|
currentNode.value.childProcessSetting.timeoutSetting.type = configForm.value.timeoutType
|
||||||
|
if (configForm.value.timeoutType === DelayTypeEnum.FIXED_TIME_DURATION) {
|
||||||
|
currentNode.value.childProcessSetting.timeoutSetting.timeExpression = getIsoTimeDuration()
|
||||||
|
}
|
||||||
|
if (configForm.value.timeoutType === DelayTypeEnum.FIXED_DATE_TIME) {
|
||||||
|
currentNode.value.childProcessSetting.timeoutSetting.timeExpression =
|
||||||
|
configForm.value.dateTime
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 8. 多实例设置
|
||||||
|
currentNode.value.childProcessSetting.multiInstanceSetting = {
|
||||||
|
enable: configForm.value.multiInstanceEnable
|
||||||
|
}
|
||||||
|
if (configForm.value.multiInstanceEnable) {
|
||||||
|
currentNode.value.childProcessSetting.multiInstanceSetting.sequential =
|
||||||
|
configForm.value.sequential
|
||||||
|
currentNode.value.childProcessSetting.multiInstanceSetting.approveRatio =
|
||||||
|
configForm.value.approveRatio
|
||||||
|
currentNode.value.childProcessSetting.multiInstanceSetting.sourceType =
|
||||||
|
configForm.value.multiInstanceSourceType
|
||||||
|
currentNode.value.childProcessSetting.multiInstanceSetting.source =
|
||||||
|
configForm.value.multiInstanceSource
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
currentNode.value.showText = `调用子流程:${childInfo.name}`
|
||||||
|
settingVisible.value = false
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
// 显示子流程节点配置, 由父组件传过来
|
||||||
|
const showChildProcessNodeConfig = (node: SimpleFlowNode) => {
|
||||||
|
nodeName.value = node.name
|
||||||
|
if (node.childProcessSetting) {
|
||||||
|
// 1. 是否异步
|
||||||
|
configForm.value.async = node.childProcessSetting.async
|
||||||
|
// 2. 调用流程
|
||||||
|
configForm.value.calledProcessDefinitionKey =
|
||||||
|
node.childProcessSetting?.calledProcessDefinitionKey
|
||||||
|
// 3. 是否跳过发起人
|
||||||
|
configForm.value.skipStartUserNode = node.childProcessSetting.skipStartUserNode
|
||||||
|
// 4. 主->子变量
|
||||||
|
configForm.value.inVariables = node.childProcessSetting.inVariables
|
||||||
|
// 5. 子->主变量
|
||||||
|
configForm.value.outVariables = node.childProcessSetting.outVariables
|
||||||
|
// 6. 发起人设置
|
||||||
|
configForm.value.startUserType = node.childProcessSetting.startUserSetting.type
|
||||||
|
configForm.value.startUserEmptyType = node.childProcessSetting.startUserSetting.emptyType ?? ChildProcessStartUserEmptyTypeEnum.MAIN_PROCESS_START_USER
|
||||||
|
configForm.value.startUserFormField = node.childProcessSetting.startUserSetting.formField ?? ''
|
||||||
|
// 7. 超时设置
|
||||||
|
configForm.value.timeoutEnable = node.childProcessSetting.timeoutSetting.enable ?? false
|
||||||
|
if (configForm.value.timeoutEnable) {
|
||||||
|
configForm.value.timeoutType =
|
||||||
|
node.childProcessSetting.timeoutSetting.type ?? DelayTypeEnum.FIXED_TIME_DURATION
|
||||||
|
// 固定时长
|
||||||
|
if (configForm.value.timeoutType === DelayTypeEnum.FIXED_TIME_DURATION) {
|
||||||
|
const strTimeDuration = node.childProcessSetting.timeoutSetting.timeExpression ?? ''
|
||||||
|
let parseTime = strTimeDuration.slice(2, strTimeDuration.length - 1)
|
||||||
|
let parseTimeUnit = strTimeDuration.slice(strTimeDuration.length - 1)
|
||||||
|
configForm.value.timeDuration = parseInt(parseTime)
|
||||||
|
configForm.value.timeUnit = convertTimeUnit(parseTimeUnit)
|
||||||
|
}
|
||||||
|
// 固定日期时间
|
||||||
|
if (configForm.value.timeoutType === DelayTypeEnum.FIXED_DATE_TIME) {
|
||||||
|
configForm.value.dateTime = node.childProcessSetting.timeoutSetting.timeExpression ?? ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 8. 多实例设置
|
||||||
|
configForm.value.multiInstanceEnable =
|
||||||
|
node.childProcessSetting.multiInstanceSetting.enable ?? false
|
||||||
|
if (configForm.value.multiInstanceEnable) {
|
||||||
|
configForm.value.sequential =
|
||||||
|
node.childProcessSetting.multiInstanceSetting.sequential ?? false
|
||||||
|
configForm.value.approveRatio =
|
||||||
|
node.childProcessSetting.multiInstanceSetting.approveRatio ?? 100
|
||||||
|
configForm.value.multiInstanceSourceType =
|
||||||
|
node.childProcessSetting.multiInstanceSetting.sourceType ??
|
||||||
|
ChildProcessMultiInstanceSourceTypeEnum.FIXED_QUANTITY
|
||||||
|
configForm.value.multiInstanceSource =
|
||||||
|
node.childProcessSetting.multiInstanceSetting.source ?? ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
loadFormInfo()
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({ openDrawer, showChildProcessNodeConfig }) // 暴露方法给父组件
|
||||||
|
|
||||||
|
const addVariable = (arr?: IOParameter[]) => {
|
||||||
|
arr?.push({
|
||||||
|
source: '',
|
||||||
|
target: ''
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const deleteVariable = (index: number, arr?: IOParameter[]) => {
|
||||||
|
arr?.splice(index, 1)
|
||||||
|
}
|
||||||
|
const handleCalledElementChange = () => {
|
||||||
|
configForm.value.inVariables = []
|
||||||
|
configForm.value.outVariables = []
|
||||||
|
loadFormInfo()
|
||||||
|
}
|
||||||
|
const loadFormInfo = async () => {
|
||||||
|
const childInfo = childProcessOptions.value.find(
|
||||||
|
(option) => option.key === configForm.value.calledProcessDefinitionKey
|
||||||
|
)
|
||||||
|
const formInfo = await getForm(childInfo.formId)
|
||||||
|
childFormFieldOptions.value = []
|
||||||
|
if (formInfo.fields) {
|
||||||
|
formInfo.fields.forEach((fieldStr: string) => {
|
||||||
|
parseFormFields(JSON.parse(fieldStr), childFormFieldOptions.value)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const getIsoTimeDuration = () => {
|
||||||
|
let strTimeDuration = 'PT'
|
||||||
|
if (configForm.value.timeUnit === TimeUnitType.MINUTE) {
|
||||||
|
strTimeDuration += configForm.value.timeDuration + 'M'
|
||||||
|
}
|
||||||
|
if (configForm.value.timeUnit === TimeUnitType.HOUR) {
|
||||||
|
strTimeDuration += configForm.value.timeDuration + 'H'
|
||||||
|
}
|
||||||
|
if (configForm.value.timeUnit === TimeUnitType.DAY) {
|
||||||
|
strTimeDuration += configForm.value.timeDuration + 'D'
|
||||||
|
}
|
||||||
|
return strTimeDuration
|
||||||
|
}
|
||||||
|
const handleMultiInstanceSourceTypeChange = () => {
|
||||||
|
configForm.value.multiInstanceSource = ''
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
childProcessOptions.value = await getModelList(undefined)
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped></style>
|
@ -26,121 +26,11 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<div>
|
<div>
|
||||||
<div class="mb-3 font-size-16px" v-if="currentNode.defaultFlow"
|
<div class="mb-3 font-size-16px" v-if="currentNode.conditionSetting?.defaultFlow"
|
||||||
>未满足其它条件时,将进入此分支(该分支不可编辑和删除)</div
|
>未满足其它条件时,将进入此分支(该分支不可编辑和删除)</div
|
||||||
>
|
>
|
||||||
<div v-else>
|
<div v-else>
|
||||||
<el-form ref="formRef" :model="currentNode" :rules="formRules" label-position="top">
|
<Condition ref="conditionRef" v-model="condition" />
|
||||||
<el-form-item label="配置方式" prop="conditionType">
|
|
||||||
<el-radio-group v-model="currentNode.conditionType" @change="changeConditionType">
|
|
||||||
<el-radio
|
|
||||||
v-for="(dict, index) in conditionConfigTypes"
|
|
||||||
:key="index"
|
|
||||||
:value="dict.value"
|
|
||||||
:label="dict.value"
|
|
||||||
>
|
|
||||||
{{ dict.label }}
|
|
||||||
</el-radio>
|
|
||||||
</el-radio-group>
|
|
||||||
</el-form-item>
|
|
||||||
|
|
||||||
<el-form-item
|
|
||||||
v-if="currentNode.conditionType === 1"
|
|
||||||
label="条件表达式"
|
|
||||||
prop="conditionExpression"
|
|
||||||
>
|
|
||||||
<el-input
|
|
||||||
type="textarea"
|
|
||||||
v-model="currentNode.conditionExpression"
|
|
||||||
clearable
|
|
||||||
style="width: 100%"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item v-if="currentNode.conditionType === 2" label="条件规则">
|
|
||||||
<div class="condition-group-tool">
|
|
||||||
<div class="flex items-center">
|
|
||||||
<div class="mr-4">条件组关系</div>
|
|
||||||
<el-switch
|
|
||||||
v-model="conditionGroups.and"
|
|
||||||
inline-prompt
|
|
||||||
active-text="且"
|
|
||||||
inactive-text="或"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<el-space direction="vertical" :spacer="conditionGroups.and ? '且' : '或'">
|
|
||||||
<el-card
|
|
||||||
class="condition-group"
|
|
||||||
style="width: 530px"
|
|
||||||
v-for="(condition, cIdx) in conditionGroups.conditions"
|
|
||||||
:key="cIdx"
|
|
||||||
>
|
|
||||||
<div class="condition-group-delete" v-if="conditionGroups.conditions.length > 1">
|
|
||||||
<Icon
|
|
||||||
color="#0089ff"
|
|
||||||
icon="ep:circle-close-filled"
|
|
||||||
:size="18"
|
|
||||||
@click="deleteConditionGroup(cIdx)"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<template #header>
|
|
||||||
<div class="flex items-center justify-between">
|
|
||||||
<div>条件组</div>
|
|
||||||
<div class="flex">
|
|
||||||
<div class="mr-4">规则关系</div>
|
|
||||||
<el-switch
|
|
||||||
v-model="condition.and"
|
|
||||||
inline-prompt
|
|
||||||
active-text="且"
|
|
||||||
inactive-text="或"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<div class="flex pt-2" v-for="(rule, rIdx) in condition.rules" :key="rIdx">
|
|
||||||
<div class="mr-2">
|
|
||||||
<el-select style="width: 160px" v-model="rule.leftSide">
|
|
||||||
<el-option
|
|
||||||
v-for="(item, index) in fieldOptions"
|
|
||||||
:key="index"
|
|
||||||
:label="item.title"
|
|
||||||
:value="item.field"
|
|
||||||
:disabled="!item.required"
|
|
||||||
/>
|
|
||||||
</el-select>
|
|
||||||
</div>
|
|
||||||
<div class="mr-2">
|
|
||||||
<el-select v-model="rule.opCode" style="width: 100px">
|
|
||||||
<el-option
|
|
||||||
v-for="item in COMPARISON_OPERATORS"
|
|
||||||
:key="item.value"
|
|
||||||
:label="item.label"
|
|
||||||
:value="item.value"
|
|
||||||
/>
|
|
||||||
</el-select>
|
|
||||||
</div>
|
|
||||||
<div class="mr-2">
|
|
||||||
<el-input v-model="rule.rightSide" style="width: 160px" />
|
|
||||||
</div>
|
|
||||||
<div class="mr-1 flex items-center" v-if="condition.rules.length > 1">
|
|
||||||
<Icon
|
|
||||||
icon="ep:delete"
|
|
||||||
:size="18"
|
|
||||||
@click="deleteConditionRule(condition, rIdx)"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="flex items-center">
|
|
||||||
<Icon icon="ep:plus" :size="18" @click="addConditionRule(condition, rIdx)" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</el-card>
|
|
||||||
</el-space>
|
|
||||||
<div title="添加条件组" class="mt-4 cursor-pointer">
|
|
||||||
<Icon color="#0089ff" icon="ep:plus" :size="24" @click="addConditionGroup" />
|
|
||||||
</div>
|
|
||||||
</el-form-item>
|
|
||||||
</el-form>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
@ -153,35 +43,15 @@
|
|||||||
</el-drawer>
|
</el-drawer>
|
||||||
</template>
|
</template>
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import {
|
import { SimpleFlowNode, ConditionType } from '../consts'
|
||||||
SimpleFlowNode,
|
|
||||||
CONDITION_CONFIG_TYPES,
|
|
||||||
ConditionType,
|
|
||||||
COMPARISON_OPERATORS,
|
|
||||||
ConditionGroup,
|
|
||||||
Condition,
|
|
||||||
ConditionRule,
|
|
||||||
ProcessVariableEnum
|
|
||||||
} from '../consts'
|
|
||||||
import { getDefaultConditionNodeName } from '../utils'
|
import { getDefaultConditionNodeName } from '../utils'
|
||||||
import { useFormFields } from '../node'
|
import { useFormFieldsAndStartUser, getConditionShowText } from '../node'
|
||||||
import { BpmModelFormType } from '@/utils/constants'
|
import Condition from './components/Condition.vue'
|
||||||
const message = useMessage() // 消息弹窗
|
import { cloneDeep } from 'lodash-es'
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: 'ConditionNodeConfig'
|
name: 'ConditionNodeConfig'
|
||||||
})
|
})
|
||||||
const formType = inject<Ref<number>>('formType') // 表单类型
|
|
||||||
const conditionConfigTypes = computed(() => {
|
|
||||||
return CONDITION_CONFIG_TYPES.filter((item) => {
|
|
||||||
// 业务表单暂时去掉条件规则选项
|
|
||||||
if (formType?.value === BpmModelFormType.CUSTOM && item.value === ConditionType.RULE) {
|
|
||||||
return false
|
|
||||||
} else {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
conditionNode: {
|
conditionNode: {
|
||||||
type: Object as () => SimpleFlowNode,
|
type: Object as () => SimpleFlowNode,
|
||||||
@ -193,10 +63,50 @@ const props = defineProps({
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
const settingVisible = ref(false)
|
const settingVisible = ref(false)
|
||||||
|
const currentNode = ref<SimpleFlowNode>(props.conditionNode)
|
||||||
|
const condition = ref<any>({
|
||||||
|
conditionType: ConditionType.RULE, // 设置默认值
|
||||||
|
conditionExpression: '',
|
||||||
|
conditionGroups: {
|
||||||
|
and: true,
|
||||||
|
conditions: [
|
||||||
|
{
|
||||||
|
and: true,
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
opCode: '==',
|
||||||
|
leftSide: '',
|
||||||
|
rightSide: ''
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
})
|
||||||
const open = () => {
|
const open = () => {
|
||||||
if (currentNode.value.conditionType === ConditionType.RULE) {
|
// 如果有已存在的配置则使用,否则使用默认值
|
||||||
if (currentNode.value.conditionGroups) {
|
if (currentNode.value.conditionSetting) {
|
||||||
conditionGroups.value = currentNode.value.conditionGroups
|
condition.value = cloneDeep(currentNode.value.conditionSetting)
|
||||||
|
} else {
|
||||||
|
// 重置为默认值
|
||||||
|
condition.value = {
|
||||||
|
conditionType: ConditionType.RULE,
|
||||||
|
conditionExpression: '',
|
||||||
|
conditionGroups: {
|
||||||
|
and: true,
|
||||||
|
conditions: [
|
||||||
|
{
|
||||||
|
and: true,
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
opCode: '==',
|
||||||
|
leftSide: '',
|
||||||
|
rightSide: ''
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
settingVisible.value = true
|
settingVisible.value = true
|
||||||
@ -219,11 +129,9 @@ const blurEvent = () => {
|
|||||||
showInput.value = false
|
showInput.value = false
|
||||||
currentNode.value.name =
|
currentNode.value.name =
|
||||||
currentNode.value.name ||
|
currentNode.value.name ||
|
||||||
getDefaultConditionNodeName(props.nodeIndex, currentNode.value?.defaultFlow)
|
getDefaultConditionNodeName(props.nodeIndex, currentNode.value?.conditionSetting?.defaultFlow)
|
||||||
}
|
}
|
||||||
|
|
||||||
const currentNode = ref<SimpleFlowNode>(props.conditionNode)
|
|
||||||
|
|
||||||
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
||||||
|
|
||||||
// 关闭
|
// 关闭
|
||||||
@ -239,157 +147,42 @@ const handleClose = async (done: (cancel?: boolean) => void) => {
|
|||||||
done()
|
done()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 表单校验规则
|
|
||||||
const formRules = reactive({
|
|
||||||
conditionType: [{ required: true, message: '配置方式不能为空', trigger: 'blur' }],
|
|
||||||
conditionExpression: [{ required: true, message: '条件表达式不能为空', trigger: 'blur' }]
|
|
||||||
})
|
|
||||||
const formRef = ref() // 表单 Ref
|
|
||||||
|
|
||||||
// 保存配置
|
/** 保存配置 */
|
||||||
|
const fieldOptions = useFormFieldsAndStartUser() // 流程表单字段和发起人字段
|
||||||
|
const conditionRef = ref()
|
||||||
const saveConfig = async () => {
|
const saveConfig = async () => {
|
||||||
if (!currentNode.value.defaultFlow) {
|
if (!currentNode.value.conditionSetting?.defaultFlow) {
|
||||||
// 校验表单
|
// 校验表单
|
||||||
if (!formRef) return false
|
const valid = await conditionRef.value.validate()
|
||||||
const valid = await formRef.value.validate()
|
|
||||||
if (!valid) return false
|
if (!valid) return false
|
||||||
const showText = getShowText()
|
const showText = getConditionShowText(
|
||||||
|
condition.value?.conditionType,
|
||||||
|
condition.value?.conditionExpression,
|
||||||
|
condition.value.conditionGroups,
|
||||||
|
fieldOptions
|
||||||
|
)
|
||||||
if (!showText) {
|
if (!showText) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
currentNode.value.showText = showText
|
currentNode.value.showText = showText
|
||||||
if (currentNode.value.conditionType === ConditionType.EXPRESSION) {
|
// 使用 cloneDeep 进行深拷贝
|
||||||
currentNode.value.conditionGroups = undefined
|
currentNode.value.conditionSetting = cloneDeep({
|
||||||
}
|
...currentNode.value.conditionSetting,
|
||||||
if (currentNode.value.conditionType === ConditionType.RULE) {
|
conditionType: condition.value?.conditionType,
|
||||||
currentNode.value.conditionExpression = undefined
|
conditionExpression:
|
||||||
currentNode.value.conditionGroups = conditionGroups.value
|
condition.value?.conditionType === ConditionType.EXPRESSION
|
||||||
}
|
? condition.value?.conditionExpression
|
||||||
|
: undefined,
|
||||||
|
conditionGroups:
|
||||||
|
condition.value?.conditionType === ConditionType.RULE
|
||||||
|
? condition.value?.conditionGroups
|
||||||
|
: undefined
|
||||||
|
})
|
||||||
}
|
}
|
||||||
settingVisible.value = false
|
settingVisible.value = false
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
const getShowText = (): string => {
|
|
||||||
let showText = ''
|
|
||||||
if (currentNode.value.conditionType === ConditionType.EXPRESSION) {
|
|
||||||
if (currentNode.value.conditionExpression) {
|
|
||||||
showText = `表达式:${currentNode.value.conditionExpression}`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (currentNode.value.conditionType === ConditionType.RULE) {
|
|
||||||
// 条件组是否为与关系
|
|
||||||
const groupAnd = conditionGroups.value.and
|
|
||||||
let warningMesg: undefined | string = undefined
|
|
||||||
const conditionGroup = conditionGroups.value.conditions.map((item) => {
|
|
||||||
return (
|
|
||||||
'(' +
|
|
||||||
item.rules
|
|
||||||
.map((rule) => {
|
|
||||||
if (rule.leftSide && rule.rightSide) {
|
|
||||||
return (
|
|
||||||
getFieldTitle(rule.leftSide) + ' ' + getOpName(rule.opCode) + ' ' + rule.rightSide
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
// 有一条规则不完善。提示错误
|
|
||||||
warningMesg = '请完善条件规则'
|
|
||||||
return ''
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.join(item.and ? ' 且 ' : ' 或 ') +
|
|
||||||
' ) '
|
|
||||||
)
|
|
||||||
})
|
|
||||||
if (warningMesg) {
|
|
||||||
message.warning(warningMesg)
|
|
||||||
showText = ''
|
|
||||||
} else {
|
|
||||||
showText = conditionGroup.join(groupAnd ? ' 且 ' : ' 或 ')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return showText
|
|
||||||
}
|
|
||||||
|
|
||||||
// 改变条件配置方式
|
|
||||||
const changeConditionType = () => {}
|
|
||||||
|
|
||||||
const conditionGroups = ref<ConditionGroup>({
|
|
||||||
and: true,
|
|
||||||
conditions: [
|
|
||||||
{
|
|
||||||
and: true,
|
|
||||||
rules: [
|
|
||||||
{
|
|
||||||
type: 1,
|
|
||||||
opName: '等于',
|
|
||||||
opCode: '==',
|
|
||||||
leftSide: '',
|
|
||||||
rightSide: ''
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
})
|
|
||||||
// 添加条件组
|
|
||||||
const addConditionGroup = () => {
|
|
||||||
const condition = {
|
|
||||||
and: true,
|
|
||||||
rules: [
|
|
||||||
{
|
|
||||||
type: 1,
|
|
||||||
opName: '等于',
|
|
||||||
opCode: '==',
|
|
||||||
leftSide: '',
|
|
||||||
rightSide: ''
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
conditionGroups.value.conditions.push(condition)
|
|
||||||
}
|
|
||||||
// 删除条件组
|
|
||||||
const deleteConditionGroup = (idx: number) => {
|
|
||||||
conditionGroups.value.conditions.splice(idx, 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 添加条件规则
|
|
||||||
const addConditionRule = (condition: Condition, idx: number) => {
|
|
||||||
const rule: ConditionRule = {
|
|
||||||
type: 1,
|
|
||||||
opName: '等于',
|
|
||||||
opCode: '==',
|
|
||||||
leftSide: '',
|
|
||||||
rightSide: ''
|
|
||||||
}
|
|
||||||
condition.rules.splice(idx + 1, 0, rule)
|
|
||||||
}
|
|
||||||
|
|
||||||
const deleteConditionRule = (condition: Condition, idx: number) => {
|
|
||||||
condition.rules.splice(idx, 1)
|
|
||||||
}
|
|
||||||
const fieldsInfo = useFormFields()
|
|
||||||
|
|
||||||
/** 条件规则可选择的表单字段 */
|
|
||||||
const fieldOptions = computed(() => {
|
|
||||||
const fieldsCopy = fieldsInfo.slice()
|
|
||||||
// 固定添加发起人 ID 字段
|
|
||||||
fieldsCopy.unshift({
|
|
||||||
field: ProcessVariableEnum.START_USER_ID,
|
|
||||||
title: '发起人',
|
|
||||||
required: true
|
|
||||||
})
|
|
||||||
return fieldsCopy
|
|
||||||
})
|
|
||||||
|
|
||||||
/** 获取字段名称 */
|
|
||||||
const getFieldTitle = (field: string) => {
|
|
||||||
const item = fieldOptions.value.find((item) => item.field === field)
|
|
||||||
return item?.title
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 获取操作符名称 */
|
|
||||||
const getOpName = (opCode: string): string => {
|
|
||||||
const opName = COMPARISON_OPERATORS.find((item: any) => item.value === opCode)
|
|
||||||
return opName?.label
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
@ -134,7 +134,7 @@
|
|||||||
:key="idx"
|
:key="idx"
|
||||||
:label="item.title"
|
:label="item.title"
|
||||||
:value="item.field"
|
:value="item.field"
|
||||||
:disabled ="!item.required"
|
:disabled="!item.required"
|
||||||
/>
|
/>
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
@ -149,7 +149,7 @@
|
|||||||
:key="idx"
|
:key="idx"
|
||||||
:label="item.title"
|
:label="item.title"
|
||||||
:value="item.field"
|
:value="item.field"
|
||||||
:disabled ="!item.required"
|
:disabled="!item.required"
|
||||||
/>
|
/>
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
@ -195,9 +195,15 @@
|
|||||||
<div class="field-permit-title">
|
<div class="field-permit-title">
|
||||||
<div class="setting-title-label first-title"> 字段名称 </div>
|
<div class="setting-title-label first-title"> 字段名称 </div>
|
||||||
<div class="other-titles">
|
<div class="other-titles">
|
||||||
<span class="setting-title-label">只读</span>
|
<span class="setting-title-label cursor-pointer" @click="updatePermission('READ')">
|
||||||
<span class="setting-title-label">可编辑</span>
|
只读
|
||||||
<span class="setting-title-label">隐藏</span>
|
</span>
|
||||||
|
<span class="setting-title-label cursor-pointer" @click="updatePermission('WRITE')">
|
||||||
|
可编辑
|
||||||
|
</span>
|
||||||
|
<span class="setting-title-label cursor-pointer" @click="updatePermission('NONE')">
|
||||||
|
隐藏
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
@ -368,6 +374,18 @@ const showCopyTaskNodeConfig = (node: SimpleFlowNode) => {
|
|||||||
getNodeConfigFormFields(node.fieldsPermission)
|
getNodeConfigFormFields(node.fieldsPermission)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 批量更新权限 */
|
||||||
|
const updatePermission = (type: string) => {
|
||||||
|
fieldsPermissionConfig.value.forEach((field) => {
|
||||||
|
field.permission =
|
||||||
|
type === 'READ'
|
||||||
|
? FieldPermissionType.READ
|
||||||
|
: type === 'WRITE'
|
||||||
|
? FieldPermissionType.WRITE
|
||||||
|
: FieldPermissionType.NONE
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
defineExpose({ openDrawer, showCopyTaskNodeConfig }) // 暴露方法给父组件
|
defineExpose({ openDrawer, showCopyTaskNodeConfig }) // 暴露方法给父组件
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -124,6 +124,7 @@ const saveConfig = async () => {
|
|||||||
if (!valid) return false
|
if (!valid) return false
|
||||||
const showText = getShowText()
|
const showText = getShowText()
|
||||||
if (!showText) return false
|
if (!showText) return false
|
||||||
|
currentNode.value.name = nodeName.value!
|
||||||
currentNode.value.showText = showText
|
currentNode.value.showText = showText
|
||||||
if (configForm.value.delayType === DelayTypeEnum.FIXED_TIME_DURATION) {
|
if (configForm.value.delayType === DelayTypeEnum.FIXED_TIME_DURATION) {
|
||||||
currentNode.value.delaySetting = {
|
currentNode.value.delaySetting = {
|
||||||
|
@ -0,0 +1,201 @@
|
|||||||
|
<template>
|
||||||
|
<el-drawer
|
||||||
|
:append-to-body="true"
|
||||||
|
v-model="settingVisible"
|
||||||
|
:show-close="false"
|
||||||
|
:size="630"
|
||||||
|
:before-close="saveConfig"
|
||||||
|
>
|
||||||
|
<template #header>
|
||||||
|
<div class="config-header">
|
||||||
|
<input
|
||||||
|
v-if="showInput"
|
||||||
|
type="text"
|
||||||
|
class="config-editable-input"
|
||||||
|
@blur="blurEvent()"
|
||||||
|
v-mountedFocus
|
||||||
|
v-model="nodeName"
|
||||||
|
:placeholder="nodeName"
|
||||||
|
/>
|
||||||
|
<div v-else class="node-name">
|
||||||
|
{{ nodeName }} <Icon class="ml-1" icon="ep:edit-pen" :size="16" @click="clickIcon()" />
|
||||||
|
</div>
|
||||||
|
<div class="divide-line"></div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<div>
|
||||||
|
<el-form label-position="top">
|
||||||
|
<el-card class="mb-15px" v-for="(item, index) in routerGroups" :key="index">
|
||||||
|
<template #header>
|
||||||
|
<div class="flex flex-items-center">
|
||||||
|
<el-text size="large">路由{{ index + 1 }}</el-text>
|
||||||
|
<el-select class="ml-15px" v-model="item.nodeId" style="width: 180px">
|
||||||
|
<el-option
|
||||||
|
v-for="node in nodeOptions"
|
||||||
|
:key="node.value"
|
||||||
|
:label="node.label"
|
||||||
|
:value="node.value"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
<el-button class="mla" type="danger" link @click="deleteRouterGroup(index)">
|
||||||
|
删除
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<Condition
|
||||||
|
:ref="($event) => (conditionRef[index] = $event)"
|
||||||
|
v-model="routerGroups[index]"
|
||||||
|
/>
|
||||||
|
</el-card>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
<el-button class="w-1/1" type="primary" :icon="Plus" @click="addRouterGroup">
|
||||||
|
新增路由分支
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
<template #footer>
|
||||||
|
<el-divider />
|
||||||
|
<div>
|
||||||
|
<el-button type="primary" @click="saveConfig">确 定</el-button>
|
||||||
|
<el-button @click="closeDrawer">取 消</el-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-drawer>
|
||||||
|
</template>
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { Plus } from '@element-plus/icons-vue'
|
||||||
|
import { SimpleFlowNode, NodeType, ConditionType, RouterSetting } from '../consts'
|
||||||
|
import { useWatchNode, useDrawer, useNodeName } from '../node'
|
||||||
|
import Condition from './components/Condition.vue'
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'RouterNodeConfig'
|
||||||
|
})
|
||||||
|
const message = useMessage() // 消息弹窗
|
||||||
|
const props = defineProps({
|
||||||
|
flowNode: {
|
||||||
|
type: Object as () => SimpleFlowNode,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const processNodeTree = inject<Ref<SimpleFlowNode>>('processNodeTree')
|
||||||
|
// 抽屉配置
|
||||||
|
const { settingVisible, closeDrawer, openDrawer } = useDrawer()
|
||||||
|
// 当前节点
|
||||||
|
const currentNode = useWatchNode(props)
|
||||||
|
// 节点名称
|
||||||
|
const { nodeName, showInput, clickIcon, blurEvent } = useNodeName(NodeType.ROUTER_BRANCH_NODE)
|
||||||
|
const routerGroups = ref<RouterSetting[]>([])
|
||||||
|
const nodeOptions = ref<any>([])
|
||||||
|
const conditionRef = ref([])
|
||||||
|
|
||||||
|
/** 保存配置 */
|
||||||
|
const saveConfig = async () => {
|
||||||
|
// 校验表单
|
||||||
|
let valid = true
|
||||||
|
for (const item of conditionRef.value) {
|
||||||
|
if (item && !(await item.validate())) {
|
||||||
|
valid = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!valid) return false
|
||||||
|
const showText = getShowText()
|
||||||
|
if (!showText) return false
|
||||||
|
currentNode.value.name = nodeName.value!
|
||||||
|
currentNode.value.showText = showText
|
||||||
|
currentNode.value.routerGroups = routerGroups.value
|
||||||
|
settingVisible.value = false
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
// 显示路由分支节点配置, 由父组件传过来
|
||||||
|
const showRouteNodeConfig = (node: SimpleFlowNode) => {
|
||||||
|
getRouterNode(processNodeTree?.value)
|
||||||
|
routerGroups.value = []
|
||||||
|
nodeName.value = node.name
|
||||||
|
if (node.routerGroups) {
|
||||||
|
routerGroups.value = node.routerGroups
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const getShowText = () => {
|
||||||
|
if (!routerGroups.value || !Array.isArray(routerGroups.value) || routerGroups.value.length <= 0) {
|
||||||
|
message.warning('请配置路由!')
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
for (const route of routerGroups.value) {
|
||||||
|
if (!route.nodeId || !route.conditionType) {
|
||||||
|
message.warning('请完善路由配置项!')
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
if (route.conditionType === ConditionType.EXPRESSION && !route.conditionExpression) {
|
||||||
|
message.warning('请完善路由配置项!')
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
if (route.conditionType === ConditionType.RULE) {
|
||||||
|
for (const condition of route.conditionGroups.conditions) {
|
||||||
|
for (const rule of condition.rules) {
|
||||||
|
if (!rule.leftSide || !rule.rightSide) {
|
||||||
|
message.warning('请完善路由配置项!')
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return `${routerGroups.value.length}条路由分支`
|
||||||
|
}
|
||||||
|
|
||||||
|
const addRouterGroup = () => {
|
||||||
|
routerGroups.value.push({
|
||||||
|
nodeId: '',
|
||||||
|
conditionType: ConditionType.RULE,
|
||||||
|
conditionExpression: '',
|
||||||
|
conditionGroups: {
|
||||||
|
and: true,
|
||||||
|
conditions: [
|
||||||
|
{
|
||||||
|
and: true,
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
opCode: '==',
|
||||||
|
leftSide: '',
|
||||||
|
rightSide: ''
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const deleteRouterGroup = (index: number) => {
|
||||||
|
routerGroups.value.splice(index, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 递归获取所有节点
|
||||||
|
const getRouterNode = (node) => {
|
||||||
|
// TODO 最好还需要满足以下要求
|
||||||
|
// 并行分支、包容分支内部节点不能跳转到外部节点
|
||||||
|
// 条件分支节点可以向上跳转到外部节点
|
||||||
|
while (true) {
|
||||||
|
if (!node) break
|
||||||
|
if (node.type !== NodeType.ROUTER_BRANCH_NODE && node.type !== NodeType.CONDITION_NODE) {
|
||||||
|
nodeOptions.value.push({
|
||||||
|
label: node.name,
|
||||||
|
value: node.id
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if (!node.childNode || node.type === NodeType.END_EVENT_NODE) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if (node.conditionNodes && node.conditionNodes.length) {
|
||||||
|
node.conditionNodes.forEach((item) => {
|
||||||
|
getRouterNode(item)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
node = node.childNode
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({ openDrawer, showRouteNodeConfig }) // 暴露方法给父组件
|
||||||
|
</script>
|
@ -36,7 +36,8 @@
|
|||||||
placement="top"
|
placement="top"
|
||||||
:content="getUserNicknames(startUserIds)"
|
:content="getUserNicknames(startUserIds)"
|
||||||
>
|
>
|
||||||
{{ getUserNicknames(startUserIds.slice(0,2)) }} 等 {{ startUserIds.length }} 人可发起流程
|
{{ getUserNicknames(startUserIds.slice(0, 2)) }} 等
|
||||||
|
{{ startUserIds.length }} 人可发起流程
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
</el-text>
|
</el-text>
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
@ -46,9 +47,15 @@
|
|||||||
<div class="field-permit-title">
|
<div class="field-permit-title">
|
||||||
<div class="setting-title-label first-title"> 字段名称 </div>
|
<div class="setting-title-label first-title"> 字段名称 </div>
|
||||||
<div class="other-titles">
|
<div class="other-titles">
|
||||||
<span class="setting-title-label">只读</span>
|
<span class="setting-title-label cursor-pointer" @click="updatePermission('READ')">
|
||||||
<span class="setting-title-label">可编辑</span>
|
只读
|
||||||
<span class="setting-title-label">隐藏</span>
|
</span>
|
||||||
|
<span class="setting-title-label cursor-pointer" @click="updatePermission('WRITE')">
|
||||||
|
可编辑
|
||||||
|
</span>
|
||||||
|
<span class="setting-title-label cursor-pointer" @click="updatePermission('NONE')">
|
||||||
|
隐藏
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
@ -157,6 +164,17 @@ const showStartUserNodeConfig = (node: SimpleFlowNode) => {
|
|||||||
getNodeConfigFormFields(node.fieldsPermission)
|
getNodeConfigFormFields(node.fieldsPermission)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 批量更新权限 */
|
||||||
|
const updatePermission = (type: string) => {
|
||||||
|
fieldsPermissionConfig.value.forEach((field) => {
|
||||||
|
field.permission =
|
||||||
|
type === 'READ'
|
||||||
|
? FieldPermissionType.READ
|
||||||
|
: type === 'WRITE'
|
||||||
|
? FieldPermissionType.WRITE
|
||||||
|
: FieldPermissionType.NONE
|
||||||
|
})
|
||||||
|
}
|
||||||
defineExpose({ openDrawer, showStartUserNodeConfig }) // 暴露方法给父组件
|
defineExpose({ openDrawer, showStartUserNodeConfig }) // 暴露方法给父组件
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -0,0 +1,524 @@
|
|||||||
|
<template>
|
||||||
|
<el-drawer
|
||||||
|
:append-to-body="true"
|
||||||
|
v-model="settingVisible"
|
||||||
|
:show-close="false"
|
||||||
|
:size="630"
|
||||||
|
:before-close="saveConfig"
|
||||||
|
>
|
||||||
|
<template #header>
|
||||||
|
<div class="config-header">
|
||||||
|
<input
|
||||||
|
v-if="showInput"
|
||||||
|
type="text"
|
||||||
|
class="config-editable-input"
|
||||||
|
@blur="blurEvent()"
|
||||||
|
v-mountedFocus
|
||||||
|
v-model="nodeName"
|
||||||
|
:placeholder="nodeName"
|
||||||
|
/>
|
||||||
|
<div v-else class="node-name">
|
||||||
|
{{ nodeName }} <Icon class="ml-1" icon="ep:edit-pen" :size="16" @click="clickIcon()" />
|
||||||
|
</div>
|
||||||
|
<div class="divide-line"></div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<div>
|
||||||
|
<el-form ref="formRef" :model="configForm" label-position="top" :rules="formRules">
|
||||||
|
<el-form-item label="触发器类型" prop="type">
|
||||||
|
<el-select v-model="configForm.type" @change="changeTriggerType">
|
||||||
|
<el-option
|
||||||
|
v-for="(item, index) in TRIGGER_TYPES"
|
||||||
|
:key="index"
|
||||||
|
:value="item.value"
|
||||||
|
:label="item.label"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<!-- HTTP 请求触发器 -->
|
||||||
|
<div
|
||||||
|
v-if="
|
||||||
|
[TriggerTypeEnum.HTTP_REQUEST, TriggerTypeEnum.HTTP_CALLBACK].includes(
|
||||||
|
configForm.type
|
||||||
|
) && configForm.httpRequestSetting
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<HttpRequestSetting
|
||||||
|
v-model:setting="configForm.httpRequestSetting"
|
||||||
|
:responseEnable="configForm.type === TriggerTypeEnum.HTTP_REQUEST"
|
||||||
|
:formItemPrefix="'httpRequestSetting'"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 表单数据修改触发器 -->
|
||||||
|
<div v-if="configForm.type === TriggerTypeEnum.FORM_UPDATE">
|
||||||
|
<div v-for="(formSetting, index) in configForm.formSettings" :key="index">
|
||||||
|
<el-card class="w-580px mt-4">
|
||||||
|
<template #header>
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<div>修改表单设置 {{ index + 1 }}</div>
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
plain
|
||||||
|
circle
|
||||||
|
v-if="configForm.formSettings!.length > 1"
|
||||||
|
@click="deleteFormSetting(index)"
|
||||||
|
>
|
||||||
|
<Icon icon="ep:close" />
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 条件设置 -->
|
||||||
|
<ConditionDialog
|
||||||
|
:ref="`condition-${index}`"
|
||||||
|
@update-condition="(val) => handleConditionUpdate(index, val)"
|
||||||
|
/>
|
||||||
|
<div class="cursor-pointer" v-if="formSetting.conditionType">
|
||||||
|
<el-tag
|
||||||
|
type="success"
|
||||||
|
effect="light"
|
||||||
|
closable
|
||||||
|
@close="deleteFormSettingCondition(formSetting)"
|
||||||
|
@click="openFormSettingCondition(index, formSetting)"
|
||||||
|
>
|
||||||
|
{{ showConditionText(formSetting) }}
|
||||||
|
</el-tag>
|
||||||
|
</div>
|
||||||
|
<el-button
|
||||||
|
v-else
|
||||||
|
type="primary"
|
||||||
|
text
|
||||||
|
@click="addFormSettingCondition(index, formSetting)"
|
||||||
|
>
|
||||||
|
<Icon icon="ep:link" class="mr-5px" />添加条件
|
||||||
|
</el-button>
|
||||||
|
<el-divider content-position="left">修改表单字段设置</el-divider>
|
||||||
|
<!-- 表单字段修改设置 -->
|
||||||
|
<div
|
||||||
|
class="flex items-center"
|
||||||
|
v-for="key in Object.keys(formSetting.updateFormFields || {})"
|
||||||
|
:key="key"
|
||||||
|
>
|
||||||
|
<div class="mr-2 flex items-center">
|
||||||
|
<el-form-item>
|
||||||
|
<el-select
|
||||||
|
class="w-160px!"
|
||||||
|
:model-value="key"
|
||||||
|
@update:model-value="(newKey) => updateFormFieldKey(formSetting, key, newKey)"
|
||||||
|
placeholder="请选择表单字段"
|
||||||
|
:disabled="key !== ''"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="(field, fIdx) in optionalUpdateFormFields"
|
||||||
|
:key="fIdx"
|
||||||
|
:label="field.title"
|
||||||
|
:value="field.field"
|
||||||
|
:disabled="field.disabled"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</div>
|
||||||
|
<div class="mx-2"><el-form-item>的值设置为</el-form-item></div>
|
||||||
|
<div class="mr-2">
|
||||||
|
<el-form-item
|
||||||
|
:prop="`formSettings.${index}.updateFormFields.${key}`"
|
||||||
|
:rules="{
|
||||||
|
required: true,
|
||||||
|
message: '值不能为空',
|
||||||
|
trigger: 'blur'
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<el-input
|
||||||
|
class="w-160px"
|
||||||
|
v-model="formSetting.updateFormFields![key]"
|
||||||
|
placeholder="请输入"
|
||||||
|
:disabled="!key"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</div>
|
||||||
|
<div class="mr-1 pt-1 cursor-pointer">
|
||||||
|
<el-form-item>
|
||||||
|
<Icon
|
||||||
|
icon="ep:delete"
|
||||||
|
:size="18"
|
||||||
|
@click="deleteFormFieldSetting(formSetting, key)"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 添加表单字段按钮 -->
|
||||||
|
<el-button type="primary" text @click="addFormFieldSetting(formSetting)">
|
||||||
|
<Icon icon="ep:memo" class="mr-5px" />添加修改字段
|
||||||
|
</el-button>
|
||||||
|
</el-card>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 添加新的设置 -->
|
||||||
|
<el-button class="mt-6" type="primary" text @click="addFormSetting">
|
||||||
|
<Icon icon="ep:setting" class="mr-5px" />添加设置
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 表单数据删除触发器 -->
|
||||||
|
<div v-if="configForm.type === TriggerTypeEnum.FORM_DELETE">
|
||||||
|
<div v-for="(formSetting, index) in configForm.formSettings" :key="index">
|
||||||
|
<el-card class="w-580px mt-4">
|
||||||
|
<template #header>
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<div>删除表单设置 {{ index + 1 }}</div>
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
plain
|
||||||
|
circle
|
||||||
|
v-if="configForm.formSettings!.length > 1"
|
||||||
|
@click="deleteFormSetting(index)"
|
||||||
|
>
|
||||||
|
<Icon icon="ep:close" />
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 条件设置 -->
|
||||||
|
<ConditionDialog
|
||||||
|
:ref="`condition-${index}`"
|
||||||
|
@update-condition="(val) => handleConditionUpdate(index, val)"
|
||||||
|
/>
|
||||||
|
<div class="cursor-pointer" v-if="formSetting.conditionType">
|
||||||
|
<el-tag
|
||||||
|
type="warning"
|
||||||
|
effect="light"
|
||||||
|
closable
|
||||||
|
@close="deleteFormSettingCondition(formSetting)"
|
||||||
|
@click="openFormSettingCondition(index, formSetting)"
|
||||||
|
>
|
||||||
|
{{ showConditionText(formSetting) }}
|
||||||
|
</el-tag>
|
||||||
|
</div>
|
||||||
|
<el-button
|
||||||
|
v-else
|
||||||
|
type="primary"
|
||||||
|
text
|
||||||
|
@click="addFormSettingCondition(index, formSetting)"
|
||||||
|
>
|
||||||
|
<Icon icon="ep:link" class="mr-5px" />添加条件
|
||||||
|
</el-button>
|
||||||
|
|
||||||
|
<el-divider content-position="left">删除表单字段设置</el-divider>
|
||||||
|
<!-- 表单字段删除设置 -->
|
||||||
|
<div class="flex flex-wrap gap-2">
|
||||||
|
<el-select
|
||||||
|
v-model="formSetting.deleteFields"
|
||||||
|
multiple
|
||||||
|
placeholder="请选择要删除的字段"
|
||||||
|
class="w-full"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="field in formFields"
|
||||||
|
:key="field.field"
|
||||||
|
:label="field.title"
|
||||||
|
:value="field.field"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 添加新的设置 -->
|
||||||
|
<el-button class="mt-6" type="primary" text @click="addFormSetting">
|
||||||
|
<Icon icon="ep:setting" class="mr-5px" />添加设置
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
</el-form>
|
||||||
|
</div>
|
||||||
|
<template #footer>
|
||||||
|
<el-divider />
|
||||||
|
<div>
|
||||||
|
<el-button type="primary" @click="saveConfig">确 定</el-button>
|
||||||
|
<el-button @click="closeDrawer">取 消</el-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-drawer>
|
||||||
|
</template>
|
||||||
|
<script setup lang="ts">
|
||||||
|
import {
|
||||||
|
SimpleFlowNode,
|
||||||
|
NodeType,
|
||||||
|
TriggerSetting,
|
||||||
|
TRIGGER_TYPES,
|
||||||
|
TriggerTypeEnum,
|
||||||
|
FormTriggerSetting,
|
||||||
|
DEFAULT_CONDITION_GROUP_VALUE
|
||||||
|
} from '../consts'
|
||||||
|
import { useWatchNode, useDrawer, useNodeName, useFormFields, getConditionShowText } from '../node'
|
||||||
|
import HttpRequestSetting from './components/HttpRequestSetting.vue'
|
||||||
|
import ConditionDialog from './components/ConditionDialog.vue'
|
||||||
|
const { proxy } = getCurrentInstance() as any
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'TriggerNodeConfig'
|
||||||
|
})
|
||||||
|
const props = defineProps({
|
||||||
|
flowNode: {
|
||||||
|
type: Object as () => SimpleFlowNode,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const message = useMessage() // 消息弹窗
|
||||||
|
// 抽屉配置
|
||||||
|
const { settingVisible, closeDrawer, openDrawer } = useDrawer()
|
||||||
|
// 当前节点
|
||||||
|
const currentNode = useWatchNode(props)
|
||||||
|
// 节点名称
|
||||||
|
const { nodeName, showInput, clickIcon, blurEvent } = useNodeName(NodeType.TRIGGER_NODE)
|
||||||
|
// 触发器表单配置
|
||||||
|
const formRef = ref() // 表单 Ref
|
||||||
|
// 表单校验规则
|
||||||
|
const formRules = reactive({
|
||||||
|
type: [{ required: true, message: '触发器类型不能为空', trigger: 'change' }],
|
||||||
|
'httpRequestSetting.url': [{ required: true, message: '请求地址不能为空', trigger: 'blur' }]
|
||||||
|
})
|
||||||
|
// 触发器配置表单数据
|
||||||
|
const configForm = ref<TriggerSetting>({
|
||||||
|
type: TriggerTypeEnum.HTTP_REQUEST,
|
||||||
|
httpRequestSetting: {
|
||||||
|
url: '',
|
||||||
|
header: [],
|
||||||
|
body: [],
|
||||||
|
response: []
|
||||||
|
},
|
||||||
|
formSettings: [
|
||||||
|
{
|
||||||
|
conditionGroups: DEFAULT_CONDITION_GROUP_VALUE,
|
||||||
|
updateFormFields: {},
|
||||||
|
deleteFields: []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
// 流程表单字段
|
||||||
|
const formFields = useFormFields()
|
||||||
|
|
||||||
|
// 可选的修改的表单字段
|
||||||
|
const optionalUpdateFormFields = computed(() => {
|
||||||
|
return formFields.map((field) => ({
|
||||||
|
title: field.title,
|
||||||
|
field: field.field,
|
||||||
|
disabled: false
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
|
||||||
|
let originalSetting: TriggerSetting | undefined
|
||||||
|
|
||||||
|
/** 触发器类型改变了 */
|
||||||
|
const changeTriggerType = () => {
|
||||||
|
if (configForm.value.type === TriggerTypeEnum.HTTP_REQUEST) {
|
||||||
|
configForm.value.httpRequestSetting =
|
||||||
|
originalSetting?.type === TriggerTypeEnum.HTTP_REQUEST && originalSetting.httpRequestSetting
|
||||||
|
? originalSetting.httpRequestSetting
|
||||||
|
: {
|
||||||
|
url: '',
|
||||||
|
header: [],
|
||||||
|
body: [],
|
||||||
|
response: []
|
||||||
|
}
|
||||||
|
configForm.value.formSettings = undefined
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (configForm.value.type === TriggerTypeEnum.HTTP_CALLBACK) {
|
||||||
|
configForm.value.httpRequestSetting =
|
||||||
|
originalSetting?.type === TriggerTypeEnum.HTTP_CALLBACK && originalSetting.httpRequestSetting
|
||||||
|
? originalSetting.httpRequestSetting
|
||||||
|
: {
|
||||||
|
url: '',
|
||||||
|
header: [],
|
||||||
|
body: [],
|
||||||
|
response: []
|
||||||
|
}
|
||||||
|
configForm.value.formSettings = undefined
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (configForm.value.type === TriggerTypeEnum.FORM_UPDATE) {
|
||||||
|
configForm.value.formSettings =
|
||||||
|
originalSetting?.type === TriggerTypeEnum.FORM_UPDATE && originalSetting.formSettings
|
||||||
|
? originalSetting.formSettings
|
||||||
|
: [
|
||||||
|
{
|
||||||
|
conditionGroups: DEFAULT_CONDITION_GROUP_VALUE,
|
||||||
|
updateFormFields: {},
|
||||||
|
deleteFields: []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
configForm.value.httpRequestSetting = undefined
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (configForm.value.type === TriggerTypeEnum.FORM_DELETE) {
|
||||||
|
configForm.value.formSettings =
|
||||||
|
originalSetting?.type === TriggerTypeEnum.FORM_DELETE && originalSetting.formSettings
|
||||||
|
? originalSetting.formSettings
|
||||||
|
: [
|
||||||
|
{
|
||||||
|
conditionGroups: DEFAULT_CONDITION_GROUP_VALUE,
|
||||||
|
updateFormFields: undefined,
|
||||||
|
deleteFields: []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
configForm.value.httpRequestSetting = undefined
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 添加新的修改表单设置 */
|
||||||
|
const addFormSetting = () => {
|
||||||
|
configForm.value.formSettings!.push({
|
||||||
|
conditionGroups: DEFAULT_CONDITION_GROUP_VALUE,
|
||||||
|
updateFormFields: {},
|
||||||
|
deleteFields: []
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 删除修改表单设置 */
|
||||||
|
const deleteFormSetting = (index: number) => {
|
||||||
|
configForm.value.formSettings!.splice(index, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 添加条件配置 */
|
||||||
|
const addFormSettingCondition = (index: number, formSetting: FormTriggerSetting) => {
|
||||||
|
const conditionDialog = proxy.$refs[`condition-${index}`][0]
|
||||||
|
conditionDialog.open(formSetting)
|
||||||
|
}
|
||||||
|
/** 删除条件配置 */
|
||||||
|
const deleteFormSettingCondition = (formSetting: FormTriggerSetting) => {
|
||||||
|
formSetting.conditionType = undefined
|
||||||
|
}
|
||||||
|
/** 打开条件配置弹窗 */
|
||||||
|
const openFormSettingCondition = (index: number, formSetting: FormTriggerSetting) => {
|
||||||
|
const conditionDialog = proxy.$refs[`condition-${index}`][0]
|
||||||
|
conditionDialog.open(formSetting)
|
||||||
|
}
|
||||||
|
/** 处理条件配置保存 */
|
||||||
|
const handleConditionUpdate = (index: number, condition: any) => {
|
||||||
|
configForm.value.formSettings![index].conditionType = condition.conditionType
|
||||||
|
configForm.value.formSettings![index].conditionExpression = condition.conditionExpression
|
||||||
|
configForm.value.formSettings![index].conditionGroups = condition.conditionGroups
|
||||||
|
}
|
||||||
|
/** 条件配置展示 */
|
||||||
|
const showConditionText = (formSetting: FormTriggerSetting) => {
|
||||||
|
return getConditionShowText(
|
||||||
|
formSetting.conditionType,
|
||||||
|
formSetting.conditionExpression,
|
||||||
|
formSetting.conditionGroups,
|
||||||
|
formFields
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 添加修改字段设置项 */
|
||||||
|
const addFormFieldSetting = (formSetting: FormTriggerSetting) => {
|
||||||
|
if (!formSetting) return
|
||||||
|
if (!formSetting.updateFormFields) {
|
||||||
|
formSetting.updateFormFields = {}
|
||||||
|
}
|
||||||
|
formSetting.updateFormFields[''] = undefined
|
||||||
|
}
|
||||||
|
/** 更新字段 KEY */
|
||||||
|
const updateFormFieldKey = (formSetting: FormTriggerSetting, oldKey: string, newKey: string) => {
|
||||||
|
if (!formSetting?.updateFormFields) return
|
||||||
|
const value = formSetting.updateFormFields[oldKey]
|
||||||
|
delete formSetting.updateFormFields[oldKey]
|
||||||
|
formSetting.updateFormFields[newKey] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 删除修改字段设置项 */
|
||||||
|
const deleteFormFieldSetting = (formSetting: FormTriggerSetting, key: string) => {
|
||||||
|
if (!formSetting?.updateFormFields) return
|
||||||
|
delete formSetting.updateFormFields[key]
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 保存配置 */
|
||||||
|
const saveConfig = async () => {
|
||||||
|
if (!formRef) return false
|
||||||
|
const valid = await formRef.value.validate()
|
||||||
|
if (!valid) return false
|
||||||
|
const showText = getShowText()
|
||||||
|
if (!showText) return false
|
||||||
|
currentNode.value.name = nodeName.value!
|
||||||
|
currentNode.value.showText = showText
|
||||||
|
if (configForm.value.type === TriggerTypeEnum.HTTP_REQUEST) {
|
||||||
|
configForm.value.formSettings = undefined
|
||||||
|
} else if (configForm.value.type === TriggerTypeEnum.FORM_UPDATE) {
|
||||||
|
configForm.value.httpRequestSetting = undefined
|
||||||
|
// 清理删除字段相关的数据
|
||||||
|
configForm.value.formSettings?.forEach((setting) => {
|
||||||
|
setting.deleteFields = undefined
|
||||||
|
})
|
||||||
|
} else if (configForm.value.type === TriggerTypeEnum.FORM_DELETE) {
|
||||||
|
configForm.value.httpRequestSetting = undefined
|
||||||
|
// 清理修改字段相关的数据
|
||||||
|
configForm.value.formSettings?.forEach((setting) => {
|
||||||
|
setting.updateFormFields = undefined
|
||||||
|
})
|
||||||
|
}
|
||||||
|
currentNode.value.triggerSetting = configForm.value
|
||||||
|
settingVisible.value = false
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 获取节点展示内容 */
|
||||||
|
const getShowText = (): string => {
|
||||||
|
let showText = ''
|
||||||
|
if (
|
||||||
|
configForm.value.type === TriggerTypeEnum.HTTP_REQUEST ||
|
||||||
|
configForm.value.type === TriggerTypeEnum.HTTP_CALLBACK
|
||||||
|
) {
|
||||||
|
showText = `${configForm.value.httpRequestSetting?.url}`
|
||||||
|
} else if (configForm.value.type === TriggerTypeEnum.FORM_UPDATE) {
|
||||||
|
for (const [index, setting] of configForm.value.formSettings!.entries()) {
|
||||||
|
if (!setting.updateFormFields || Object.keys(setting.updateFormFields).length === 0) {
|
||||||
|
message.warning(`请添加表单设置${index + 1}的修改字段`)
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
showText = '修改表单数据'
|
||||||
|
} else if (configForm.value.type === TriggerTypeEnum.FORM_DELETE) {
|
||||||
|
for (const [index, setting] of configForm.value.formSettings!.entries()) {
|
||||||
|
if (!setting.deleteFields || setting.deleteFields.length === 0) {
|
||||||
|
message.warning(`请选择表单设置${index + 1}要删除的字段`)
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
showText = '删除表单数据'
|
||||||
|
}
|
||||||
|
return showText
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 显示触发器节点配置, 由父组件传过来 */
|
||||||
|
const showTriggerNodeConfig = (node: SimpleFlowNode) => {
|
||||||
|
nodeName.value = node.name
|
||||||
|
originalSetting = node.triggerSetting ? JSON.parse(JSON.stringify(node.triggerSetting)) : {}
|
||||||
|
if (node.triggerSetting) {
|
||||||
|
configForm.value = {
|
||||||
|
type: node.triggerSetting.type,
|
||||||
|
httpRequestSetting: node.triggerSetting.httpRequestSetting || {
|
||||||
|
url: '',
|
||||||
|
header: [],
|
||||||
|
body: [],
|
||||||
|
response: []
|
||||||
|
},
|
||||||
|
formSettings: node.triggerSetting.formSettings || [
|
||||||
|
{
|
||||||
|
conditionGroups: DEFAULT_CONDITION_GROUP_VALUE,
|
||||||
|
updateFormFields: {},
|
||||||
|
deleteFields: []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({ openDrawer, showTriggerNodeConfig }) // 暴露方法给父组件
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped></style>
|
@ -3,7 +3,7 @@
|
|||||||
:append-to-body="true"
|
:append-to-body="true"
|
||||||
v-model="settingVisible"
|
v-model="settingVisible"
|
||||||
:show-close="false"
|
:show-close="false"
|
||||||
:size="550"
|
:size="580"
|
||||||
:before-close="saveConfig"
|
:before-close="saveConfig"
|
||||||
class="justify-start"
|
class="justify-start"
|
||||||
>
|
>
|
||||||
@ -19,12 +19,13 @@
|
|||||||
:placeholder="nodeName"
|
:placeholder="nodeName"
|
||||||
/>
|
/>
|
||||||
<div v-else class="node-name">
|
<div v-else class="node-name">
|
||||||
{{ nodeName }} <Icon class="ml-1" icon="ep:edit-pen" :size="16" @click="clickIcon()" />
|
{{ nodeName }}
|
||||||
|
<Icon class="ml-1" icon="ep:edit-pen" :size="16" @click="clickIcon()" />
|
||||||
</div>
|
</div>
|
||||||
<div class="divide-line"></div>
|
<div class="divide-line"></div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<div class="flex flex-items-center mb-3">
|
<div v-if="currentNode.type === NodeType.USER_TASK_NODE" class="flex flex-items-center mb-3">
|
||||||
<span class="font-size-16px mr-3">审批类型 :</span>
|
<span class="font-size-16px mr-3">审批类型 :</span>
|
||||||
<el-radio-group v-model="approveType">
|
<el-radio-group v-model="approveType">
|
||||||
<el-radio
|
<el-radio
|
||||||
@ -38,22 +39,21 @@
|
|||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
</div>
|
</div>
|
||||||
<el-tabs type="border-card" v-model="activeTabName" v-if="approveType === ApproveType.USER">
|
<el-tabs type="border-card" v-model="activeTabName" v-if="approveType === ApproveType.USER">
|
||||||
<el-tab-pane label="审批人" name="user">
|
<el-tab-pane :label="`${nodeTypeName}人`" name="user">
|
||||||
<div>
|
<div>
|
||||||
<el-form ref="formRef" :model="configForm" label-position="top" :rules="formRules">
|
<el-form ref="formRef" :model="configForm" label-position="top" :rules="formRules">
|
||||||
<el-form-item label="审批人设置" prop="candidateStrategy">
|
<el-form-item :label="`${nodeTypeName}人设置`" prop="candidateStrategy">
|
||||||
<el-radio-group
|
<el-radio-group
|
||||||
v-model="configForm.candidateStrategy"
|
v-model="configForm.candidateStrategy"
|
||||||
@change="changeCandidateStrategy"
|
@change="changeCandidateStrategy"
|
||||||
>
|
>
|
||||||
<el-radio
|
<el-row>
|
||||||
v-for="(dict, index) in CANDIDATE_STRATEGY"
|
<el-col v-for="(dict, index) in CANDIDATE_STRATEGY" :key="index" :span="8">
|
||||||
:key="index"
|
<el-radio :value="dict.value" :label="dict.value">
|
||||||
:value="dict.value"
|
{{ dict.label }}
|
||||||
:label="dict.value"
|
</el-radio>
|
||||||
>
|
</el-col>
|
||||||
{{ dict.label }}
|
</el-row>
|
||||||
</el-radio>
|
|
||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item
|
<el-form-item
|
||||||
@ -61,7 +61,13 @@
|
|||||||
label="指定角色"
|
label="指定角色"
|
||||||
prop="roleIds"
|
prop="roleIds"
|
||||||
>
|
>
|
||||||
<el-select v-model="configForm.roleIds" clearable multiple style="width: 100%">
|
<el-select
|
||||||
|
filterable
|
||||||
|
v-model="configForm.roleIds"
|
||||||
|
clearable
|
||||||
|
multiple
|
||||||
|
style="width: 100%"
|
||||||
|
>
|
||||||
<el-option
|
<el-option
|
||||||
v-for="item in roleOptions"
|
v-for="item in roleOptions"
|
||||||
:key="item.id"
|
:key="item.id"
|
||||||
@ -99,7 +105,13 @@
|
|||||||
prop="postIds"
|
prop="postIds"
|
||||||
span="24"
|
span="24"
|
||||||
>
|
>
|
||||||
<el-select v-model="configForm.postIds" clearable multiple style="width: 100%">
|
<el-select
|
||||||
|
filterable
|
||||||
|
v-model="configForm.postIds"
|
||||||
|
clearable
|
||||||
|
multiple
|
||||||
|
style="width: 100%"
|
||||||
|
>
|
||||||
<el-option
|
<el-option
|
||||||
v-for="item in postOptions"
|
v-for="item in postOptions"
|
||||||
:key="item.id"
|
:key="item.id"
|
||||||
@ -114,7 +126,13 @@
|
|||||||
prop="userIds"
|
prop="userIds"
|
||||||
span="24"
|
span="24"
|
||||||
>
|
>
|
||||||
<el-select v-model="configForm.userIds" clearable multiple style="width: 100%">
|
<el-select
|
||||||
|
filterable
|
||||||
|
v-model="configForm.userIds"
|
||||||
|
clearable
|
||||||
|
multiple
|
||||||
|
style="width: 100%"
|
||||||
|
>
|
||||||
<el-option
|
<el-option
|
||||||
v-for="item in userOptions"
|
v-for="item in userOptions"
|
||||||
:key="item.id"
|
:key="item.id"
|
||||||
@ -128,7 +146,13 @@
|
|||||||
label="指定用户组"
|
label="指定用户组"
|
||||||
prop="userGroups"
|
prop="userGroups"
|
||||||
>
|
>
|
||||||
<el-select v-model="configForm.userGroups" clearable multiple style="width: 100%">
|
<el-select
|
||||||
|
filterable
|
||||||
|
v-model="configForm.userGroups"
|
||||||
|
clearable
|
||||||
|
multiple
|
||||||
|
style="width: 100%"
|
||||||
|
>
|
||||||
<el-option
|
<el-option
|
||||||
v-for="item in userGroupOptions"
|
v-for="item in userGroupOptions"
|
||||||
:key="item.id"
|
:key="item.id"
|
||||||
@ -142,13 +166,13 @@
|
|||||||
label="表单内用户字段"
|
label="表单内用户字段"
|
||||||
prop="formUser"
|
prop="formUser"
|
||||||
>
|
>
|
||||||
<el-select v-model="configForm.formUser" clearable style="width: 100%">
|
<el-select filterable v-model="configForm.formUser" clearable style="width: 100%">
|
||||||
<el-option
|
<el-option
|
||||||
v-for="(item, idx) in userFieldOnFormOptions"
|
v-for="(item, idx) in userFieldOnFormOptions"
|
||||||
:key="idx"
|
:key="idx"
|
||||||
:label="item.title"
|
:label="item.title"
|
||||||
:value="item.field"
|
:value="item.field"
|
||||||
:disabled ="!item.required"
|
:disabled="!item.required"
|
||||||
/>
|
/>
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
@ -157,13 +181,13 @@
|
|||||||
label="表单内部门字段"
|
label="表单内部门字段"
|
||||||
prop="formDept"
|
prop="formDept"
|
||||||
>
|
>
|
||||||
<el-select v-model="configForm.formDept" clearable style="width: 100%">
|
<el-select filterable v-model="configForm.formDept" clearable style="width: 100%">
|
||||||
<el-option
|
<el-option
|
||||||
v-for="(item, idx) in deptFieldOnFormOptions"
|
v-for="(item, idx) in deptFieldOnFormOptions"
|
||||||
:key="idx"
|
:key="idx"
|
||||||
:label="item.title"
|
:label="item.title"
|
||||||
:value="item.field"
|
:value="item.field"
|
||||||
:disabled ="!item.required"
|
:disabled="!item.required"
|
||||||
/>
|
/>
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
@ -179,7 +203,7 @@
|
|||||||
prop="deptLevel"
|
prop="deptLevel"
|
||||||
span="24"
|
span="24"
|
||||||
>
|
>
|
||||||
<el-select v-model="configForm.deptLevel" clearable>
|
<el-select filterable v-model="configForm.deptLevel" clearable>
|
||||||
<el-option
|
<el-option
|
||||||
v-for="(item, index) in MULTI_LEVEL_DEPT"
|
v-for="(item, index) in MULTI_LEVEL_DEPT"
|
||||||
:key="index"
|
:key="index"
|
||||||
@ -201,7 +225,7 @@
|
|||||||
style="width: 100%"
|
style="width: 100%"
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="多人审批方式" prop="approveMethod">
|
<el-form-item :label="`多人${nodeTypeName}方式`" prop="approveMethod">
|
||||||
<el-radio-group v-model="configForm.approveMethod" @change="approveMethodChanged">
|
<el-radio-group v-model="configForm.approveMethod" @change="approveMethodChanged">
|
||||||
<div class="flex-col">
|
<div class="flex-col">
|
||||||
<div
|
<div
|
||||||
@ -230,92 +254,102 @@
|
|||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
<el-divider content-position="left">审批人拒绝时</el-divider>
|
<div v-if="currentNode.type === NodeType.USER_TASK_NODE">
|
||||||
<el-form-item prop="rejectHandlerType">
|
<el-divider content-position="left">审批人拒绝时</el-divider>
|
||||||
<el-radio-group v-model="configForm.rejectHandlerType">
|
<el-form-item prop="rejectHandlerType">
|
||||||
<div class="flex-col">
|
<el-radio-group v-model="configForm.rejectHandlerType">
|
||||||
<div v-for="(item, index) in REJECT_HANDLER_TYPES" :key="index">
|
<div class="flex-col">
|
||||||
<el-radio :key="item.value" :value="item.value" :label="item.label" />
|
<div v-for="(item, index) in REJECT_HANDLER_TYPES" :key="index">
|
||||||
|
<el-radio :key="item.value" :value="item.value" :label="item.label" />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</el-radio-group>
|
||||||
</el-radio-group>
|
</el-form-item>
|
||||||
</el-form-item>
|
<el-form-item
|
||||||
<el-form-item
|
v-if="configForm.rejectHandlerType == RejectHandlerType.RETURN_USER_TASK"
|
||||||
v-if="configForm.rejectHandlerType == RejectHandlerType.RETURN_USER_TASK"
|
label="驳回节点"
|
||||||
label="驳回节点"
|
prop="returnNodeId"
|
||||||
prop="returnNodeId"
|
|
||||||
>
|
|
||||||
<el-select v-model="configForm.returnNodeId" clearable style="width: 100%">
|
|
||||||
<el-option
|
|
||||||
v-for="item in returnTaskList"
|
|
||||||
:key="item.id"
|
|
||||||
:label="item.name"
|
|
||||||
:value="item.id"
|
|
||||||
/>
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
|
|
||||||
<el-divider content-position="left">审批人超时未处理时</el-divider>
|
|
||||||
<el-form-item label="启用开关" prop="timeoutHandlerEnable">
|
|
||||||
<el-switch
|
|
||||||
v-model="configForm.timeoutHandlerEnable"
|
|
||||||
active-text="开启"
|
|
||||||
inactive-text="关闭"
|
|
||||||
@change="timeoutHandlerChange"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item
|
|
||||||
label="执行动作"
|
|
||||||
prop="timeoutHandlerType"
|
|
||||||
v-if="configForm.timeoutHandlerEnable"
|
|
||||||
>
|
|
||||||
<el-radio-group
|
|
||||||
v-model="configForm.timeoutHandlerType"
|
|
||||||
@change="timeoutHandlerTypeChanged"
|
|
||||||
>
|
>
|
||||||
<el-radio-button
|
<el-select
|
||||||
v-for="item in TIMEOUT_HANDLER_TYPES"
|
filterable
|
||||||
:key="item.value"
|
v-model="configForm.returnNodeId"
|
||||||
:value="item.value"
|
clearable
|
||||||
:label="item.label"
|
style="width: 100%"
|
||||||
/>
|
>
|
||||||
</el-radio-group>
|
<el-option
|
||||||
</el-form-item>
|
v-for="item in returnTaskList"
|
||||||
<el-form-item label="超时时间设置" v-if="configForm.timeoutHandlerEnable">
|
:key="item.id"
|
||||||
<span class="mr-2">当超过</span>
|
:label="item.name"
|
||||||
<el-form-item prop="timeDuration">
|
:value="item.id"
|
||||||
<el-input-number
|
/>
|
||||||
class="mr-2"
|
</el-select>
|
||||||
:style="{ width: '100px' }"
|
</el-form-item>
|
||||||
v-model="configForm.timeDuration"
|
</div>
|
||||||
:min="1"
|
|
||||||
controls-position="right"
|
<div v-if="currentNode.type === NodeType.USER_TASK_NODE">
|
||||||
|
<el-divider content-position="left">审批人超时未处理时</el-divider>
|
||||||
|
<el-form-item label="启用开关" prop="timeoutHandlerEnable">
|
||||||
|
<el-switch
|
||||||
|
v-model="configForm.timeoutHandlerEnable"
|
||||||
|
active-text="开启"
|
||||||
|
inactive-text="关闭"
|
||||||
|
@change="timeoutHandlerChange"
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-select
|
<el-form-item
|
||||||
v-model="timeUnit"
|
label="执行动作"
|
||||||
class="mr-2"
|
prop="timeoutHandlerType"
|
||||||
:style="{ width: '100px' }"
|
v-if="configForm.timeoutHandlerEnable"
|
||||||
@change="timeUnitChange"
|
|
||||||
>
|
>
|
||||||
<el-option
|
<el-radio-group
|
||||||
v-for="item in TIME_UNIT_TYPES"
|
v-model="configForm.timeoutHandlerType"
|
||||||
:key="item.value"
|
@change="timeoutHandlerTypeChanged"
|
||||||
:label="item.label"
|
>
|
||||||
:value="item.value"
|
<el-radio-button
|
||||||
/>
|
v-for="item in TIMEOUT_HANDLER_TYPES"
|
||||||
</el-select>
|
:key="item.value"
|
||||||
未处理
|
:value="item.value"
|
||||||
</el-form-item>
|
:label="item.label"
|
||||||
<el-form-item
|
/>
|
||||||
label="最大提醒次数"
|
</el-radio-group>
|
||||||
prop="maxRemindCount"
|
</el-form-item>
|
||||||
v-if="configForm.timeoutHandlerEnable && configForm.timeoutHandlerType === 1"
|
<el-form-item label="超时时间设置" v-if="configForm.timeoutHandlerEnable">
|
||||||
>
|
<span class="mr-2">当超过</span>
|
||||||
<el-input-number v-model="configForm.maxRemindCount" :min="1" :max="10" />
|
<el-form-item prop="timeDuration">
|
||||||
</el-form-item>
|
<el-input-number
|
||||||
|
class="mr-2"
|
||||||
|
:style="{ width: '100px' }"
|
||||||
|
v-model="configForm.timeDuration"
|
||||||
|
:min="1"
|
||||||
|
controls-position="right"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-select
|
||||||
|
filterable
|
||||||
|
v-model="timeUnit"
|
||||||
|
class="mr-2"
|
||||||
|
:style="{ width: '100px' }"
|
||||||
|
@change="timeUnitChange"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="item in TIME_UNIT_TYPES"
|
||||||
|
:key="item.value"
|
||||||
|
:label="item.label"
|
||||||
|
:value="item.value"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
未处理
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
label="最大提醒次数"
|
||||||
|
prop="maxRemindCount"
|
||||||
|
v-if="configForm.timeoutHandlerEnable && configForm.timeoutHandlerType === 1"
|
||||||
|
>
|
||||||
|
<el-input-number v-model="configForm.maxRemindCount" :min="1" :max="10" />
|
||||||
|
</el-form-item>
|
||||||
|
</div>
|
||||||
|
|
||||||
<el-divider content-position="left">审批人为空时</el-divider>
|
<el-divider content-position="left">{{ nodeTypeName }}人为空时</el-divider>
|
||||||
<el-form-item prop="assignEmptyHandlerType">
|
<el-form-item prop="assignEmptyHandlerType">
|
||||||
<el-radio-group v-model="configForm.assignEmptyHandlerType">
|
<el-radio-group v-model="configForm.assignEmptyHandlerType">
|
||||||
<div class="flex-col">
|
<div class="flex-col">
|
||||||
@ -332,6 +366,7 @@
|
|||||||
span="24"
|
span="24"
|
||||||
>
|
>
|
||||||
<el-select
|
<el-select
|
||||||
|
filterable
|
||||||
v-model="configForm.assignEmptyHandlerUserIds"
|
v-model="configForm.assignEmptyHandlerUserIds"
|
||||||
clearable
|
clearable
|
||||||
multiple
|
multiple
|
||||||
@ -346,20 +381,44 @@
|
|||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
<el-divider content-position="left">审批人与提交人为同一人时</el-divider>
|
<div v-if="currentNode.type === NodeType.USER_TASK_NODE">
|
||||||
<el-form-item prop="assignStartUserHandlerType">
|
<el-divider content-position="left">审批人与提交人为同一人时</el-divider>
|
||||||
<el-radio-group v-model="configForm.assignStartUserHandlerType">
|
<el-form-item prop="assignStartUserHandlerType">
|
||||||
<div class="flex-col">
|
<el-radio-group v-model="configForm.assignStartUserHandlerType">
|
||||||
<div v-for="(item, index) in ASSIGN_START_USER_HANDLER_TYPES" :key="index">
|
<div class="flex-col">
|
||||||
<el-radio :key="item.value" :value="item.value" :label="item.label" />
|
<div v-for="(item, index) in ASSIGN_START_USER_HANDLER_TYPES" :key="index">
|
||||||
|
<el-radio :key="item.value" :value="item.value" :label="item.label" />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</el-radio-group>
|
||||||
</el-radio-group>
|
</el-form-item>
|
||||||
</el-form-item>
|
</div>
|
||||||
|
|
||||||
|
<div v-if="currentNode.type === NodeType.USER_TASK_NODE">
|
||||||
|
<el-divider content-position="left">是否需要签名</el-divider>
|
||||||
|
<el-form-item prop="signEnable">
|
||||||
|
<el-switch v-model="configForm.signEnable" active-text="是" inactive-text="否" />
|
||||||
|
</el-form-item>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="currentNode.type === NodeType.USER_TASK_NODE">
|
||||||
|
<el-divider content-position="left">审批意见</el-divider>
|
||||||
|
<el-form-item prop="reasonRequire">
|
||||||
|
<el-switch
|
||||||
|
v-model="configForm.reasonRequire"
|
||||||
|
active-text="必填"
|
||||||
|
inactive-text="非必填"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</div>
|
||||||
</el-form>
|
</el-form>
|
||||||
</div>
|
</div>
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
<el-tab-pane label="操作按钮设置" name="buttons">
|
<el-tab-pane
|
||||||
|
label="操作按钮设置"
|
||||||
|
v-if="currentNode.type === NodeType.USER_TASK_NODE"
|
||||||
|
name="buttons"
|
||||||
|
>
|
||||||
<div class="button-setting-pane">
|
<div class="button-setting-pane">
|
||||||
<div class="button-setting-desc">操作按钮</div>
|
<div class="button-setting-desc">操作按钮</div>
|
||||||
<div class="button-setting-title">
|
<div class="button-setting-title">
|
||||||
@ -395,9 +454,15 @@
|
|||||||
<div class="field-permit-title">
|
<div class="field-permit-title">
|
||||||
<div class="setting-title-label first-title"> 字段名称 </div>
|
<div class="setting-title-label first-title"> 字段名称 </div>
|
||||||
<div class="other-titles">
|
<div class="other-titles">
|
||||||
<span class="setting-title-label">只读</span>
|
<span class="setting-title-label cursor-pointer" @click="updatePermission('READ')">
|
||||||
<span class="setting-title-label">可编辑</span>
|
只读
|
||||||
<span class="setting-title-label">隐藏</span>
|
</span>
|
||||||
|
<span class="setting-title-label cursor-pointer" @click="updatePermission('WRITE')">
|
||||||
|
可编辑
|
||||||
|
</span>
|
||||||
|
<span class="setting-title-label cursor-pointer" @click="updatePermission('NONE')">
|
||||||
|
隐藏
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
@ -435,6 +500,13 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
|
<el-tab-pane label="监听器" name="listener">
|
||||||
|
<UserTaskListener
|
||||||
|
ref="userTaskListenerRef"
|
||||||
|
v-model="configForm"
|
||||||
|
:form-field-options="formFieldOptions"
|
||||||
|
/>
|
||||||
|
</el-tab-pane>
|
||||||
</el-tabs>
|
</el-tabs>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<el-divider />
|
<el-divider />
|
||||||
@ -470,7 +542,8 @@ import {
|
|||||||
ASSIGN_EMPTY_HANDLER_TYPES,
|
ASSIGN_EMPTY_HANDLER_TYPES,
|
||||||
AssignEmptyHandlerType,
|
AssignEmptyHandlerType,
|
||||||
FieldPermissionType,
|
FieldPermissionType,
|
||||||
ProcessVariableEnum
|
ProcessVariableEnum,
|
||||||
|
TRANSACTOR_DEFAULT_BUTTON_SETTING
|
||||||
} from '../consts'
|
} from '../consts'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@ -484,6 +557,7 @@ import {
|
|||||||
import { defaultProps } from '@/utils/tree'
|
import { defaultProps } from '@/utils/tree'
|
||||||
import { cloneDeep } from 'lodash-es'
|
import { cloneDeep } from 'lodash-es'
|
||||||
import { convertTimeUnit, getApproveTypeText } from '../utils'
|
import { convertTimeUnit, getApproveTypeText } from '../utils'
|
||||||
|
import UserTaskListener from './components/UserTaskListener.vue'
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: 'UserTaskNodeConfig'
|
name: 'UserTaskNodeConfig'
|
||||||
})
|
})
|
||||||
@ -572,7 +646,7 @@ const {
|
|||||||
handleCandidateParam,
|
handleCandidateParam,
|
||||||
parseCandidateParam,
|
parseCandidateParam,
|
||||||
getShowText
|
getShowText
|
||||||
} = useNodeForm(NodeType.USER_TASK_NODE)
|
} = useNodeForm(currentNode.value.type)
|
||||||
const configForm = tempConfigForm as Ref<UserTaskFormType>
|
const configForm = tempConfigForm as Ref<UserTaskFormType>
|
||||||
|
|
||||||
// 改变审批人设置策略
|
// 改变审批人设置策略
|
||||||
@ -609,9 +683,16 @@ const {
|
|||||||
cTimeoutMaxRemindCount
|
cTimeoutMaxRemindCount
|
||||||
} = useTimeoutHandler()
|
} = useTimeoutHandler()
|
||||||
|
|
||||||
// 保存配置
|
const userTaskListenerRef = ref()
|
||||||
|
|
||||||
|
/** 节点类型名称 */
|
||||||
|
const nodeTypeName = computed(() => {
|
||||||
|
return currentNode.value.type === NodeType.TRANSACTOR_NODE ? '办理' : '审批'
|
||||||
|
})
|
||||||
|
|
||||||
|
/** 保存配置 */
|
||||||
const saveConfig = async () => {
|
const saveConfig = async () => {
|
||||||
activeTabName.value = 'user'
|
// activeTabName.value = 'user'
|
||||||
// 设置审批节点名称
|
// 设置审批节点名称
|
||||||
currentNode.value.name = nodeName.value!
|
currentNode.value.name = nodeName.value!
|
||||||
// 设置审批类型
|
// 设置审批类型
|
||||||
@ -624,7 +705,8 @@ const saveConfig = async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!formRef) return false
|
if (!formRef) return false
|
||||||
const valid = await formRef.value.validate()
|
if (!userTaskListenerRef) return false
|
||||||
|
const valid = (await formRef.value.validate()) && (await userTaskListenerRef.value.validate())
|
||||||
if (!valid) return false
|
if (!valid) return false
|
||||||
const showText = getShowText()
|
const showText = getShowText()
|
||||||
if (!showText) return false
|
if (!showText) return false
|
||||||
@ -663,13 +745,38 @@ const saveConfig = async () => {
|
|||||||
currentNode.value.fieldsPermission = fieldsPermissionConfig.value
|
currentNode.value.fieldsPermission = fieldsPermissionConfig.value
|
||||||
// 设置按钮权限
|
// 设置按钮权限
|
||||||
currentNode.value.buttonsSetting = buttonsSetting.value
|
currentNode.value.buttonsSetting = buttonsSetting.value
|
||||||
|
// 创建任务监听器
|
||||||
|
currentNode.value.taskCreateListener = {
|
||||||
|
enable: configForm.value.taskCreateListenerEnable ?? false,
|
||||||
|
path: configForm.value.taskCreateListenerPath,
|
||||||
|
header: configForm.value.taskCreateListener?.header,
|
||||||
|
body: configForm.value.taskCreateListener?.body
|
||||||
|
}
|
||||||
|
// 指派任务监听器
|
||||||
|
currentNode.value.taskAssignListener = {
|
||||||
|
enable: configForm.value.taskAssignListenerEnable ?? false,
|
||||||
|
path: configForm.value.taskAssignListenerPath,
|
||||||
|
header: configForm.value.taskAssignListener?.header,
|
||||||
|
body: configForm.value.taskAssignListener?.body
|
||||||
|
}
|
||||||
|
// 完成任务监听器
|
||||||
|
currentNode.value.taskCompleteListener = {
|
||||||
|
enable: configForm.value.taskCompleteListenerEnable ?? false,
|
||||||
|
path: configForm.value.taskCompleteListenerPath,
|
||||||
|
header: configForm.value.taskCompleteListener?.header,
|
||||||
|
body: configForm.value.taskCompleteListener?.body
|
||||||
|
}
|
||||||
|
// 签名
|
||||||
|
currentNode.value.signEnable = configForm.value.signEnable
|
||||||
|
// 审批意见
|
||||||
|
currentNode.value.reasonRequire = configForm.value.reasonRequire
|
||||||
|
|
||||||
currentNode.value.showText = showText
|
currentNode.value.showText = showText
|
||||||
settingVisible.value = false
|
settingVisible.value = false
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// 显示审批节点配置, 由父组件传过来
|
/** 显示审批节点配置, 由父组件传过来 */
|
||||||
const showUserTaskNodeConfig = (node: SimpleFlowNode) => {
|
const showUserTaskNodeConfig = (node: SimpleFlowNode) => {
|
||||||
nodeName.value = node.name
|
nodeName.value = node.name
|
||||||
// 1 审批类型
|
// 1 审批类型
|
||||||
@ -689,13 +796,13 @@ const showUserTaskNodeConfig = (node: SimpleFlowNode) => {
|
|||||||
configForm.value.approveRatio = node.approveRatio!
|
configForm.value.approveRatio = node.approveRatio!
|
||||||
}
|
}
|
||||||
// 2.3 设置审批拒绝处理
|
// 2.3 设置审批拒绝处理
|
||||||
configForm.value.rejectHandlerType = node.rejectHandler!.type
|
configForm.value.rejectHandlerType = node.rejectHandler?.type
|
||||||
configForm.value.returnNodeId = node.rejectHandler?.returnNodeId
|
configForm.value.returnNodeId = node.rejectHandler?.returnNodeId
|
||||||
const matchNodeList = []
|
const matchNodeList = []
|
||||||
emits('find:returnTaskNodes', matchNodeList)
|
emits('find:returnTaskNodes', matchNodeList)
|
||||||
returnTaskList.value = matchNodeList
|
returnTaskList.value = matchNodeList
|
||||||
// 2.4 设置审批超时处理
|
// 2.4 设置审批超时处理
|
||||||
configForm.value.timeoutHandlerEnable = node.timeoutHandler!.enable
|
configForm.value.timeoutHandlerEnable = node.timeoutHandler?.enable
|
||||||
if (node.timeoutHandler?.enable && node.timeoutHandler?.timeDuration) {
|
if (node.timeoutHandler?.enable && node.timeoutHandler?.timeDuration) {
|
||||||
const strTimeDuration = node.timeoutHandler.timeDuration
|
const strTimeDuration = node.timeoutHandler.timeDuration
|
||||||
let parseTime = strTimeDuration.slice(2, strTimeDuration.length - 1)
|
let parseTime = strTimeDuration.slice(2, strTimeDuration.length - 1)
|
||||||
@ -711,16 +818,44 @@ const showUserTaskNodeConfig = (node: SimpleFlowNode) => {
|
|||||||
// 2.6 设置用户任务的审批人与发起人相同时
|
// 2.6 设置用户任务的审批人与发起人相同时
|
||||||
configForm.value.assignStartUserHandlerType = node.assignStartUserHandlerType
|
configForm.value.assignStartUserHandlerType = node.assignStartUserHandlerType
|
||||||
// 3. 操作按钮设置
|
// 3. 操作按钮设置
|
||||||
buttonsSetting.value = cloneDeep(node.buttonsSetting) || DEFAULT_BUTTON_SETTING
|
buttonsSetting.value =
|
||||||
|
cloneDeep(node.buttonsSetting) ||
|
||||||
|
(node.type === NodeType.TRANSACTOR_NODE
|
||||||
|
? TRANSACTOR_DEFAULT_BUTTON_SETTING
|
||||||
|
: DEFAULT_BUTTON_SETTING)
|
||||||
// 4. 表单字段权限配置
|
// 4. 表单字段权限配置
|
||||||
getNodeConfigFormFields(node.fieldsPermission)
|
getNodeConfigFormFields(node.fieldsPermission)
|
||||||
|
// 5. 监听器
|
||||||
|
// 5.1 创建任务
|
||||||
|
configForm.value.taskCreateListenerEnable = node.taskCreateListener?.enable
|
||||||
|
configForm.value.taskCreateListenerPath = node.taskCreateListener?.path
|
||||||
|
configForm.value.taskCreateListener = {
|
||||||
|
header: node.taskCreateListener?.header ?? [],
|
||||||
|
body: node.taskCreateListener?.body ?? []
|
||||||
|
}
|
||||||
|
// 5.2 指派任务
|
||||||
|
configForm.value.taskAssignListenerEnable = node.taskAssignListener?.enable
|
||||||
|
configForm.value.taskAssignListenerPath = node.taskAssignListener?.path
|
||||||
|
configForm.value.taskAssignListener = {
|
||||||
|
header: node.taskAssignListener?.header ?? [],
|
||||||
|
body: node.taskAssignListener?.body ?? []
|
||||||
|
}
|
||||||
|
// 5.3 完成任务
|
||||||
|
configForm.value.taskCompleteListenerEnable = node.taskCompleteListener?.enable
|
||||||
|
configForm.value.taskCompleteListenerPath = node.taskCompleteListener?.path
|
||||||
|
configForm.value.taskCompleteListener = {
|
||||||
|
header: node.taskCompleteListener?.header ?? [],
|
||||||
|
body: node.taskCompleteListener?.body ?? []
|
||||||
|
}
|
||||||
|
// 6. 签名
|
||||||
|
configForm.value.signEnable = node?.signEnable ?? false
|
||||||
|
// 7. 审批意见
|
||||||
|
configForm.value.reasonRequire = node?.reasonRequire ?? false
|
||||||
}
|
}
|
||||||
|
|
||||||
defineExpose({ openDrawer, showUserTaskNodeConfig }) // 暴露方法给父组件
|
defineExpose({ openDrawer, showUserTaskNodeConfig }) // 暴露方法给父组件
|
||||||
|
|
||||||
/**
|
/** 操作按钮设置 */
|
||||||
* @description 操作按钮设置
|
|
||||||
*/
|
|
||||||
function useButtonsSetting() {
|
function useButtonsSetting() {
|
||||||
const buttonsSetting = ref<ButtonSetting[]>()
|
const buttonsSetting = ref<ButtonSetting[]>()
|
||||||
// 操作按钮显示名称可编辑
|
// 操作按钮显示名称可编辑
|
||||||
@ -741,9 +876,7 @@ function useButtonsSetting() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** 审批人超时未处理配置 */
|
||||||
* @description 审批人超时未处理配置
|
|
||||||
*/
|
|
||||||
function useTimeoutHandler() {
|
function useTimeoutHandler() {
|
||||||
// 时间单位
|
// 时间单位
|
||||||
const timeUnit = ref(TimeUnitType.HOUR)
|
const timeUnit = ref(TimeUnitType.HOUR)
|
||||||
@ -826,6 +959,18 @@ function useTimeoutHandler() {
|
|||||||
cTimeoutMaxRemindCount
|
cTimeoutMaxRemindCount
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 批量更新权限 */
|
||||||
|
const updatePermission = (type: string) => {
|
||||||
|
fieldsPermissionConfig.value.forEach((field) => {
|
||||||
|
field.permission =
|
||||||
|
type === 'READ'
|
||||||
|
? FieldPermissionType.READ
|
||||||
|
: type === 'WRITE'
|
||||||
|
? FieldPermissionType.WRITE
|
||||||
|
: FieldPermissionType.NONE
|
||||||
|
})
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
@ -0,0 +1,276 @@
|
|||||||
|
<template>
|
||||||
|
<el-form ref="formRef" :model="condition" :rules="formRules" label-position="top">
|
||||||
|
<el-form-item label="配置方式" prop="conditionType">
|
||||||
|
<el-radio-group v-model="condition.conditionType" @change="changeConditionType">
|
||||||
|
<el-radio
|
||||||
|
v-for="(dict, indexConditionType) in conditionConfigTypes"
|
||||||
|
:key="indexConditionType"
|
||||||
|
:value="dict.value"
|
||||||
|
:label="dict.value"
|
||||||
|
>
|
||||||
|
{{ dict.label }}
|
||||||
|
</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
v-if="condition.conditionType === ConditionType.RULE && condition.conditionGroups"
|
||||||
|
label="条件规则"
|
||||||
|
>
|
||||||
|
<div class="condition-group-tool">
|
||||||
|
<div class="flex items-center">
|
||||||
|
<div class="mr-4">条件组关系</div>
|
||||||
|
<el-switch
|
||||||
|
v-model="condition.conditionGroups.and"
|
||||||
|
inline-prompt
|
||||||
|
active-text="且"
|
||||||
|
inactive-text="或"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<el-space direction="vertical" :spacer="condition.conditionGroups.and ? '且' : '或'">
|
||||||
|
<el-card
|
||||||
|
class="condition-group"
|
||||||
|
style="width: 530px"
|
||||||
|
v-for="(equation, cIdx) in condition.conditionGroups.conditions"
|
||||||
|
:key="cIdx"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="condition-group-delete"
|
||||||
|
v-if="condition.conditionGroups.conditions.length > 1"
|
||||||
|
>
|
||||||
|
<Icon
|
||||||
|
color="#0089ff"
|
||||||
|
icon="ep:circle-close-filled"
|
||||||
|
:size="18"
|
||||||
|
@click="deleteConditionGroup(condition.conditionGroups.conditions, cIdx)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<template #header>
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<div>条件组</div>
|
||||||
|
<div class="flex">
|
||||||
|
<div class="mr-4">规则关系</div>
|
||||||
|
<el-switch
|
||||||
|
v-model="equation.and"
|
||||||
|
inline-prompt
|
||||||
|
active-text="且"
|
||||||
|
inactive-text="或"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<div class="flex pt-2" v-for="(rule, rIdx) in equation.rules" :key="rIdx">
|
||||||
|
<div class="mr-2">
|
||||||
|
<el-form-item
|
||||||
|
:prop="`conditionGroups.conditions.${cIdx}.rules.${rIdx}.leftSide`"
|
||||||
|
:rules="{
|
||||||
|
required: true,
|
||||||
|
message: '左值不能为空',
|
||||||
|
trigger: 'change'
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<el-select style="width: 160px" v-model="rule.leftSide" clearable>
|
||||||
|
<el-option
|
||||||
|
v-for="(field, fIdx) in fieldOptions"
|
||||||
|
:key="fIdx"
|
||||||
|
:label="field.title"
|
||||||
|
:value="field.field"
|
||||||
|
:disabled="!field.required"
|
||||||
|
>
|
||||||
|
<el-tooltip
|
||||||
|
content="表单字段非必填时不能作为流程分支条件"
|
||||||
|
effect="dark"
|
||||||
|
placement="right-start"
|
||||||
|
v-if="!field.required"
|
||||||
|
>
|
||||||
|
<span>{{ field.title }}</span>
|
||||||
|
</el-tooltip>
|
||||||
|
</el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</div>
|
||||||
|
<div class="mr-2">
|
||||||
|
<el-select v-model="rule.opCode" style="width: 100px">
|
||||||
|
<el-option
|
||||||
|
v-for="operator in COMPARISON_OPERATORS"
|
||||||
|
:key="operator.value"
|
||||||
|
:label="operator.label"
|
||||||
|
:value="operator.value"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</div>
|
||||||
|
<div class="mr-2">
|
||||||
|
<el-form-item
|
||||||
|
:prop="`conditionGroups.conditions.${cIdx}.rules.${rIdx}.rightSide`"
|
||||||
|
:rules="{
|
||||||
|
required: true,
|
||||||
|
message: '右值不能为空',
|
||||||
|
trigger: 'blur'
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<el-input v-model="rule.rightSide" style="width: 160px" />
|
||||||
|
</el-form-item>
|
||||||
|
</div>
|
||||||
|
<div class="mr-1 flex items-center" v-if="equation.rules.length > 1">
|
||||||
|
<Icon icon="ep:delete" :size="18" @click="deleteConditionRule(equation, rIdx)" />
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center">
|
||||||
|
<Icon icon="ep:plus" :size="18" @click="addConditionRule(equation, rIdx)" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
</el-space>
|
||||||
|
<div title="添加条件组" class="mt-4 cursor-pointer">
|
||||||
|
<Icon
|
||||||
|
color="#0089ff"
|
||||||
|
icon="ep:plus"
|
||||||
|
:size="24"
|
||||||
|
@click="addConditionGroup(condition.conditionGroups?.conditions)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
v-if="condition.conditionType === ConditionType.EXPRESSION"
|
||||||
|
label="条件表达式"
|
||||||
|
prop="conditionExpression"
|
||||||
|
>
|
||||||
|
<el-input
|
||||||
|
type="textarea"
|
||||||
|
v-model="condition.conditionExpression"
|
||||||
|
clearable
|
||||||
|
style="width: 100%"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import {
|
||||||
|
COMPARISON_OPERATORS,
|
||||||
|
CONDITION_CONFIG_TYPES,
|
||||||
|
ConditionType,
|
||||||
|
DEFAULT_CONDITION_GROUP_VALUE
|
||||||
|
} from '../../consts'
|
||||||
|
import { BpmModelFormType } from '@/utils/constants'
|
||||||
|
import { useFormFieldsAndStartUser } from '../../node'
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
modelValue: {
|
||||||
|
type: Object,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const emit = defineEmits(['update:modelValue'])
|
||||||
|
const condition = computed({
|
||||||
|
get() {
|
||||||
|
return props.modelValue
|
||||||
|
},
|
||||||
|
set(newValue) {
|
||||||
|
emit('update:modelValue', newValue)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const formType = inject<Ref<number>>('formType') // 表单类型
|
||||||
|
const conditionConfigTypes = computed(() => {
|
||||||
|
return CONDITION_CONFIG_TYPES.filter((item) => {
|
||||||
|
// 业务表单暂时去掉条件规则选项
|
||||||
|
if (formType?.value === BpmModelFormType.CUSTOM && item.value === ConditionType.RULE) {
|
||||||
|
return false
|
||||||
|
} else {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
/** 条件规则可选择的表单字段 */
|
||||||
|
const fieldOptions = useFormFieldsAndStartUser()
|
||||||
|
|
||||||
|
// 表单校验规则
|
||||||
|
const formRules = reactive({
|
||||||
|
conditionType: [{ required: true, message: '配置方式不能为空', trigger: 'blur' }],
|
||||||
|
conditionExpression: [{ required: true, message: '条件表达式不能为空', trigger: 'blur' }]
|
||||||
|
})
|
||||||
|
const formRef = ref() // 表单 Ref
|
||||||
|
|
||||||
|
/** 切换条件配置方式 */
|
||||||
|
const changeConditionType = () => {
|
||||||
|
if (condition.value.conditionType === ConditionType.RULE) {
|
||||||
|
if (!condition.value.conditionGroups) {
|
||||||
|
condition.value.conditionGroups = DEFAULT_CONDITION_GROUP_VALUE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const deleteConditionGroup = (conditions, index) => {
|
||||||
|
conditions.splice(index, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
const deleteConditionRule = (condition, index) => {
|
||||||
|
condition.rules.splice(index, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
const addConditionRule = (condition, index) => {
|
||||||
|
const rule = {
|
||||||
|
opCode: '==',
|
||||||
|
leftSide: '',
|
||||||
|
rightSide: ''
|
||||||
|
}
|
||||||
|
condition.rules.splice(index + 1, 0, rule)
|
||||||
|
}
|
||||||
|
|
||||||
|
const addConditionGroup = (conditions) => {
|
||||||
|
const condition = {
|
||||||
|
and: true,
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
opCode: '==',
|
||||||
|
leftSide: '',
|
||||||
|
rightSide: ''
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
conditions.push(condition)
|
||||||
|
}
|
||||||
|
|
||||||
|
const validate = async () => {
|
||||||
|
if (!formRef) return false
|
||||||
|
return await formRef.value.validate()
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({ validate })
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.condition-group-tool {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
width: 500px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.condition-group {
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
border-color: #0089ff;
|
||||||
|
|
||||||
|
.condition-group-delete {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.condition-group-delete {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
display: flex;
|
||||||
|
cursor: pointer;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
::v-deep(.el-card__header) {
|
||||||
|
padding: 8px var(--el-card-padding);
|
||||||
|
border-bottom: 1px solid var(--el-card-border-color);
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
</style>
|
@ -0,0 +1,308 @@
|
|||||||
|
<!-- TODO @jason:有可能,它里面套 Condition 么? -->
|
||||||
|
<!-- TODO 怕影响其它节点功能,后面看看如何如何复用 Condtion -->
|
||||||
|
<template>
|
||||||
|
<Dialog v-model="dialogVisible" title="条件配置" width="600px" :fullscreen="false">
|
||||||
|
<div class="h-410px">
|
||||||
|
<el-scrollbar wrap-class="h-full">
|
||||||
|
<el-form ref="formRef" :model="condition" :rules="formRules" label-position="top">
|
||||||
|
<el-form-item label="配置方式" prop="conditionType">
|
||||||
|
<el-radio-group v-model="condition.conditionType" @change="changeConditionType">
|
||||||
|
<el-radio
|
||||||
|
v-for="(dict, indexConditionType) in conditionConfigTypes"
|
||||||
|
:key="indexConditionType"
|
||||||
|
:value="dict.value"
|
||||||
|
:label="dict.value"
|
||||||
|
>
|
||||||
|
{{ dict.label }}
|
||||||
|
</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
v-if="condition.conditionType === ConditionType.RULE && condition.conditionGroups"
|
||||||
|
label="条件规则"
|
||||||
|
>
|
||||||
|
<div class="condition-group-tool">
|
||||||
|
<div class="flex items-center">
|
||||||
|
<div class="mr-4">条件组关系</div>
|
||||||
|
<el-switch
|
||||||
|
v-model="condition.conditionGroups.and"
|
||||||
|
inline-prompt
|
||||||
|
active-text="且"
|
||||||
|
inactive-text="或"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<el-space direction="vertical" :spacer="condition.conditionGroups.and ? '且' : '或'">
|
||||||
|
<el-card
|
||||||
|
class="condition-group"
|
||||||
|
style="width: 530px"
|
||||||
|
v-for="(equation, cIdx) in condition.conditionGroups.conditions"
|
||||||
|
:key="cIdx"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="condition-group-delete"
|
||||||
|
v-if="condition.conditionGroups.conditions.length > 1"
|
||||||
|
>
|
||||||
|
<Icon
|
||||||
|
color="#0089ff"
|
||||||
|
icon="ep:circle-close-filled"
|
||||||
|
:size="18"
|
||||||
|
@click="deleteConditionGroup(condition.conditionGroups.conditions, cIdx)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<template #header>
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<div>条件组</div>
|
||||||
|
<div class="flex">
|
||||||
|
<div class="mr-4">规则关系</div>
|
||||||
|
<el-switch
|
||||||
|
v-model="equation.and"
|
||||||
|
inline-prompt
|
||||||
|
active-text="且"
|
||||||
|
inactive-text="或"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<div class="flex pt-2" v-for="(rule, rIdx) in equation.rules" :key="rIdx">
|
||||||
|
<div class="mr-2">
|
||||||
|
<el-form-item
|
||||||
|
:prop="`conditionGroups.conditions.${cIdx}.rules.${rIdx}.leftSide`"
|
||||||
|
:rules="{
|
||||||
|
required: true,
|
||||||
|
message: '左值不能为空',
|
||||||
|
trigger: 'change'
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<el-select style="width: 160px" v-model="rule.leftSide">
|
||||||
|
<el-option
|
||||||
|
v-for="(field, fIdx) in fieldOptions"
|
||||||
|
:key="fIdx"
|
||||||
|
:label="field.title"
|
||||||
|
:value="field.field"
|
||||||
|
:disabled="!field.required"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</div>
|
||||||
|
<div class="mr-2">
|
||||||
|
<el-select v-model="rule.opCode" style="width: 100px">
|
||||||
|
<el-option
|
||||||
|
v-for="operator in COMPARISON_OPERATORS"
|
||||||
|
:key="operator.value"
|
||||||
|
:label="operator.label"
|
||||||
|
:value="operator.value"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</div>
|
||||||
|
<div class="mr-2">
|
||||||
|
<el-form-item
|
||||||
|
:prop="`conditionGroups.conditions.${cIdx}.rules.${rIdx}.rightSide`"
|
||||||
|
:rules="{
|
||||||
|
required: true,
|
||||||
|
message: '右值不能为空',
|
||||||
|
trigger: 'blur'
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<el-input v-model="rule.rightSide" style="width: 160px" />
|
||||||
|
</el-form-item>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="cursor-pointer mr-1 flex items-center"
|
||||||
|
v-if="equation.rules.length > 1"
|
||||||
|
>
|
||||||
|
<Icon
|
||||||
|
icon="ep:delete"
|
||||||
|
:size="18"
|
||||||
|
@click="deleteConditionRule(equation, rIdx)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="cursor-pointer flex items-center">
|
||||||
|
<Icon icon="ep:plus" :size="18" @click="addConditionRule(equation, rIdx)" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
</el-space>
|
||||||
|
<div title="添加条件组" class="mt-4 cursor-pointer">
|
||||||
|
<Icon
|
||||||
|
color="#0089ff"
|
||||||
|
icon="ep:plus"
|
||||||
|
:size="24"
|
||||||
|
@click="addConditionGroup(condition.conditionGroups?.conditions)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
v-if="condition.conditionType === ConditionType.EXPRESSION"
|
||||||
|
label="条件表达式"
|
||||||
|
prop="conditionExpression"
|
||||||
|
>
|
||||||
|
<el-input
|
||||||
|
type="textarea"
|
||||||
|
v-model="condition.conditionExpression"
|
||||||
|
clearable
|
||||||
|
style="width: 100%"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</el-scrollbar>
|
||||||
|
</div>
|
||||||
|
<template #footer>
|
||||||
|
<el-button type="primary" @click="submitForm">确 定</el-button>
|
||||||
|
<el-button @click="dialogVisible = false">取 消</el-button>
|
||||||
|
</template>
|
||||||
|
</Dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import {
|
||||||
|
COMPARISON_OPERATORS,
|
||||||
|
CONDITION_CONFIG_TYPES,
|
||||||
|
ConditionType,
|
||||||
|
ConditionGroup,
|
||||||
|
DEFAULT_CONDITION_GROUP_VALUE
|
||||||
|
} from '../../consts'
|
||||||
|
import { BpmModelFormType } from '@/utils/constants'
|
||||||
|
import { useFormFieldsAndStartUser } from '../../node'
|
||||||
|
defineOptions({
|
||||||
|
name: 'ConditionDialog'
|
||||||
|
})
|
||||||
|
|
||||||
|
const condition = ref<{
|
||||||
|
conditionType: ConditionType
|
||||||
|
conditionExpression?: string
|
||||||
|
conditionGroups?: ConditionGroup
|
||||||
|
}>({
|
||||||
|
conditionType: ConditionType.RULE,
|
||||||
|
conditionGroups: DEFAULT_CONDITION_GROUP_VALUE
|
||||||
|
})
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
updateCondition: [condition: object]
|
||||||
|
}>()
|
||||||
|
const message = useMessage() // 消息弹窗
|
||||||
|
const dialogVisible = ref(false) // 弹窗的是否展示
|
||||||
|
|
||||||
|
const formType = inject<Ref<number>>('formType') // 表单类型
|
||||||
|
const conditionConfigTypes = computed(() => {
|
||||||
|
return CONDITION_CONFIG_TYPES.filter((item) => {
|
||||||
|
// 业务表单暂时去掉条件规则选项
|
||||||
|
if (formType?.value === BpmModelFormType.CUSTOM && item.value === ConditionType.RULE) {
|
||||||
|
return false
|
||||||
|
} else {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
/** 条件规则可选择的表单字段 */
|
||||||
|
const fieldOptions = useFormFieldsAndStartUser()
|
||||||
|
|
||||||
|
// 表单校验规则
|
||||||
|
const formRules = reactive({
|
||||||
|
conditionType: [{ required: true, message: '配置方式不能为空', trigger: 'blur' }],
|
||||||
|
conditionExpression: [{ required: true, message: '条件表达式不能为空', trigger: 'blur' }]
|
||||||
|
})
|
||||||
|
const formRef = ref() // 表单 Ref
|
||||||
|
|
||||||
|
/** 切换条件配置方式 */
|
||||||
|
const changeConditionType = () => {
|
||||||
|
if (condition.value.conditionType === ConditionType.RULE) {
|
||||||
|
if (!condition.value.conditionGroups) {
|
||||||
|
condition.value.conditionGroups = DEFAULT_CONDITION_GROUP_VALUE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const deleteConditionGroup = (conditions, index) => {
|
||||||
|
conditions.splice(index, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
const deleteConditionRule = (condition, index) => {
|
||||||
|
condition.rules.splice(index, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
const addConditionRule = (condition, index) => {
|
||||||
|
const rule = {
|
||||||
|
opCode: '==',
|
||||||
|
leftSide: '',
|
||||||
|
rightSide: ''
|
||||||
|
}
|
||||||
|
condition.rules.splice(index + 1, 0, rule)
|
||||||
|
}
|
||||||
|
|
||||||
|
const addConditionGroup = (conditions) => {
|
||||||
|
const condition = {
|
||||||
|
and: true,
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
opCode: '==',
|
||||||
|
leftSide: '',
|
||||||
|
rightSide: ''
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
conditions.push(condition)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 保存条件设置 */
|
||||||
|
const submitForm = async () => {
|
||||||
|
// 校验表单
|
||||||
|
if (!formRef) return
|
||||||
|
const valid = await formRef.value.validate()
|
||||||
|
if (!valid) {
|
||||||
|
message.warning('请完善条件规则')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
dialogVisible.value = false
|
||||||
|
// 设置完的条件传递给父组件
|
||||||
|
emit('updateCondition', condition.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
const open = (conditionObj: any | undefined) => {
|
||||||
|
if (conditionObj) {
|
||||||
|
condition.value.conditionType = conditionObj.conditionType
|
||||||
|
condition.value.conditionExpression = conditionObj.conditionExpression
|
||||||
|
condition.value.conditionGroups = conditionObj.conditionGroups
|
||||||
|
}
|
||||||
|
dialogVisible.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({ open })
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.condition-group-tool {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
width: 500px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.condition-group {
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
border-color: #0089ff;
|
||||||
|
|
||||||
|
.condition-group-delete {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.condition-group-delete {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
display: flex;
|
||||||
|
cursor: pointer;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
::v-deep(.el-card__header) {
|
||||||
|
padding: 8px var(--el-card-padding);
|
||||||
|
border-bottom: 1px solid var(--el-card-border-color);
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
</style>
|
@ -0,0 +1,188 @@
|
|||||||
|
<template>
|
||||||
|
<el-form-item label-position="top" label="请求头">
|
||||||
|
<div class="flex pt-2" v-for="(item, index) in props.header" :key="index">
|
||||||
|
<div class="mr-2">
|
||||||
|
<el-form-item
|
||||||
|
:prop="`${bind}.header.${index}.key`"
|
||||||
|
:rules="{
|
||||||
|
required: true,
|
||||||
|
message: '参数名不能为空',
|
||||||
|
trigger: 'blur'
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<el-input class="w-160px" v-model="item.key" />
|
||||||
|
</el-form-item>
|
||||||
|
</div>
|
||||||
|
<div class="mr-2">
|
||||||
|
<el-select class="w-100px!" v-model="item.type">
|
||||||
|
<el-option
|
||||||
|
v-for="types in BPM_HTTP_REQUEST_PARAM_TYPES"
|
||||||
|
:key="types.value"
|
||||||
|
:label="types.label"
|
||||||
|
:value="types.value"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</div>
|
||||||
|
<div class="mr-2">
|
||||||
|
<el-form-item
|
||||||
|
:prop="`${bind}.header.${index}.value`"
|
||||||
|
:rules="{
|
||||||
|
required: true,
|
||||||
|
message: '参数值不能为空',
|
||||||
|
trigger: 'blur'
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<el-input
|
||||||
|
v-if="item.type === BpmHttpRequestParamTypeEnum.FIXED_VALUE"
|
||||||
|
class="w-160px"
|
||||||
|
v-model="item.value"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
:prop="`${bind}.header.${index}.value`"
|
||||||
|
:rules="{
|
||||||
|
required: true,
|
||||||
|
message: '参数值不能为空',
|
||||||
|
trigger: 'change'
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<el-select
|
||||||
|
v-if="item.type === BpmHttpRequestParamTypeEnum.FROM_FORM"
|
||||||
|
class="w-160px!"
|
||||||
|
v-model="item.value"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="(field, fIdx) in formFieldOptions"
|
||||||
|
:key="fIdx"
|
||||||
|
:label="field.title"
|
||||||
|
:value="field.field"
|
||||||
|
:disabled="!field.required"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</div>
|
||||||
|
<div class="mr-1 flex items-center">
|
||||||
|
<Icon icon="ep:delete" :size="18" @click="deleteHttpRequestParam(props.header, index)" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<el-button type="primary" text @click="addHttpRequestParam(props.header)">
|
||||||
|
<Icon icon="ep:plus" class="mr-5px" />添加一行
|
||||||
|
</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label-position="top" label="请求体">
|
||||||
|
<div class="flex pt-2" v-for="(item, index) in props.body" :key="index">
|
||||||
|
<div class="mr-2">
|
||||||
|
<el-form-item
|
||||||
|
:prop="`${bind}.body.${index}.key`"
|
||||||
|
:rules="{
|
||||||
|
required: true,
|
||||||
|
message: '参数名不能为空',
|
||||||
|
trigger: 'blur'
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<el-input class="w-160px" v-model="item.key" />
|
||||||
|
</el-form-item>
|
||||||
|
</div>
|
||||||
|
<div class="mr-2">
|
||||||
|
<el-select class="w-100px!" v-model="item.type">
|
||||||
|
<el-option
|
||||||
|
v-for="types in BPM_HTTP_REQUEST_PARAM_TYPES"
|
||||||
|
:key="types.value"
|
||||||
|
:label="types.label"
|
||||||
|
:value="types.value"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</div>
|
||||||
|
<div class="mr-2">
|
||||||
|
<el-form-item
|
||||||
|
:prop="`${bind}.body.${index}.value`"
|
||||||
|
:rules="{
|
||||||
|
required: true,
|
||||||
|
message: '参数值不能为空',
|
||||||
|
trigger: 'blur'
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<el-input
|
||||||
|
v-if="item.type === BpmHttpRequestParamTypeEnum.FIXED_VALUE"
|
||||||
|
class="w-160px"
|
||||||
|
v-model="item.value"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
:prop="`${bind}.body.${index}.value`"
|
||||||
|
:rules="{
|
||||||
|
required: true,
|
||||||
|
message: '参数值不能为空',
|
||||||
|
trigger: 'change'
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<el-select
|
||||||
|
v-if="item.type === BpmHttpRequestParamTypeEnum.FROM_FORM"
|
||||||
|
class="w-160px!"
|
||||||
|
v-model="item.value"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="(field, fIdx) in formFieldOptions"
|
||||||
|
:key="fIdx"
|
||||||
|
:label="field.title"
|
||||||
|
:value="field.field"
|
||||||
|
:disabled="!field.required"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</div>
|
||||||
|
<div class="mr-1 flex items-center">
|
||||||
|
<Icon icon="ep:delete" :size="18" @click="deleteHttpRequestParam(props.body, index)" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<el-button type="primary" text @click="addHttpRequestParam(props.body)">
|
||||||
|
<Icon icon="ep:plus" class="mr-5px" />添加一行
|
||||||
|
</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</template>
|
||||||
|
<script setup lang="ts">
|
||||||
|
import {
|
||||||
|
HttpRequestParam,
|
||||||
|
BPM_HTTP_REQUEST_PARAM_TYPES,
|
||||||
|
BpmHttpRequestParamTypeEnum
|
||||||
|
} from '../../consts'
|
||||||
|
import { useFormFieldsAndStartUser } from '../../node'
|
||||||
|
defineOptions({
|
||||||
|
name: 'HttpRequestParamSetting'
|
||||||
|
})
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
header: {
|
||||||
|
type: Array as () => HttpRequestParam[],
|
||||||
|
required: false,
|
||||||
|
default: () => []
|
||||||
|
},
|
||||||
|
body: {
|
||||||
|
type: Array as () => HttpRequestParam[],
|
||||||
|
required: false,
|
||||||
|
default: () => []
|
||||||
|
},
|
||||||
|
bind: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 流程表单字段,发起人字段
|
||||||
|
const formFieldOptions = useFormFieldsAndStartUser()
|
||||||
|
/** 添加请求配置项 */
|
||||||
|
const addHttpRequestParam = (arr: HttpRequestParam[]) => {
|
||||||
|
arr.push({
|
||||||
|
key: '',
|
||||||
|
type: BpmHttpRequestParamTypeEnum.FIXED_VALUE,
|
||||||
|
value: ''
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 删除请求配置项 */
|
||||||
|
const deleteHttpRequestParam = (arr: HttpRequestParam[], index: number) => {
|
||||||
|
arr.splice(index, 1)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped></style>
|
@ -0,0 +1,127 @@
|
|||||||
|
<template>
|
||||||
|
<el-form-item>
|
||||||
|
<el-alert
|
||||||
|
title="仅支持 POST 请求,以请求体方式接收参数"
|
||||||
|
type="warning"
|
||||||
|
show-icon
|
||||||
|
:closable="false"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<!-- 请求地址-->
|
||||||
|
<el-form-item
|
||||||
|
label-position="top"
|
||||||
|
label="请求地址"
|
||||||
|
:prop="`${formItemPrefix}.url`"
|
||||||
|
:rules="{
|
||||||
|
required: true,
|
||||||
|
message: '请求地址不能为空',
|
||||||
|
trigger: 'blur'
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<el-input v-model="setting.url" />
|
||||||
|
</el-form-item>
|
||||||
|
<!-- 请求头,请求体设置-->
|
||||||
|
<HttpRequestParamSetting :header="setting.header" :body="setting.body" :bind="formItemPrefix" />
|
||||||
|
<!-- 返回值设置-->
|
||||||
|
<div v-if="responseEnable">
|
||||||
|
<el-form-item label="返回值" label-position="top">
|
||||||
|
<el-alert
|
||||||
|
title="通过请求返回值, 可以修改流程表单的值"
|
||||||
|
type="warning"
|
||||||
|
show-icon
|
||||||
|
:closable="false"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<div class="flex pt-2" v-for="(item, index) in setting.response" :key="index">
|
||||||
|
<div class="mr-2">
|
||||||
|
<el-form-item
|
||||||
|
:prop="`${formItemPrefix}.response.${index}.key`"
|
||||||
|
:rules="{
|
||||||
|
required: true,
|
||||||
|
message: '表单字段不能为空',
|
||||||
|
trigger: 'blur'
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<el-select class="w-160px!" v-model="item.key" placeholder="请选择表单字段">
|
||||||
|
<el-option
|
||||||
|
v-for="(field, fIdx) in formFields"
|
||||||
|
:key="fIdx"
|
||||||
|
:label="field.title"
|
||||||
|
:value="field.field"
|
||||||
|
:disabled="!field.required"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</div>
|
||||||
|
<div class="mr-2">
|
||||||
|
<el-form-item
|
||||||
|
:prop="`${formItemPrefix}.response.${index}.value`"
|
||||||
|
:rules="{
|
||||||
|
required: true,
|
||||||
|
message: '请求返回字段不能为空',
|
||||||
|
trigger: 'blur'
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<el-input class="w-160px" v-model="item.value" placeholder="请求返回字段" />
|
||||||
|
</el-form-item>
|
||||||
|
</div>
|
||||||
|
<div class="mr-1 pt-1 cursor-pointer">
|
||||||
|
<Icon
|
||||||
|
icon="ep:delete"
|
||||||
|
:size="18"
|
||||||
|
@click="deleteHttpResponseSetting(setting.response!, index)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<el-button type="primary" text @click="addHttpResponseSetting(setting.response!)">
|
||||||
|
<Icon icon="ep:plus" class="mr-5px" />添加一行
|
||||||
|
</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script setup lang="ts">
|
||||||
|
import HttpRequestParamSetting from './HttpRequestParamSetting.vue'
|
||||||
|
import { useFormFields } from '../../node'
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
setting: {
|
||||||
|
type: Object,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
responseEnable: {
|
||||||
|
type: Boolean,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
formItemPrefix: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const { setting } = toRefs(props)
|
||||||
|
const emits = defineEmits(['update:setting'])
|
||||||
|
watch(
|
||||||
|
() => setting,
|
||||||
|
(val) => {
|
||||||
|
emits('update:setting', val)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
/** 流程表单字段 */
|
||||||
|
const formFields = useFormFields()
|
||||||
|
|
||||||
|
/** 添加 HTTP 请求返回值设置项 */
|
||||||
|
const addHttpResponseSetting = (responseSetting: Record<string, string>[]) => {
|
||||||
|
responseSetting.push({
|
||||||
|
key: '',
|
||||||
|
value: ''
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 删除 HTTP 请求返回值设置项 */
|
||||||
|
const deleteHttpResponseSetting = (responseSetting: Record<string, string>[], index: number) => {
|
||||||
|
responseSetting.splice(index, 1)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped></style>
|
@ -0,0 +1,88 @@
|
|||||||
|
<template>
|
||||||
|
<el-form ref="listenerFormRef" :model="configForm" label-position="top">
|
||||||
|
<div v-for="(listener, listenerIdx) in taskListener" :key="listenerIdx">
|
||||||
|
<el-divider content-position="left">
|
||||||
|
<el-text tag="b" size="large">{{ listener.name }}</el-text>
|
||||||
|
</el-divider>
|
||||||
|
<el-form-item>
|
||||||
|
<el-switch
|
||||||
|
v-model="configForm[`task${listener.type}ListenerEnable`]"
|
||||||
|
active-text="开启"
|
||||||
|
inactive-text="关闭"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<div v-if="configForm[`task${listener.type}ListenerEnable`]">
|
||||||
|
<el-form-item>
|
||||||
|
<el-alert
|
||||||
|
title="仅支持 POST 请求,以请求体方式接收参数"
|
||||||
|
type="warning"
|
||||||
|
show-icon
|
||||||
|
:closable="false"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
label="请求地址"
|
||||||
|
:prop="`task${listener.type}ListenerPath`"
|
||||||
|
:rules="{
|
||||||
|
required: true,
|
||||||
|
message: '请求地址不能为空',
|
||||||
|
trigger: 'blur'
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<el-input v-model="configForm[`task${listener.type}ListenerPath`]" />
|
||||||
|
</el-form-item>
|
||||||
|
<HttpRequestParamSetting
|
||||||
|
:header="configForm[`task${listener.type}Listener`].header"
|
||||||
|
:body="configForm[`task${listener.type}Listener`].body"
|
||||||
|
:bind="`task${listener.type}Listener`"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-form>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import HttpRequestParamSetting from './HttpRequestParamSetting.vue'
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
modelValue: {
|
||||||
|
type: Object,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
formFieldOptions: {
|
||||||
|
type: Object,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const emit = defineEmits(['update:modelValue'])
|
||||||
|
const listenerFormRef = ref()
|
||||||
|
const configForm = computed({
|
||||||
|
get() {
|
||||||
|
return props.modelValue
|
||||||
|
},
|
||||||
|
set(newValue) {
|
||||||
|
emit('update:modelValue', newValue)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const taskListener = ref([
|
||||||
|
{
|
||||||
|
name: '创建任务',
|
||||||
|
type: 'Create'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '指派任务执行人员',
|
||||||
|
type: 'Assign'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '完成任务',
|
||||||
|
type: 'Complete'
|
||||||
|
}
|
||||||
|
])
|
||||||
|
|
||||||
|
const validate = async () => {
|
||||||
|
if (!listenerFormRef) return false
|
||||||
|
return await listenerFormRef.value.validate()
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({ validate })
|
||||||
|
</script>
|
@ -0,0 +1,106 @@
|
|||||||
|
<template>
|
||||||
|
<div class="node-wrapper">
|
||||||
|
<div class="node-container">
|
||||||
|
<div
|
||||||
|
class="node-box"
|
||||||
|
:class="[
|
||||||
|
{ 'node-config-error': !currentNode.showText },
|
||||||
|
`${useTaskStatusClass(currentNode?.activityStatus)}`
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<div class="node-title-container">
|
||||||
|
<div
|
||||||
|
:class="`node-title-icon ${currentNode.childProcessSetting?.async === true ? 'async-child-process' : 'child-process'}`"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
:class="`iconfont ${currentNode.childProcessSetting?.async === true ? 'icon-async-child-process' : 'icon-child-process'}`"
|
||||||
|
>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<input
|
||||||
|
v-if="!readonly && showInput"
|
||||||
|
type="text"
|
||||||
|
class="editable-title-input"
|
||||||
|
@blur="blurEvent()"
|
||||||
|
v-mountedFocus
|
||||||
|
v-model="currentNode.name"
|
||||||
|
:placeholder="currentNode.name"
|
||||||
|
/>
|
||||||
|
<div v-else class="node-title" @click="clickTitle">
|
||||||
|
{{ currentNode.name }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="node-content" @click="openNodeConfig">
|
||||||
|
<div class="node-text" :title="currentNode.showText" v-if="currentNode.showText">
|
||||||
|
{{ currentNode.showText }}
|
||||||
|
</div>
|
||||||
|
<div class="node-text" v-else>
|
||||||
|
{{ NODE_DEFAULT_TEXT.get(NodeType.CHILD_PROCESS_NODE) }}
|
||||||
|
</div>
|
||||||
|
<Icon v-if="!readonly" icon="ep:arrow-right-bold" />
|
||||||
|
</div>
|
||||||
|
<div v-if="!readonly" class="node-toolbar">
|
||||||
|
<div class="toolbar-icon"
|
||||||
|
><Icon color="#0089ff" icon="ep:circle-close-filled" :size="18" @click="deleteNode"
|
||||||
|
/></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 传递子节点给添加节点组件。会在子节点前面添加节点 -->
|
||||||
|
<NodeHandler
|
||||||
|
v-if="currentNode"
|
||||||
|
v-model:child-node="currentNode.childNode"
|
||||||
|
:current-node="currentNode"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<ChildProcessNodeConfig
|
||||||
|
v-if="!readonly && currentNode"
|
||||||
|
ref="nodeSetting"
|
||||||
|
:flow-node="currentNode"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { SimpleFlowNode, NodeType, NODE_DEFAULT_TEXT } from '../consts'
|
||||||
|
import NodeHandler from '../NodeHandler.vue'
|
||||||
|
import { useNodeName2, useWatchNode, useTaskStatusClass } from '../node'
|
||||||
|
import ChildProcessNodeConfig from '../nodes-config/ChildProcessNodeConfig.vue'
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'ChildProcessNode'
|
||||||
|
})
|
||||||
|
const props = defineProps({
|
||||||
|
flowNode: {
|
||||||
|
type: Object as () => SimpleFlowNode,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
// 定义事件,更新父组件。
|
||||||
|
const emits = defineEmits<{
|
||||||
|
'update:flowNode': [node: SimpleFlowNode | undefined]
|
||||||
|
}>()
|
||||||
|
// 是否只读
|
||||||
|
const readonly = inject<Boolean>('readonly')
|
||||||
|
// 监控节点的变化
|
||||||
|
const currentNode = useWatchNode(props)
|
||||||
|
// 节点名称编辑
|
||||||
|
const { showInput, blurEvent, clickTitle } = useNodeName2(currentNode, NodeType.CHILD_PROCESS_NODE)
|
||||||
|
const nodeSetting = ref()
|
||||||
|
|
||||||
|
// 打开节点配置
|
||||||
|
const openNodeConfig = () => {
|
||||||
|
if (readonly) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
nodeSetting.value.showChildProcessNodeConfig(currentNode.value)
|
||||||
|
nodeSetting.value.openDrawer()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除节点。更新当前节点为孩子节点
|
||||||
|
const deleteNode = () => {
|
||||||
|
emits('update:flowNode', currentNode.value.childNode)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped></style>
|
@ -9,8 +9,7 @@
|
|||||||
]"
|
]"
|
||||||
>
|
>
|
||||||
<div class="node-title-container">
|
<div class="node-title-container">
|
||||||
<!-- TODO @芋艿 需要更换图标 -->
|
<div class="node-title-icon delay-node"><span class="iconfont icon-delay"></span></div>
|
||||||
<div class="node-title-icon copy-task"><span class="iconfont icon-copy"></span></div>
|
|
||||||
<input
|
<input
|
||||||
v-if="!readonly && showInput"
|
v-if="!readonly && showInput"
|
||||||
type="text"
|
type="text"
|
||||||
|
@ -77,7 +77,7 @@ const props = defineProps({
|
|||||||
const currentNode = useWatchNode(props)
|
const currentNode = useWatchNode(props)
|
||||||
// 是否只读
|
// 是否只读
|
||||||
const readonly = inject<Boolean>('readonly')
|
const readonly = inject<Boolean>('readonly')
|
||||||
const processInstance = inject<Ref<any>>('processInstance')
|
const processInstance = inject<Ref<any>>('processInstance', ref({}))
|
||||||
// 审批信息的弹窗显示,用于只读模式
|
// 审批信息的弹窗显示,用于只读模式
|
||||||
const dialogVisible = ref(false) // 弹窗可见性
|
const dialogVisible = ref(false) // 弹窗可见性
|
||||||
const processInstanceInfos = ref<any[]>([]) // 流程的审批信息
|
const processInstanceInfos = ref<any[]>([]) // 流程的审批信息
|
||||||
|
@ -108,7 +108,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import NodeHandler from '../NodeHandler.vue'
|
import NodeHandler from '../NodeHandler.vue'
|
||||||
import ProcessNodeTree from '../ProcessNodeTree.vue'
|
import ProcessNodeTree from '../ProcessNodeTree.vue'
|
||||||
import { SimpleFlowNode, NodeType, NODE_DEFAULT_TEXT } from '../consts'
|
import { SimpleFlowNode, NodeType, ConditionType, DEFAULT_CONDITION_GROUP_VALUE, NODE_DEFAULT_TEXT } from '../consts'
|
||||||
import { getDefaultConditionNodeName } from '../utils'
|
import { getDefaultConditionNodeName } from '../utils'
|
||||||
import { useTaskStatusClass } from '../node'
|
import { useTaskStatusClass } from '../node'
|
||||||
import { generateUUID } from '@/utils'
|
import { generateUUID } from '@/utils'
|
||||||
@ -149,7 +149,7 @@ const blurEvent = (index: number) => {
|
|||||||
showInputs.value[index] = false
|
showInputs.value[index] = false
|
||||||
const conditionNode = currentNode.value.conditionNodes?.at(index) as SimpleFlowNode
|
const conditionNode = currentNode.value.conditionNodes?.at(index) as SimpleFlowNode
|
||||||
conditionNode.name =
|
conditionNode.name =
|
||||||
conditionNode.name || getDefaultConditionNodeName(index, conditionNode.defaultFlow)
|
conditionNode.name || getDefaultConditionNodeName(index, conditionNode.conditionSetting?.defaultFlow)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 点击条件名称
|
// 点击条件名称
|
||||||
@ -178,8 +178,11 @@ const addCondition = () => {
|
|||||||
type: NodeType.CONDITION_NODE,
|
type: NodeType.CONDITION_NODE,
|
||||||
childNode: undefined,
|
childNode: undefined,
|
||||||
conditionNodes: [],
|
conditionNodes: [],
|
||||||
conditionType: 1,
|
conditionSetting: {
|
||||||
defaultFlow: false
|
defaultFlow: false,
|
||||||
|
conditionType: ConditionType.RULE,
|
||||||
|
conditionGroups: DEFAULT_CONDITION_GROUP_VALUE
|
||||||
|
}
|
||||||
}
|
}
|
||||||
conditionNodes.splice(lastIndex, 0, conditionData)
|
conditionNodes.splice(lastIndex, 0, conditionData)
|
||||||
}
|
}
|
||||||
|
@ -34,7 +34,7 @@
|
|||||||
]"
|
]"
|
||||||
>
|
>
|
||||||
<div class="branch-node-title-container">
|
<div class="branch-node-title-container">
|
||||||
<div v-if="showInputs[index]">
|
<div v-if="!readonly && showInputs[index]">
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
class="editable-title-input"
|
class="editable-title-input"
|
||||||
@ -110,7 +110,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import NodeHandler from '../NodeHandler.vue'
|
import NodeHandler from '../NodeHandler.vue'
|
||||||
import ProcessNodeTree from '../ProcessNodeTree.vue'
|
import ProcessNodeTree from '../ProcessNodeTree.vue'
|
||||||
import { SimpleFlowNode, NodeType, NODE_DEFAULT_TEXT } from '../consts'
|
import { SimpleFlowNode, NodeType, ConditionType, DEFAULT_CONDITION_GROUP_VALUE, NODE_DEFAULT_TEXT } from '../consts'
|
||||||
import { useTaskStatusClass } from '../node'
|
import { useTaskStatusClass } from '../node'
|
||||||
import { getDefaultInclusiveConditionNodeName } from '../utils'
|
import { getDefaultInclusiveConditionNodeName } from '../utils'
|
||||||
import { generateUUID } from '@/utils'
|
import { generateUUID } from '@/utils'
|
||||||
@ -153,7 +153,7 @@ const blurEvent = (index: number) => {
|
|||||||
showInputs.value[index] = false
|
showInputs.value[index] = false
|
||||||
const conditionNode = currentNode.value.conditionNodes?.at(index) as SimpleFlowNode
|
const conditionNode = currentNode.value.conditionNodes?.at(index) as SimpleFlowNode
|
||||||
conditionNode.name =
|
conditionNode.name =
|
||||||
conditionNode.name || getDefaultInclusiveConditionNodeName(index, conditionNode.defaultFlow)
|
conditionNode.name || getDefaultInclusiveConditionNodeName(index, conditionNode.conditionSetting?.defaultFlow)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 点击条件名称
|
// 点击条件名称
|
||||||
@ -182,8 +182,11 @@ const addCondition = () => {
|
|||||||
type: NodeType.CONDITION_NODE,
|
type: NodeType.CONDITION_NODE,
|
||||||
childNode: undefined,
|
childNode: undefined,
|
||||||
conditionNodes: [],
|
conditionNodes: [],
|
||||||
conditionType: 1,
|
conditionSetting: {
|
||||||
defaultFlow: false
|
defaultFlow: false,
|
||||||
|
conditionType: ConditionType.RULE,
|
||||||
|
conditionGroups: DEFAULT_CONDITION_GROUP_VALUE
|
||||||
|
}
|
||||||
}
|
}
|
||||||
conditionNodes.splice(lastIndex, 0, conditionData)
|
conditionNodes.splice(lastIndex, 0, conditionData)
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,97 @@
|
|||||||
|
<template>
|
||||||
|
<div class="node-wrapper">
|
||||||
|
<div class="node-container">
|
||||||
|
<div
|
||||||
|
class="node-box"
|
||||||
|
:class="[
|
||||||
|
{ 'node-config-error': !currentNode.showText },
|
||||||
|
`${useTaskStatusClass(currentNode?.activityStatus)}`
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<div class="node-title-container">
|
||||||
|
<div class="node-title-icon router-node">
|
||||||
|
<span class="iconfont icon-router"></span>
|
||||||
|
</div>
|
||||||
|
<input
|
||||||
|
v-if="!readonly && showInput"
|
||||||
|
type="text"
|
||||||
|
class="editable-title-input"
|
||||||
|
@blur="blurEvent()"
|
||||||
|
v-mountedFocus
|
||||||
|
v-model="currentNode.name"
|
||||||
|
:placeholder="currentNode.name"
|
||||||
|
/>
|
||||||
|
<div v-else class="node-title" @click="clickTitle">
|
||||||
|
{{ currentNode.name }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="node-content" @click="openNodeConfig">
|
||||||
|
<div class="node-text" :title="currentNode.showText" v-if="currentNode.showText">
|
||||||
|
{{ currentNode.showText }}
|
||||||
|
</div>
|
||||||
|
<div class="node-text" v-else>
|
||||||
|
{{ NODE_DEFAULT_TEXT.get(NodeType.ROUTER_BRANCH_NODE) }}
|
||||||
|
</div>
|
||||||
|
<Icon v-if="!readonly" icon="ep:arrow-right-bold" />
|
||||||
|
</div>
|
||||||
|
<div v-if="!readonly" class="node-toolbar">
|
||||||
|
<div class="toolbar-icon"
|
||||||
|
><Icon color="#0089ff" icon="ep:circle-close-filled" :size="18" @click="deleteNode"
|
||||||
|
/></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 传递子节点给添加节点组件。会在子节点前面添加节点 -->
|
||||||
|
<NodeHandler
|
||||||
|
v-if="currentNode"
|
||||||
|
v-model:child-node="currentNode.childNode"
|
||||||
|
:current-node="currentNode"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<RouterNodeConfig v-if="!readonly && currentNode" ref="nodeSetting" :flow-node="currentNode" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { SimpleFlowNode, NodeType, NODE_DEFAULT_TEXT } from '../consts'
|
||||||
|
import NodeHandler from '../NodeHandler.vue'
|
||||||
|
import { useNodeName2, useWatchNode, useTaskStatusClass } from '../node'
|
||||||
|
import RouterNodeConfig from '../nodes-config/RouterNodeConfig.vue'
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'RouterNode'
|
||||||
|
})
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
flowNode: {
|
||||||
|
type: Object as () => SimpleFlowNode,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
// 定义事件,更新父组件
|
||||||
|
const emits = defineEmits<{
|
||||||
|
'update:flowNode': [node: SimpleFlowNode | undefined]
|
||||||
|
}>()
|
||||||
|
// 是否只读
|
||||||
|
const readonly = inject<Boolean>('readonly')
|
||||||
|
// 监控节点的变化
|
||||||
|
const currentNode = useWatchNode(props)
|
||||||
|
// 节点名称编辑
|
||||||
|
const { showInput, blurEvent, clickTitle } = useNodeName2(currentNode, NodeType.ROUTER_BRANCH_NODE)
|
||||||
|
|
||||||
|
const nodeSetting = ref()
|
||||||
|
// 打开节点配置
|
||||||
|
const openNodeConfig = () => {
|
||||||
|
if (readonly) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
nodeSetting.value.showRouteNodeConfig(currentNode.value)
|
||||||
|
nodeSetting.value.openDrawer()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除节点。更新当前节点为孩子节点
|
||||||
|
const deleteNode = () => {
|
||||||
|
emits('update:flowNode', currentNode.value.childNode)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped></style>
|
@ -13,7 +13,7 @@
|
|||||||
><span class="iconfont icon-start-user"></span
|
><span class="iconfont icon-start-user"></span
|
||||||
></div>
|
></div>
|
||||||
<input
|
<input
|
||||||
v-if="showInput"
|
v-if="!readonly && showInput"
|
||||||
type="text"
|
type="text"
|
||||||
class="editable-title-input"
|
class="editable-title-input"
|
||||||
@blur="blurEvent()"
|
@blur="blurEvent()"
|
||||||
@ -117,7 +117,7 @@ const props = defineProps({
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
const readonly = inject<Boolean>('readonly') // 是否只读
|
const readonly = inject<Boolean>('readonly') // 是否只读
|
||||||
const tasks = inject<Ref<any[]>>('tasks')
|
const tasks = inject<Ref<any[]>>('tasks', ref([]))
|
||||||
// 定义事件,更新父组件。
|
// 定义事件,更新父组件。
|
||||||
const emits = defineEmits<{
|
const emits = defineEmits<{
|
||||||
'update:modelValue': [node: SimpleFlowNode | undefined]
|
'update:modelValue': [node: SimpleFlowNode | undefined]
|
||||||
|
@ -0,0 +1,97 @@
|
|||||||
|
<template>
|
||||||
|
<div class="node-wrapper">
|
||||||
|
<div class="node-container">
|
||||||
|
<div
|
||||||
|
class="node-box"
|
||||||
|
:class="[
|
||||||
|
{ 'node-config-error': !currentNode.showText },
|
||||||
|
`${useTaskStatusClass(currentNode?.activityStatus)}`
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<div class="node-title-container">
|
||||||
|
<div class="node-title-icon trigger-node">
|
||||||
|
<span class="iconfont icon-trigger"></span>
|
||||||
|
</div>
|
||||||
|
<input
|
||||||
|
v-if="!readonly && showInput"
|
||||||
|
type="text"
|
||||||
|
class="editable-title-input"
|
||||||
|
@blur="blurEvent()"
|
||||||
|
v-mountedFocus
|
||||||
|
v-model="currentNode.name"
|
||||||
|
:placeholder="currentNode.name"
|
||||||
|
/>
|
||||||
|
<div v-else class="node-title" @click="clickTitle">
|
||||||
|
{{ currentNode.name }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="node-content" @click="openNodeConfig">
|
||||||
|
<div class="node-text" :title="currentNode.showText" v-if="currentNode.showText">
|
||||||
|
{{ currentNode.showText }}
|
||||||
|
</div>
|
||||||
|
<div class="node-text" v-else>
|
||||||
|
{{ NODE_DEFAULT_TEXT.get(NodeType.TRIGGER_NODE) }}
|
||||||
|
</div>
|
||||||
|
<Icon v-if="!readonly" icon="ep:arrow-right-bold" />
|
||||||
|
</div>
|
||||||
|
<div v-if="!readonly" class="node-toolbar">
|
||||||
|
<div class="toolbar-icon"
|
||||||
|
><Icon color="#0089ff" icon="ep:circle-close-filled" :size="18" @click="deleteNode"
|
||||||
|
/></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 传递子节点给添加节点组件。会在子节点前面添加节点 -->
|
||||||
|
<NodeHandler
|
||||||
|
v-if="currentNode"
|
||||||
|
v-model:child-node="currentNode.childNode"
|
||||||
|
:current-node="currentNode"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<TriggerNodeConfig v-if="!readonly && currentNode" ref="nodeSetting" :flow-node="currentNode" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { SimpleFlowNode, NodeType, NODE_DEFAULT_TEXT } from '../consts'
|
||||||
|
import NodeHandler from '../NodeHandler.vue'
|
||||||
|
import { useNodeName2, useWatchNode, useTaskStatusClass } from '../node'
|
||||||
|
import TriggerNodeConfig from '../nodes-config/TriggerNodeConfig.vue'
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'TriggerNode'
|
||||||
|
})
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
flowNode: {
|
||||||
|
type: Object as () => SimpleFlowNode,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
// 定义事件,更新父组件
|
||||||
|
const emits = defineEmits<{
|
||||||
|
'update:flowNode': [node: SimpleFlowNode | undefined]
|
||||||
|
}>()
|
||||||
|
// 是否只读
|
||||||
|
const readonly = inject<Boolean>('readonly')
|
||||||
|
// 监控节点的变化
|
||||||
|
const currentNode = useWatchNode(props)
|
||||||
|
// 节点名称编辑
|
||||||
|
const { showInput, blurEvent, clickTitle } = useNodeName2(currentNode, NodeType.TRIGGER_NODE)
|
||||||
|
|
||||||
|
const nodeSetting = ref()
|
||||||
|
// 打开节点配置
|
||||||
|
const openNodeConfig = () => {
|
||||||
|
if (readonly) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
nodeSetting.value.showTriggerNodeConfig(currentNode.value)
|
||||||
|
nodeSetting.value.openDrawer()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除节点。更新当前节点为孩子节点
|
||||||
|
const deleteNode = () => {
|
||||||
|
emits('update:flowNode', currentNode.value.childNode)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped></style>
|
@ -9,7 +9,14 @@
|
|||||||
]"
|
]"
|
||||||
>
|
>
|
||||||
<div class="node-title-container">
|
<div class="node-title-container">
|
||||||
<div class="node-title-icon user-task"><span class="iconfont icon-approve"></span></div>
|
<div
|
||||||
|
:class="`node-title-icon ${currentNode.type === NodeType.TRANSACTOR_NODE ? 'transactor-task' : 'user-task'}`"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
:class="`iconfont ${currentNode.type === NodeType.TRANSACTOR_NODE ? 'icon-transactor' : 'icon-approve'}`"
|
||||||
|
>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
<input
|
<input
|
||||||
v-if="!readonly && showInput"
|
v-if="!readonly && showInput"
|
||||||
type="text"
|
type="text"
|
||||||
@ -28,7 +35,7 @@
|
|||||||
{{ currentNode.showText }}
|
{{ currentNode.showText }}
|
||||||
</div>
|
</div>
|
||||||
<div class="node-text" v-else>
|
<div class="node-text" v-else>
|
||||||
{{ NODE_DEFAULT_TEXT.get(NodeType.USER_TASK_NODE) }}
|
{{ NODE_DEFAULT_TEXT.get(currentNode.type) }}
|
||||||
</div>
|
</div>
|
||||||
<Icon icon="ep:arrow-right-bold" v-if="!readonly" />
|
<Icon icon="ep:arrow-right-bold" v-if="!readonly" />
|
||||||
</div>
|
</div>
|
||||||
@ -131,7 +138,7 @@ const emits = defineEmits<{
|
|||||||
|
|
||||||
// 是否只读
|
// 是否只读
|
||||||
const readonly = inject<Boolean>('readonly')
|
const readonly = inject<Boolean>('readonly')
|
||||||
const tasks = inject<Ref<any[]>>('tasks')
|
const tasks = inject<Ref<any[]>>('tasks', ref([]))
|
||||||
// 监控节点变化
|
// 监控节点变化
|
||||||
const currentNode = useWatchNode(props)
|
const currentNode = useWatchNode(props)
|
||||||
// 节点名称编辑
|
// 节点名称编辑
|
||||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -113,18 +113,21 @@
|
|||||||
|
|
||||||
// 节点连线气泡卡片样式
|
// 节点连线气泡卡片样式
|
||||||
.handler-item-wrapper {
|
.handler-item-wrapper {
|
||||||
|
width: 320px;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
.handler-item {
|
.handler-item {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
margin-top: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.handler-item-icon {
|
.handler-item-icon {
|
||||||
width: 60px;
|
width: 50px;
|
||||||
height: 60px;
|
height: 50px;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
border: 1px solid #e2e2e2;
|
border: 1px solid #e2e2e2;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
@ -138,13 +141,14 @@
|
|||||||
|
|
||||||
.icon-size {
|
.icon-size {
|
||||||
font-size: 25px;
|
font-size: 25px;
|
||||||
line-height: 60px;
|
line-height: 50px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.approve {
|
.approve {
|
||||||
color: #ff943e;
|
color: #ff943e;
|
||||||
}
|
}
|
||||||
|
|
||||||
.copy {
|
.copy {
|
||||||
color: #3296fa;
|
color: #3296fa;
|
||||||
}
|
}
|
||||||
@ -161,6 +165,30 @@
|
|||||||
color: #345da2;
|
color: #345da2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.delay {
|
||||||
|
color: #e47470;
|
||||||
|
}
|
||||||
|
|
||||||
|
.trigger {
|
||||||
|
color: #3373d2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.router {
|
||||||
|
color: #ca3a31
|
||||||
|
}
|
||||||
|
|
||||||
|
.transactor {
|
||||||
|
color: #330099;
|
||||||
|
}
|
||||||
|
|
||||||
|
.child-process {
|
||||||
|
color: #996633;
|
||||||
|
}
|
||||||
|
|
||||||
|
.async-child-process {
|
||||||
|
color: #006666;
|
||||||
|
}
|
||||||
|
|
||||||
.handler-item-text {
|
.handler-item-text {
|
||||||
margin-top: 4px;
|
margin-top: 4px;
|
||||||
width: 80px;
|
width: 80px;
|
||||||
@ -266,6 +294,30 @@
|
|||||||
&.start-user {
|
&.start-user {
|
||||||
color: #676565;
|
color: #676565;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.delay-node {
|
||||||
|
color: #e47470;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.trigger-node {
|
||||||
|
color: #3373d2;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.router-node {
|
||||||
|
color: #ca3a31
|
||||||
|
}
|
||||||
|
|
||||||
|
&.transactor-task {
|
||||||
|
color: #330099;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.child-process {
|
||||||
|
color: #996633;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.async-child-process {
|
||||||
|
color: #006666;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.node-title {
|
.node-title {
|
||||||
@ -711,45 +763,64 @@
|
|||||||
|
|
||||||
// iconfont 样式
|
// iconfont 样式
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'iconfont'; /* Project id 4495938 */
|
font-family: "iconfont"; /* Project id 4495938 */
|
||||||
src:
|
src: url('iconfont.woff2?t=1737639517142') format('woff2'),
|
||||||
url('iconfont.woff2?t=1724339470412') format('woff2'),
|
url('iconfont.woff?t=1737639517142') format('woff'),
|
||||||
url('iconfont.woff?t=1724339470412') format('woff'),
|
url('iconfont.ttf?t=1737639517142') format('truetype');
|
||||||
url('iconfont.ttf?t=1724339470412') format('truetype');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.iconfont {
|
.iconfont {
|
||||||
font-family: 'iconfont' !important;
|
font-family: "iconfont" !important;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
-webkit-font-smoothing: antialiased;
|
-webkit-font-smoothing: antialiased;
|
||||||
-moz-osx-font-smoothing: grayscale;
|
-moz-osx-font-smoothing: grayscale;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.icon-trigger:before {
|
||||||
|
content: "\e6d3";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-router:before {
|
||||||
|
content: "\e6b2";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-delay:before {
|
||||||
|
content: "\e600";
|
||||||
|
}
|
||||||
|
|
||||||
.icon-start-user:before {
|
.icon-start-user:before {
|
||||||
content: '\e679';
|
content: "\e679";
|
||||||
}
|
}
|
||||||
|
|
||||||
.icon-inclusive:before {
|
.icon-inclusive:before {
|
||||||
content: '\e602';
|
content: "\e602";
|
||||||
}
|
}
|
||||||
|
|
||||||
.icon-copy:before {
|
.icon-copy:before {
|
||||||
content: '\e7eb';
|
content: "\e7eb";
|
||||||
}
|
}
|
||||||
|
|
||||||
.icon-handle:before {
|
.icon-transactor:before {
|
||||||
content: '\e61c';
|
content: "\e61c";
|
||||||
}
|
}
|
||||||
|
|
||||||
.icon-exclusive:before {
|
.icon-exclusive:before {
|
||||||
content: '\e717';
|
content: "\e717";
|
||||||
}
|
}
|
||||||
|
|
||||||
.icon-approve:before {
|
.icon-approve:before {
|
||||||
content: '\e715';
|
content: "\e715";
|
||||||
}
|
}
|
||||||
|
|
||||||
.icon-parallel:before {
|
.icon-parallel:before {
|
||||||
content: '\e688';
|
content: "\e688";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-async-child-process:before {
|
||||||
|
content: "\e6f2";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-child-process:before {
|
||||||
|
content: "\e6c1";
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
<!-- 列表选择通用组件,参考 ProductList 组件使用 -->
|
<!-- 列表选择通用组件,参考 ProductList 组件使用 -->
|
||||||
|
<!-- TODO 芋艿:可能会移除 -->
|
||||||
<template>
|
<template>
|
||||||
<Dialog v-model="dialogVisible" :appendToBody="true" :scroll="true" :title="title" width="60%">
|
<Dialog v-model="dialogVisible" :appendToBody="true" :scroll="true" :title="title" width="60%">
|
||||||
<el-table
|
<el-table
|
||||||
|
@ -188,12 +188,8 @@
|
|||||||
:scroll="true"
|
:scroll="true"
|
||||||
max-height="600px"
|
max-height="600px"
|
||||||
>
|
>
|
||||||
<!-- append-to-body -->
|
<div>
|
||||||
<div v-highlight>
|
<pre><code v-dompurify-html="highlightedCode(previewResult)" class="hljs"></code></pre>
|
||||||
<code class="hljs">
|
|
||||||
<!-- 高亮代码块 -->
|
|
||||||
{{ previewResult }}
|
|
||||||
</code>
|
|
||||||
</div>
|
</div>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
</div>
|
</div>
|
||||||
@ -237,6 +233,8 @@ import { XmlNode, XmlNodeType, parseXmlString } from 'steady-xml'
|
|||||||
// const eventName = reactive({
|
// const eventName = reactive({
|
||||||
// name: ''
|
// name: ''
|
||||||
// })
|
// })
|
||||||
|
import hljs from 'highlight.js' // 导入代码高亮文件
|
||||||
|
import 'highlight.js/styles/github.css' // 导入代码高亮样式
|
||||||
|
|
||||||
defineOptions({ name: 'MyProcessDesigner' })
|
defineOptions({ name: 'MyProcessDesigner' })
|
||||||
|
|
||||||
@ -308,27 +306,17 @@ const props = defineProps({
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// 监听value变化,重新加载流程图
|
/**
|
||||||
watch(
|
* 代码高亮
|
||||||
() => props.value,
|
*/
|
||||||
(newValue) => {
|
const highlightedCode = (code: string) => {
|
||||||
if (newValue && bpmnModeler) {
|
// 高亮
|
||||||
createNewDiagram(newValue)
|
if (previewType.value === 'json') {
|
||||||
}
|
code = JSON.stringify(code, null, 2)
|
||||||
},
|
}
|
||||||
{ immediate: true }
|
const result = hljs.highlight(code, { language: previewType.value, ignoreIllegals: true })
|
||||||
)
|
return result.value || ' '
|
||||||
|
}
|
||||||
// 监听processId和processName变化
|
|
||||||
watch(
|
|
||||||
[() => props.processId, () => props.processName],
|
|
||||||
([newId, newName]) => {
|
|
||||||
if (newId && newName && !props.value) {
|
|
||||||
createNewDiagram(null)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{ immediate: true }
|
|
||||||
)
|
|
||||||
|
|
||||||
provide('configGlobal', props)
|
provide('configGlobal', props)
|
||||||
let bpmnModeler: any = null
|
let bpmnModeler: any = null
|
||||||
@ -480,6 +468,7 @@ const initModelListeners = () => {
|
|||||||
emit('commandStack-changed', event)
|
emit('commandStack-changed', event)
|
||||||
emit('input', xml)
|
emit('input', xml)
|
||||||
emit('change', xml)
|
emit('change', xml)
|
||||||
|
emit('save', xml)
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
console.error(`[Process Designer Warn]: ${e.message || e}`)
|
console.error(`[Process Designer Warn]: ${e.message || e}`)
|
||||||
}
|
}
|
||||||
@ -568,6 +557,7 @@ const importLocalFile = () => {
|
|||||||
reader.onload = function () {
|
reader.onload = function () {
|
||||||
let xmlStr = this.result
|
let xmlStr = this.result
|
||||||
createNewDiagram(xmlStr)
|
createNewDiagram(xmlStr)
|
||||||
|
emit('save', xmlStr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* ------------------------------------------------ refs methods ------------------------------------------------------ */
|
/* ------------------------------------------------ refs methods ------------------------------------------------------ */
|
||||||
|
@ -1438,6 +1438,45 @@
|
|||||||
"isBody": true
|
"isBody": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "SignEnable",
|
||||||
|
"superClass": ["Element"],
|
||||||
|
"meta": {
|
||||||
|
"allowedIn": ["bpmn:UserTask"]
|
||||||
|
},
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"name": "value",
|
||||||
|
"type": "Boolean",
|
||||||
|
"isBody": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "SkipExpression",
|
||||||
|
"extends": ["bpmn:UserTask"],
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"name": "skipExpression",
|
||||||
|
"isAttr": true,
|
||||||
|
"type": "String"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ReasonRequire",
|
||||||
|
"superClass": ["Element"],
|
||||||
|
"meta": {
|
||||||
|
"allowedIn": ["bpmn:UserTask"]
|
||||||
|
},
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"name": "value",
|
||||||
|
"type": "Boolean",
|
||||||
|
"isBody": true
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"emumerations": []
|
"emumerations": []
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="process-panel__container" :style="{ width: `${width}px` }">
|
<div class="process-panel__container" :style="{ width: `${width}px`, maxHeight: '600px' }">
|
||||||
<el-collapse v-model="activeTab" v-if="isReady">
|
<el-collapse v-model="activeTab" v-if="isReady">
|
||||||
<el-collapse-item name="base">
|
<el-collapse-item name="base">
|
||||||
<!-- class="panel-tab__title" -->
|
<!-- class="panel-tab__title" -->
|
||||||
|
@ -152,6 +152,9 @@ watch(
|
|||||||
handleKeyUpdate(props.model.key)
|
handleKeyUpdate(props.model.key)
|
||||||
handleNameUpdate(props.model.name)
|
handleNameUpdate(props.model.name)
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
immediate: true
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
4. 操作按钮
|
4. 操作按钮
|
||||||
5. 字段权限
|
5. 字段权限
|
||||||
6. 审批类型
|
6. 审批类型
|
||||||
|
7. 是否需要签名
|
||||||
-->
|
-->
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
@ -122,13 +123,19 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<el-divider content-position="left">字段权限</el-divider>
|
<el-divider content-position="left">字段权限</el-divider>
|
||||||
<div class="field-setting-pane" v-if="formType === 10">
|
<div class="field-setting-pane" v-if="formType === BpmModelFormType.NORMAL">
|
||||||
<div class="field-permit-title">
|
<div class="field-permit-title">
|
||||||
<div class="setting-title-label first-title"> 字段名称 </div>
|
<div class="setting-title-label first-title"> 字段名称 </div>
|
||||||
<div class="other-titles">
|
<div class="other-titles">
|
||||||
<span class="setting-title-label">只读</span>
|
<span class="setting-title-label cursor-pointer" @click="updatePermission('READ')"
|
||||||
<span class="setting-title-label">可编辑</span>
|
>只读</span
|
||||||
<span class="setting-title-label">隐藏</span>
|
>
|
||||||
|
<span class="setting-title-label cursor-pointer" @click="updatePermission('WRITE')"
|
||||||
|
>可编辑</span
|
||||||
|
>
|
||||||
|
<span class="setting-title-label cursor-pointer" @click="updatePermission('NONE')"
|
||||||
|
>隐藏</span
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="field-setting-item" v-for="(item, index) in fieldsPermissionEl" :key="index">
|
<div class="field-setting-item" v-for="(item, index) in fieldsPermissionEl" :key="index">
|
||||||
@ -139,28 +146,54 @@
|
|||||||
:value="FieldPermissionType.READ"
|
:value="FieldPermissionType.READ"
|
||||||
size="large"
|
size="large"
|
||||||
:label="FieldPermissionType.READ"
|
:label="FieldPermissionType.READ"
|
||||||
><span></span
|
@change="updateElementExtensions"
|
||||||
></el-radio>
|
>
|
||||||
|
<span></span>
|
||||||
|
</el-radio>
|
||||||
</div>
|
</div>
|
||||||
<div class="item-radio-wrap">
|
<div class="item-radio-wrap">
|
||||||
<el-radio
|
<el-radio
|
||||||
:value="FieldPermissionType.WRITE"
|
:value="FieldPermissionType.WRITE"
|
||||||
size="large"
|
size="large"
|
||||||
:label="FieldPermissionType.WRITE"
|
:label="FieldPermissionType.WRITE"
|
||||||
><span></span
|
@change="updateElementExtensions"
|
||||||
></el-radio>
|
>
|
||||||
|
<span></span>
|
||||||
|
</el-radio>
|
||||||
</div>
|
</div>
|
||||||
<div class="item-radio-wrap">
|
<div class="item-radio-wrap">
|
||||||
<el-radio
|
<el-radio
|
||||||
:value="FieldPermissionType.NONE"
|
:value="FieldPermissionType.NONE"
|
||||||
size="large"
|
size="large"
|
||||||
:label="FieldPermissionType.NONE"
|
:label="FieldPermissionType.NONE"
|
||||||
><span></span
|
@change="updateElementExtensions"
|
||||||
></el-radio>
|
>
|
||||||
|
<span></span>
|
||||||
|
</el-radio>
|
||||||
</div>
|
</div>
|
||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<el-divider content-position="left">是否需要签名</el-divider>
|
||||||
|
<el-form-item prop="signEnable">
|
||||||
|
<el-switch
|
||||||
|
v-model="signEnable.value"
|
||||||
|
active-text="是"
|
||||||
|
inactive-text="否"
|
||||||
|
@change="updateElementExtensions"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-divider content-position="left">审批意见</el-divider>
|
||||||
|
<el-form-item prop="reasonRequire">
|
||||||
|
<el-switch
|
||||||
|
v-model="reasonRequire.value"
|
||||||
|
active-text="必填"
|
||||||
|
inactive-text="非必填"
|
||||||
|
@change="updateElementExtensions"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -180,6 +213,7 @@ import {
|
|||||||
} from '@/components/SimpleProcessDesignerV2/src/consts'
|
} from '@/components/SimpleProcessDesignerV2/src/consts'
|
||||||
import * as UserApi from '@/api/system/user'
|
import * as UserApi from '@/api/system/user'
|
||||||
import { useFormFieldsPermission } from '@/components/SimpleProcessDesignerV2/src/node'
|
import { useFormFieldsPermission } from '@/components/SimpleProcessDesignerV2/src/node'
|
||||||
|
import { BpmModelFormType } from '@/utils/constants'
|
||||||
|
|
||||||
defineOptions({ name: 'ElementCustomConfig4UserTask' })
|
defineOptions({ name: 'ElementCustomConfig4UserTask' })
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
@ -218,6 +252,12 @@ const { formType, fieldsPermissionConfig, getNodeConfigFormFields } = useFormFie
|
|||||||
// 审批类型
|
// 审批类型
|
||||||
const approveType = ref({ value: ApproveType.USER })
|
const approveType = ref({ value: ApproveType.USER })
|
||||||
|
|
||||||
|
// 是否需要签名
|
||||||
|
const signEnable = ref({ value: false })
|
||||||
|
|
||||||
|
// 审批意见
|
||||||
|
const reasonRequire = ref({ value: false })
|
||||||
|
|
||||||
const elExtensionElements = ref()
|
const elExtensionElements = ref()
|
||||||
const otherExtensions = ref()
|
const otherExtensions = ref()
|
||||||
const bpmnElement = ref()
|
const bpmnElement = ref()
|
||||||
@ -231,7 +271,6 @@ const resetCustomConfigList = () => {
|
|||||||
bpmnElement.value.id,
|
bpmnElement.value.id,
|
||||||
bpmnInstances().modeler
|
bpmnInstances().modeler
|
||||||
)
|
)
|
||||||
|
|
||||||
// 获取元素扩展属性 或者 创建扩展属性
|
// 获取元素扩展属性 或者 创建扩展属性
|
||||||
elExtensionElements.value =
|
elExtensionElements.value =
|
||||||
bpmnElement.value.businessObject?.extensionElements ??
|
bpmnElement.value.businessObject?.extensionElements ??
|
||||||
@ -294,14 +333,13 @@ const resetCustomConfigList = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 字段权限
|
// 字段权限
|
||||||
if (formType.value === 10) {
|
if (formType.value === BpmModelFormType.NORMAL) {
|
||||||
const fieldsPermissionList = elExtensionElements.value.values?.filter(
|
const fieldsPermissionList = elExtensionElements.value.values?.filter(
|
||||||
(ex) => ex.$type === `${prefix}:FieldsPermission`
|
(ex) => ex.$type === `${prefix}:FieldsPermission`
|
||||||
)
|
)
|
||||||
fieldsPermissionEl.value = []
|
fieldsPermissionEl.value = []
|
||||||
getNodeConfigFormFields()
|
getNodeConfigFormFields()
|
||||||
// 由于默认添加了发起人元素,这里需要删掉
|
fieldsPermissionConfig.value = fieldsPermissionConfig.value
|
||||||
fieldsPermissionConfig.value = fieldsPermissionConfig.value.slice(1)
|
|
||||||
fieldsPermissionConfig.value.forEach((element) => {
|
fieldsPermissionConfig.value.forEach((element) => {
|
||||||
element.permission =
|
element.permission =
|
||||||
fieldsPermissionList?.find((obj) => obj.field === element.field)?.permission ?? '1'
|
fieldsPermissionList?.find((obj) => obj.field === element.field)?.permission ?? '1'
|
||||||
@ -311,6 +349,16 @@ const resetCustomConfigList = () => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 是否需要签名
|
||||||
|
signEnable.value =
|
||||||
|
elExtensionElements.value.values?.filter((ex) => ex.$type === `${prefix}:SignEnable`)?.[0] ||
|
||||||
|
bpmnInstances().moddle.create(`${prefix}:SignEnable`, { value: false })
|
||||||
|
|
||||||
|
// 审批意见
|
||||||
|
reasonRequire.value =
|
||||||
|
elExtensionElements.value.values?.filter((ex) => ex.$type === `${prefix}:ReasonRequire`)?.[0] ||
|
||||||
|
bpmnInstances().moddle.create(`${prefix}:ReasonRequire`, { value: false })
|
||||||
|
|
||||||
// 保留剩余扩展元素,便于后面更新该元素对应属性
|
// 保留剩余扩展元素,便于后面更新该元素对应属性
|
||||||
otherExtensions.value =
|
otherExtensions.value =
|
||||||
elExtensionElements.value.values?.filter(
|
elExtensionElements.value.values?.filter(
|
||||||
@ -322,7 +370,9 @@ const resetCustomConfigList = () => {
|
|||||||
ex.$type !== `${prefix}:AssignEmptyUserIds` &&
|
ex.$type !== `${prefix}:AssignEmptyUserIds` &&
|
||||||
ex.$type !== `${prefix}:ButtonsSetting` &&
|
ex.$type !== `${prefix}:ButtonsSetting` &&
|
||||||
ex.$type !== `${prefix}:FieldsPermission` &&
|
ex.$type !== `${prefix}:FieldsPermission` &&
|
||||||
ex.$type !== `${prefix}:ApproveType`
|
ex.$type !== `${prefix}:ApproveType` &&
|
||||||
|
ex.$type !== `${prefix}:SignEnable` &&
|
||||||
|
ex.$type !== `${prefix}:ReasonRequire`
|
||||||
) ?? []
|
) ?? []
|
||||||
|
|
||||||
// 更新元素扩展属性,避免后续报错
|
// 更新元素扩展属性,避免后续报错
|
||||||
@ -373,7 +423,9 @@ const updateElementExtensions = () => {
|
|||||||
assignEmptyUserIdsEl.value,
|
assignEmptyUserIdsEl.value,
|
||||||
approveType.value,
|
approveType.value,
|
||||||
...buttonsSettingEl.value,
|
...buttonsSettingEl.value,
|
||||||
...fieldsPermissionEl.value
|
...fieldsPermissionEl.value,
|
||||||
|
signEnable.value,
|
||||||
|
reasonRequire.value
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
bpmnInstances().modeling.updateProperties(toRaw(bpmnElement.value), {
|
bpmnInstances().modeling.updateProperties(toRaw(bpmnElement.value), {
|
||||||
@ -456,6 +508,19 @@ function useButtonsSetting() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 批量更新权限 */
|
||||||
|
// TODO @lesan:这个页面,有一些 idea 红色报错,咱要不要 fix 下!
|
||||||
|
const updatePermission = (type: string) => {
|
||||||
|
fieldsPermissionEl.value.forEach((field) => {
|
||||||
|
field.permission =
|
||||||
|
type === 'READ'
|
||||||
|
? FieldPermissionType.READ
|
||||||
|
: type === 'WRITE'
|
||||||
|
? FieldPermissionType.WRITE
|
||||||
|
: FieldPermissionType.NONE
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
const userOptions = ref<UserApi.UserVO[]>([]) // 用户列表
|
const userOptions = ref<UserApi.UserVO[]>([]) // 用户列表
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
// 获得用户列表
|
// 获得用户列表
|
||||||
@ -466,9 +531,9 @@ onMounted(async () => {
|
|||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.button-setting-pane {
|
.button-setting-pane {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
|
||||||
font-size: 14px;
|
|
||||||
margin-top: 8px;
|
margin-top: 8px;
|
||||||
|
font-size: 14px;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
.button-setting-desc {
|
.button-setting-desc {
|
||||||
padding-right: 8px;
|
padding-right: 8px;
|
||||||
|
@ -1,6 +1,10 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="panel-tab__content">
|
<div class="panel-tab__content">
|
||||||
<el-radio-group v-model="approveMethod" @change="onApproveMethodChange">
|
<el-radio-group
|
||||||
|
v-if="type === 'UserTask'"
|
||||||
|
v-model="approveMethod"
|
||||||
|
@change="onApproveMethodChange"
|
||||||
|
>
|
||||||
<div class="flex-col">
|
<div class="flex-col">
|
||||||
<div v-for="(item, index) in APPROVE_METHODS" :key="index">
|
<div v-for="(item, index) in APPROVE_METHODS" :key="index">
|
||||||
<el-radio :value="item.value" :label="item.value">
|
<el-radio :value="item.value" :label="item.value">
|
||||||
@ -23,6 +27,9 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
|
<div v-else>
|
||||||
|
除了UserTask以外节点的多实例待实现
|
||||||
|
</div>
|
||||||
<!-- 与Simple设计器配置合并,保留以前的代码 -->
|
<!-- 与Simple设计器配置合并,保留以前的代码 -->
|
||||||
<el-form label-width="90px" style="display: none">
|
<el-form label-width="90px" style="display: none">
|
||||||
<el-form-item label="快捷配置">
|
<el-form-item label="快捷配置">
|
||||||
@ -301,19 +308,21 @@ const approveMethod = ref()
|
|||||||
const approveRatio = ref(100)
|
const approveRatio = ref(100)
|
||||||
const otherExtensions = ref()
|
const otherExtensions = ref()
|
||||||
const getElementLoopNew = () => {
|
const getElementLoopNew = () => {
|
||||||
const extensionElements =
|
if (props.type === 'UserTask') {
|
||||||
bpmnElement.value.businessObject?.extensionElements ??
|
const extensionElements =
|
||||||
bpmnInstances().moddle.create('bpmn:ExtensionElements', { values: [] })
|
bpmnElement.value.businessObject?.extensionElements ??
|
||||||
approveMethod.value = extensionElements.values.filter(
|
bpmnInstances().moddle.create('bpmn:ExtensionElements', { values: [] })
|
||||||
(ex) => ex.$type === `${prefix}:ApproveMethod`
|
approveMethod.value = extensionElements.values.filter(
|
||||||
)?.[0]?.value
|
(ex) => ex.$type === `${prefix}:ApproveMethod`
|
||||||
|
)?.[0]?.value
|
||||||
|
|
||||||
otherExtensions.value =
|
otherExtensions.value =
|
||||||
extensionElements.values.filter((ex) => ex.$type !== `${prefix}:ApproveMethod`) ?? []
|
extensionElements.values.filter((ex) => ex.$type !== `${prefix}:ApproveMethod`) ?? []
|
||||||
|
|
||||||
if (!approveMethod.value) {
|
if (!approveMethod.value) {
|
||||||
approveMethod.value = ApproveMethodType.SEQUENTIAL_APPROVE
|
approveMethod.value = ApproveMethodType.SEQUENTIAL_APPROVE
|
||||||
updateLoopCharacteristics()
|
updateLoopCharacteristics()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const onApproveMethodChange = () => {
|
const onApproveMethodChange = () => {
|
||||||
|
@ -192,6 +192,16 @@
|
|||||||
<!-- 选择弹窗 -->
|
<!-- 选择弹窗 -->
|
||||||
<ProcessExpressionDialog ref="processExpressionDialogRef" @select="selectProcessExpression" />
|
<ProcessExpressionDialog ref="processExpressionDialogRef" @select="selectProcessExpression" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item label="跳过表达式" prop="skipExpression">
|
||||||
|
<el-input
|
||||||
|
type="textarea"
|
||||||
|
v-model="userTaskForm.skipExpression"
|
||||||
|
clearable
|
||||||
|
style="width: 100%"
|
||||||
|
@change="updateSkipExpression"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -220,7 +230,8 @@ const props = defineProps({
|
|||||||
const prefix = inject('prefix')
|
const prefix = inject('prefix')
|
||||||
const userTaskForm = ref({
|
const userTaskForm = ref({
|
||||||
candidateStrategy: undefined, // 分配规则
|
candidateStrategy: undefined, // 分配规则
|
||||||
candidateParam: [] // 分配选项
|
candidateParam: [], // 分配选项
|
||||||
|
skipExpression: '' // 跳过表达式
|
||||||
})
|
})
|
||||||
const bpmnElement = ref()
|
const bpmnElement = ref()
|
||||||
const bpmnInstances = () => (window as any)?.bpmnInstances
|
const bpmnInstances = () => (window as any)?.bpmnInstances
|
||||||
@ -311,6 +322,13 @@ const resetTaskForm = () => {
|
|||||||
(ex) => ex.$type !== `${prefix}:CandidateStrategy` && ex.$type !== `${prefix}:CandidateParam`
|
(ex) => ex.$type !== `${prefix}:CandidateStrategy` && ex.$type !== `${prefix}:CandidateParam`
|
||||||
) ?? []
|
) ?? []
|
||||||
|
|
||||||
|
// 跳过表达式
|
||||||
|
if (businessObject.skipExpression != undefined) {
|
||||||
|
userTaskForm.value.skipExpression = businessObject.skipExpression
|
||||||
|
} else {
|
||||||
|
userTaskForm.value.skipExpression = ''
|
||||||
|
}
|
||||||
|
|
||||||
// 改用通过extensionElements来存储数据
|
// 改用通过extensionElements来存储数据
|
||||||
return
|
return
|
||||||
if (businessObject.candidateStrategy != undefined) {
|
if (businessObject.candidateStrategy != undefined) {
|
||||||
@ -390,6 +408,18 @@ const updateElementTask = () => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const updateSkipExpression = () => {
|
||||||
|
if (userTaskForm.value.skipExpression && userTaskForm.value.skipExpression !== '') {
|
||||||
|
bpmnInstances().modeling.updateProperties(toRaw(bpmnElement.value), {
|
||||||
|
skipExpression: userTaskForm.value.skipExpression
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
bpmnInstances().modeling.updateProperties(toRaw(bpmnElement.value), {
|
||||||
|
skipExpression: null
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 打开监听器弹窗
|
// 打开监听器弹窗
|
||||||
const processExpressionDialogRef = ref()
|
const processExpressionDialogRef = ref()
|
||||||
const openProcessExpressionDialog = async () => {
|
const openProcessExpressionDialog = async () => {
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
@use 'bpmn-js-token-simulation/assets/css/bpmn-js-token-simulation.css';
|
@use 'bpmn-js-token-simulation/assets/css/bpmn-js-token-simulation.css';
|
||||||
@use 'bpmn-js-token-simulation/assets/css/font-awesome.min.css';
|
|
||||||
@use 'bpmn-js-token-simulation/assets/css/normalize.css';
|
|
||||||
|
|
||||||
// 边框被 token-simulation 样式覆盖了
|
// 边框被 token-simulation 样式覆盖了
|
||||||
.djs-palette {
|
.djs-palette {
|
||||||
@ -97,12 +95,12 @@
|
|||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
svg {
|
// svg {
|
||||||
width: 100%;
|
// width: 100%;
|
||||||
height: 100%;
|
// height: 100%;
|
||||||
min-height: 100%;
|
// min-height: 100%;
|
||||||
overflow: hidden;
|
// overflow: hidden;
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user