mirror of
https://gitee.com/myxzgzs/boyue-ui-admin-vue3
synced 2025-08-08 16:32:43 +08:00
commit
d2b993311f
51
README.md
51
README.md
@ -117,37 +117,59 @@
|
|||||||
|
|
||||||
### 工作流程
|
### 工作流程
|
||||||
|
|
||||||
| | 功能 | 描述 |
|
|
||||||
|----|-------|-----------------------------------------|
|
|
||||||
| 🚀 | 流程模型 | 配置工作流的流程模型,支持 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 格式 |
|
||||||
@ -155,6 +177,7 @@ ps:核心功能已经实现,正在对接微信小程序中...
|
|||||||
| 🚀 | 配置管理 | 对系统动态配置常用参数,支持 SpringBoot 加载 |
|
| 🚀 | 配置管理 | 对系统动态配置常用参数,支持 SpringBoot 加载 |
|
||||||
| ⭐️ | 定时任务 | 在线(添加、修改、删除)任务调度包含执行结果日志 |
|
| ⭐️ | 定时任务 | 在线(添加、修改、删除)任务调度包含执行结果日志 |
|
||||||
| 🚀 | 文件服务 | 支持将文件存储到 S3(MinIO、阿里云、腾讯云、七牛云)、本地、FTP、数据库等 |
|
| 🚀 | 文件服务 | 支持将文件存储到 S3(MinIO、阿里云、腾讯云、七牛云)、本地、FTP、数据库等 |
|
||||||
|
| 🚀 | WebSocket | 提供 WebSocket 接入示例,支持一对一、一对多发送方式 |
|
||||||
| 🚀 | API 日志 | 包括 RESTful API 访问日志、异常日志两部分,方便排查 API 相关的问题 |
|
| 🚀 | API 日志 | 包括 RESTful API 访问日志、异常日志两部分,方便排查 API 相关的问题 |
|
||||||
| | MySQL 监控 | 监视当前系统数据库连接池状态,可进行分析SQL找出系统性能瓶颈 |
|
| | MySQL 监控 | 监视当前系统数据库连接池状态,可进行分析SQL找出系统性能瓶颈 |
|
||||||
| | Redis 监控 | 监控 Redis 数据库的使用情况,使用的 Redis Key 管理 |
|
| | Redis 监控 | 监控 Redis 数据库的使用情况,使用的 Redis Key 管理 |
|
||||||
|
@ -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']
|
||||||
|
@ -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'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
@ -90,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 })
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取表单字段权限
|
// 获取表单字段权限
|
||||||
|
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 |
@ -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>
|
||||||
@ -57,6 +63,12 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="handler-item-text">触发器</div>
|
<div class="handler-item-text">触发器</div>
|
||||||
</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>
|
||||||
@ -114,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: {
|
||||||
@ -277,6 +289,31 @@ const addNode = (type: number) => {
|
|||||||
}
|
}
|
||||||
emits('update:childNode', data)
|
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"
|
||||||
@ -56,6 +60,12 @@
|
|||||||
:flow-node="currentNode"
|
:flow-node="currentNode"
|
||||||
@update:flow-node="handleModelValueUpdate"
|
@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"
|
||||||
@ -81,6 +91,7 @@ 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 RouterNode from './nodes/RouterNode.vue'
|
||||||
import TriggerNode from './nodes/TriggerNode.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({
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<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">
|
<el-button v-if="!readonly" size="default" @click="exportJson">
|
||||||
@ -23,10 +23,19 @@
|
|||||||
<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>
|
||||||
@ -76,11 +85,51 @@ const emits = defineEmits<{
|
|||||||
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
|
||||||
@ -88,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
|
||||||
@ -100,20 +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[] = []
|
||||||
|
|
||||||
const saveSimpleFlowModel = async () => {
|
|
||||||
errorNodes = []
|
|
||||||
validateNode(processNodeTree.value, errorNodes)
|
|
||||||
if (errorNodes.length > 0) {
|
|
||||||
errorDialogVisible.value = true
|
|
||||||
return
|
|
||||||
}
|
|
||||||
emits('save', processNodeTree.value)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 校验节点设置。 暂时以 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
|
||||||
@ -193,6 +236,30 @@ const importLocalFile = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 在组件初始化时记录初始位置
|
||||||
|
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,6 +23,11 @@ export enum NodeType {
|
|||||||
*/
|
*/
|
||||||
COPY_TASK_NODE = 12,
|
COPY_TASK_NODE = 12,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 办理人节点
|
||||||
|
*/
|
||||||
|
TRANSACTOR_NODE = 13,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 延迟器节点
|
* 延迟器节点
|
||||||
*/
|
*/
|
||||||
@ -33,6 +38,11 @@ export enum NodeType {
|
|||||||
*/
|
*/
|
||||||
TRIGGER_NODE = 15,
|
TRIGGER_NODE = 15,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 子流程节点
|
||||||
|
*/
|
||||||
|
CHILD_PROCESS_NODE = 20,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 条件节点
|
* 条件节点
|
||||||
*/
|
*/
|
||||||
@ -123,6 +133,8 @@ export interface SimpleFlowNode {
|
|||||||
reasonRequire?: boolean
|
reasonRequire?: boolean
|
||||||
// 触发器设置
|
// 触发器设置
|
||||||
triggerSetting?: TriggerSetting
|
triggerSetting?: TriggerSetting
|
||||||
|
// 子流程
|
||||||
|
childProcessSetting?: ChildProcessSetting
|
||||||
}
|
}
|
||||||
// 候选人策略枚举 ( 用于审批节点。抄送节点 )
|
// 候选人策略枚举 ( 用于审批节点。抄送节点 )
|
||||||
export enum CandidateStrategy {
|
export enum CandidateStrategy {
|
||||||
@ -150,6 +162,10 @@ export enum CandidateStrategy {
|
|||||||
* 指定用户
|
* 指定用户
|
||||||
*/
|
*/
|
||||||
USER = 30,
|
USER = 30,
|
||||||
|
/**
|
||||||
|
* 审批人自选
|
||||||
|
*/
|
||||||
|
APPROVE_USER_SELECT = 34,
|
||||||
/**
|
/**
|
||||||
* 发起人自选
|
* 发起人自选
|
||||||
*/
|
*/
|
||||||
@ -506,6 +522,8 @@ 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.ROUTER_BRANCH_NODE, '请设置路由节点')
|
||||||
NODE_DEFAULT_TEXT.set(NodeType.TRIGGER_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, '审批人')
|
||||||
@ -515,15 +533,20 @@ 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.ROUTER_BRANCH_NODE, '路由分支')
|
||||||
NODE_DEFAULT_NAME.set(NodeType.TRIGGER_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.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 },
|
||||||
@ -627,6 +650,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 },
|
||||||
@ -717,7 +750,7 @@ export type RouterSetting = {
|
|||||||
export type TriggerSetting = {
|
export type TriggerSetting = {
|
||||||
type: TriggerTypeEnum
|
type: TriggerTypeEnum
|
||||||
httpRequestSetting?: HttpRequestTriggerSetting
|
httpRequestSetting?: HttpRequestTriggerSetting
|
||||||
normalFormSetting?: NormalFormTriggerSetting
|
formSettings?: FormTriggerSetting[]
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -729,9 +762,17 @@ export enum TriggerTypeEnum {
|
|||||||
*/
|
*/
|
||||||
HTTP_REQUEST = 1,
|
HTTP_REQUEST = 1,
|
||||||
/**
|
/**
|
||||||
* 更新流程表单触发器
|
* 接收 HTTP 回调请求触发器
|
||||||
*/
|
*/
|
||||||
UPDATE_NORMAL_FORM = 2 // TODO @jason:FORM_UPDATE?
|
HTTP_CALLBACK = 2,
|
||||||
|
/**
|
||||||
|
* 表单数据更新触发器
|
||||||
|
*/
|
||||||
|
FORM_UPDATE = 10,
|
||||||
|
/**
|
||||||
|
* 表单数据删除触发器
|
||||||
|
*/
|
||||||
|
FORM_DELETE = 11
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -751,12 +792,110 @@ export type HttpRequestTriggerSetting = {
|
|||||||
/**
|
/**
|
||||||
* 流程表单触发器配置结构定义
|
* 流程表单触发器配置结构定义
|
||||||
*/
|
*/
|
||||||
export type NormalFormTriggerSetting = {
|
export type FormTriggerSetting = {
|
||||||
// 更新表单字段
|
// 条件类型
|
||||||
|
conditionType?: ConditionType
|
||||||
|
// 条件表达式
|
||||||
|
conditionExpression?: string
|
||||||
|
// 条件组
|
||||||
|
conditionGroups?: ConditionGroup
|
||||||
|
// 更新表单字段配置
|
||||||
updateFormFields?: Record<string, any>
|
updateFormFields?: Record<string, any>
|
||||||
|
// 删除表单字段配置
|
||||||
|
deleteFields?: string[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export const TRIGGER_TYPES: DictDataVO[] = [
|
export const TRIGGER_TYPES: DictDataVO[] = [
|
||||||
{ label: 'HTTP 请求', value: TriggerTypeEnum.HTTP_REQUEST },
|
{ label: '发送 HTTP 请求', value: TriggerTypeEnum.HTTP_REQUEST },
|
||||||
{ label: '修改表单数据', value: TriggerTypeEnum.UPDATE_NORMAL_FORM }
|
{ 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 }
|
||||||
]
|
]
|
||||||
|
@ -15,7 +15,10 @@ import {
|
|||||||
AssignEmptyHandlerType,
|
AssignEmptyHandlerType,
|
||||||
FieldPermissionType,
|
FieldPermissionType,
|
||||||
HttpRequestParam,
|
HttpRequestParam,
|
||||||
ProcessVariableEnum
|
ProcessVariableEnum,
|
||||||
|
ConditionType,
|
||||||
|
ConditionGroup,
|
||||||
|
COMPARISON_OPERATORS
|
||||||
} from './consts'
|
} from './consts'
|
||||||
import { parseFormFields } from '@/components/FormCreate/src/utils'
|
import { parseFormFields } from '@/components/FormCreate/src/utils'
|
||||||
|
|
||||||
@ -201,7 +204,7 @@ export function useNodeForm(nodeType: NodeType) {
|
|||||||
const deptTreeOptions = inject('deptTree', ref()) // 部门树
|
const deptTreeOptions = inject('deptTree', ref()) // 部门树
|
||||||
const formFields = inject<Ref<string[]>>('formFields', ref([])) // 流程表单字段
|
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,
|
||||||
@ -307,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 = `发起人自选`
|
||||||
@ -543,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>
|
@ -43,15 +43,12 @@
|
|||||||
</el-drawer>
|
</el-drawer>
|
||||||
</template>
|
</template>
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import {
|
import { SimpleFlowNode, ConditionType } from '../consts'
|
||||||
SimpleFlowNode,
|
|
||||||
ConditionType,
|
|
||||||
COMPARISON_OPERATORS,
|
|
||||||
} from '../consts'
|
|
||||||
import { getDefaultConditionNodeName } from '../utils'
|
import { getDefaultConditionNodeName } from '../utils'
|
||||||
import { useFormFieldsAndStartUser } from '../node'
|
import { useFormFieldsAndStartUser, getConditionShowText } from '../node'
|
||||||
import Condition from './components/Condition.vue'
|
import Condition from './components/Condition.vue'
|
||||||
const message = useMessage() // 消息弹窗
|
import { cloneDeep } from 'lodash-es'
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: 'ConditionNodeConfig'
|
name: 'ConditionNodeConfig'
|
||||||
})
|
})
|
||||||
@ -67,9 +64,51 @@ const props = defineProps({
|
|||||||
})
|
})
|
||||||
const settingVisible = ref(false)
|
const settingVisible = ref(false)
|
||||||
const currentNode = ref<SimpleFlowNode>(props.conditionNode)
|
const currentNode = ref<SimpleFlowNode>(props.conditionNode)
|
||||||
const condition = ref<any>()
|
const condition = ref<any>({
|
||||||
|
conditionType: ConditionType.RULE, // 设置默认值
|
||||||
|
conditionExpression: '',
|
||||||
|
conditionGroups: {
|
||||||
|
and: true,
|
||||||
|
conditions: [
|
||||||
|
{
|
||||||
|
and: true,
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
opCode: '==',
|
||||||
|
leftSide: '',
|
||||||
|
rightSide: ''
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
})
|
||||||
const open = () => {
|
const open = () => {
|
||||||
condition.value = currentNode.value.conditionSetting
|
// 如果有已存在的配置则使用,否则使用默认值
|
||||||
|
if (currentNode.value.conditionSetting) {
|
||||||
|
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
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -93,8 +132,6 @@ const blurEvent = () => {
|
|||||||
getDefaultConditionNodeName(props.nodeIndex, currentNode.value?.conditionSetting?.defaultFlow)
|
getDefaultConditionNodeName(props.nodeIndex, currentNode.value?.conditionSetting?.defaultFlow)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
||||||
|
|
||||||
// 关闭
|
// 关闭
|
||||||
@ -111,84 +148,41 @@ const handleClose = async (done: (cancel?: boolean) => void) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 保存配置 */
|
||||||
|
const fieldOptions = useFormFieldsAndStartUser() // 流程表单字段和发起人字段
|
||||||
const conditionRef = ref()
|
const conditionRef = ref()
|
||||||
// 保存配置
|
|
||||||
const saveConfig = async () => {
|
const saveConfig = async () => {
|
||||||
if (!currentNode.value.conditionSetting?.defaultFlow) {
|
if (!currentNode.value.conditionSetting?.defaultFlow) {
|
||||||
// 校验表单
|
// 校验表单
|
||||||
const valid = await conditionRef.value.validate()
|
const valid = await conditionRef.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
|
||||||
currentNode.value.conditionSetting!.conditionType = condition.value?.conditionType
|
// 使用 cloneDeep 进行深拷贝
|
||||||
if (currentNode.value.conditionSetting?.conditionType === ConditionType.EXPRESSION) {
|
currentNode.value.conditionSetting = cloneDeep({
|
||||||
currentNode.value.conditionSetting.conditionGroups = undefined
|
...currentNode.value.conditionSetting,
|
||||||
currentNode.value.conditionSetting.conditionExpression = condition.value?.conditionExpression
|
conditionType: condition.value?.conditionType,
|
||||||
}
|
conditionExpression:
|
||||||
if (currentNode.value.conditionSetting!.conditionType === ConditionType.RULE) {
|
condition.value?.conditionType === ConditionType.EXPRESSION
|
||||||
currentNode.value.conditionSetting!.conditionExpression = undefined
|
? condition.value?.conditionExpression
|
||||||
currentNode.value.conditionSetting!.conditionGroups = condition.value?.conditionGroups
|
: 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 (condition.value?.conditionType === ConditionType.EXPRESSION) {
|
|
||||||
if (condition.value.conditionExpression) {
|
|
||||||
showText = `表达式:${condition.value.conditionExpression}`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (condition.value?.conditionType === ConditionType.RULE) {
|
|
||||||
// 条件组是否为与关系
|
|
||||||
const groupAnd = condition.value.conditionGroups?.and
|
|
||||||
let warningMesg: undefined | string = undefined
|
|
||||||
const conditionGroup = condition.value.conditionGroups?.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 fieldOptions = useFormFieldsAndStartUser()
|
|
||||||
|
|
||||||
/** 获取字段名称 */
|
|
||||||
const getFieldTitle = (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
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
@ -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>
|
||||||
|
|
||||||
|
@ -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>
|
||||||
|
|
||||||
|
@ -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="630"
|
||||||
:before-close="saveConfig"
|
:before-close="saveConfig"
|
||||||
>
|
>
|
||||||
<template #header>
|
<template #header>
|
||||||
@ -26,7 +26,7 @@
|
|||||||
<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="type">
|
<el-form-item label="触发器类型" prop="type">
|
||||||
<el-select v-model="configForm.type">
|
<el-select v-model="configForm.type" @change="changeTriggerType">
|
||||||
<el-option
|
<el-option
|
||||||
v-for="(item, index) in TRIGGER_TYPES"
|
v-for="(item, index) in TRIGGER_TYPES"
|
||||||
:key="index"
|
:key="index"
|
||||||
@ -36,100 +36,68 @@
|
|||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<!-- HTTP 请求触发器 -->
|
<!-- HTTP 请求触发器 -->
|
||||||
<div
|
|
||||||
v-if="configForm.type === TriggerTypeEnum.HTTP_REQUEST && configForm.httpRequestSetting"
|
|
||||||
>
|
|
||||||
<el-form-item>
|
|
||||||
<el-alert
|
|
||||||
title="仅支持 POST 请求,以请求体方式接收参数"
|
|
||||||
type="warning"
|
|
||||||
show-icon
|
|
||||||
:closable="false"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
<!-- 请求地址-->
|
|
||||||
<el-form-item label="请求地址" prop="httpRequestSetting.url">
|
|
||||||
<el-input v-model="configForm.httpRequestSetting.url" />
|
|
||||||
</el-form-item>
|
|
||||||
<!-- 请求头,请求体设置-->
|
|
||||||
<HttpRequestParamSetting
|
|
||||||
:header="configForm.httpRequestSetting.header"
|
|
||||||
:body="configForm.httpRequestSetting.body"
|
|
||||||
:bind="'httpRequestSetting'"
|
|
||||||
/>
|
|
||||||
<!-- 返回值设置-->
|
|
||||||
<el-form-item label="返回值">
|
|
||||||
<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 configForm.httpRequestSetting.response"
|
|
||||||
:key="index"
|
|
||||||
>
|
|
||||||
<div class="mr-2">
|
|
||||||
<el-form-item
|
|
||||||
:prop="`httpRequestSetting.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="`httpRequestSetting.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(configForm.httpRequestSetting.response!, index)"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<el-button
|
|
||||||
type="primary"
|
|
||||||
text
|
|
||||||
@click="addHttpResponseSetting(configForm.httpRequestSetting.response!)"
|
|
||||||
>
|
|
||||||
<Icon icon="ep:plus" class="mr-5px" />添加一行
|
|
||||||
</el-button>
|
|
||||||
</el-form-item>
|
|
||||||
</div>
|
|
||||||
<div
|
<div
|
||||||
v-if="
|
v-if="
|
||||||
configForm.type === TriggerTypeEnum.UPDATE_NORMAL_FORM && configForm.normalFormSetting
|
[TriggerTypeEnum.HTTP_REQUEST, TriggerTypeEnum.HTTP_CALLBACK].includes(
|
||||||
|
configForm.type
|
||||||
|
) && configForm.httpRequestSetting
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
<el-divider content-position="left">修改表单设置</el-divider>
|
<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
|
<div
|
||||||
class="flex items-center"
|
class="flex items-center"
|
||||||
v-for="key in Object.keys(configForm.normalFormSetting.updateFormFields!)"
|
v-for="key in Object.keys(formSetting.updateFormFields || {})"
|
||||||
:key="key"
|
:key="key"
|
||||||
>
|
>
|
||||||
<div class="mr-2 flex items-center">
|
<div class="mr-2 flex items-center">
|
||||||
@ -137,7 +105,7 @@
|
|||||||
<el-select
|
<el-select
|
||||||
class="w-160px!"
|
class="w-160px!"
|
||||||
:model-value="key"
|
:model-value="key"
|
||||||
@update:model-value="(newKey) => updateFormFieldKey(key, newKey)"
|
@update:model-value="(newKey) => updateFormFieldKey(formSetting, key, newKey)"
|
||||||
placeholder="请选择表单字段"
|
placeholder="请选择表单字段"
|
||||||
:disabled="key !== ''"
|
:disabled="key !== ''"
|
||||||
>
|
>
|
||||||
@ -154,7 +122,7 @@
|
|||||||
<div class="mx-2"><el-form-item>的值设置为</el-form-item></div>
|
<div class="mx-2"><el-form-item>的值设置为</el-form-item></div>
|
||||||
<div class="mr-2">
|
<div class="mr-2">
|
||||||
<el-form-item
|
<el-form-item
|
||||||
:prop="`normalFormSetting.updateFormFields.${key}`"
|
:prop="`formSettings.${index}.updateFormFields.${key}`"
|
||||||
:rules="{
|
:rules="{
|
||||||
required: true,
|
required: true,
|
||||||
message: '值不能为空',
|
message: '值不能为空',
|
||||||
@ -163,7 +131,7 @@
|
|||||||
>
|
>
|
||||||
<el-input
|
<el-input
|
||||||
class="w-160px"
|
class="w-160px"
|
||||||
v-model="configForm.normalFormSetting.updateFormFields![key]"
|
v-model="formSetting.updateFormFields![key]"
|
||||||
placeholder="请输入"
|
placeholder="请输入"
|
||||||
:disabled="!key"
|
:disabled="!key"
|
||||||
/>
|
/>
|
||||||
@ -171,12 +139,95 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="mr-1 pt-1 cursor-pointer">
|
<div class="mr-1 pt-1 cursor-pointer">
|
||||||
<el-form-item>
|
<el-form-item>
|
||||||
<Icon icon="ep:delete" :size="18" @click="deleteFormFieldSetting(key)" />
|
<Icon
|
||||||
|
icon="ep:delete"
|
||||||
|
:size="18"
|
||||||
|
@click="deleteFormFieldSetting(formSetting, key)"
|
||||||
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<el-button type="primary" text @click="addFormFieldSetting()">
|
|
||||||
<Icon icon="ep:plus" class="mr-5px" />添加修改字段
|
<!-- 添加表单字段按钮 -->
|
||||||
|
<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>
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
</el-form>
|
</el-form>
|
||||||
@ -191,9 +242,19 @@
|
|||||||
</el-drawer>
|
</el-drawer>
|
||||||
</template>
|
</template>
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { SimpleFlowNode, NodeType, TriggerSetting, TRIGGER_TYPES, TriggerTypeEnum } from '../consts'
|
import {
|
||||||
import { useWatchNode, useDrawer, useNodeName, useFormFields } from '../node'
|
SimpleFlowNode,
|
||||||
import HttpRequestParamSetting from './components/HttpRequestParamSetting.vue'
|
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({
|
defineOptions({
|
||||||
name: 'TriggerNodeConfig'
|
name: 'TriggerNodeConfig'
|
||||||
@ -227,52 +288,153 @@ const configForm = ref<TriggerSetting>({
|
|||||||
body: [],
|
body: [],
|
||||||
response: []
|
response: []
|
||||||
},
|
},
|
||||||
normalFormSetting: { updateFormFields: {} }
|
formSettings: [
|
||||||
|
{
|
||||||
|
conditionGroups: DEFAULT_CONDITION_GROUP_VALUE,
|
||||||
|
updateFormFields: {},
|
||||||
|
deleteFields: []
|
||||||
|
}
|
||||||
|
]
|
||||||
})
|
})
|
||||||
// 流程表单字段
|
// 流程表单字段
|
||||||
const formFields = useFormFields()
|
const formFields = useFormFields()
|
||||||
|
|
||||||
// 可选的修改的表单字段
|
// 可选的修改的表单字段
|
||||||
const optionalUpdateFormFields = computed(() => {
|
const optionalUpdateFormFields = computed(() => {
|
||||||
const usedFields = Object.keys(configForm.value.normalFormSetting?.updateFormFields || {})
|
|
||||||
return formFields.map((field) => ({
|
return formFields.map((field) => ({
|
||||||
title: field.title,
|
title: field.title,
|
||||||
field: field.field,
|
field: field.field,
|
||||||
disabled: usedFields.includes(field.field)
|
disabled: false
|
||||||
}))
|
}))
|
||||||
})
|
})
|
||||||
|
|
||||||
const updateFormFieldKey = (oldKey: string, newKey: string) => {
|
let originalSetting: TriggerSetting | undefined
|
||||||
if (!configForm.value.normalFormSetting?.updateFormFields) return
|
|
||||||
const value = configForm.value.normalFormSetting.updateFormFields[oldKey]
|
/** 触发器类型改变了 */
|
||||||
delete configForm.value.normalFormSetting.updateFormFields[oldKey]
|
const changeTriggerType = () => {
|
||||||
configForm.value.normalFormSetting.updateFormFields[newKey] = value
|
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
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 添加 HTTP 请求返回值设置项*/
|
if (configForm.value.type === TriggerTypeEnum.HTTP_CALLBACK) {
|
||||||
const addHttpResponseSetting = (responseSetting: Record<string, string>[]) => {
|
configForm.value.httpRequestSetting =
|
||||||
responseSetting.push({
|
originalSetting?.type === TriggerTypeEnum.HTTP_CALLBACK && originalSetting.httpRequestSetting
|
||||||
key: '',
|
? originalSetting.httpRequestSetting
|
||||||
value: ''
|
: {
|
||||||
|
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: []
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 删除 HTTP 请求返回值设置项 */
|
/** 删除修改表单设置 */
|
||||||
const deleteHttpResponseSetting = (responseSetting: Record<string, string>[], index: number) => {
|
const deleteFormSetting = (index: number) => {
|
||||||
responseSetting.splice(index, 1)
|
configForm.value.formSettings!.splice(index, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 添加修改表单设置项 */
|
/** 添加条件配置 */
|
||||||
const addFormFieldSetting = () => {
|
const addFormSettingCondition = (index: number, formSetting: FormTriggerSetting) => {
|
||||||
if (configForm.value.normalFormSetting!.updateFormFields === undefined) {
|
const conditionDialog = proxy.$refs[`condition-${index}`][0]
|
||||||
configForm.value.normalFormSetting!.updateFormFields = {}
|
conditionDialog.open(formSetting)
|
||||||
}
|
}
|
||||||
configForm.value.normalFormSetting!.updateFormFields[''] = undefined
|
/** 删除条件配置 */
|
||||||
|
const deleteFormSettingCondition = (formSetting: FormTriggerSetting) => {
|
||||||
|
formSetting.conditionType = undefined
|
||||||
}
|
}
|
||||||
/** 删除修改表单设置项 */
|
/** 打开条件配置弹窗 */
|
||||||
const deleteFormFieldSetting = (key: string) => {
|
const openFormSettingCondition = (index: number, formSetting: FormTriggerSetting) => {
|
||||||
if (!configForm.value.normalFormSetting?.updateFormFields) return
|
const conditionDialog = proxy.$refs[`condition-${index}`][0]
|
||||||
delete configForm.value.normalFormSetting.updateFormFields[key]
|
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]
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 保存配置 */
|
/** 保存配置 */
|
||||||
@ -285,10 +447,19 @@ const saveConfig = async () => {
|
|||||||
currentNode.value.name = nodeName.value!
|
currentNode.value.name = nodeName.value!
|
||||||
currentNode.value.showText = showText
|
currentNode.value.showText = showText
|
||||||
if (configForm.value.type === TriggerTypeEnum.HTTP_REQUEST) {
|
if (configForm.value.type === TriggerTypeEnum.HTTP_REQUEST) {
|
||||||
configForm.value.normalFormSetting = undefined
|
configForm.value.formSettings = undefined
|
||||||
}
|
} else if (configForm.value.type === TriggerTypeEnum.FORM_UPDATE) {
|
||||||
if (configForm.value.type === TriggerTypeEnum.UPDATE_NORMAL_FORM) {
|
|
||||||
configForm.value.httpRequestSetting = undefined
|
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
|
currentNode.value.triggerSetting = configForm.value
|
||||||
settingVisible.value = false
|
settingVisible.value = false
|
||||||
@ -298,22 +469,35 @@ const saveConfig = async () => {
|
|||||||
/** 获取节点展示内容 */
|
/** 获取节点展示内容 */
|
||||||
const getShowText = (): string => {
|
const getShowText = (): string => {
|
||||||
let showText = ''
|
let showText = ''
|
||||||
if (configForm.value.type === TriggerTypeEnum.HTTP_REQUEST) {
|
if (
|
||||||
|
configForm.value.type === TriggerTypeEnum.HTTP_REQUEST ||
|
||||||
|
configForm.value.type === TriggerTypeEnum.HTTP_CALLBACK
|
||||||
|
) {
|
||||||
showText = `${configForm.value.httpRequestSetting?.url}`
|
showText = `${configForm.value.httpRequestSetting?.url}`
|
||||||
} else if (configForm.value.type === TriggerTypeEnum.UPDATE_NORMAL_FORM) {
|
} else if (configForm.value.type === TriggerTypeEnum.FORM_UPDATE) {
|
||||||
const updatefields = Object.keys(configForm.value.normalFormSetting?.updateFormFields || {})
|
for (const [index, setting] of configForm.value.formSettings!.entries()) {
|
||||||
if (updatefields.length === 0) {
|
if (!setting.updateFormFields || Object.keys(setting.updateFormFields).length === 0) {
|
||||||
message.warning('请设置修改表单字段')
|
message.warning(`请添加表单设置${index + 1}的修改字段`)
|
||||||
} else {
|
return ''
|
||||||
showText = '修改表单数据'
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
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
|
return showText
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 显示触发器节点配置, 由父组件传过来 */
|
/** 显示触发器节点配置, 由父组件传过来 */
|
||||||
const showTriggerNodeConfig = (node: SimpleFlowNode) => {
|
const showTriggerNodeConfig = (node: SimpleFlowNode) => {
|
||||||
nodeName.value = node.name
|
nodeName.value = node.name
|
||||||
|
originalSetting = node.triggerSetting ? JSON.parse(JSON.stringify(node.triggerSetting)) : {}
|
||||||
if (node.triggerSetting) {
|
if (node.triggerSetting) {
|
||||||
configForm.value = {
|
configForm.value = {
|
||||||
type: node.triggerSetting.type,
|
type: node.triggerSetting.type,
|
||||||
@ -323,7 +507,13 @@ const showTriggerNodeConfig = (node: SimpleFlowNode) => {
|
|||||||
body: [],
|
body: [],
|
||||||
response: []
|
response: []
|
||||||
},
|
},
|
||||||
normalFormSetting: node.triggerSetting.normalFormSetting || { updateFormFields: {} }
|
formSettings: node.triggerSetting.formSettings || [
|
||||||
|
{
|
||||||
|
conditionGroups: DEFAULT_CONDITION_GROUP_VALUE,
|
||||||
|
updateFormFields: {},
|
||||||
|
deleteFields: []
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,7 @@
|
|||||||
<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
|
||||||
@ -39,10 +39,10 @@
|
|||||||
</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"
|
||||||
@ -61,7 +61,13 @@
|
|||||||
label="指定角色"
|
label="指定角色"
|
||||||
prop="roleIds"
|
prop="roleIds"
|
||||||
>
|
>
|
||||||
<el-select filterable 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 filterable 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 filterable 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 filterable 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"
|
||||||
@ -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,6 +254,7 @@
|
|||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
|
<div v-if="currentNode.type === NodeType.USER_TASK_NODE">
|
||||||
<el-divider content-position="left">审批人拒绝时</el-divider>
|
<el-divider content-position="left">审批人拒绝时</el-divider>
|
||||||
<el-form-item prop="rejectHandlerType">
|
<el-form-item prop="rejectHandlerType">
|
||||||
<el-radio-group v-model="configForm.rejectHandlerType">
|
<el-radio-group v-model="configForm.rejectHandlerType">
|
||||||
@ -245,7 +270,12 @@
|
|||||||
label="驳回节点"
|
label="驳回节点"
|
||||||
prop="returnNodeId"
|
prop="returnNodeId"
|
||||||
>
|
>
|
||||||
<el-select filterable v-model="configForm.returnNodeId" clearable style="width: 100%">
|
<el-select
|
||||||
|
filterable
|
||||||
|
v-model="configForm.returnNodeId"
|
||||||
|
clearable
|
||||||
|
style="width: 100%"
|
||||||
|
>
|
||||||
<el-option
|
<el-option
|
||||||
v-for="item in returnTaskList"
|
v-for="item in returnTaskList"
|
||||||
:key="item.id"
|
:key="item.id"
|
||||||
@ -254,7 +284,9 @@
|
|||||||
/>
|
/>
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="currentNode.type === NodeType.USER_TASK_NODE">
|
||||||
<el-divider content-position="left">审批人超时未处理时</el-divider>
|
<el-divider content-position="left">审批人超时未处理时</el-divider>
|
||||||
<el-form-item label="启用开关" prop="timeoutHandlerEnable">
|
<el-form-item label="启用开关" prop="timeoutHandlerEnable">
|
||||||
<el-switch
|
<el-switch
|
||||||
@ -315,8 +347,9 @@
|
|||||||
>
|
>
|
||||||
<el-input-number v-model="configForm.maxRemindCount" :min="1" :max="10" />
|
<el-input-number v-model="configForm.maxRemindCount" :min="1" :max="10" />
|
||||||
</el-form-item>
|
</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">
|
||||||
@ -348,6 +381,7 @@
|
|||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
|
<div v-if="currentNode.type === NodeType.USER_TASK_NODE">
|
||||||
<el-divider content-position="left">审批人与提交人为同一人时</el-divider>
|
<el-divider content-position="left">审批人与提交人为同一人时</el-divider>
|
||||||
<el-form-item prop="assignStartUserHandlerType">
|
<el-form-item prop="assignStartUserHandlerType">
|
||||||
<el-radio-group v-model="configForm.assignStartUserHandlerType">
|
<el-radio-group v-model="configForm.assignStartUserHandlerType">
|
||||||
@ -358,20 +392,33 @@
|
|||||||
</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-divider content-position="left">是否需要签名</el-divider>
|
||||||
<el-form-item prop="signEnable">
|
<el-form-item prop="signEnable">
|
||||||
<el-switch v-model="configForm.signEnable" active-text="是" inactive-text="否" />
|
<el-switch v-model="configForm.signEnable" active-text="是" inactive-text="否" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="currentNode.type === NodeType.USER_TASK_NODE">
|
||||||
<el-divider content-position="left">审批意见</el-divider>
|
<el-divider content-position="left">审批意见</el-divider>
|
||||||
<el-form-item prop="reasonRequire">
|
<el-form-item prop="reasonRequire">
|
||||||
<el-switch v-model="configForm.reasonRequire" active-text="必填" inactive-text="非必填" />
|
<el-switch
|
||||||
|
v-model="configForm.reasonRequire"
|
||||||
|
active-text="必填"
|
||||||
|
inactive-text="非必填"
|
||||||
|
/>
|
||||||
</el-form-item>
|
</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">
|
||||||
@ -407,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
|
||||||
@ -448,7 +501,11 @@
|
|||||||
</div>
|
</div>
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
<el-tab-pane label="监听器" name="listener">
|
<el-tab-pane label="监听器" name="listener">
|
||||||
<UserTaskListener ref="userTaskListenerRef" v-model="configForm" :form-field-options="formFieldOptions" />
|
<UserTaskListener
|
||||||
|
ref="userTaskListenerRef"
|
||||||
|
v-model="configForm"
|
||||||
|
:form-field-options="formFieldOptions"
|
||||||
|
/>
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
</el-tabs>
|
</el-tabs>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
@ -485,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 {
|
||||||
@ -588,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>
|
||||||
|
|
||||||
// 改变审批人设置策略
|
// 改变审批人设置策略
|
||||||
@ -627,7 +685,12 @@ const {
|
|||||||
|
|
||||||
const userTaskListenerRef = ref()
|
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'
|
||||||
// 设置审批节点名称
|
// 设置审批节点名称
|
||||||
@ -713,7 +776,7 @@ const saveConfig = async () => {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// 显示审批节点配置, 由父组件传过来
|
/** 显示审批节点配置, 由父组件传过来 */
|
||||||
const showUserTaskNodeConfig = (node: SimpleFlowNode) => {
|
const showUserTaskNodeConfig = (node: SimpleFlowNode) => {
|
||||||
nodeName.value = node.name
|
nodeName.value = node.name
|
||||||
// 1 审批类型
|
// 1 审批类型
|
||||||
@ -733,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)
|
||||||
@ -755,7 +818,11 @@ 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. 监听器
|
||||||
@ -788,9 +855,7 @@ const showUserTaskNodeConfig = (node: SimpleFlowNode) => {
|
|||||||
|
|
||||||
defineExpose({ openDrawer, showUserTaskNodeConfig }) // 暴露方法给父组件
|
defineExpose({ openDrawer, showUserTaskNodeConfig }) // 暴露方法给父组件
|
||||||
|
|
||||||
/**
|
/** 操作按钮设置 */
|
||||||
* @description 操作按钮设置
|
|
||||||
*/
|
|
||||||
function useButtonsSetting() {
|
function useButtonsSetting() {
|
||||||
const buttonsSetting = ref<ButtonSetting[]>()
|
const buttonsSetting = ref<ButtonSetting[]>()
|
||||||
// 操作按钮显示名称可编辑
|
// 操作按钮显示名称可编辑
|
||||||
@ -811,9 +876,7 @@ function useButtonsSetting() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** 审批人超时未处理配置 */
|
||||||
* @description 审批人超时未处理配置
|
|
||||||
*/
|
|
||||||
function useTimeoutHandler() {
|
function useTimeoutHandler() {
|
||||||
// 时间单位
|
// 时间单位
|
||||||
const timeUnit = ref(TimeUnitType.HOUR)
|
const timeUnit = ref(TimeUnitType.HOUR)
|
||||||
@ -896,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,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>
|
@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<el-form-item label="请求头">
|
<el-form-item label-position="top" label="请求头">
|
||||||
<div class="flex pt-2" v-for="(item, index) in props.header" :key="index">
|
<div class="flex pt-2" v-for="(item, index) in props.header" :key="index">
|
||||||
<div class="mr-2">
|
<div class="mr-2">
|
||||||
<el-form-item
|
<el-form-item
|
||||||
@ -69,7 +69,7 @@
|
|||||||
<Icon icon="ep:plus" class="mr-5px" />添加一行
|
<Icon icon="ep:plus" class="mr-5px" />添加一行
|
||||||
</el-button>
|
</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="请求体">
|
<el-form-item label-position="top" label="请求体">
|
||||||
<div class="flex pt-2" v-for="(item, index) in props.body" :key="index">
|
<div class="flex pt-2" v-for="(item, index) in props.body" :key="index">
|
||||||
<div class="mr-2">
|
<div class="mr-2">
|
||||||
<el-form-item
|
<el-form-item
|
||||||
@ -141,7 +141,11 @@
|
|||||||
</el-form-item>
|
</el-form-item>
|
||||||
</template>
|
</template>
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { HttpRequestParam, BPM_HTTP_REQUEST_PARAM_TYPES, BpmHttpRequestParamTypeEnum } from '../../consts'
|
import {
|
||||||
|
HttpRequestParam,
|
||||||
|
BPM_HTTP_REQUEST_PARAM_TYPES,
|
||||||
|
BpmHttpRequestParamTypeEnum
|
||||||
|
} from '../../consts'
|
||||||
import { useFormFieldsAndStartUser } from '../../node'
|
import { useFormFieldsAndStartUser } from '../../node'
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: 'HttpRequestParamSetting'
|
name: 'HttpRequestParamSetting'
|
||||||
|
@ -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,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,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>
|
||||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -177,6 +177,18 @@
|
|||||||
color: #ca3a31
|
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;
|
||||||
@ -294,6 +306,18 @@
|
|||||||
&.router-node {
|
&.router-node {
|
||||||
color: #ca3a31
|
color: #ca3a31
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.transactor-task {
|
||||||
|
color: #330099;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.child-process {
|
||||||
|
color: #996633;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.async-child-process {
|
||||||
|
color: #006666;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.node-title {
|
.node-title {
|
||||||
@ -777,7 +801,7 @@
|
|||||||
content: "\e7eb";
|
content: "\e7eb";
|
||||||
}
|
}
|
||||||
|
|
||||||
.icon-handle:before {
|
.icon-transactor:before {
|
||||||
content: "\e61c";
|
content: "\e61c";
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -792,3 +816,11 @@
|
|||||||
.icon-parallel:before {
|
.icon-parallel:before {
|
||||||
content: "\e688";
|
content: "\e688";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.icon-async-child-process:before {
|
||||||
|
content: "\e6f2";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-child-process:before {
|
||||||
|
content: "\e6c1";
|
||||||
|
}
|
||||||
|
@ -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,6 +306,18 @@ const props = defineProps({
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 代码高亮
|
||||||
|
*/
|
||||||
|
const highlightedCode = (code: string) => {
|
||||||
|
// 高亮
|
||||||
|
if (previewType.value === 'json') {
|
||||||
|
code = JSON.stringify(code, null, 2)
|
||||||
|
}
|
||||||
|
const result = hljs.highlight(code, { language: previewType.value, ignoreIllegals: true })
|
||||||
|
return result.value || ' '
|
||||||
|
}
|
||||||
|
|
||||||
provide('configGlobal', props)
|
provide('configGlobal', props)
|
||||||
let bpmnModeler: any = null
|
let bpmnModeler: any = null
|
||||||
const defaultZoom = ref(1)
|
const defaultZoom = ref(1)
|
||||||
|
@ -123,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">
|
||||||
@ -140,24 +146,30 @@
|
|||||||
: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>
|
||||||
@ -165,12 +177,22 @@
|
|||||||
|
|
||||||
<el-divider content-position="left">是否需要签名</el-divider>
|
<el-divider content-position="left">是否需要签名</el-divider>
|
||||||
<el-form-item prop="signEnable">
|
<el-form-item prop="signEnable">
|
||||||
<el-switch v-model="signEnable.value" active-text="是" inactive-text="否" />
|
<el-switch
|
||||||
|
v-model="signEnable.value"
|
||||||
|
active-text="是"
|
||||||
|
inactive-text="否"
|
||||||
|
@change="updateElementExtensions"
|
||||||
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
<el-divider content-position="left">审批意见</el-divider>
|
<el-divider content-position="left">审批意见</el-divider>
|
||||||
<el-form-item prop="reasonRequire">
|
<el-form-item prop="reasonRequire">
|
||||||
<el-switch v-model="reasonRequire.value" active-text="必填" inactive-text="非必填" />
|
<el-switch
|
||||||
|
v-model="reasonRequire.value"
|
||||||
|
active-text="必填"
|
||||||
|
inactive-text="非必填"
|
||||||
|
@change="updateElementExtensions"
|
||||||
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -191,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({
|
||||||
@ -248,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 ??
|
||||||
@ -311,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'
|
||||||
@ -487,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 () => {
|
||||||
// 获得用户列表
|
// 获得用户列表
|
||||||
@ -497,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;
|
||||||
|
@ -254,33 +254,9 @@ const remainingRouter: AppRouteRecordRaw[] = [
|
|||||||
activeMenu: '/bpm/manager/form'
|
activeMenu: '/bpm/manager/form'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
|
||||||
path: 'manager/model/edit',
|
|
||||||
component: () => import('@/views/bpm/model/editor/index.vue'),
|
|
||||||
name: 'BpmModelEditor',
|
|
||||||
meta: {
|
|
||||||
noCache: true,
|
|
||||||
hidden: true,
|
|
||||||
canTo: true,
|
|
||||||
title: '设计流程',
|
|
||||||
activeMenu: '/bpm/manager/model'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: 'manager/simple/model',
|
|
||||||
component: () => import('@/views/bpm/simple/SimpleModelDesign.vue'),
|
|
||||||
name: 'SimpleModelDesign',
|
|
||||||
meta: {
|
|
||||||
noCache: true,
|
|
||||||
hidden: true,
|
|
||||||
canTo: true,
|
|
||||||
title: '仿钉钉设计流程',
|
|
||||||
activeMenu: '/bpm/manager/model'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
path: 'manager/definition',
|
path: 'manager/definition',
|
||||||
component: () => import('@/views/bpm/definition/index.vue'),
|
component: () => import('@/views/bpm/model/definition/index.vue'),
|
||||||
name: 'BpmProcessDefinition',
|
name: 'BpmProcessDefinition',
|
||||||
meta: {
|
meta: {
|
||||||
noCache: true,
|
noCache: true,
|
||||||
@ -356,7 +332,6 @@ const remainingRouter: AppRouteRecordRaw[] = [
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// TODO @zws:1)建议,在加一个路由。然后标题是“复制流程”,这样体验会好点;2)复制出来的数据,在名字前面,加“副本 ”,和钉钉保持一致!
|
|
||||||
path: 'manager/model/:type/:id',
|
path: 'manager/model/:type/:id',
|
||||||
component: () => import('@/views/bpm/model/form/index.vue'),
|
component: () => import('@/views/bpm/model/form/index.vue'),
|
||||||
name: 'BpmModelUpdate',
|
name: 'BpmModelUpdate',
|
||||||
|
@ -511,14 +511,14 @@ export function jsonParse(str: string) {
|
|||||||
/**
|
/**
|
||||||
* 截取字符串
|
* 截取字符串
|
||||||
*
|
*
|
||||||
* @param name
|
* @param str 字符串
|
||||||
* @param start
|
* @param start 开始位置
|
||||||
* @param end
|
* @param end 结束位置
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export const sliceName = (name: string,start: number, end : number) => {
|
export const subString = (str: string, start: number, end: number) => {
|
||||||
if (name.length > end) {
|
if (str.length > end) {
|
||||||
return name.slice(start, end)
|
return str.slice(start, end)
|
||||||
}
|
}
|
||||||
return name
|
return str
|
||||||
}
|
}
|
||||||
|
@ -89,7 +89,7 @@
|
|||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
<el-image v-if="row.icon" :src="row.icon" class="h-38px w-38px mr-10px rounded" />
|
<el-image v-if="row.icon" :src="row.icon" class="h-38px w-38px mr-10px rounded" />
|
||||||
<div v-else class="flow-icon">
|
<div v-else class="flow-icon">
|
||||||
<span style="font-size: 12px; color: #fff">{{ sliceName(row.name,0,2) }}</span>
|
<span style="font-size: 12px; color: #fff">{{ subString(row.name, 0, 2) }}</span>
|
||||||
</div>
|
</div>
|
||||||
{{ row.name }}
|
{{ row.name }}
|
||||||
</div>
|
</div>
|
||||||
@ -113,6 +113,11 @@
|
|||||||
</el-text>
|
</el-text>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
|
<el-table-column label="流程类型" prop="type" min-width="120">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<dict-tag :value="row.type" :type="DICT_TYPE.BPM_MODEL_TYPE" />
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
<el-table-column label="表单信息" prop="formType" min-width="150">
|
<el-table-column label="表单信息" prop="formType" min-width="150">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<el-button
|
<el-button
|
||||||
@ -260,6 +265,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import { DICT_TYPE } from '@/utils/dict'
|
||||||
import { CategoryApi, CategoryVO } from '@/api/bpm/category'
|
import { CategoryApi, CategoryVO } from '@/api/bpm/category'
|
||||||
import Sortable from 'sortablejs'
|
import Sortable from 'sortablejs'
|
||||||
import { formatDate } from '@/utils/formatTime'
|
import { formatDate } from '@/utils/formatTime'
|
||||||
@ -271,9 +277,8 @@ import { checkPermi } from '@/utils/permission'
|
|||||||
import { useUserStoreWithOut } from '@/store/modules/user'
|
import { useUserStoreWithOut } from '@/store/modules/user'
|
||||||
import { useAppStore } from '@/store/modules/app'
|
import { useAppStore } from '@/store/modules/app'
|
||||||
import { cloneDeep, isEqual } from 'lodash-es'
|
import { cloneDeep, isEqual } from 'lodash-es'
|
||||||
import { useTagsView } from '@/hooks/web/useTagsView'
|
|
||||||
import { useDebounceFn } from '@vueuse/core'
|
import { useDebounceFn } from '@vueuse/core'
|
||||||
import { sliceName } from '@/utils/index'
|
import { subString } from '@/utils/index'
|
||||||
|
|
||||||
defineOptions({ name: 'BpmModel' })
|
defineOptions({ name: 'BpmModel' })
|
||||||
|
|
||||||
@ -583,8 +588,7 @@ const handleDeleteCategory = async () => {
|
|||||||
} catch {}
|
} catch {}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 添加流程模型弹窗 */
|
/** 添加/修改/复制流程模型弹窗 */
|
||||||
const tagsView = useTagsView()
|
|
||||||
const openModelForm = async (type: string, id?: number) => {
|
const openModelForm = async (type: string, id?: number) => {
|
||||||
if (type === 'create') {
|
if (type === 'create') {
|
||||||
await push({ name: 'BpmModelCreate' })
|
await push({ name: 'BpmModelCreate' })
|
||||||
@ -593,10 +597,6 @@ const openModelForm = async (type: string, id?: number) => {
|
|||||||
name: 'BpmModelUpdate',
|
name: 'BpmModelUpdate',
|
||||||
params: { id, type }
|
params: { id, type }
|
||||||
})
|
})
|
||||||
// 设置标题
|
|
||||||
if (type === 'copy') {
|
|
||||||
tagsView.setTitle('复制流程')
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,40 +3,60 @@
|
|||||||
|
|
||||||
<ContentWrap>
|
<ContentWrap>
|
||||||
<el-table v-loading="loading" :data="list">
|
<el-table v-loading="loading" :data="list">
|
||||||
<el-table-column label="定义编号" align="center" prop="id" width="400" />
|
<el-table-column label="定义编号" align="center" prop="id" min-width="250" />
|
||||||
<el-table-column label="流程名称" align="center" prop="name" width="200">
|
<el-table-column label="流程名称" align="center" prop="name" min-width="150" />
|
||||||
<template #default="scope">
|
<el-table-column label="流程图标" align="center" min-width="50">
|
||||||
<el-button type="primary" link @click="handleBpmnDetail(scope.row)">
|
<template #default="{ row }">
|
||||||
<span>{{ scope.row.name }}</span>
|
<el-image v-if="row.icon" :src="row.icon" class="h-24px w-24pxrounded" />
|
||||||
</el-button>
|
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="定义分类" align="center" prop="categoryName" width="100" />
|
<el-table-column label="可见范围" prop="startUserIds" min-width="100">
|
||||||
<el-table-column label="表单信息" align="center" prop="formType" width="200">
|
<template #default="{ row }">
|
||||||
|
<el-text v-if="!row.startUsers?.length"> 全部可见 </el-text>
|
||||||
|
<el-text v-else-if="row.startUsers.length === 1">
|
||||||
|
{{ row.startUsers[0].nickname }}
|
||||||
|
</el-text>
|
||||||
|
<el-text v-else>
|
||||||
|
<el-tooltip
|
||||||
|
class="box-item"
|
||||||
|
effect="dark"
|
||||||
|
placement="top"
|
||||||
|
:content="row.startUsers.map((user: any) => user.nickname).join('、')"
|
||||||
|
>
|
||||||
|
{{ row.startUsers[0].nickname }}等 {{ row.startUsers.length }} 人可见
|
||||||
|
</el-tooltip>
|
||||||
|
</el-text>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="流程类型" prop="modelType" min-width="120">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<dict-tag :value="row.modelType" :type="DICT_TYPE.BPM_MODEL_TYPE" />
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="表单信息" prop="formType" min-width="150">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<el-button
|
<el-button
|
||||||
v-if="scope.row.formType === 10"
|
v-if="scope.row.formType === BpmModelFormType.NORMAL"
|
||||||
type="primary"
|
type="primary"
|
||||||
link
|
link
|
||||||
@click="handleFormDetail(scope.row)"
|
@click="handleFormDetail(scope.row)"
|
||||||
>
|
>
|
||||||
<span>{{ scope.row.formName }}</span>
|
<span>{{ scope.row.formName }}</span>
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button v-else type="primary" link @click="handleFormDetail(scope.row)">
|
<el-button
|
||||||
|
v-else-if="scope.row.formType === BpmModelFormType.CUSTOM"
|
||||||
|
type="primary"
|
||||||
|
link
|
||||||
|
@click="handleFormDetail(scope.row)"
|
||||||
|
>
|
||||||
<span>{{ scope.row.formCustomCreatePath }}</span>
|
<span>{{ scope.row.formCustomCreatePath }}</span>
|
||||||
</el-button>
|
</el-button>
|
||||||
|
<label v-else>暂无表单</label>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="流程版本" align="center" prop="processDefinition.version" width="80">
|
<el-table-column label="流程版本" align="center" min-width="80">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<el-tag v-if="scope.row">v{{ scope.row.version }}</el-tag>
|
<el-tag>v{{ scope.row.version }}</el-tag>
|
||||||
<el-tag type="warning" v-else>未部署</el-tag>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column label="状态" align="center" prop="version" width="80">
|
|
||||||
<template #default="scope">
|
|
||||||
<el-tag type="success" v-if="scope.row.suspensionState === 1">激活</el-tag>
|
|
||||||
<el-tag type="warning" v-if="scope.row.suspensionState === 2">挂起</el-tag>
|
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column
|
<el-table-column
|
||||||
@ -46,13 +66,18 @@
|
|||||||
width="180"
|
width="180"
|
||||||
:formatter="dateFormatter"
|
:formatter="dateFormatter"
|
||||||
/>
|
/>
|
||||||
<el-table-column
|
<el-table-column label="操作" align="center">
|
||||||
label="定义描述"
|
<template #default="scope">
|
||||||
align="center"
|
<el-button
|
||||||
prop="description"
|
link
|
||||||
width="300"
|
type="primary"
|
||||||
show-overflow-tooltip
|
@click="openModelForm(scope.row.id)"
|
||||||
/>
|
v-hasPermi="['bpm:model:update']"
|
||||||
|
>
|
||||||
|
恢复
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
<!-- 分页 -->
|
<!-- 分页 -->
|
||||||
<Pagination
|
<Pagination
|
||||||
@ -67,18 +92,14 @@
|
|||||||
<Dialog title="表单详情" v-model="formDetailVisible" width="800">
|
<Dialog title="表单详情" v-model="formDetailVisible" width="800">
|
||||||
<form-create :rule="formDetailPreview.rule" :option="formDetailPreview.option" />
|
<form-create :rule="formDetailPreview.rule" :option="formDetailPreview.option" />
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
|
||||||
<!-- 弹窗:流程模型图的预览 -->
|
|
||||||
<Dialog title="流程图" v-model="bpmnDetailVisible" width="800">
|
|
||||||
<MyProcessViewer style="height: 700px" key="designer" :xml="bpmnXml" />
|
|
||||||
</Dialog>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { dateFormatter } from '@/utils/formatTime'
|
import { dateFormatter } from '@/utils/formatTime'
|
||||||
import { MyProcessViewer } from '@/components/bpmnProcessDesigner/package'
|
|
||||||
import * as DefinitionApi from '@/api/bpm/definition'
|
import * as DefinitionApi from '@/api/bpm/definition'
|
||||||
import { setConfAndFields2 } from '@/utils/formCreate'
|
import { setConfAndFields2 } from '@/utils/formCreate'
|
||||||
|
import { DICT_TYPE } from '@/utils/dict'
|
||||||
|
import { BpmModelFormType } from '@/utils/constants'
|
||||||
|
|
||||||
defineOptions({ name: 'BpmProcessDefinition' })
|
defineOptions({ name: 'BpmProcessDefinition' })
|
||||||
|
|
||||||
@ -113,7 +134,7 @@ const formDetailPreview = ref({
|
|||||||
option: {}
|
option: {}
|
||||||
})
|
})
|
||||||
const handleFormDetail = async (row: any) => {
|
const handleFormDetail = async (row: any) => {
|
||||||
if (row.formType == 10) {
|
if (row.formType == BpmModelFormType.NORMAL) {
|
||||||
// 设置表单
|
// 设置表单
|
||||||
setConfAndFields2(formDetailPreview, row.formConf, row.formFields)
|
setConfAndFields2(formDetailPreview, row.formConf, row.formFields)
|
||||||
// 弹窗打开
|
// 弹窗打开
|
||||||
@ -125,15 +146,12 @@ const handleFormDetail = async (row: any) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 流程图的详情按钮操作 */
|
/** 恢复流程模型弹窗 */
|
||||||
const bpmnDetailVisible = ref(false)
|
const openModelForm = async (id?: number) => {
|
||||||
const bpmnXml = ref('')
|
await push({
|
||||||
const handleBpmnDetail = async (row: any) => {
|
name: 'BpmModelUpdate',
|
||||||
// 设置可见
|
params: { id, type: 'definition' }
|
||||||
bpmnXml.value = ''
|
})
|
||||||
bpmnDetailVisible.value = true
|
|
||||||
// 加载 BPMN XML
|
|
||||||
bpmnXml.value = (await DefinitionApi.getProcessDefinition(row.id))?.bpmnXml
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 初始化 **/
|
/** 初始化 **/
|
||||||
@ -141,3 +159,16 @@ onMounted(() => {
|
|||||||
getList()
|
getList()
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.flow-icon {
|
||||||
|
display: flex;
|
||||||
|
width: 38px;
|
||||||
|
height: 38px;
|
||||||
|
margin-right: 10px;
|
||||||
|
background-color: var(--el-color-primary);
|
||||||
|
border-radius: 0.25rem;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
</style>
|
@ -140,6 +140,46 @@
|
|||||||
</el-select>
|
</el-select>
|
||||||
</div>
|
</div>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
<el-form-item class="mb-20px">
|
||||||
|
<template #label>
|
||||||
|
<el-text size="large" tag="b">流程前置通知</el-text>
|
||||||
|
</template>
|
||||||
|
<div class="flex flex-col w-100%">
|
||||||
|
<div class="flex">
|
||||||
|
<el-switch
|
||||||
|
v-model="preProcessNotifyEnable"
|
||||||
|
@change="handlePreProcessNotifyEnableChange"
|
||||||
|
/>
|
||||||
|
<div class="ml-80px">流程启动后通知</div>
|
||||||
|
</div>
|
||||||
|
<HttpRequestSetting
|
||||||
|
v-if="preProcessNotifyEnable"
|
||||||
|
v-model:setting="modelData.preProcessNotifySetting"
|
||||||
|
:responseEnable="true"
|
||||||
|
:formItemPrefix="'preProcessNotifySetting'"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item class="mb-20px">
|
||||||
|
<template #label>
|
||||||
|
<el-text size="large" tag="b">流程后置通知</el-text>
|
||||||
|
</template>
|
||||||
|
<div class="flex flex-col w-100%">
|
||||||
|
<div class="flex">
|
||||||
|
<el-switch
|
||||||
|
v-model="postProcessNotifyEnable"
|
||||||
|
@change="handlePostProcessNotifyEnableChange"
|
||||||
|
/>
|
||||||
|
<div class="ml-80px">流程启动后通知</div>
|
||||||
|
</div>
|
||||||
|
<HttpRequestSetting
|
||||||
|
v-if="postProcessNotifyEnable"
|
||||||
|
v-model:setting="modelData.postProcessNotifySetting"
|
||||||
|
:responseEnable="true"
|
||||||
|
:formItemPrefix="'postProcessNotifySetting'"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -149,6 +189,7 @@ import { BpmAutoApproveType, BpmModelFormType } from '@/utils/constants'
|
|||||||
import * as FormApi from '@/api/bpm/form'
|
import * as FormApi from '@/api/bpm/form'
|
||||||
import { parseFormFields } from '@/components/FormCreate/src/utils'
|
import { parseFormFields } from '@/components/FormCreate/src/utils'
|
||||||
import { ProcessVariableEnum } from '@/components/SimpleProcessDesignerV2/src/consts'
|
import { ProcessVariableEnum } from '@/components/SimpleProcessDesignerV2/src/consts'
|
||||||
|
import HttpRequestSetting from '@/components/SimpleProcessDesignerV2/src/nodes-config/components/HttpRequestSetting.vue'
|
||||||
|
|
||||||
const modelData = defineModel<any>()
|
const modelData = defineModel<any>()
|
||||||
|
|
||||||
@ -205,6 +246,36 @@ const numberExample = computed(() => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
/** 是否开启流程前置通知 */
|
||||||
|
const preProcessNotifyEnable = ref(false)
|
||||||
|
const handlePreProcessNotifyEnableChange = (val: boolean | string | number) => {
|
||||||
|
if (val) {
|
||||||
|
modelData.value.preProcessNotifySetting = {
|
||||||
|
url: '',
|
||||||
|
header: [],
|
||||||
|
body: [],
|
||||||
|
response: []
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
modelData.value.preProcessNotifySetting = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 是否开启流程后置通知 */
|
||||||
|
const postProcessNotifyEnable = ref(false)
|
||||||
|
const handlePostProcessNotifyEnableChange = (val: boolean | string | number) => {
|
||||||
|
if (val) {
|
||||||
|
modelData.value.postProcessNotifySetting = {
|
||||||
|
url: '',
|
||||||
|
header: [],
|
||||||
|
body: [],
|
||||||
|
response: []
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
modelData.value.postProcessNotifySetting = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** 表单选项 */
|
/** 表单选项 */
|
||||||
const formField = ref<Array<{ field: string; title: string }>>([])
|
const formField = ref<Array<{ field: string; title: string }>>([])
|
||||||
const formFieldOptions4Title = computed(() => {
|
const formFieldOptions4Title = computed(() => {
|
||||||
@ -264,6 +335,12 @@ const initData = () => {
|
|||||||
summary: []
|
summary: []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (modelData.value.preProcessNotifySetting) {
|
||||||
|
preProcessNotifyEnable.value = true
|
||||||
|
}
|
||||||
|
if (modelData.value.postProcessNotifySetting) {
|
||||||
|
postProcessNotifyEnable.value = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
defineExpose({ initData })
|
defineExpose({ initData })
|
||||||
|
|
||||||
|
@ -11,12 +11,12 @@
|
|||||||
</el-radio>
|
</el-radio>
|
||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item v-if="modelData.formType === 10" label="流程表单" prop="formId">
|
<el-form-item v-if="modelData.formType === BpmModelFormType.NORMAL" label="流程表单" prop="formId">
|
||||||
<el-select v-model="modelData.formId" clearable style="width: 100%">
|
<el-select v-model="modelData.formId" clearable style="width: 100%">
|
||||||
<el-option v-for="form in formList" :key="form.id" :label="form.name" :value="form.id" />
|
<el-option v-for="form in formList" :key="form.id" :label="form.name" :value="form.id" />
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item v-if="modelData.formType === 20" label="表单提交路由" prop="formCustomCreatePath">
|
<el-form-item v-if="modelData.formType === BpmModelFormType.CUSTOM" label="表单提交路由" prop="formCustomCreatePath">
|
||||||
<el-input
|
<el-input
|
||||||
v-model="modelData.formCustomCreatePath"
|
v-model="modelData.formCustomCreatePath"
|
||||||
placeholder="请输入表单提交路由"
|
placeholder="请输入表单提交路由"
|
||||||
@ -31,7 +31,7 @@
|
|||||||
<Icon icon="ep:question" class="ml-5px" />
|
<Icon icon="ep:question" class="ml-5px" />
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item v-if="modelData.formType === 20" label="表单查看地址" prop="formCustomViewPath">
|
<el-form-item v-if="modelData.formType === BpmModelFormType.CUSTOM" label="表单查看地址" prop="formCustomViewPath">
|
||||||
<el-input
|
<el-input
|
||||||
v-model="modelData.formCustomViewPath"
|
v-model="modelData.formCustomViewPath"
|
||||||
placeholder="请输入表单查看的组件地址"
|
placeholder="请输入表单查看的组件地址"
|
||||||
@ -48,7 +48,7 @@
|
|||||||
</el-form-item>
|
</el-form-item>
|
||||||
<!-- 表单预览 -->
|
<!-- 表单预览 -->
|
||||||
<div
|
<div
|
||||||
v-if="modelData.formType === 10 && modelData.formId && formPreview.rule.length > 0"
|
v-if="modelData.formType === BpmModelFormType.NORMAL && modelData.formId && formPreview.rule.length > 0"
|
||||||
class="mt-20px"
|
class="mt-20px"
|
||||||
>
|
>
|
||||||
<div class="flex items-center mb-15px">
|
<div class="flex items-center mb-15px">
|
||||||
@ -68,6 +68,7 @@
|
|||||||
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
||||||
import * as FormApi from '@/api/bpm/form'
|
import * as FormApi from '@/api/bpm/form'
|
||||||
import { setConfAndFields2 } from '@/utils/formCreate'
|
import { setConfAndFields2 } from '@/utils/formCreate'
|
||||||
|
import { BpmModelFormType } from '@/utils/constants'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
formList: {
|
formList: {
|
||||||
@ -96,7 +97,7 @@ const formPreview = ref({
|
|||||||
watch(
|
watch(
|
||||||
() => modelData.value.formId,
|
() => modelData.value.formId,
|
||||||
async (newFormId) => {
|
async (newFormId) => {
|
||||||
if (newFormId && modelData.value.formType === 10) {
|
if (newFormId && modelData.value.formType === BpmModelFormType.NORMAL) {
|
||||||
const data = await FormApi.getForm(newFormId)
|
const data = await FormApi.getForm(newFormId)
|
||||||
setConfAndFields2(formPreview.value, data.conf, data.fields)
|
setConfAndFields2(formPreview.value, data.conf, data.fields)
|
||||||
// 设置只读
|
// 设置只读
|
||||||
|
@ -25,7 +25,7 @@
|
|||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { BpmModelType } from '@/utils/constants'
|
import { BpmModelType } from '@/utils/constants'
|
||||||
import BpmModelEditor from '../editor/index.vue'
|
import BpmModelEditor from './editor/index.vue'
|
||||||
import SimpleModelDesign from '../../simple/SimpleModelDesign.vue'
|
import SimpleModelDesign from '../../simple/SimpleModelDesign.vue'
|
||||||
|
|
||||||
// 创建本地数据副本
|
// 创建本地数据副本
|
||||||
|
@ -34,10 +34,12 @@ import CustomContentPadProvider from '@/components/bpmnProcessDesigner/package/d
|
|||||||
// 自定义左侧菜单(修改 默认任务 为 用户任务)
|
// 自定义左侧菜单(修改 默认任务 为 用户任务)
|
||||||
import CustomPaletteProvider from '@/components/bpmnProcessDesigner/package/designer/plugins/palette'
|
import CustomPaletteProvider from '@/components/bpmnProcessDesigner/package/designer/plugins/palette'
|
||||||
import * as ModelApi from '@/api/bpm/model'
|
import * as ModelApi from '@/api/bpm/model'
|
||||||
|
import { BpmModelFormType } from '@/utils/constants'
|
||||||
|
import * as FormApi from '@/api/bpm/form'
|
||||||
|
|
||||||
defineOptions({ name: 'BpmModelEditor' })
|
defineOptions({ name: 'BpmModelEditor' })
|
||||||
|
|
||||||
const props = defineProps<{
|
defineProps<{
|
||||||
modelId?: string
|
modelId?: string
|
||||||
modelKey: string
|
modelKey: string
|
||||||
modelName: string
|
modelName: string
|
||||||
@ -49,7 +51,8 @@ const message = useMessage() // 国际化
|
|||||||
|
|
||||||
// 表单信息
|
// 表单信息
|
||||||
const formFields = ref<string[]>([])
|
const formFields = ref<string[]>([])
|
||||||
const formType = ref(20)
|
// 表单类型,暂仅限流程表单
|
||||||
|
const formType = ref(BpmModelFormType.NORMAL)
|
||||||
provide('formFields', formFields)
|
provide('formFields', formFields)
|
||||||
provide('formType', formType)
|
provide('formType', formType)
|
||||||
|
|
||||||
@ -88,6 +91,20 @@ const save = async (bpmnXml: string) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 监听表单 ID 变化,加载表单数据 */
|
||||||
|
watch(
|
||||||
|
() => modelData.value.formId,
|
||||||
|
async (newFormId) => {
|
||||||
|
if (newFormId && modelData.value.formType === BpmModelFormType.NORMAL) {
|
||||||
|
const data = await FormApi.getForm(newFormId)
|
||||||
|
formFields.value = data.fields
|
||||||
|
} else {
|
||||||
|
formFields.value = []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ immediate: true }
|
||||||
|
)
|
||||||
|
|
||||||
// 在组件卸载时清理
|
// 在组件卸载时清理
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
modeler.value = null
|
modeler.value = null
|
@ -44,8 +44,13 @@
|
|||||||
|
|
||||||
<!-- 右侧按钮 -->
|
<!-- 右侧按钮 -->
|
||||||
<div class="w-200px flex items-center justify-end gap-2">
|
<div class="w-200px flex items-center justify-end gap-2">
|
||||||
<el-button v-if="route.params.id" type="success" @click="handleDeploy">发 布</el-button>
|
<el-button v-if="actionType === 'update'" type="success" @click="handleDeploy">
|
||||||
<el-button type="primary" @click="handleSave">保 存</el-button>
|
发 布
|
||||||
|
</el-button>
|
||||||
|
<el-button type="primary" @click="handleSave">
|
||||||
|
<span v-if="actionType === 'definition'">恢 复</span>
|
||||||
|
<span v-else>保 存</span>
|
||||||
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -81,20 +86,23 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { useRoute, useRouter } from 'vue-router'
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
import { useMessage } from '@/hooks/web/useMessage'
|
import { useMessage } from '@/hooks/web/useMessage'
|
||||||
|
import { useTagsViewStore } from '@/store/modules/tagsView'
|
||||||
|
import { useUserStoreWithOut } from '@/store/modules/user'
|
||||||
import * as ModelApi from '@/api/bpm/model'
|
import * as ModelApi from '@/api/bpm/model'
|
||||||
import * as FormApi from '@/api/bpm/form'
|
import * as FormApi from '@/api/bpm/form'
|
||||||
import { CategoryApi, CategoryVO } from '@/api/bpm/category'
|
import { CategoryApi, CategoryVO } from '@/api/bpm/category'
|
||||||
import * as UserApi from '@/api/system/user'
|
import * as UserApi from '@/api/system/user'
|
||||||
import { useUserStoreWithOut } from '@/store/modules/user'
|
import * as DefinitionApi from '@/api/bpm/definition'
|
||||||
import { BpmModelFormType, BpmModelType, BpmAutoApproveType } from '@/utils/constants'
|
import { BpmModelFormType, BpmModelType, BpmAutoApproveType } from '@/utils/constants'
|
||||||
import BasicInfo from './BasicInfo.vue'
|
import BasicInfo from './BasicInfo.vue'
|
||||||
import FormDesign from './FormDesign.vue'
|
import FormDesign from './FormDesign.vue'
|
||||||
import ProcessDesign from './ProcessDesign.vue'
|
import ProcessDesign from './ProcessDesign.vue'
|
||||||
import { useTagsViewStore } from '@/store/modules/tagsView'
|
|
||||||
import ExtraSettings from './ExtraSettings.vue'
|
import ExtraSettings from './ExtraSettings.vue'
|
||||||
|
import { useTagsView } from '@/hooks/web/useTagsView'
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const { delView } = useTagsViewStore() // 视图操作
|
const { delView } = useTagsViewStore() // 视图操作
|
||||||
|
const tagsView = useTagsView()
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const message = useMessage()
|
const message = useMessage()
|
||||||
const userStore = useUserStoreWithOut()
|
const userStore = useUserStoreWithOut()
|
||||||
@ -177,20 +185,36 @@ const categoryList = ref<CategoryVO[]>([])
|
|||||||
const userList = ref<UserApi.UserVO[]>([])
|
const userList = ref<UserApi.UserVO[]>([])
|
||||||
|
|
||||||
/** 初始化数据 */
|
/** 初始化数据 */
|
||||||
|
const actionType = route.params.type as string
|
||||||
const initData = async () => {
|
const initData = async () => {
|
||||||
|
if (actionType === 'definition') {
|
||||||
|
// 情况一:流程定义场景(恢复)
|
||||||
|
const definitionId = route.params.id as string
|
||||||
|
const data = await DefinitionApi.getProcessDefinition(definitionId)
|
||||||
|
// 将 definition => model,最终赋值
|
||||||
|
data.type = data.modelType
|
||||||
|
delete data.modelType
|
||||||
|
data.id = data.modelId
|
||||||
|
delete data.modelId
|
||||||
|
if (data.simpleModel) {
|
||||||
|
data.simpleModel = JSON.parse(data.simpleModel)
|
||||||
|
}
|
||||||
|
formData.value = data
|
||||||
|
formData.value.startUserType = formData.value.startUserIds?.length > 0 ? 1 : 0
|
||||||
|
} else if (['update', 'copy'].includes(actionType)) {
|
||||||
|
// 情况二:修改场景/复制场景
|
||||||
const modelId = route.params.id as string
|
const modelId = route.params.id as string
|
||||||
if (modelId) {
|
|
||||||
// 修改场景
|
|
||||||
formData.value = await ModelApi.getModel(modelId)
|
formData.value = await ModelApi.getModel(modelId)
|
||||||
formData.value.startUserType = formData.value.startUserIds?.length > 0 ? 1 : 0
|
formData.value.startUserType = formData.value.startUserIds?.length > 0 ? 1 : 0
|
||||||
// 复制场景
|
// 特殊:复制场景
|
||||||
if (route.params.type === 'copy') {
|
if (actionType === 'copy') {
|
||||||
delete formData.value.id
|
delete formData.value.id
|
||||||
formData.value.name += '副本'
|
formData.value.name += '副本'
|
||||||
formData.value.key += '_copy'
|
formData.value.key += '_copy'
|
||||||
|
tagsView.setTitle('复制流程')
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// 新增场景
|
// 情况三:新增场景
|
||||||
formData.value.startUserType = 0 // 全体
|
formData.value.startUserType = 0 // 全体
|
||||||
formData.value.managerUserIds.push(userStore.getUser.id)
|
formData.value.managerUserIds.push(userStore.getUser.id)
|
||||||
}
|
}
|
||||||
@ -271,37 +295,31 @@ const handleSave = async () => {
|
|||||||
...formData.value
|
...formData.value
|
||||||
}
|
}
|
||||||
|
|
||||||
if (formData.value.id) {
|
if (actionType === 'definition') {
|
||||||
|
// 情况一:流程定义场景(恢复)
|
||||||
|
await ModelApi.updateModel(modelData)
|
||||||
|
// 提示成功
|
||||||
|
message.success('恢复成功,可点击【发布】按钮,进行发布模型')
|
||||||
|
} else if (actionType === 'update') {
|
||||||
// 修改场景
|
// 修改场景
|
||||||
await ModelApi.updateModel(modelData)
|
await ModelApi.updateModel(modelData)
|
||||||
// 询问是否发布流程
|
// 提示成功
|
||||||
try {
|
message.success('修改成功,可点击【发布】按钮,进行发布模型')
|
||||||
await message.confirm('修改流程成功,是否发布流程?')
|
} else if (actionType === 'copy') {
|
||||||
// 用户点击确认,执行发布
|
// 情况三:复制场景
|
||||||
await handleDeploy()
|
|
||||||
} catch {
|
|
||||||
// 用户点击取消,停留在当前页面
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// 新增场景
|
|
||||||
formData.value.id = await ModelApi.createModel(modelData)
|
formData.value.id = await ModelApi.createModel(modelData)
|
||||||
try {
|
// 提示成功
|
||||||
await message.confirm('流程创建成功,是否继续编辑?')
|
message.success('复制成功,可点击【发布】按钮,进行发布模型')
|
||||||
// 用户点击继续编辑,跳转到编辑页面
|
} else {
|
||||||
await nextTick()
|
// 情况四:新增场景
|
||||||
// 先删除当前页签
|
formData.value.id = await ModelApi.createModel(modelData)
|
||||||
delView(unref(router.currentRoute))
|
// 提示成功
|
||||||
// 跳转到编辑页面
|
message.success('新建成功,可点击【发布】按钮,进行发布模型')
|
||||||
await router.push({
|
|
||||||
name: 'BpmModelUpdate',
|
|
||||||
params: { id: formData.value.id }
|
|
||||||
})
|
|
||||||
} catch {
|
|
||||||
// 先删除当前页签
|
|
||||||
delView(unref(router.currentRoute))
|
|
||||||
// 用户点击返回列表
|
|
||||||
await router.push({ name: 'BpmModel' })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 返回列表页(排除更新的情况)
|
||||||
|
if (actionType !== 'update') {
|
||||||
|
await router.push({ name: 'BpmModel' })
|
||||||
}
|
}
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error('保存失败:', error)
|
console.error('保存失败:', error)
|
||||||
@ -346,7 +364,6 @@ const handleDeploy = async () => {
|
|||||||
/** 步骤切换处理 */
|
/** 步骤切换处理 */
|
||||||
const handleStepClick = async (index: number) => {
|
const handleStepClick = async (index: number) => {
|
||||||
try {
|
try {
|
||||||
console.log('index', index)
|
|
||||||
if (index !== 0) {
|
if (index !== 0) {
|
||||||
await validateBasic()
|
await validateBasic()
|
||||||
}
|
}
|
||||||
|
@ -74,7 +74,7 @@
|
|||||||
</template>
|
</template>
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { decodeFields, setConfAndFields2 } from '@/utils/formCreate'
|
import { decodeFields, setConfAndFields2 } from '@/utils/formCreate'
|
||||||
import { BpmModelType } from '@/utils/constants'
|
import { BpmModelType, BpmModelFormType } from '@/utils/constants'
|
||||||
import {
|
import {
|
||||||
CandidateStrategy,
|
CandidateStrategy,
|
||||||
NodeId,
|
NodeId,
|
||||||
@ -108,6 +108,7 @@ const fApi = ref<ApiAttrs>()
|
|||||||
// 指定审批人
|
// 指定审批人
|
||||||
const startUserSelectTasks: any = ref([]) // 发起人需要选择审批人或抄送人的任务列表
|
const startUserSelectTasks: any = ref([]) // 发起人需要选择审批人或抄送人的任务列表
|
||||||
const startUserSelectAssignees = ref({}) // 发起人选择审批人的数据
|
const startUserSelectAssignees = ref({}) // 发起人选择审批人的数据
|
||||||
|
const tempStartUserSelectAssignees = ref({}) // 历史发起人选择审批人的数据,用于每次表单变更时,临时保存
|
||||||
const bpmnXML: any = ref(null) // BPMN 数据
|
const bpmnXML: any = ref(null) // BPMN 数据
|
||||||
const simpleJson = ref<string | undefined>() // Simple 设计器数据 json 格式
|
const simpleJson = ref<string | undefined>() // Simple 设计器数据 json 格式
|
||||||
|
|
||||||
@ -121,7 +122,7 @@ const initProcessInfo = async (row: any, formVariables?: any) => {
|
|||||||
startUserSelectAssignees.value = {}
|
startUserSelectAssignees.value = {}
|
||||||
|
|
||||||
// 情况一:流程表单
|
// 情况一:流程表单
|
||||||
if (row.formType == 10) {
|
if (row.formType == BpmModelFormType.NORMAL) {
|
||||||
// 设置表单
|
// 设置表单
|
||||||
// 注意:需要从 formVariables 中,移除不在 row.formFields 的值。
|
// 注意:需要从 formVariables 中,移除不在 row.formFields 的值。
|
||||||
// 原因是:后端返回的 formVariables 里面,会有一些非表单的信息。例如说,某个流程节点的审批人。
|
// 原因是:后端返回的 formVariables 里面,会有一些非表单的信息。例如说,某个流程节点的审批人。
|
||||||
@ -137,8 +138,11 @@ const initProcessInfo = async (row: any, formVariables?: any) => {
|
|||||||
await nextTick()
|
await nextTick()
|
||||||
fApi.value?.btn.show(false) // 隐藏提交按钮
|
fApi.value?.btn.show(false) // 隐藏提交按钮
|
||||||
|
|
||||||
// 获取流程审批信息
|
// 获取流程审批信息,当再次发起时,流程审批节点要根据原始表单参数预测出来
|
||||||
await getApprovalDetail(row)
|
await getApprovalDetail({
|
||||||
|
id: row.id,
|
||||||
|
processVariablesStr: JSON.stringify(formVariables)
|
||||||
|
})
|
||||||
|
|
||||||
// 加载流程图
|
// 加载流程图
|
||||||
const processDefinitionDetail = await DefinitionApi.getProcessDefinition(row.id)
|
const processDefinitionDetail = await DefinitionApi.getProcessDefinition(row.id)
|
||||||
@ -155,32 +159,61 @@ const initProcessInfo = async (row: any, formVariables?: any) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 预测流程节点会因为输入的参数值而产生新的预测结果值,所以需重新预测一次 */
|
||||||
|
watch(
|
||||||
|
detailForm.value,
|
||||||
|
(newValue) => {
|
||||||
|
if (newValue && Object.keys(newValue.value).length > 0) {
|
||||||
|
// 记录之前的节点审批人
|
||||||
|
tempStartUserSelectAssignees.value = startUserSelectAssignees.value
|
||||||
|
startUserSelectAssignees.value = {}
|
||||||
|
// 加载最新的审批详情
|
||||||
|
getApprovalDetail({
|
||||||
|
id: props.selectProcessDefinition.id,
|
||||||
|
processVariablesStr: JSON.stringify(newValue.value) // 解决 GET 无法传递对象的问题,后端 String 再转 JSON
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
immediate: true
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
/** 获取审批详情 */
|
/** 获取审批详情 */
|
||||||
const getApprovalDetail = async (row: any) => {
|
const getApprovalDetail = async (row: any) => {
|
||||||
try {
|
try {
|
||||||
// TODO 获取审批详情,设置 activityId 为发起人节点(为了获取字段权限。暂时只对 Simple 设计器有效)
|
// TODO 获取审批详情,设置 activityId 为发起人节点(为了获取字段权限。暂时只对 Simple 设计器有效);@jason:这里可以去掉 activityId 么?
|
||||||
const data = await ProcessInstanceApi.getApprovalDetail({
|
const data = await ProcessInstanceApi.getApprovalDetail({
|
||||||
processDefinitionId: row.id,
|
processDefinitionId: row.id,
|
||||||
activityId: NodeId.START_USER_NODE_ID
|
activityId: NodeId.START_USER_NODE_ID,
|
||||||
|
processVariablesStr: row.processVariablesStr // 解决 GET 无法传递对象的问题,后端 String 再转 JSON
|
||||||
})
|
})
|
||||||
|
|
||||||
if (!data) {
|
if (!data) {
|
||||||
message.error('查询不到审批详情信息!')
|
message.error('查询不到审批详情信息!')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
// 获取审批节点,显示 Timeline 的数据
|
||||||
|
activityNodes.value = data.activityNodes
|
||||||
|
|
||||||
// 获取发起人自选的任务
|
// 获取发起人自选的任务
|
||||||
startUserSelectTasks.value = data.activityNodes?.filter(
|
startUserSelectTasks.value = data.activityNodes?.filter(
|
||||||
(node: ApprovalNodeInfo) => CandidateStrategy.START_USER_SELECT === node.candidateStrategy
|
(node: ApprovalNodeInfo) => CandidateStrategy.START_USER_SELECT === node.candidateStrategy
|
||||||
)
|
)
|
||||||
|
// 恢复之前的选择审批人
|
||||||
if (startUserSelectTasks.value?.length > 0) {
|
if (startUserSelectTasks.value?.length > 0) {
|
||||||
for (const node of startUserSelectTasks.value) {
|
for (const node of startUserSelectTasks.value) {
|
||||||
|
if (
|
||||||
|
tempStartUserSelectAssignees.value[node.id] &&
|
||||||
|
tempStartUserSelectAssignees.value[node.id].length > 0
|
||||||
|
) {
|
||||||
|
startUserSelectAssignees.value[node.id] = tempStartUserSelectAssignees.value[node.id]
|
||||||
|
} else {
|
||||||
startUserSelectAssignees.value[node.id] = []
|
startUserSelectAssignees.value[node.id] = []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 获取审批节点,显示 Timeline 的数据
|
|
||||||
activityNodes.value = data.activityNodes
|
|
||||||
// 获取表单字段权限
|
// 获取表单字段权限
|
||||||
const formFieldsPermission = data.formFieldsPermission
|
const formFieldsPermission = data.formFieldsPermission
|
||||||
// 设置表单字段权限
|
// 设置表单字段权限
|
||||||
|
@ -64,9 +64,9 @@
|
|||||||
class="w-32px h-32px"
|
class="w-32px h-32px"
|
||||||
/>
|
/>
|
||||||
<div v-else class="flow-icon">
|
<div v-else class="flow-icon">
|
||||||
<span style="font-size: 12px; color: #fff">{{
|
<span style="font-size: 12px; color: #fff">
|
||||||
sliceName(definition.name,0,2)
|
{{ subString(definition.name, 0, 2) }}
|
||||||
}}</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<el-text class="!ml-10px" size="large">{{ definition.name }}</el-text>
|
<el-text class="!ml-10px" size="large">{{ definition.name }}</el-text>
|
||||||
</div>
|
</div>
|
||||||
@ -97,7 +97,7 @@ import * as ProcessInstanceApi from '@/api/bpm/processInstance'
|
|||||||
import { CategoryApi, CategoryVO } from '@/api/bpm/category'
|
import { CategoryApi, CategoryVO } from '@/api/bpm/category'
|
||||||
import ProcessDefinitionDetail from './ProcessDefinitionDetail.vue'
|
import ProcessDefinitionDetail from './ProcessDefinitionDetail.vue'
|
||||||
import { groupBy } from 'lodash-es'
|
import { groupBy } from 'lodash-es'
|
||||||
import { sliceName } from '@/utils/index'
|
import { subString } from '@/utils/index'
|
||||||
|
|
||||||
defineOptions({ name: 'BpmProcessInstanceCreate' })
|
defineOptions({ name: 'BpmProcessInstanceCreate' })
|
||||||
|
|
||||||
|
@ -1,267 +0,0 @@
|
|||||||
<template>
|
|
||||||
<doc-alert title="流程发起、取消、重新发起" url="https://doc.iocoder.cn/bpm/process-instance/" />
|
|
||||||
|
|
||||||
<!-- 第一步,通过流程定义的列表,选择对应的流程 -->
|
|
||||||
<ContentWrap v-if="!selectProcessDefinition" v-loading="loading">
|
|
||||||
<el-tabs tab-position="left" v-model="categoryActive">
|
|
||||||
<el-tab-pane
|
|
||||||
:label="category.name"
|
|
||||||
:name="category.code"
|
|
||||||
:key="category.code"
|
|
||||||
v-for="category in categoryList"
|
|
||||||
>
|
|
||||||
<el-row :gutter="20">
|
|
||||||
<el-col
|
|
||||||
:lg="6"
|
|
||||||
:sm="12"
|
|
||||||
:xs="24"
|
|
||||||
v-for="definition in categoryProcessDefinitionList"
|
|
||||||
:key="definition.id"
|
|
||||||
>
|
|
||||||
<el-card
|
|
||||||
shadow="hover"
|
|
||||||
class="mb-20px cursor-pointer"
|
|
||||||
@click="handleSelect(definition)"
|
|
||||||
>
|
|
||||||
<template #default>
|
|
||||||
<div class="flex">
|
|
||||||
<el-image :src="definition.icon" class="w-32px h-32px" />
|
|
||||||
<el-text class="!ml-10px" size="large">{{ definition.name }}</el-text>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</el-card>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
</el-tab-pane>
|
|
||||||
</el-tabs>
|
|
||||||
</ContentWrap>
|
|
||||||
|
|
||||||
<!-- 第二步,填写表单,进行流程的提交 -->
|
|
||||||
<ContentWrap v-else>
|
|
||||||
<el-card class="box-card">
|
|
||||||
<div class="clearfix">
|
|
||||||
<span class="el-icon-document">申请信息【{{ selectProcessDefinition.name }}】</span>
|
|
||||||
<el-button style="float: right" type="primary" @click="selectProcessDefinition = undefined">
|
|
||||||
<Icon icon="ep:delete" /> 选择其它流程
|
|
||||||
</el-button>
|
|
||||||
</div>
|
|
||||||
<el-col :span="16" :offset="6" style="margin-top: 20px">
|
|
||||||
<form-create
|
|
||||||
:rule="detailForm.rule"
|
|
||||||
v-model:api="fApi"
|
|
||||||
v-model="detailForm.value"
|
|
||||||
:option="detailForm.option"
|
|
||||||
@submit="submitForm"
|
|
||||||
>
|
|
||||||
<template #type-startUserSelect>
|
|
||||||
<el-col :span="24">
|
|
||||||
<el-card class="mb-10px">
|
|
||||||
<template #header>指定审批人</template>
|
|
||||||
<el-form
|
|
||||||
:model="startUserSelectAssignees"
|
|
||||||
:rules="startUserSelectAssigneesFormRules"
|
|
||||||
ref="startUserSelectAssigneesFormRef"
|
|
||||||
>
|
|
||||||
<el-form-item
|
|
||||||
v-for="userTask in startUserSelectTasks"
|
|
||||||
:key="userTask.id"
|
|
||||||
:label="`任务【${userTask.name}】`"
|
|
||||||
:prop="userTask.id"
|
|
||||||
>
|
|
||||||
<el-select
|
|
||||||
v-model="startUserSelectAssignees[userTask.id]"
|
|
||||||
multiple
|
|
||||||
placeholder="请选择审批人"
|
|
||||||
>
|
|
||||||
<el-option
|
|
||||||
v-for="user in userList"
|
|
||||||
:key="user.id"
|
|
||||||
:label="user.nickname"
|
|
||||||
:value="user.id"
|
|
||||||
/>
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
</el-form>
|
|
||||||
</el-card>
|
|
||||||
</el-col>
|
|
||||||
</template>
|
|
||||||
</form-create>
|
|
||||||
</el-col>
|
|
||||||
</el-card>
|
|
||||||
<!-- 流程图预览 -->
|
|
||||||
<ProcessInstanceBpmnViewer :bpmn-xml="bpmnXML as any" />
|
|
||||||
</ContentWrap>
|
|
||||||
</template>
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import * as DefinitionApi from '@/api/bpm/definition'
|
|
||||||
import * as ProcessInstanceApi from '@/api/bpm/processInstance'
|
|
||||||
import { decodeFields, setConfAndFields2 } from '@/utils/formCreate'
|
|
||||||
import type { ApiAttrs } from '@form-create/element-ui/types/config'
|
|
||||||
import ProcessInstanceBpmnViewer from '../detail/ProcessInstanceBpmnViewer.vue'
|
|
||||||
import { CategoryApi } from '@/api/bpm/category'
|
|
||||||
import { useTagsViewStore } from '@/store/modules/tagsView'
|
|
||||||
import * as UserApi from '@/api/system/user'
|
|
||||||
|
|
||||||
defineOptions({ name: 'BpmProcessInstanceCreate' })
|
|
||||||
|
|
||||||
const route = useRoute() // 路由
|
|
||||||
const { push, currentRoute } = useRouter() // 路由
|
|
||||||
const message = useMessage() // 消息
|
|
||||||
const { delView } = useTagsViewStore() // 视图操作
|
|
||||||
|
|
||||||
const processInstanceId = route.query.processInstanceId
|
|
||||||
const loading = ref(true) // 加载中
|
|
||||||
const categoryList = ref([]) // 分类的列表
|
|
||||||
const categoryActive = ref('') // 选中的分类
|
|
||||||
const processDefinitionList = ref([]) // 流程定义的列表
|
|
||||||
|
|
||||||
/** 查询列表 */
|
|
||||||
const getList = async () => {
|
|
||||||
loading.value = true
|
|
||||||
try {
|
|
||||||
// 流程分类
|
|
||||||
categoryList.value = await CategoryApi.getCategorySimpleList()
|
|
||||||
if (categoryList.value.length > 0) {
|
|
||||||
categoryActive.value = categoryList.value[0].code
|
|
||||||
}
|
|
||||||
// 流程定义
|
|
||||||
processDefinitionList.value = await DefinitionApi.getProcessDefinitionList({
|
|
||||||
suspensionState: 1
|
|
||||||
})
|
|
||||||
|
|
||||||
// 如果 processInstanceId 非空,说明是重新发起
|
|
||||||
if (processInstanceId?.length > 0) {
|
|
||||||
const processInstance = await ProcessInstanceApi.getProcessInstance(processInstanceId)
|
|
||||||
if (!processInstance) {
|
|
||||||
message.error('重新发起流程失败,原因:流程实例不存在')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const processDefinition = processDefinitionList.value.find(
|
|
||||||
(item) => item.key == processInstance.processDefinition?.key
|
|
||||||
)
|
|
||||||
if (!processDefinition) {
|
|
||||||
message.error('重新发起流程失败,原因:流程定义不存在')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
await handleSelect(processDefinition, processInstance.formVariables)
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
loading.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 选中分类对应的流程定义列表 */
|
|
||||||
const categoryProcessDefinitionList = computed(() => {
|
|
||||||
return processDefinitionList.value.filter((item) => item.category == categoryActive.value)
|
|
||||||
})
|
|
||||||
|
|
||||||
// ========== 表单相关 ==========
|
|
||||||
const fApi = ref<ApiAttrs>()
|
|
||||||
const detailForm = ref({
|
|
||||||
rule: [],
|
|
||||||
option: {},
|
|
||||||
value: {}
|
|
||||||
}) // 流程表单详情
|
|
||||||
const selectProcessDefinition = ref() // 选择的流程定义
|
|
||||||
|
|
||||||
// 指定审批人
|
|
||||||
const bpmnXML = ref(null) // BPMN 数据
|
|
||||||
const startUserSelectTasks = ref([]) // 发起人需要选择审批人的用户任务列表
|
|
||||||
const startUserSelectAssignees = ref({}) // 发起人选择审批人的数据
|
|
||||||
const startUserSelectAssigneesFormRef = ref() // 发起人选择审批人的表单 Ref
|
|
||||||
const startUserSelectAssigneesFormRules = ref({}) // 发起人选择审批人的表单 Rules
|
|
||||||
const userList = ref<any[]>([]) // 用户列表
|
|
||||||
|
|
||||||
/** 处理选择流程的按钮操作 **/
|
|
||||||
const handleSelect = async (row, formVariables) => {
|
|
||||||
// 设置选择的流程
|
|
||||||
selectProcessDefinition.value = row
|
|
||||||
|
|
||||||
// 重置指定审批人
|
|
||||||
startUserSelectTasks.value = []
|
|
||||||
startUserSelectAssignees.value = {}
|
|
||||||
startUserSelectAssigneesFormRules.value = {}
|
|
||||||
|
|
||||||
// 情况一:流程表单
|
|
||||||
if (row.formType == 10) {
|
|
||||||
// 设置表单
|
|
||||||
// 注意:需要从 formVariables 中,移除不在 row.formFields 的值。
|
|
||||||
// 原因是:后端返回的 formVariables 里面,会有一些非表单的信息。例如说,某个流程节点的审批人。
|
|
||||||
// 这样,就可能导致一个流程被审批不通过后,重新发起时,会直接后端报错!!!
|
|
||||||
const allowedFields = decodeFields(row.formFields).map((fieldObj: any) => fieldObj.field)
|
|
||||||
for (const key in formVariables) {
|
|
||||||
if (!allowedFields.includes(key)) {
|
|
||||||
delete formVariables[key]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
setConfAndFields2(detailForm, row.formConf, row.formFields, formVariables)
|
|
||||||
|
|
||||||
// 加载流程图
|
|
||||||
const processDefinitionDetail = await DefinitionApi.getProcessDefinition(row.id)
|
|
||||||
if (processDefinitionDetail) {
|
|
||||||
bpmnXML.value = processDefinitionDetail.bpmnXml
|
|
||||||
startUserSelectTasks.value = processDefinitionDetail.startUserSelectTasks
|
|
||||||
|
|
||||||
// 设置指定审批人
|
|
||||||
if (startUserSelectTasks.value?.length > 0) {
|
|
||||||
detailForm.value.rule.push({
|
|
||||||
type: 'startUserSelect',
|
|
||||||
props: {
|
|
||||||
title: '指定审批人'
|
|
||||||
}
|
|
||||||
})
|
|
||||||
// 设置校验规则
|
|
||||||
for (const userTask of startUserSelectTasks.value) {
|
|
||||||
startUserSelectAssignees.value[userTask.id] = []
|
|
||||||
startUserSelectAssigneesFormRules.value[userTask.id] = [
|
|
||||||
{ required: true, message: '请选择审批人', trigger: 'blur' }
|
|
||||||
]
|
|
||||||
}
|
|
||||||
// 加载用户列表
|
|
||||||
userList.value = await UserApi.getSimpleUserList()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 情况二:业务表单
|
|
||||||
} else if (row.formCustomCreatePath) {
|
|
||||||
await push({
|
|
||||||
path: row.formCustomCreatePath
|
|
||||||
})
|
|
||||||
// 这里暂时无需加载流程图,因为跳出到另外个 Tab;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 提交按钮 */
|
|
||||||
const submitForm = async (formData) => {
|
|
||||||
if (!fApi.value || !selectProcessDefinition.value) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// 如果有指定审批人,需要校验
|
|
||||||
if (startUserSelectTasks.value?.length > 0) {
|
|
||||||
await startUserSelectAssigneesFormRef.value.validate()
|
|
||||||
}
|
|
||||||
|
|
||||||
// 提交请求
|
|
||||||
fApi.value.btn.loading(true)
|
|
||||||
try {
|
|
||||||
await ProcessInstanceApi.createProcessInstance({
|
|
||||||
processDefinitionId: selectProcessDefinition.value.id,
|
|
||||||
variables: formData,
|
|
||||||
startUserSelectAssignees: startUserSelectAssignees.value
|
|
||||||
})
|
|
||||||
// 提示
|
|
||||||
message.success('发起流程成功')
|
|
||||||
// 跳转回去
|
|
||||||
delView(unref(currentRoute))
|
|
||||||
await push({
|
|
||||||
name: 'BpmProcessInstanceMy'
|
|
||||||
})
|
|
||||||
} finally {
|
|
||||||
fApi.value.btn.loading(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 初始化 */
|
|
||||||
onMounted(() => {
|
|
||||||
getList()
|
|
||||||
})
|
|
||||||
</script>
|
|
@ -36,14 +36,27 @@
|
|||||||
:rule="approveForm.rule"
|
:rule="approveForm.rule"
|
||||||
/>
|
/>
|
||||||
</el-card>
|
</el-card>
|
||||||
<el-form-item label="审批意见" prop="reason">
|
<el-form-item :label="`${nodeTypeName}意见`" prop="reason">
|
||||||
<el-input
|
<el-input
|
||||||
v-model="approveReasonForm.reason"
|
v-model="approveReasonForm.reason"
|
||||||
placeholder="请输入审批意见"
|
:placeholder="`请输入${nodeTypeName}意见`"
|
||||||
type="textarea"
|
type="textarea"
|
||||||
:rows="4"
|
:rows="4"
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
label="下一个节点的审批人"
|
||||||
|
prop="nextAssignees"
|
||||||
|
v-if="nextAssigneesActivityNode.length > 0"
|
||||||
|
>
|
||||||
|
<div class="ml-10px -mt-15px -mb-35px">
|
||||||
|
<ProcessInstanceTimeline
|
||||||
|
:activity-nodes="nextAssigneesActivityNode"
|
||||||
|
:show-status-icon="false"
|
||||||
|
@select-user-confirm="selectNextAssigneesConfirm"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
<el-form-item
|
<el-form-item
|
||||||
v-if="runningTask.signEnable"
|
v-if="runningTask.signEnable"
|
||||||
label="签名"
|
label="签名"
|
||||||
@ -66,7 +79,7 @@
|
|||||||
>
|
>
|
||||||
{{ getButtonDisplayName(OperationButtonType.APPROVE) }}
|
{{ getButtonDisplayName(OperationButtonType.APPROVE) }}
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button @click="closePropover('approve', approveFormRef)"> 取消 </el-button>
|
<el-button @click="closePopover('approve', approveFormRef)"> 取消 </el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
</div>
|
</div>
|
||||||
@ -111,7 +124,7 @@
|
|||||||
>
|
>
|
||||||
{{ getButtonDisplayName(OperationButtonType.REJECT) }}
|
{{ getButtonDisplayName(OperationButtonType.REJECT) }}
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button @click="closePropover('reject', rejectFormRef)"> 取消 </el-button>
|
<el-button @click="closePopover('reject', rejectFormRef)"> 取消 </el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
</div>
|
</div>
|
||||||
@ -169,7 +182,7 @@
|
|||||||
<el-button :disabled="formLoading" type="primary" @click="handleCopy">
|
<el-button :disabled="formLoading" type="primary" @click="handleCopy">
|
||||||
{{ getButtonDisplayName(OperationButtonType.COPY) }}
|
{{ getButtonDisplayName(OperationButtonType.COPY) }}
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button @click="closePropover('copy', copyFormRef)"> 取消 </el-button>
|
<el-button @click="closePopover('copy', copyFormRef)"> 取消 </el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
</div>
|
</div>
|
||||||
@ -221,7 +234,7 @@
|
|||||||
<el-button :disabled="formLoading" type="primary" @click="handleTransfer()">
|
<el-button :disabled="formLoading" type="primary" @click="handleTransfer()">
|
||||||
{{ getButtonDisplayName(OperationButtonType.TRANSFER) }}
|
{{ getButtonDisplayName(OperationButtonType.TRANSFER) }}
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button @click="closePropover('transfer', transferFormRef)"> 取消 </el-button>
|
<el-button @click="closePopover('transfer', transferFormRef)"> 取消 </el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
</div>
|
</div>
|
||||||
@ -273,7 +286,7 @@
|
|||||||
<el-button :disabled="formLoading" type="primary" @click="handleDelegate()">
|
<el-button :disabled="formLoading" type="primary" @click="handleDelegate()">
|
||||||
{{ getButtonDisplayName(OperationButtonType.DELEGATE) }}
|
{{ getButtonDisplayName(OperationButtonType.DELEGATE) }}
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button @click="closePropover('delegate', delegateFormRef)"> 取消 </el-button>
|
<el-button @click="closePopover('delegate', delegateFormRef)"> 取消 </el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
</div>
|
</div>
|
||||||
@ -328,7 +341,7 @@
|
|||||||
<el-button :disabled="formLoading" type="primary" @click="handlerAddSign('after')">
|
<el-button :disabled="formLoading" type="primary" @click="handlerAddSign('after')">
|
||||||
向后{{ getButtonDisplayName(OperationButtonType.ADD_SIGN) }}
|
向后{{ getButtonDisplayName(OperationButtonType.ADD_SIGN) }}
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button @click="closePropover('addSign', addSignFormRef)"> 取消 </el-button>
|
<el-button @click="closePopover('addSign', addSignFormRef)"> 取消 </el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
</div>
|
</div>
|
||||||
@ -379,7 +392,7 @@
|
|||||||
<el-button :disabled="formLoading" type="primary" @click="handlerDeleteSign()">
|
<el-button :disabled="formLoading" type="primary" @click="handlerDeleteSign()">
|
||||||
减签
|
减签
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button @click="closePropover('deleteSign', deleteSignFormRef)"> 取消 </el-button>
|
<el-button @click="closePopover('deleteSign', deleteSignFormRef)"> 取消 </el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
</div>
|
</div>
|
||||||
@ -431,7 +444,7 @@
|
|||||||
<el-button :disabled="formLoading" type="primary" @click="handleReturn()">
|
<el-button :disabled="formLoading" type="primary" @click="handleReturn()">
|
||||||
{{ getButtonDisplayName(OperationButtonType.RETURN) }}
|
{{ getButtonDisplayName(OperationButtonType.RETURN) }}
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button @click="closePropover('return', returnFormRef)"> 取消 </el-button>
|
<el-button @click="closePopover('return', returnFormRef)"> 取消 </el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
</div>
|
</div>
|
||||||
@ -475,7 +488,7 @@
|
|||||||
<el-button :disabled="formLoading" type="primary" @click="handleCancel()">
|
<el-button :disabled="formLoading" type="primary" @click="handleCancel()">
|
||||||
确认
|
确认
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button @click="closePropover('cancel', cancelFormRef)"> 取消 </el-button>
|
<el-button @click="closePopover('cancel', cancelFormRef)"> 取消 </el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
</div>
|
</div>
|
||||||
@ -504,12 +517,16 @@ import * as TaskApi from '@/api/bpm/task'
|
|||||||
import * as ProcessInstanceApi from '@/api/bpm/processInstance'
|
import * as ProcessInstanceApi from '@/api/bpm/processInstance'
|
||||||
import * as UserApi from '@/api/system/user'
|
import * as UserApi from '@/api/system/user'
|
||||||
import {
|
import {
|
||||||
|
NodeType,
|
||||||
OPERATION_BUTTON_NAME,
|
OPERATION_BUTTON_NAME,
|
||||||
OperationButtonType
|
OperationButtonType,
|
||||||
|
CandidateStrategy
|
||||||
} from '@/components/SimpleProcessDesignerV2/src/consts'
|
} from '@/components/SimpleProcessDesignerV2/src/consts'
|
||||||
import { BpmModelFormType, BpmProcessInstanceStatus } from '@/utils/constants'
|
import { BpmModelFormType, BpmProcessInstanceStatus } from '@/utils/constants'
|
||||||
import type { FormInstance, FormRules } from 'element-plus'
|
import type { FormInstance, FormRules } from 'element-plus'
|
||||||
import SignDialog from './SignDialog.vue'
|
import SignDialog from './SignDialog.vue'
|
||||||
|
import ProcessInstanceTimeline from '../detail/ProcessInstanceTimeline.vue'
|
||||||
|
import { isEmpty } from '@/utils/is'
|
||||||
|
|
||||||
defineOptions({ name: 'ProcessInstanceBtnContainer' })
|
defineOptions({ name: 'ProcessInstanceBtnContainer' })
|
||||||
|
|
||||||
@ -546,22 +563,29 @@ const returnList = ref([] as any) // 退回节点
|
|||||||
const runningTask = ref<any>() // 运行中的任务
|
const runningTask = ref<any>() // 运行中的任务
|
||||||
const approveForm = ref<any>({}) // 审批通过时,额外的补充信息
|
const approveForm = ref<any>({}) // 审批通过时,额外的补充信息
|
||||||
const approveFormFApi = ref<any>({}) // approveForms 的 fAPi
|
const approveFormFApi = ref<any>({}) // approveForms 的 fAPi
|
||||||
|
const nodeTypeName = ref('审批') // 节点类型名称
|
||||||
|
|
||||||
// 审批通过意见表单
|
// 审批通过意见表单
|
||||||
const reasonRequire = ref()
|
const reasonRequire = ref()
|
||||||
const approveFormRef = ref<FormInstance>()
|
const approveFormRef = ref<FormInstance>()
|
||||||
const signRef = ref()
|
const signRef = ref()
|
||||||
const approveSignFormRef = ref()
|
const approveSignFormRef = ref()
|
||||||
|
const nextAssigneesActivityNode = ref<ProcessInstanceApi.ApprovalNodeInfo[]>([]) // 下一个审批节点信息
|
||||||
const approveReasonForm = reactive({
|
const approveReasonForm = reactive({
|
||||||
reason: '',
|
reason: '',
|
||||||
signPicUrl: ''
|
signPicUrl: '',
|
||||||
|
nextAssignees: {}
|
||||||
})
|
})
|
||||||
const approveReasonRule = computed(() => {
|
const approveReasonRule = computed(() => {
|
||||||
return {
|
return {
|
||||||
reason: [{ required: reasonRequire.value, message: '审批意见不能为空', trigger: 'blur' }],
|
reason: [
|
||||||
signPicUrl: [{ required: true, message: '签名不能为空', trigger: 'change' }]
|
{ required: reasonRequire.value, message: nodeTypeName + '意见不能为空', trigger: 'blur' }
|
||||||
|
],
|
||||||
|
signPicUrl: [{ required: true, message: '签名不能为空', trigger: 'change' }],
|
||||||
|
nextAssignees: [{ required: true, message: '审批人不能为空', trigger: 'blur' }]
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// 拒绝表单
|
// 拒绝表单
|
||||||
const rejectFormRef = ref<FormInstance>()
|
const rejectFormRef = ref<FormInstance>()
|
||||||
const rejectReasonForm = reactive({
|
const rejectReasonForm = reactive({
|
||||||
@ -668,6 +692,7 @@ const openPopover = async (type: string) => {
|
|||||||
message.warning('表单校验不通过,请先完善表单!!')
|
message.warning('表单校验不通过,请先完善表单!!')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
initNextAssigneesFormField()
|
||||||
}
|
}
|
||||||
if (type === 'return') {
|
if (type === 'return') {
|
||||||
// 获取退回节点
|
// 获取退回节点
|
||||||
@ -685,11 +710,58 @@ const openPopover = async (type: string) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** 关闭气泡卡 */
|
/** 关闭气泡卡 */
|
||||||
const closePropover = (type: string, formRef: FormInstance | undefined) => {
|
const closePopover = (type: string, formRef: FormInstance | undefined) => {
|
||||||
if (formRef) {
|
if (formRef) {
|
||||||
formRef.resetFields()
|
formRef.resetFields()
|
||||||
}
|
}
|
||||||
popOverVisible.value[type] = false
|
popOverVisible.value[type] = false
|
||||||
|
nextAssigneesActivityNode.value = []
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 流程通过时,根据表单变量查询新的流程节点,判断下一个节点类型是否为自选审批人 */
|
||||||
|
const initNextAssigneesFormField = async () => {
|
||||||
|
// 获取修改的流程变量, 暂时只支持流程表单
|
||||||
|
const variables = getUpdatedProcessInstanceVariables()
|
||||||
|
const data = await ProcessInstanceApi.getNextApprovalNodes({
|
||||||
|
processInstanceId: props.processInstance.id,
|
||||||
|
taskId: runningTask.value.id,
|
||||||
|
processVariablesStr: JSON.stringify(variables)
|
||||||
|
})
|
||||||
|
if (data && data.length > 0) {
|
||||||
|
data.forEach((node: any) => {
|
||||||
|
if (
|
||||||
|
// 情况一:当前节点没有审批人,并且是发起人自选
|
||||||
|
(isEmpty(node.tasks) &&
|
||||||
|
isEmpty(node.candidateUsers) &&
|
||||||
|
CandidateStrategy.START_USER_SELECT === node.candidateStrategy) ||
|
||||||
|
// 情况二:当前节点是审批人自选
|
||||||
|
CandidateStrategy.APPROVE_USER_SELECT === node.candidateStrategy
|
||||||
|
) {
|
||||||
|
nextAssigneesActivityNode.value.push(node)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 选择下一个节点的审批人 */
|
||||||
|
const selectNextAssigneesConfirm = (id: string, userList: any[]) => {
|
||||||
|
approveReasonForm.nextAssignees[id] = userList?.map((item: any) => item.id)
|
||||||
|
}
|
||||||
|
/** 审批通过时,校验每个自选审批人的节点是否都已配置了审批人 */
|
||||||
|
const validateNextAssignees = () => {
|
||||||
|
// TODO @小北:可以考虑 Object.keys(nextAssigneesActivityNode.value).length === 0) return true;减少括号层级
|
||||||
|
// 如果需要自选审批人,则校验自选审批人
|
||||||
|
if (Object.keys(nextAssigneesActivityNode.value).length > 0) {
|
||||||
|
// 校验每个节点是否都已配置审批人
|
||||||
|
for (const item of nextAssigneesActivityNode.value) {
|
||||||
|
if (isEmpty(approveReasonForm.nextAssignees[item.id])) {
|
||||||
|
// TODO @小北:可以打印下节点名,嘿嘿。
|
||||||
|
message.warning('下一个节点的审批人不能为空!')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 处理审批通过和不通过的操作 */
|
/** 处理审批通过和不通过的操作 */
|
||||||
@ -699,15 +771,24 @@ const handleAudit = async (pass: boolean, formRef: FormInstance | undefined) =>
|
|||||||
// 校验表单
|
// 校验表单
|
||||||
if (!formRef) return
|
if (!formRef) return
|
||||||
await formRef.validate()
|
await formRef.validate()
|
||||||
|
// 校验流程表单必填字段
|
||||||
|
const valid = await validateNormalForm()
|
||||||
|
if (!valid) {
|
||||||
|
message.warning('表单校验不通过,请先完善表单!!')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if (pass) {
|
if (pass) {
|
||||||
// 获取修改的流程变量, 暂时只支持流程表单
|
const nextAssigneesValid = validateNextAssignees()
|
||||||
|
if (!nextAssigneesValid) return
|
||||||
const variables = getUpdatedProcessInstanceVariables()
|
const variables = getUpdatedProcessInstanceVariables()
|
||||||
// 审批通过数据
|
// 审批通过数据
|
||||||
const data = {
|
const data = {
|
||||||
id: runningTask.value.id,
|
id: runningTask.value.id,
|
||||||
reason: approveReasonForm.reason,
|
reason: approveReasonForm.reason,
|
||||||
variables // 审批通过, 把修改的字段值赋于流程实例变量
|
variables, // 审批通过, 把修改的字段值赋于流程实例变量
|
||||||
}
|
nextAssignees: approveReasonForm.nextAssignees // 下个自选节点选择的审批人信息
|
||||||
|
} as any
|
||||||
// 签名
|
// 签名
|
||||||
if (runningTask.value.signEnable) {
|
if (runningTask.value.signEnable) {
|
||||||
data.signPicUrl = approveReasonForm.signPicUrl
|
data.signPicUrl = approveReasonForm.signPicUrl
|
||||||
@ -722,6 +803,7 @@ const handleAudit = async (pass: boolean, formRef: FormInstance | undefined) =>
|
|||||||
}
|
}
|
||||||
await TaskApi.approveTask(data)
|
await TaskApi.approveTask(data)
|
||||||
popOverVisible.value.approve = false
|
popOverVisible.value.approve = false
|
||||||
|
nextAssigneesActivityNode.value = []
|
||||||
message.success('审批通过成功')
|
message.success('审批通过成功')
|
||||||
} else {
|
} else {
|
||||||
// 审批不通过数据
|
// 审批不通过数据
|
||||||
@ -969,9 +1051,10 @@ const getButtonDisplayName = (btnType: OperationButtonType) => {
|
|||||||
|
|
||||||
const loadTodoTask = (task: any) => {
|
const loadTodoTask = (task: any) => {
|
||||||
approveForm.value = {}
|
approveForm.value = {}
|
||||||
approveFormFApi.value = {}
|
|
||||||
runningTask.value = task
|
runningTask.value = task
|
||||||
|
approveFormFApi.value = {}
|
||||||
reasonRequire.value = task?.reasonRequire ?? false
|
reasonRequire.value = task?.reasonRequire ?? false
|
||||||
|
nodeTypeName.value = task?.nodeType === NodeType.TRANSACTOR_NODE ? '办理' : '审批'
|
||||||
// 处理 approve 表单.
|
// 处理 approve 表单.
|
||||||
if (task && task.formId && task.formConf) {
|
if (task && task.formId && task.formConf) {
|
||||||
const tempApproveForm = {}
|
const tempApproveForm = {}
|
||||||
@ -997,6 +1080,11 @@ const validateNormalForm = async () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO @小北 TO @芋道
|
||||||
|
* 问题:这里存在一种场景会出现问题,流程发起后,A节点审批完成,B节点没有可编辑的流程字段且B节点为自选审批人节点,会导致流程审批人为空,
|
||||||
|
* 原因:因为没有可编辑的流程字段时props.writableFields为空,参数variables传递时也为空
|
||||||
|
*/
|
||||||
/** 从可以编辑的流程表单字段,获取需要修改的流程实例的变量 */
|
/** 从可以编辑的流程表单字段,获取需要修改的流程实例的变量 */
|
||||||
const getUpdatedProcessInstanceVariables = () => {
|
const getUpdatedProcessInstanceVariables = () => {
|
||||||
const variables = {}
|
const variables = {}
|
||||||
|
@ -42,13 +42,13 @@ watch(
|
|||||||
const finishedSequenceFlowActivityIds: string[] = newModelView.finishedSequenceFlowActivityIds
|
const finishedSequenceFlowActivityIds: string[] = newModelView.finishedSequenceFlowActivityIds
|
||||||
setSimpleModelNodeTaskStatus(
|
setSimpleModelNodeTaskStatus(
|
||||||
newModelView.simpleModel,
|
newModelView.simpleModel,
|
||||||
newModelView.processInstance.status,
|
newModelView.processInstance?.status,
|
||||||
rejectedTaskActivityIds,
|
rejectedTaskActivityIds,
|
||||||
unfinishedTaskActivityIds,
|
unfinishedTaskActivityIds,
|
||||||
finishedActivityIds,
|
finishedActivityIds,
|
||||||
finishedSequenceFlowActivityIds
|
finishedSequenceFlowActivityIds
|
||||||
)
|
)
|
||||||
simpleModel.value = newModelView.simpleModel
|
simpleModel.value = newModelView.simpleModel ? newModelView.simpleModel : {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@ -84,7 +84,9 @@ const setSimpleModelNodeTaskStatus = (
|
|||||||
// 审批节点
|
// 审批节点
|
||||||
if (
|
if (
|
||||||
simpleModel.type === NodeType.START_USER_NODE ||
|
simpleModel.type === NodeType.START_USER_NODE ||
|
||||||
simpleModel.type === NodeType.USER_TASK_NODE
|
simpleModel.type === NodeType.USER_TASK_NODE ||
|
||||||
|
simpleModel.type === NodeType.TRANSACTOR_NODE ||
|
||||||
|
simpleModel.type === NodeType.CHILD_PROCESS_NODE
|
||||||
) {
|
) {
|
||||||
simpleModel.activityStatus = TaskStatusEnum.NOT_START
|
simpleModel.activityStatus = TaskStatusEnum.NOT_START
|
||||||
if (rejectedTaskActivityIds.includes(simpleModel.id)) {
|
if (rejectedTaskActivityIds.includes(simpleModel.id)) {
|
||||||
@ -169,5 +171,4 @@ const setSimpleModelNodeTaskStatus = (
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped></style>
|
||||||
</style>
|
|
||||||
|
@ -43,7 +43,8 @@
|
|||||||
v-if="
|
v-if="
|
||||||
isEmpty(activity.tasks) &&
|
isEmpty(activity.tasks) &&
|
||||||
isEmpty(activity.candidateUsers) &&
|
isEmpty(activity.candidateUsers) &&
|
||||||
CandidateStrategy.START_USER_SELECT === activity.candidateStrategy
|
(CandidateStrategy.START_USER_SELECT === activity.candidateStrategy ||
|
||||||
|
CandidateStrategy.APPROVE_USER_SELECT === activity.candidateStrategy)
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
<!-- && activity.nodeType === NodeType.USER_TASK_NODE -->
|
<!-- && activity.nodeType === NodeType.USER_TASK_NODE -->
|
||||||
@ -121,6 +122,7 @@
|
|||||||
"
|
"
|
||||||
class="text-#a5a5a5 text-13px mt-1 w-full bg-#f8f8fa p2 rounded-md"
|
class="text-#a5a5a5 text-13px mt-1 w-full bg-#f8f8fa p2 rounded-md"
|
||||||
>
|
>
|
||||||
|
<!-- TODO lesan:这里如果是办理,需要是办理意见 -->
|
||||||
审批意见:{{ task.reason }}
|
审批意见:{{ task.reason }}
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
@ -179,6 +181,8 @@ import copySvg from '@/assets/svgs/bpm/copy.svg'
|
|||||||
import conditionSvg from '@/assets/svgs/bpm/condition.svg'
|
import conditionSvg from '@/assets/svgs/bpm/condition.svg'
|
||||||
import parallelSvg from '@/assets/svgs/bpm/parallel.svg'
|
import parallelSvg from '@/assets/svgs/bpm/parallel.svg'
|
||||||
import finishSvg from '@/assets/svgs/bpm/finish.svg'
|
import finishSvg from '@/assets/svgs/bpm/finish.svg'
|
||||||
|
import transactorSvg from '@/assets/svgs/bpm/transactor.svg'
|
||||||
|
import childProcessSvg from '@/assets/svgs/bpm/child-process.svg'
|
||||||
|
|
||||||
defineOptions({ name: 'BpmProcessInstanceTimeline' })
|
defineOptions({ name: 'BpmProcessInstanceTimeline' })
|
||||||
withDefaults(
|
withDefaults(
|
||||||
@ -240,12 +244,16 @@ const nodeTypeSvgMap = {
|
|||||||
[NodeType.START_USER_NODE]: { color: '#909398', svg: starterSvg },
|
[NodeType.START_USER_NODE]: { color: '#909398', svg: starterSvg },
|
||||||
// 审批人节点
|
// 审批人节点
|
||||||
[NodeType.USER_TASK_NODE]: { color: '#ff943e', svg: auditorSvg },
|
[NodeType.USER_TASK_NODE]: { color: '#ff943e', svg: auditorSvg },
|
||||||
|
// 办理人节点
|
||||||
|
[NodeType.TRANSACTOR_NODE]: { color: '#ff943e', svg: transactorSvg },
|
||||||
// 抄送人节点
|
// 抄送人节点
|
||||||
[NodeType.COPY_TASK_NODE]: { color: '#3296fb', svg: copySvg },
|
[NodeType.COPY_TASK_NODE]: { color: '#3296fb', svg: copySvg },
|
||||||
// 条件分支节点
|
// 条件分支节点
|
||||||
[NodeType.CONDITION_NODE]: { color: '#14bb83', svg: conditionSvg },
|
[NodeType.CONDITION_NODE]: { color: '#14bb83', svg: conditionSvg },
|
||||||
// 并行分支节点
|
// 并行分支节点
|
||||||
[NodeType.PARALLEL_BRANCH_NODE]: { color: '#14bb83', svg: parallelSvg }
|
[NodeType.PARALLEL_BRANCH_NODE]: { color: '#14bb83', svg: parallelSvg },
|
||||||
|
// 子流程节点
|
||||||
|
[NodeType.CHILD_PROCESS_NODE]: { color: '#14bb83', svg: childProcessSvg }
|
||||||
}
|
}
|
||||||
|
|
||||||
// 只有只有状态是 -1、0、1 才展示头像右小角状态小icon
|
// 只有只有状态是 -1、0、1 才展示头像右小角状态小icon
|
||||||
@ -264,6 +272,8 @@ const getApprovalNodeIcon = (taskStatus: number, nodeType: NodeType) => {
|
|||||||
if (
|
if (
|
||||||
nodeType === NodeType.START_USER_NODE ||
|
nodeType === NodeType.START_USER_NODE ||
|
||||||
nodeType === NodeType.USER_TASK_NODE ||
|
nodeType === NodeType.USER_TASK_NODE ||
|
||||||
|
nodeType === NodeType.TRANSACTOR_NODE ||
|
||||||
|
nodeType === NodeType.CHILD_PROCESS_NODE ||
|
||||||
nodeType === NodeType.END_EVENT_NODE
|
nodeType === NodeType.END_EVENT_NODE
|
||||||
) {
|
) {
|
||||||
return statusIconMap[taskStatus]?.icon
|
return statusIconMap[taskStatus]?.icon
|
||||||
|
@ -178,8 +178,9 @@ const writableFields: Array<string> = [] // 表单可以编辑的字段
|
|||||||
|
|
||||||
/** 获得详情 */
|
/** 获得详情 */
|
||||||
const getDetail = () => {
|
const getDetail = () => {
|
||||||
|
// 获得审批详情
|
||||||
getApprovalDetail()
|
getApprovalDetail()
|
||||||
|
// 获得流程模型视图
|
||||||
getProcessModelView()
|
getProcessModelView()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,9 +24,7 @@
|
|||||||
<el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
|
<el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
<!-- TODO @ tuituji:style 可以使用 unocss -->
|
<el-form-item label="" prop="category" class="absolute right-[300px]">
|
||||||
<el-form-item label="" prop="category" :style="{ position: 'absolute', right: '300px' }">
|
|
||||||
<!-- TODO @tuituji:应该选择好分类,就触发搜索啦。 RE:done & to check-->
|
|
||||||
<el-select
|
<el-select
|
||||||
v-model="queryParams.category"
|
v-model="queryParams.category"
|
||||||
placeholder="请选择流程分类"
|
placeholder="请选择流程分类"
|
||||||
@ -42,8 +40,7 @@
|
|||||||
/>
|
/>
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
<el-form-item label="" prop="status" class="absolute right-[130px]">
|
||||||
<el-form-item label="" prop="status" :style="{ position: 'absolute', right: '130px' }">
|
|
||||||
<el-select
|
<el-select
|
||||||
v-model="queryParams.status"
|
v-model="queryParams.status"
|
||||||
placeholder="请选择流程状态"
|
placeholder="请选择流程状态"
|
||||||
@ -61,8 +58,7 @@
|
|||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
<!-- 高级筛选 -->
|
<!-- 高级筛选 -->
|
||||||
<!-- TODO @ tuituji:style 可以使用 unocss -->
|
<el-form-item class="absolute right-0">
|
||||||
<el-form-item :style="{ position: 'absolute', right: '0px' }">
|
|
||||||
<el-popover
|
<el-popover
|
||||||
:visible="showPopover"
|
:visible="showPopover"
|
||||||
persistent
|
persistent
|
||||||
@ -75,36 +71,28 @@
|
|||||||
<Icon icon="ep:plus" class="mr-5px" />高级筛选
|
<Icon icon="ep:plus" class="mr-5px" />高级筛选
|
||||||
</el-button>
|
</el-button>
|
||||||
</template>
|
</template>
|
||||||
<!-- <el-form-item label="流程发起人" class="bold-label" label-position="top" prop="category">
|
|
||||||
<el-select
|
|
||||||
v-model="queryParams.category"
|
|
||||||
placeholder="请选择流程发起人"
|
|
||||||
clearable
|
|
||||||
class="!w-390px"
|
|
||||||
>
|
|
||||||
<el-option
|
|
||||||
v-for="category in categoryList"
|
|
||||||
:key="category.code"
|
|
||||||
:label="category.name"
|
|
||||||
:value="category.code"
|
|
||||||
/>
|
|
||||||
</el-select>
|
|
||||||
</el-form-item> -->
|
|
||||||
<el-form-item
|
<el-form-item
|
||||||
label="所属流程"
|
label="所属流程"
|
||||||
class="bold-label"
|
class="font-bold"
|
||||||
label-position="top"
|
label-position="top"
|
||||||
prop="processDefinitionKey"
|
prop="processDefinitionKey"
|
||||||
>
|
>
|
||||||
<el-input
|
<el-select
|
||||||
v-model="queryParams.processDefinitionKey"
|
v-model="queryParams.processDefinitionKey"
|
||||||
placeholder="请输入流程定义的标识"
|
placeholder="请选择流程定义"
|
||||||
clearable
|
clearable
|
||||||
@keyup.enter="handleQuery"
|
|
||||||
class="!w-390px"
|
class="!w-390px"
|
||||||
|
@change="handleQuery"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="item in processDefinitionList"
|
||||||
|
:key="item.key"
|
||||||
|
:label="item.name"
|
||||||
|
:value="item.key"
|
||||||
/>
|
/>
|
||||||
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="发起时间" class="bold-label" label-position="top" prop="createTime">
|
<el-form-item label="发起时间" class="font-bold" label-position="top" prop="createTime">
|
||||||
<el-date-picker
|
<el-date-picker
|
||||||
v-model="queryParams.createTime"
|
v-model="queryParams.createTime"
|
||||||
value-format="YYYY-MM-DD HH:mm:ss"
|
value-format="YYYY-MM-DD HH:mm:ss"
|
||||||
@ -115,11 +103,12 @@
|
|||||||
class="!w-240px"
|
class="!w-240px"
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<!-- TODO tuituiji:参考钉钉,1)按照清空、取消、确认排序。2)右对齐。3)确认增加 primary -->
|
<el-form-item class="font-bold" label-position="top">
|
||||||
<el-form-item class="bold-label" label-position="top">
|
<div class="flex justify-end w-full">
|
||||||
<el-button @click="handleQuery"> 确认</el-button>
|
|
||||||
<el-button @click="showPopover = false"> 取消</el-button>
|
|
||||||
<el-button @click="resetQuery">清空</el-button>
|
<el-button @click="resetQuery">清空</el-button>
|
||||||
|
<el-button @click="showPopover = false">取消</el-button>
|
||||||
|
<el-button type="primary" @click="handleQuery">确认</el-button>
|
||||||
|
</div>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-popover>
|
</el-popover>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
@ -130,7 +119,7 @@
|
|||||||
<ContentWrap>
|
<ContentWrap>
|
||||||
<el-table v-loading="loading" :data="list">
|
<el-table v-loading="loading" :data="list">
|
||||||
<el-table-column label="流程名称" align="center" prop="name" min-width="200px" fixed="left" />
|
<el-table-column label="流程名称" align="center" prop="name" min-width="200px" fixed="left" />
|
||||||
<el-table-column label="摘要" prop="summary" min-width="180" fixed="left">
|
<el-table-column label="摘要" prop="summary" width="180" fixed="left">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<div class="flex flex-col" v-if="scope.row.summary && scope.row.summary.length > 0">
|
<div class="flex flex-col" v-if="scope.row.summary && scope.row.summary.length > 0">
|
||||||
<div v-for="(item, index) in scope.row.summary" :key="index">
|
<div v-for="(item, index) in scope.row.summary" :key="index">
|
||||||
@ -146,12 +135,38 @@
|
|||||||
min-width="100"
|
min-width="100"
|
||||||
fixed="left"
|
fixed="left"
|
||||||
/>
|
/>
|
||||||
<!-- TODO @芋艿:摘要 -->
|
<el-table-column label="流程状态" prop="status" min-width="200">
|
||||||
<!-- TODO tuituiji:参考钉钉;1)审批中时,展示审批任务;2)非审批中,展示状态 -->
|
|
||||||
<el-table-column label="流程状态" prop="status" width="120">
|
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
|
<!-- 审批中状态 -->
|
||||||
|
<template
|
||||||
|
v-if="
|
||||||
|
scope.row.status === BpmProcessInstanceStatus.RUNNING && scope.row.tasks?.length > 0
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<!-- 单人审批 -->
|
||||||
|
<template v-if="scope.row.tasks.length === 1">
|
||||||
|
<span>
|
||||||
|
<el-button link type="primary" @click="handleDetail(scope.row)">
|
||||||
|
{{ scope.row.tasks[0].assigneeUser?.nickname }}
|
||||||
|
</el-button>
|
||||||
|
({{ scope.row.tasks[0].name }}) 审批中
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
<!-- 多人审批 -->
|
||||||
|
<template v-else>
|
||||||
|
<span>
|
||||||
|
<el-button link type="primary" @click="handleDetail(scope.row)">
|
||||||
|
{{ scope.row.tasks[0].assigneeUser?.nickname }}
|
||||||
|
</el-button>
|
||||||
|
等 {{ scope.row.tasks.length }} 人 ({{ scope.row.tasks[0].name }})审批中
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
<!-- 非审批中状态 -->
|
||||||
|
<template v-else>
|
||||||
<dict-tag :type="DICT_TYPE.BPM_PROCESS_INSTANCE_STATUS" :value="scope.row.status" />
|
<dict-tag :type="DICT_TYPE.BPM_PROCESS_INSTANCE_STATUS" :value="scope.row.status" />
|
||||||
</template>
|
</template>
|
||||||
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column
|
<el-table-column
|
||||||
label="发起时间"
|
label="发起时间"
|
||||||
@ -167,19 +182,6 @@
|
|||||||
width="180"
|
width="180"
|
||||||
:formatter="dateFormatter"
|
:formatter="dateFormatter"
|
||||||
/>
|
/>
|
||||||
<!--<el-table-column align="center" label="耗时" prop="durationInMillis" width="160">
|
|
||||||
<template #default="scope">
|
|
||||||
{{ scope.row.durationInMillis > 0 ? formatPast2(scope.row.durationInMillis) : '-' }}
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column label="当前审批任务" align="center" prop="tasks" min-width="120px">
|
|
||||||
<template #default="scope">
|
|
||||||
<el-button type="primary" v-for="task in scope.row.tasks" :key="task.id" link>
|
|
||||||
<span>{{ task.name }}</span>
|
|
||||||
</el-button>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
-->
|
|
||||||
<el-table-column label="操作" align="center" fixed="right" width="180">
|
<el-table-column label="操作" align="center" fixed="right" width="180">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<el-button
|
<el-button
|
||||||
@ -215,7 +217,6 @@
|
|||||||
</ContentWrap>
|
</ContentWrap>
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
// TODO @tuituji:List 改成 <Icon icon="ep:plus" class="mr-5px" /> 类似这种组件哈。 RE:done & to check
|
|
||||||
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
||||||
import { dateFormatter } from '@/utils/formatTime'
|
import { dateFormatter } from '@/utils/formatTime'
|
||||||
import { ElMessageBox } from 'element-plus'
|
import { ElMessageBox } from 'element-plus'
|
||||||
@ -223,6 +224,7 @@ import * as ProcessInstanceApi from '@/api/bpm/processInstance'
|
|||||||
import { CategoryApi, CategoryVO } from '@/api/bpm/category'
|
import { CategoryApi, CategoryVO } from '@/api/bpm/category'
|
||||||
import { ProcessInstanceVO } from '@/api/bpm/processInstance'
|
import { ProcessInstanceVO } from '@/api/bpm/processInstance'
|
||||||
import * as DefinitionApi from '@/api/bpm/definition'
|
import * as DefinitionApi from '@/api/bpm/definition'
|
||||||
|
import { BpmProcessInstanceStatus } from '@/utils/constants'
|
||||||
|
|
||||||
defineOptions({ name: 'BpmProcessInstanceMy' })
|
defineOptions({ name: 'BpmProcessInstanceMy' })
|
||||||
|
|
||||||
@ -233,6 +235,7 @@ const { t } = useI18n() // 国际化
|
|||||||
const loading = ref(true) // 列表的加载中
|
const loading = ref(true) // 列表的加载中
|
||||||
const total = ref(0) // 列表的总页数
|
const total = ref(0) // 列表的总页数
|
||||||
const list = ref([]) // 列表的数据
|
const list = ref([]) // 列表的数据
|
||||||
|
const processDefinitionList = ref<any[]>([]) // 流程定义列表
|
||||||
const queryParams = reactive({
|
const queryParams = reactive({
|
||||||
pageNo: 1,
|
pageNo: 1,
|
||||||
pageSize: 10,
|
pageSize: 10,
|
||||||
@ -244,6 +247,7 @@ const queryParams = reactive({
|
|||||||
})
|
})
|
||||||
const queryFormRef = ref() // 搜索的表单
|
const queryFormRef = ref() // 搜索的表单
|
||||||
const categoryList = ref<CategoryVO[]>([]) // 流程分类列表
|
const categoryList = ref<CategoryVO[]>([]) // 流程分类列表
|
||||||
|
const showPopover = ref(false) // 高级筛选是否展示
|
||||||
|
|
||||||
/** 查询列表 */
|
/** 查询列表 */
|
||||||
const getList = async () => {
|
const getList = async () => {
|
||||||
@ -257,8 +261,6 @@ const getList = async () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const showPopover = ref(false)
|
|
||||||
|
|
||||||
/** 搜索按钮操作 */
|
/** 搜索按钮操作 */
|
||||||
const handleQuery = () => {
|
const handleQuery = () => {
|
||||||
queryParams.pageNo = 1
|
queryParams.pageNo = 1
|
||||||
@ -325,10 +327,7 @@ onActivated(() => {
|
|||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
await getList()
|
await getList()
|
||||||
categoryList.value = await CategoryApi.getCategorySimpleList()
|
categoryList.value = await CategoryApi.getCategorySimpleList()
|
||||||
|
// 获取流程定义列表
|
||||||
|
processDefinitionList.value = await DefinitionApi.getSimpleProcessDefinitionList()
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
<style>
|
|
||||||
.bold-label .el-form-item__label {
|
|
||||||
font-weight: bold; /* 将字体加粗 */
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
@ -79,23 +79,28 @@
|
|||||||
<el-button @click="showPopover = !showPopover">
|
<el-button @click="showPopover = !showPopover">
|
||||||
<Icon icon="ep:plus" class="mr-5px" />高级筛选
|
<Icon icon="ep:plus" class="mr-5px" />高级筛选
|
||||||
</el-button>
|
</el-button>
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
<!-- <el-form-item label="流程发起人" class="bold-label" label-position="top" prop="category">
|
<el-form-item
|
||||||
|
label="所属流程"
|
||||||
|
class="font-bold"
|
||||||
|
label-position="top"
|
||||||
|
prop="processDefinitionKey"
|
||||||
|
>
|
||||||
<el-select
|
<el-select
|
||||||
v-model="queryParams.category"
|
v-model="queryParams.processDefinitionKey"
|
||||||
placeholder="请选择流程发起人"
|
placeholder="请选择流程定义"
|
||||||
clearable
|
clearable
|
||||||
|
@change="handleQuery"
|
||||||
class="!w-390px"
|
class="!w-390px"
|
||||||
>
|
>
|
||||||
<el-option
|
<el-option
|
||||||
v-for="category in categoryList"
|
v-for="item in processDefinitionList"
|
||||||
:key="category.code"
|
:key="item.key"
|
||||||
:label="category.name"
|
:label="item.name"
|
||||||
:value="category.code"
|
:value="item.key"
|
||||||
/>
|
/>
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item> -->
|
</el-form-item>
|
||||||
<el-form-item label="发起时间" class="bold-label" label-position="top" prop="createTime">
|
<el-form-item label="发起时间" class="bold-label" label-position="top" prop="createTime">
|
||||||
<el-date-picker
|
<el-date-picker
|
||||||
v-model="queryParams.createTime"
|
v-model="queryParams.createTime"
|
||||||
@ -114,7 +119,6 @@
|
|||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-popover>
|
</el-popover>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
</el-form>
|
</el-form>
|
||||||
</ContentWrap>
|
</ContentWrap>
|
||||||
|
|
||||||
@ -122,9 +126,12 @@
|
|||||||
<ContentWrap>
|
<ContentWrap>
|
||||||
<el-table v-loading="loading" :data="list">
|
<el-table v-loading="loading" :data="list">
|
||||||
<el-table-column align="center" label="流程" prop="processInstance.name" width="180" />
|
<el-table-column align="center" label="流程" prop="processInstance.name" width="180" />
|
||||||
<el-table-column label="摘要" prop="processInstance.summary" min-width="180">
|
<el-table-column label="摘要" prop="processInstance.summary" width="180">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<div class="flex flex-col" v-if="scope.row.processInstance.summary && scope.row.processInstance.summary.length > 0">
|
<div
|
||||||
|
class="flex flex-col"
|
||||||
|
v-if="scope.row.processInstance.summary && scope.row.processInstance.summary.length > 0"
|
||||||
|
>
|
||||||
<div v-for="(item, index) in scope.row.processInstance.summary" :key="index">
|
<div v-for="(item, index) in scope.row.processInstance.summary" :key="index">
|
||||||
<el-text type="info"> {{ item.key }} : {{ item.value }} </el-text>
|
<el-text type="info"> {{ item.key }} : {{ item.value }} </el-text>
|
||||||
</div>
|
</div>
|
||||||
@ -170,7 +177,12 @@
|
|||||||
{{ formatPast2(scope.row.durationInMillis) }}
|
{{ formatPast2(scope.row.durationInMillis) }}
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column align="center" label="流程编号" prop="processInstanceId" :show-overflow-tooltip="true" />
|
<el-table-column
|
||||||
|
align="center"
|
||||||
|
label="流程编号"
|
||||||
|
prop="processInstanceId"
|
||||||
|
:show-overflow-tooltip="true"
|
||||||
|
/>
|
||||||
<el-table-column align="center" label="任务编号" prop="id" :show-overflow-tooltip="true" />
|
<el-table-column align="center" label="任务编号" prop="id" :show-overflow-tooltip="true" />
|
||||||
<el-table-column align="center" label="操作" fixed="right" width="80">
|
<el-table-column align="center" label="操作" fixed="right" width="80">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
@ -192,6 +204,7 @@ import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
|||||||
import { dateFormatter, formatPast2 } from '@/utils/formatTime'
|
import { dateFormatter, formatPast2 } from '@/utils/formatTime'
|
||||||
import * as TaskApi from '@/api/bpm/task'
|
import * as TaskApi from '@/api/bpm/task'
|
||||||
import { CategoryApi, CategoryVO } from '@/api/bpm/category'
|
import { CategoryApi, CategoryVO } from '@/api/bpm/category'
|
||||||
|
import * as DefinitionApi from '@/api/bpm/definition'
|
||||||
|
|
||||||
defineOptions({ name: 'BpmDoneTask' })
|
defineOptions({ name: 'BpmDoneTask' })
|
||||||
|
|
||||||
@ -200,17 +213,19 @@ const { push } = useRouter() // 路由
|
|||||||
const loading = ref(true) // 列表的加载中
|
const loading = ref(true) // 列表的加载中
|
||||||
const total = ref(0) // 列表的总页数
|
const total = ref(0) // 列表的总页数
|
||||||
const list = ref([]) // 列表的数据
|
const list = ref([]) // 列表的数据
|
||||||
|
const processDefinitionList = ref<any[]>([]) // 流程定义列表
|
||||||
const queryParams = reactive({
|
const queryParams = reactive({
|
||||||
pageNo: 1,
|
pageNo: 1,
|
||||||
pageSize: 10,
|
pageSize: 10,
|
||||||
name: '',
|
name: '',
|
||||||
category: undefined,
|
category: undefined,
|
||||||
status: undefined,
|
status: undefined,
|
||||||
|
processDefinitionKey: '',
|
||||||
createTime: []
|
createTime: []
|
||||||
})
|
})
|
||||||
const queryFormRef = ref() // 搜索的表单
|
const queryFormRef = ref() // 搜索的表单
|
||||||
const categoryList = ref<CategoryVO[]>([]) // 流程分类列表
|
const categoryList = ref<CategoryVO[]>([]) // 流程分类列表
|
||||||
const showPopover = ref(false)
|
const showPopover = ref(false) // 高级筛选是否展示
|
||||||
|
|
||||||
/** 查询任务列表 */
|
/** 查询任务列表 */
|
||||||
const getList = async () => {
|
const getList = async () => {
|
||||||
@ -251,5 +266,7 @@ const handleAudit = (row: any) => {
|
|||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
await getList()
|
await getList()
|
||||||
categoryList.value = await CategoryApi.getCategorySimpleList()
|
categoryList.value = await CategoryApi.getCategorySimpleList()
|
||||||
|
// 获取流程定义列表
|
||||||
|
processDefinitionList.value = await DefinitionApi.getSimpleProcessDefinitionList()
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
@ -31,8 +31,7 @@
|
|||||||
搜索
|
搜索
|
||||||
</el-button>
|
</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
<el-form-item label="" prop="category" class="absolute right-130px">
|
||||||
<el-form-item label="" prop="category" :style="{ position: 'absolute', right: '130px' }">
|
|
||||||
<el-select
|
<el-select
|
||||||
v-model="queryParams.category"
|
v-model="queryParams.category"
|
||||||
placeholder="请选择流程分类"
|
placeholder="请选择流程分类"
|
||||||
@ -48,9 +47,8 @@
|
|||||||
/>
|
/>
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
<!-- 高级筛选 -->
|
<!-- 高级筛选 -->
|
||||||
<el-form-item :style="{ position: 'absolute', right: '0px' }">
|
<el-form-item class="absolute right-0">
|
||||||
<el-popover
|
<el-popover
|
||||||
:visible="showPopover"
|
:visible="showPopover"
|
||||||
persistent
|
persistent
|
||||||
@ -62,24 +60,29 @@
|
|||||||
<el-button @click="showPopover = !showPopover">
|
<el-button @click="showPopover = !showPopover">
|
||||||
<Icon icon="ep:plus" class="mr-5px" />高级筛选
|
<Icon icon="ep:plus" class="mr-5px" />高级筛选
|
||||||
</el-button>
|
</el-button>
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
<!-- <el-form-item label="流程发起人" class="bold-label" label-position="top" prop="category">
|
<el-form-item
|
||||||
|
label="所属流程"
|
||||||
|
class="font-bold"
|
||||||
|
label-position="top"
|
||||||
|
prop="processDefinitionKey"
|
||||||
|
>
|
||||||
<el-select
|
<el-select
|
||||||
v-model="queryParams.category"
|
v-model="queryParams.processDefinitionKey"
|
||||||
placeholder="请选择流程发起人"
|
placeholder="请选择流程定义"
|
||||||
clearable
|
clearable
|
||||||
|
@change="handleQuery"
|
||||||
class="!w-390px"
|
class="!w-390px"
|
||||||
>
|
>
|
||||||
<el-option
|
<el-option
|
||||||
v-for="category in categoryList"
|
v-for="item in processDefinitionList"
|
||||||
:key="category.code"
|
:key="item.key"
|
||||||
:label="category.name"
|
:label="item.name"
|
||||||
:value="category.code"
|
:value="item.key"
|
||||||
/>
|
/>
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item> -->
|
</el-form-item>
|
||||||
<el-form-item label="发起时间" class="bold-label" label-position="top" prop="createTime">
|
<el-form-item label="发起时间" class="font-bold" label-position="top" prop="createTime">
|
||||||
<el-date-picker
|
<el-date-picker
|
||||||
v-model="queryParams.createTime"
|
v-model="queryParams.createTime"
|
||||||
value-format="YYYY-MM-DD HH:mm:ss"
|
value-format="YYYY-MM-DD HH:mm:ss"
|
||||||
@ -87,17 +90,18 @@
|
|||||||
start-placeholder="开始日期"
|
start-placeholder="开始日期"
|
||||||
end-placeholder="结束日期"
|
end-placeholder="结束日期"
|
||||||
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
|
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
|
||||||
class="!w-240px"
|
class="w-240px!"
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item class="bold-label" label-position="top">
|
<el-form-item class="font-bold" label-position="top">
|
||||||
<el-button @click="handleQuery"> 确认</el-button>
|
<div class="flex justify-end w-full">
|
||||||
<el-button @click="showPopover = false"> 取消</el-button>
|
|
||||||
<el-button @click="resetQuery">清空</el-button>
|
<el-button @click="resetQuery">清空</el-button>
|
||||||
|
<el-button @click="showPopover = false">取消</el-button>
|
||||||
|
<el-button type="primary" @click="handleQuery">确认</el-button>
|
||||||
|
</div>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-popover>
|
</el-popover>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
</el-form>
|
</el-form>
|
||||||
</ContentWrap>
|
</ContentWrap>
|
||||||
|
|
||||||
@ -105,9 +109,12 @@
|
|||||||
<ContentWrap>
|
<ContentWrap>
|
||||||
<el-table v-loading="loading" :data="list">
|
<el-table v-loading="loading" :data="list">
|
||||||
<el-table-column align="center" label="流程" prop="processInstance.name" width="180" />
|
<el-table-column align="center" label="流程" prop="processInstance.name" width="180" />
|
||||||
<el-table-column label="摘要" prop="processInstance.summary" min-width="180">
|
<el-table-column label="摘要" prop="processInstance.summary" width="180">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<div class="flex flex-col" v-if="scope.row.processInstance.summary && scope.row.processInstance.summary.length > 0">
|
<div
|
||||||
|
class="flex flex-col"
|
||||||
|
v-if="scope.row.processInstance.summary && scope.row.processInstance.summary.length > 0"
|
||||||
|
>
|
||||||
<div v-for="(item, index) in scope.row.processInstance.summary" :key="index">
|
<div v-for="(item, index) in scope.row.processInstance.summary" :key="index">
|
||||||
<el-text type="info"> {{ item.key }} : {{ item.value }} </el-text>
|
<el-text type="info"> {{ item.key }} : {{ item.value }} </el-text>
|
||||||
</div>
|
</div>
|
||||||
@ -135,7 +142,12 @@
|
|||||||
prop="createTime"
|
prop="createTime"
|
||||||
width="180"
|
width="180"
|
||||||
/>
|
/>
|
||||||
<el-table-column align="center" label="流程编号" prop="processInstanceId" :show-overflow-tooltip="true" />
|
<el-table-column
|
||||||
|
align="center"
|
||||||
|
label="流程编号"
|
||||||
|
prop="processInstanceId"
|
||||||
|
:show-overflow-tooltip="true"
|
||||||
|
/>
|
||||||
<el-table-column align="center" label="任务编号" prop="id" :show-overflow-tooltip="true" />
|
<el-table-column align="center" label="任务编号" prop="id" :show-overflow-tooltip="true" />
|
||||||
<el-table-column align="center" label="操作" fixed="right" width="80">
|
<el-table-column align="center" label="操作" fixed="right" width="80">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
@ -157,6 +169,7 @@
|
|||||||
import { dateFormatter } from '@/utils/formatTime'
|
import { dateFormatter } from '@/utils/formatTime'
|
||||||
import * as TaskApi from '@/api/bpm/task'
|
import * as TaskApi from '@/api/bpm/task'
|
||||||
import { CategoryApi, CategoryVO } from '@/api/bpm/category'
|
import { CategoryApi, CategoryVO } from '@/api/bpm/category'
|
||||||
|
import * as DefinitionApi from '@/api/bpm/definition'
|
||||||
|
|
||||||
defineOptions({ name: 'BpmTodoTask' })
|
defineOptions({ name: 'BpmTodoTask' })
|
||||||
|
|
||||||
@ -165,15 +178,18 @@ const { push } = useRouter() // 路由
|
|||||||
const loading = ref(true) // 列表的加载中
|
const loading = ref(true) // 列表的加载中
|
||||||
const total = ref(0) // 列表的总页数
|
const total = ref(0) // 列表的总页数
|
||||||
const list = ref([]) // 列表的数据
|
const list = ref([]) // 列表的数据
|
||||||
|
const processDefinitionList = ref<any[]>([]) // 流程定义列表
|
||||||
const queryParams = reactive({
|
const queryParams = reactive({
|
||||||
pageNo: 1,
|
pageNo: 1,
|
||||||
pageSize: 10,
|
pageSize: 10,
|
||||||
name: '',
|
name: '',
|
||||||
category: undefined,
|
category: undefined,
|
||||||
|
processDefinitionKey: '',
|
||||||
createTime: []
|
createTime: []
|
||||||
})
|
})
|
||||||
const queryFormRef = ref() // 搜索的表单
|
const queryFormRef = ref() // 搜索的表单
|
||||||
const categoryList = ref<CategoryVO[]>([]) // 流程分类列表
|
const categoryList = ref<CategoryVO[]>([]) // 流程分类列表
|
||||||
|
const showPopover = ref(false) // 高级筛选是否展示
|
||||||
|
|
||||||
/** 查询任务列表 */
|
/** 查询任务列表 */
|
||||||
const getList = async () => {
|
const getList = async () => {
|
||||||
@ -187,8 +203,6 @@ const getList = async () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const showPopover = ref(false)
|
|
||||||
|
|
||||||
/** 搜索按钮操作 */
|
/** 搜索按钮操作 */
|
||||||
const handleQuery = () => {
|
const handleQuery = () => {
|
||||||
queryParams.pageNo = 1
|
queryParams.pageNo = 1
|
||||||
@ -216,5 +230,7 @@ const handleAudit = (row: any) => {
|
|||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
await getList()
|
await getList()
|
||||||
categoryList.value = await CategoryApi.getCategorySimpleList()
|
categoryList.value = await CategoryApi.getCategorySimpleList()
|
||||||
|
// 获取流程定义列表
|
||||||
|
processDefinitionList.value = await DefinitionApi.getSimpleProcessDefinitionList()
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
@ -1,151 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="upload-container">
|
|
||||||
<!-- 标题 -->
|
|
||||||
<div class="title">
|
|
||||||
<div>选择数据源</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 数据源选择 -->
|
|
||||||
<div class="resource-btn" >导入已有文本</div>
|
|
||||||
|
|
||||||
<!-- 上传文件区域 -->
|
|
||||||
<el-form>
|
|
||||||
<div class="upload-section">
|
|
||||||
<div class="upload-label">上传文本文件</div>
|
|
||||||
<el-upload
|
|
||||||
class="upload-area"
|
|
||||||
action="#"
|
|
||||||
:file-list="fileList"
|
|
||||||
:on-remove="handleRemove"
|
|
||||||
:before-upload="beforeUpload"
|
|
||||||
list-type="text"
|
|
||||||
drag
|
|
||||||
>
|
|
||||||
<i class="el-icon-upload"></i>
|
|
||||||
<div class="el-upload__text">拖拽文件至此,或者 <em>选择文件</em></div>
|
|
||||||
<div class="el-upload__tip">
|
|
||||||
已支持 TXT、MARKDOWN、PDF、HTML、XLSX、XLS、DOCX、CSV、EML、MSG、PPTX、PPT、XML、EPUB,每个文件不超过 15MB。
|
|
||||||
</div>
|
|
||||||
</el-upload>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 下一步按钮 -->
|
|
||||||
<div class="next-button">
|
|
||||||
<el-button type="primary" :disabled="!fileList.length">下一步</el-button>
|
|
||||||
</div>
|
|
||||||
</el-form>
|
|
||||||
|
|
||||||
<!-- 知识库创建 -->
|
|
||||||
<div class="create-knowledge">
|
|
||||||
<el-link type="primary" underline>创建一个空知识库</el-link>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
import { ref } from 'vue'
|
|
||||||
|
|
||||||
const fileList = ref([])
|
|
||||||
|
|
||||||
const handleRemove = (file, fileList) => {
|
|
||||||
console.log(file, fileList)
|
|
||||||
}
|
|
||||||
|
|
||||||
const beforeUpload = (file) => {
|
|
||||||
fileList.value.push(file)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped lang="scss">
|
|
||||||
.upload-container {
|
|
||||||
width: 600px;
|
|
||||||
margin: 0 auto;
|
|
||||||
padding: 20px;
|
|
||||||
background-color: #fff;
|
|
||||||
border-radius: 8px;
|
|
||||||
border: 1px solid #ebebeb;
|
|
||||||
}
|
|
||||||
|
|
||||||
.title {
|
|
||||||
font-size: 22px;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
.resource-btn {
|
|
||||||
margin-top: 20px;
|
|
||||||
border-radius: 10px;
|
|
||||||
cursor: pointer;
|
|
||||||
width: 150px;
|
|
||||||
border: 1.5px solid #528bff;
|
|
||||||
padding: 10px;
|
|
||||||
text-align: center;
|
|
||||||
font-weight: 500;
|
|
||||||
font-size: 14px;
|
|
||||||
line-height: 30px;
|
|
||||||
color: #101828;
|
|
||||||
}
|
|
||||||
|
|
||||||
.upload-section {
|
|
||||||
margin: 20px 0;
|
|
||||||
padding-top: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.upload-label {
|
|
||||||
font-size: 16px;
|
|
||||||
font-weight: bold;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
color: #303133;
|
|
||||||
}
|
|
||||||
|
|
||||||
.upload-area {
|
|
||||||
margin-top: 10px;
|
|
||||||
border: 1px dashed #d9d9d9;
|
|
||||||
padding: 40px;
|
|
||||||
text-align: center;
|
|
||||||
background-color: #f5f7fa;
|
|
||||||
border-radius: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.el-upload__text em {
|
|
||||||
color: #409eff;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.el-upload__tip {
|
|
||||||
margin-top: 10px;
|
|
||||||
font-size: 12px;
|
|
||||||
color: #909399;
|
|
||||||
}
|
|
||||||
|
|
||||||
.next-button {
|
|
||||||
text-align: left;
|
|
||||||
margin-top: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.create-knowledge {
|
|
||||||
text-align: left;
|
|
||||||
margin-top: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.el-form-item {
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.source-radio-group {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
}
|
|
||||||
|
|
||||||
.el-radio-button {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
font-size: 14px;
|
|
||||||
padding: 10px 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.el-radio-button .el-icon {
|
|
||||||
margin-right: 8px;
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,168 +0,0 @@
|
|||||||
<template>
|
|
||||||
<el-row>
|
|
||||||
<!-- Left Section -->
|
|
||||||
<el-col :span="12">
|
|
||||||
<el-card>
|
|
||||||
<!-- 分段设置 -->
|
|
||||||
<el-form>
|
|
||||||
<el-form-item label="分段设置">
|
|
||||||
<el-radio-group v-model="segmentSetting">
|
|
||||||
<el-radio label="自动分段与清洗">自动分段与清洗</el-radio>
|
|
||||||
<el-radio label="自定义">自定义</el-radio>
|
|
||||||
</el-radio-group>
|
|
||||||
</el-form-item>
|
|
||||||
|
|
||||||
<!-- 索引方式 -->
|
|
||||||
<el-form-item label="索引方式">
|
|
||||||
<el-radio-group v-model="indexingMethod">
|
|
||||||
<el-radio label="高质量">高质量</el-radio>
|
|
||||||
<el-radio label="经济">经济</el-radio>
|
|
||||||
</el-radio-group>
|
|
||||||
</el-form-item>
|
|
||||||
|
|
||||||
<!-- Embedding 模型 -->
|
|
||||||
<el-form-item label="Embedding 模型">
|
|
||||||
<el-select v-model="embeddingModel" placeholder="Select Embedding Model">
|
|
||||||
<el-option label="text-embedding-3-large" value="text-embedding-3-large"/>
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
|
|
||||||
<!-- 检索设置 -->
|
|
||||||
<el-form-item label="检索设置">
|
|
||||||
<el-card style="width: 400px;">
|
|
||||||
<div class="card-header">
|
|
||||||
<span>向量检索</span>
|
|
||||||
</div>
|
|
||||||
<el-slider v-model="topK" :min="1" :max="10" label="Top K"/>
|
|
||||||
<el-slider v-model="scoreThreshold" :min="0" :max="1" step="0.1" label="Score 阈值"/>
|
|
||||||
</el-card>
|
|
||||||
|
|
||||||
<el-card style="width: 400px;">
|
|
||||||
<div class="card-header">
|
|
||||||
<span>全文检索</span>
|
|
||||||
</div>
|
|
||||||
<el-slider v-model="topK" :min="1" :max="10" label="Top K"/>
|
|
||||||
<el-slider v-model="scoreThreshold" :min="0" :max="1" step="0.1" label="Score 阈值"/>
|
|
||||||
</el-card>
|
|
||||||
|
|
||||||
<el-card style="width: 400px;">
|
|
||||||
<div class="card-header">
|
|
||||||
<span>混合检索</span>
|
|
||||||
</div>
|
|
||||||
<el-slider v-model="topK" :min="1" :max="10" label="Top K"/>
|
|
||||||
<el-slider v-model="scoreThreshold" :min="0" :max="1" step="0.1" label="Score 阈值"/>
|
|
||||||
</el-card>
|
|
||||||
</el-form-item>
|
|
||||||
</el-form>
|
|
||||||
</el-card>
|
|
||||||
</el-col>
|
|
||||||
|
|
||||||
<!-- Right Section: 分段预览 -->
|
|
||||||
<el-col :span="9">
|
|
||||||
<el-card shadow="never">
|
|
||||||
<div class="previews-title">分段预览</div>
|
|
||||||
<template v-for="(segment, index) in segmentPreviews" :key="index">
|
|
||||||
<div class="segment-preview">
|
|
||||||
<div class="title">
|
|
||||||
<div class="left">
|
|
||||||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none"
|
|
||||||
xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<path
|
|
||||||
d="M4.74999 1.5L3.24999 10.5M8.74998 1.5L7.24998 10.5M10.25 4H1.75M9.75 8H1.25"
|
|
||||||
stroke="#98A2B3" stroke-linecap="round" stroke-linejoin="round"/>
|
|
||||||
</svg>
|
|
||||||
<span class="id">{{ segment.number }}</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="right">
|
|
||||||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none"
|
|
||||||
xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<path
|
|
||||||
d="M4 3.5H8M6 3.5V8.5M3.9 10.5H8.1C8.94008 10.5 9.36012 10.5 9.68099 10.3365C9.96323 10.1927 10.1927 9.96323 10.3365 9.68099C10.5 9.36012 10.5 8.94008 10.5 8.1V3.9C10.5 3.05992 10.5 2.63988 10.3365 2.31901C10.1927 2.03677 9.96323 1.8073 9.68099 1.66349C9.36012 1.5 8.94008 1.5 8.1 1.5H3.9C3.05992 1.5 2.63988 1.5 2.31901 1.66349C2.03677 1.8073 1.8073 2.03677 1.66349 2.31901C1.5 2.63988 1.5 3.05992 1.5 3.9V8.1C1.5 8.94008 1.5 9.36012 1.66349 9.68099C1.8073 9.96323 2.03677 10.1927 2.31901 10.3365C2.63988 10.5 3.05992 10.5 3.9 10.5Z"
|
|
||||||
stroke="#667085" stroke-linecap="round" stroke-linejoin="round"/>
|
|
||||||
</svg>
|
|
||||||
<span class="char-size">7777 字符</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="content">{{ segment.text }}</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</el-card>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
import {ref} from 'vue';
|
|
||||||
|
|
||||||
// Reactive variables for form control
|
|
||||||
const segmentSetting = ref('自动分段与清洗');
|
|
||||||
const indexingMethod = ref('高质量');
|
|
||||||
const embeddingModel = ref('text-embedding-3-large');
|
|
||||||
const directionalSearch = ref(true);
|
|
||||||
const topK = ref(3);
|
|
||||||
const scoreThreshold = ref(0.5);
|
|
||||||
|
|
||||||
// Mock data for segment previews
|
|
||||||
const segmentPreviews = ref([
|
|
||||||
{number: '001', text: "同步obs模型...'UAE-large-V1'"},
|
|
||||||
{number: '002', text: "同步obs模型...'plip'"},
|
|
||||||
{number: '003', text: "同步obs模型...'phoBERT-base-v2'"},
|
|
||||||
{number: '004', text: "同步obs模型...'lama3-bb-bnb-4bit'"},
|
|
||||||
{number: '005', text: "同步obs模型...'t5-base-split-and-rephrase'"}
|
|
||||||
]);
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped lang="scss">
|
|
||||||
/* Add any custom styles here */
|
|
||||||
|
|
||||||
.previews-title {
|
|
||||||
font-size: 18px;
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
|
|
||||||
.segment-preview {
|
|
||||||
background-color: rgba(228, 228, 228, 0.38);
|
|
||||||
border-radius: 10px;
|
|
||||||
padding: 15px;
|
|
||||||
margin-top: 15px;
|
|
||||||
|
|
||||||
.title {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
|
|
||||||
.left {
|
|
||||||
border-right: 5px;
|
|
||||||
font-size: 13px;
|
|
||||||
font-style: italic;
|
|
||||||
font-weight: 500;
|
|
||||||
color: #676767;
|
|
||||||
box-sizing: border-box;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
.id {
|
|
||||||
margin-left: 5px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.right {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
.char-size {
|
|
||||||
margin-left: 5px;
|
|
||||||
font-size: 13px;
|
|
||||||
color: rgba(57, 57, 57, 0.66);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.content {
|
|
||||||
margin-top: 10px;
|
|
||||||
font-size: 15px;
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,152 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="knowledge-base-container">
|
|
||||||
<div class="card-container">
|
|
||||||
<el-card class="create-card" shadow="hover">
|
|
||||||
<div class="create-content">
|
|
||||||
<el-icon class="create-icon"><Plus /></el-icon>
|
|
||||||
<span class="create-text">创建知识库</span>
|
|
||||||
</div>
|
|
||||||
<div class="create-footer">
|
|
||||||
导入您自己的文本数据或通过 Webhook 实时写入数据以增强 LLM 的上下文。
|
|
||||||
</div>
|
|
||||||
</el-card>
|
|
||||||
|
|
||||||
<el-card class="document-card" shadow="hover" v-for="index in 4" :key="index">
|
|
||||||
<div class="document-header">
|
|
||||||
<el-icon><Folder /></el-icon>
|
|
||||||
<span>接口鉴权示例代码.md</span>
|
|
||||||
</div>
|
|
||||||
<div class="document-info">
|
|
||||||
<el-tag size="small">1 文档</el-tag>
|
|
||||||
<el-tag size="small" type="info">5 千字符</el-tag>
|
|
||||||
<el-tag size="small" type="warning">0 关联应用</el-tag>
|
|
||||||
</div>
|
|
||||||
<p class="document-description">
|
|
||||||
useful for when you want to answer queries about the 接口鉴权示例代码.md
|
|
||||||
</p>
|
|
||||||
</el-card>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="pagination-container">
|
|
||||||
<el-pagination
|
|
||||||
v-model:current-page="currentPage"
|
|
||||||
v-model:page-size="pageSize"
|
|
||||||
:page-sizes="[10, 20, 30, 40]"
|
|
||||||
:small="false"
|
|
||||||
:disabled="false"
|
|
||||||
:background="true"
|
|
||||||
layout="total, sizes, prev, pager, next, jumper"
|
|
||||||
:total="total"
|
|
||||||
@size-change="handleSizeChange"
|
|
||||||
@current-change="handleCurrentChange"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
import { ref } from 'vue'
|
|
||||||
import { Folder, Plus } from '@element-plus/icons-vue'
|
|
||||||
|
|
||||||
const currentPage = ref(1)
|
|
||||||
const pageSize = ref(10)
|
|
||||||
const total = ref(100) // 假设总共有100条数据
|
|
||||||
|
|
||||||
const handleSizeChange = (val) => {
|
|
||||||
console.log(`每页 ${val} 条`)
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleCurrentChange = (val) => {
|
|
||||||
console.log(`当前页: ${val}`)
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.knowledge-base-container {
|
|
||||||
font-family: 'Helvetica Neue', Helvetica, 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', '微软雅黑', Arial, sans-serif;
|
|
||||||
position: absolute;
|
|
||||||
padding: 20px;
|
|
||||||
margin: 0 auto;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
top: 0;
|
|
||||||
bottom: 40px;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-container {
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap; /* Enable wrapping */
|
|
||||||
gap: 20px;
|
|
||||||
margin-bottom: auto; /* Pushes pagination to the bottom */
|
|
||||||
}
|
|
||||||
|
|
||||||
.create-card, .document-card {
|
|
||||||
flex: 1 1 360px; /* Allow cards to grow and shrink */
|
|
||||||
min-width: 0;
|
|
||||||
max-width: 400px;
|
|
||||||
border-radius: 10px;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.create-card {
|
|
||||||
background-color: rgba(168, 168, 168, 0.22);
|
|
||||||
}
|
|
||||||
.create-card:hover {
|
|
||||||
background-color: #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.create-content {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 10px;
|
|
||||||
margin-bottom: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.create-icon {
|
|
||||||
font-size: 24px;
|
|
||||||
color: #409EFF;
|
|
||||||
}
|
|
||||||
|
|
||||||
.create-text {
|
|
||||||
font-size: 18px;
|
|
||||||
font-weight: bold;
|
|
||||||
color: #303133;
|
|
||||||
}
|
|
||||||
|
|
||||||
.create-footer {
|
|
||||||
font-size: 14px;
|
|
||||||
color: #909399;
|
|
||||||
line-height: 1.5;
|
|
||||||
}
|
|
||||||
|
|
||||||
.document-header {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 10px;
|
|
||||||
font-size: 16px;
|
|
||||||
font-weight: bold;
|
|
||||||
margin-bottom: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.document-info {
|
|
||||||
display: flex;
|
|
||||||
gap: 10px;
|
|
||||||
margin-bottom: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.document-description {
|
|
||||||
color: #606266;
|
|
||||||
font-size: 14px;
|
|
||||||
line-height: 1.5;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pagination-container {
|
|
||||||
position: absolute;
|
|
||||||
width: 100%;
|
|
||||||
bottom: 0;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
margin-top: 20px;
|
|
||||||
}
|
|
||||||
</style>
|
|
Loading…
x
Reference in New Issue
Block a user