Commit 77971f9c by YunaiV

Merge branch 'feature/bpm' of https://gitee.com/yudaocode/yudao-ui-admin-vue3

parents ecf9f265 624594bb
import request from '@/config/axios'
// BPM 流程分类 VO
export interface CategoryVO {
id: number // 分类编号
name: string // 分类名
code: string // 分类标志
status: number // 分类状态
sort: number // 分类排序
}
// BPM 流程分类 API
export const CategoryApi = {
// 查询流程分类分页
getCategoryPage: async (params: any) => {
return await request.get({ url: `/bpm/category/page`, params })
},
// 查询流程分类列表
getCategorySimpleList: async () => {
return await request.get({ url: `/bpm/category/simple-list` })
},
// 查询流程分类详情
getCategory: async (id: number) => {
return await request.get({ url: `/bpm/category/get?id=` + id })
},
// 新增流程分类
createCategory: async (data: CategoryVO) => {
return await request.post({ url: `/bpm/category/create`, data })
},
// 修改流程分类
updateCategory: async (data: CategoryVO) => {
return await request.put({ url: `/bpm/category/update`, data })
},
// 删除流程分类
deleteCategory: async (id: number) => {
return await request.delete({ url: `/bpm/category/delete?id=` + id })
}
}
import request from '@/config/axios' import request from '@/config/axios'
export const getProcessDefinitionBpmnXML = async (id: number) => { export const getProcessDefinition = async (id: number, key: string) => {
return await request.get({ return await request.get({
url: '/bpm/process-definition/get-bpmn-xml?id=' + id url: '/bpm/process-definition/get',
params: { id, key }
}) })
} }
......
...@@ -49,8 +49,8 @@ export const getFormPage = async (params) => { ...@@ -49,8 +49,8 @@ export const getFormPage = async (params) => {
} }
// 获得动态表单的精简列表 // 获得动态表单的精简列表
export const getSimpleFormList = async () => { export const getFormSimpleList = async () => {
return await request.get({ return await request.get({
url: '/bpm/form/list-all-simple' url: '/bpm/form/simple-list'
}) })
} }
...@@ -2,7 +2,7 @@ import request from '@/config/axios' ...@@ -2,7 +2,7 @@ import request from '@/config/axios'
export type LeaveVO = { export type LeaveVO = {
id: number id: number
result: number status: number
type: number type: number
reason: string reason: string
processInstanceId: string processInstanceId: string
......
import request from '@/config/axios'
// BPM 流程表达式 VO
export interface ProcessExpressionVO {
id: number // 编号
name: string // 表达式名字
status: number // 表达式状态
expression: string // 表达式
}
// BPM 流程表达式 API
export const ProcessExpressionApi = {
// 查询BPM 流程表达式分页
getProcessExpressionPage: async (params: any) => {
return await request.get({ url: `/bpm/process-expression/page`, params })
},
// 查询BPM 流程表达式详情
getProcessExpression: async (id: number) => {
return await request.get({ url: `/bpm/process-expression/get?id=` + id })
},
// 新增BPM 流程表达式
createProcessExpression: async (data: ProcessExpressionVO) => {
return await request.post({ url: `/bpm/process-expression/create`, data })
},
// 修改BPM 流程表达式
updateProcessExpression: async (data: ProcessExpressionVO) => {
return await request.put({ url: `/bpm/process-expression/update`, data })
},
// 删除BPM 流程表达式
deleteProcessExpression: async (id: number) => {
return await request.delete({ url: `/bpm/process-expression/delete?id=` + id })
},
// 导出BPM 流程表达式 Excel
exportProcessExpression: async (params) => {
return await request.download({ url: `/bpm/process-expression/export-excel`, params })
}
}
\ No newline at end of file
...@@ -20,51 +20,49 @@ export type ProcessInstanceVO = { ...@@ -20,51 +20,49 @@ export type ProcessInstanceVO = {
endTime: string endTime: string
} }
export type ProcessInstanceCCVO = { export type ProcessInstanceCopyVO = {
type: number, type: number
taskName: string, taskName: string
taskKey: string, taskKey: string
processInstanceName: string, processInstanceName: string
processInstanceKey: string, processInstanceKey: string
startUserId: string, startUserId: string
options:string [], options: string[]
reason: string reason: string
} }
export const getMyProcessInstancePage = async (params) => { export const getProcessInstanceMyPage = async (params: any) => {
return await request.get({ url: '/bpm/process-instance/my-page', params }) return await request.get({ url: '/bpm/process-instance/my-page', params })
} }
export const getProcessInstanceManagerPage = async (params: any) => {
return await request.get({ url: '/bpm/process-instance/manager-page', params })
}
export const createProcessInstance = async (data) => { export const createProcessInstance = async (data) => {
return await request.post({ url: '/bpm/process-instance/create', data: data }) return await request.post({ url: '/bpm/process-instance/create', data: data })
} }
export const cancelProcessInstance = async (id: number, reason: string) => { export const cancelProcessInstanceByStartUser = async (id: number, reason: string) => {
const data = { const data = {
id: id, id: id,
reason: reason reason: reason
} }
return await request.delete({ url: '/bpm/process-instance/cancel', data: data }) return await request.delete({ url: '/bpm/process-instance/cancel-by-start-user', data: data })
} }
export const getProcessInstance = async (id: number) => { export const cancelProcessInstanceByAdmin = async (id: number, reason: string) => {
return await request.get({ url: '/bpm/process-instance/get?id=' + id }) const data = {
id: id,
reason: reason
}
return await request.delete({ url: '/bpm/process-instance/cancel-by-admin', data: data })
} }
/** export const getProcessInstance = async (id: string) => {
* 抄送 return await request.get({ url: '/bpm/process-instance/get?id=' + id })
* @param data 抄送数据
* @returns 是否抄送成功
*/
export const createProcessInstanceCC = async (data) => {
return await request.post({ url: '/bpm/process-instance/cc/create', data: data })
} }
/** export const getProcessInstanceCopyPage = async (params: any) => {
* 抄送列表 return await request.get({ url: '/bpm/process-instance/copy/page', params })
* @param params }
* @returns
*/
export const getProcessInstanceCCPage = async (params) => {
return await request.get({ url: '/bpm/process-instance/cc/my-page', params })
}
\ No newline at end of file
import request from '@/config/axios'
// BPM 流程监听器 VO
export interface ProcessListenerVO {
id: number // 编号
name: string // 监听器名字
type: string // 监听器类型
status: number // 监听器状态
event: string // 监听事件
valueType: string // 监听器值类型
value: string // 监听器值
}
// BPM 流程监听器 API
export const ProcessListenerApi = {
// 查询流程监听器分页
getProcessListenerPage: async (params: any) => {
return await request.get({ url: `/bpm/process-listener/page`, params })
},
// 查询流程监听器详情
getProcessListener: async (id: number) => {
return await request.get({ url: `/bpm/process-listener/get?id=` + id })
},
// 新增流程监听器
createProcessListener: async (data: ProcessListenerVO) => {
return await request.post({ url: `/bpm/process-listener/create`, data })
},
// 修改流程监听器
updateProcessListener: async (data: ProcessListenerVO) => {
return await request.put({ url: `/bpm/process-listener/update`, data })
},
// 删除流程监听器
deleteProcessListener: async (id: number) => {
return await request.delete({ url: `/bpm/process-listener/delete?id=` + id })
}
}
...@@ -4,78 +4,63 @@ export type TaskVO = { ...@@ -4,78 +4,63 @@ export type TaskVO = {
id: number id: number
} }
export const getTodoTaskPage = async (params) => { export const getTaskTodoPage = async (params: any) => {
return await request.get({ url: '/bpm/task/todo-page', params }) return await request.get({ url: '/bpm/task/todo-page', params })
} }
export const getDoneTaskPage = async (params) => { export const getTaskDonePage = async (params: any) => {
return await request.get({ url: '/bpm/task/done-page', params }) return await request.get({ url: '/bpm/task/done-page', params })
} }
export const completeTask = async (data) => { export const getTaskManagerPage = async (params: any) => {
return await request.put({ url: '/bpm/task/complete', data }) return await request.get({ url: '/bpm/task/manager-page', params })
} }
export const approveTask = async (data) => { export const approveTask = async (data: any) => {
return await request.put({ url: '/bpm/task/approve', data }) return await request.put({ url: '/bpm/task/approve', data })
} }
export const rejectTask = async (data) => { export const rejectTask = async (data: any) => {
return await request.put({ url: '/bpm/task/reject', data }) return await request.put({ url: '/bpm/task/reject', data })
} }
export const backTask = async (data) => {
return await request.put({ url: '/bpm/task/back', data })
}
export const updateTaskAssignee = async (data) => {
return await request.put({ url: '/bpm/task/update-assignee', data })
}
export const getTaskListByProcessInstanceId = async (processInstanceId) => { export const getTaskListByProcessInstanceId = async (processInstanceId: string) => {
return await request.get({ return await request.get({
url: '/bpm/task/list-by-process-instance-id?processInstanceId=' + processInstanceId url: '/bpm/task/list-by-process-instance-id?processInstanceId=' + processInstanceId
}) })
} }
// 导出任务
export const exportTask = async (params) => {
return await request.download({ url: '/bpm/task/export', params })
}
// 获取所有可回退的节点 // 获取所有可回退的节点
export const getReturnList = async (params) => { export const getTaskListByReturn = async (id: string) => {
return await request.get({ url: '/bpm/task/return-list', params }) return await request.get({ url: '/bpm/task/list-by-return', params: { id } })
} }
// 回退 // 回退
export const returnTask = async (data) => { export const returnTask = async (data: any) => {
return await request.put({ url: '/bpm/task/return', data }) return await request.put({ url: '/bpm/task/return', data })
} }
/** // 委派
* 委派 export const delegateTask = async (data: any) => {
*/
export const delegateTask = async (data) => {
return await request.put({ url: '/bpm/task/delegate', data }) return await request.put({ url: '/bpm/task/delegate', data })
} }
/** // 转派
* 加签 export const transferTask = async (data: any) => {
*/ return await request.put({ url: '/bpm/task/transfer', data })
export const taskAddSign = async (data) => {
return await request.put({ url: '/bpm/task/create-sign', data })
} }
/** // 加签
* 获取减签任务列表 export const signCreateTask = async (data: any) => {
*/ return await request.put({ url: '/bpm/task/create-sign', data })
export const getChildrenTaskList = async (id: string) => {
return await request.get({ url: '/bpm/task/children-list?taskId=' + id })
} }
/** // 减签
* 减签 export const signDeleteTask = async (data: any) => {
*/
export const taskSubSign = async (data) => {
return await request.delete({ url: '/bpm/task/delete-sign', data }) return await request.delete({ url: '/bpm/task/delete-sign', data })
} }
// 获取减签任务列表
export const getChildrenTaskList = async (id: string) => {
return await request.get({ url: '/bpm/task/list-by-parent-task-id?parentTaskId=' + id })
}
import request from '@/config/axios'
export type TaskAssignVO = {
id: number
modelId: string
processDefinitionId: string
taskDefinitionKey: string
taskDefinitionName: string
options: string[]
type: number
}
export const getTaskAssignRuleList = async (params) => {
return await request.get({ url: '/bpm/task-assign-rule/list', params })
}
export const createTaskAssignRule = async (data: TaskAssignVO) => {
return await request.post({
url: '/bpm/task-assign-rule/create',
data: data
})
}
export const updateTaskAssignRule = async (data: TaskAssignVO) => {
return await request.put({
url: '/bpm/task-assign-rule/update',
data: data
})
}
...@@ -4,7 +4,7 @@ export type UserGroupVO = { ...@@ -4,7 +4,7 @@ export type UserGroupVO = {
id: number id: number
name: string name: string
description: string description: string
memberUserIds: number[] userIds: number[]
status: number status: number
remark: string remark: string
createTime: string createTime: string
...@@ -42,6 +42,6 @@ export const getUserGroupPage = async (params) => { ...@@ -42,6 +42,6 @@ export const getUserGroupPage = async (params) => {
} }
// 获取用户组精简信息列表 // 获取用户组精简信息列表
export const getSimpleUserGroupList = async (): Promise<UserGroupVO[]> => { export const getUserGroupSimpleList = async (): Promise<UserGroupVO[]> => {
return await request.get({ url: '/bpm/user-group/list-all-simple' }) return await request.get({ url: '/bpm/user-group/simple-list' })
} }
import request from '@/config/axios' import request from '@/config/axios'
import { TransferReqVO } from '@/api/crm/customer' import { TransferReqVO } from '@/api/crm/permission'
export interface BusinessVO { export interface BusinessVO {
id: number id: number
......
import request from '@/config/axios' import request from '@/config/axios'
import { TransferReqVO } from '@/api/crm/customer' import { TransferReqVO } from '@/api/crm/permission'
export interface ClueVO { export interface ClueVO {
id: number // 编号 id: number // 编号
......
import request from '@/config/axios' import request from '@/config/axios'
import { TransferReqVO } from '@/api/crm/customer' import { TransferReqVO } from '@/api/crm/permission'
export interface ContactVO { export interface ContactVO {
id: number // 编号 id: number // 编号
......
import request from '@/config/axios' import request from '@/config/axios'
import { TransferReqVO } from '@/api/crm/customer' import { TransferReqVO } from '@/api/crm/permission'
export interface ContractVO { export interface ContractVO {
id: number id: number
......
import request from '@/config/axios' import request from '@/config/axios'
import { TransferReqVO } from '@/api/crm/permission'
export interface CustomerVO { export interface CustomerVO {
id: number // 编号 id: number // 编号
...@@ -102,12 +103,6 @@ export const getCustomerSimpleList = async () => { ...@@ -102,12 +103,6 @@ export const getCustomerSimpleList = async () => {
// ======================= 业务操作 ======================= // ======================= 业务操作 =======================
export interface TransferReqVO {
id: number | undefined // 客户编号
newOwnerUserId: number | undefined // 新负责人的用户编号
oldOwnerPermissionLevel: number | undefined // 老负责人加入团队后的权限级别
}
// 客户转移 // 客户转移
export const transferCustomer = async (data: TransferReqVO) => { export const transferCustomer = async (data: TransferReqVO) => {
return await request.put({ url: '/crm/customer/transfer', data }) return await request.put({ url: '/crm/customer/transfer', data })
......
...@@ -6,6 +6,7 @@ export interface PermissionVO { ...@@ -6,6 +6,7 @@ export interface PermissionVO {
bizType: number // Crm 类型 bizType: number // Crm 类型
bizId: number // Crm 类型数据编号 bizId: number // Crm 类型数据编号
level: number // 权限级别 level: number // 权限级别
toBizTypes?: number[] // 同时添加至
deptName?: string // 部门名称 deptName?: string // 部门名称
nickname?: string // 用户昵称 nickname?: string // 用户昵称
postNames?: string[] // 岗位名称数组 postNames?: string[] // 岗位名称数组
...@@ -13,6 +14,13 @@ export interface PermissionVO { ...@@ -13,6 +14,13 @@ export interface PermissionVO {
ids?: number[] ids?: number[]
} }
export interface TransferReqVO {
bizId: number // 模块编号
newOwnerUserId: number // 新负责人的用户编号
oldOwnerPermissionLevel: number // 老负责人加入团队后的权限级别
toBizTypes?: number[] // 转移客户时,需要额外有【联系人】【商机】【合同】的 checkbox 选择
}
/** /**
* CRM 业务类型枚举 * CRM 业务类型枚举
* *
......
...@@ -3,18 +3,20 @@ import request from '@/config/axios' ...@@ -3,18 +3,20 @@ import request from '@/config/axios'
export interface ReceivableVO { export interface ReceivableVO {
id: number id: number
no: string no: string
planId: number planId?: number
customerId: number customerId?: number
customerName?: string customerName?: string
contractId: number contractId?: number
contract?: { contract?: {
id?: number
name?: string
no: string no: string
totalPrice: number totalPrice: number
} }
auditStatus: number auditStatus: number
processInstanceId: number processInstanceId: number
returnTime: Date returnTime: Date
returnType: string returnType: number
price: number price: number
ownerUserId: number ownerUserId: number
ownerUserName?: string ownerUserName?: string
......
...@@ -11,7 +11,7 @@ export interface ReceivablePlanVO { ...@@ -11,7 +11,7 @@ export interface ReceivablePlanVO {
remindTime: Date remindTime: Date
customerId: number customerId: number
customerName?: string customerName?: string
contractId: number contractId?: number
contractNo?: string contractNo?: string
ownerUserId: number ownerUserId: number
ownerUserName?: string ownerUserName?: string
......
import request from '@/config/axios'
export interface CrmStatisticsCustomerSummaryByDateRespVO {
time: string
customerCreateCount: number
customerDealCount: number
}
export interface CrmStatisticsCustomerSummaryByUserRespVO {
ownerUserName: string
customerCreateCount: number
customerDealCount: number
contractPrice: number
receivablePrice: number
}
export interface CrmStatisticsFollowupSummaryByDateRespVO {
time: string
followupRecordCount: number
followupCustomerCount: number
}
export interface CrmStatisticsFollowupSummaryByUserRespVO {
ownerUserName: string
followupRecordCount: number
followupCustomerCount: number
}
export interface CrmStatisticsFollowupSummaryByTypeRespVO {
followupType: string
followupRecordCount: number
}
export interface CrmStatisticsCustomerContractSummaryRespVO {
customerName: string
contractName: string
totalPrice: number
receivablePrice: number
customerType: string
customerSource: string
ownerUserName: string
creatorUserName: string
createTime: Date
orderDate: Date
}
export interface CrmStatisticsCustomerDealCycleByDateRespVO {
time: string
customerDealCycle: number
}
export interface CrmStatisticsCustomerDealCycleByUserRespVO {
ownerUserName: string
customerDealCycle: number
customerDealCount: number
}
// 客户分析 API
export const StatisticsCustomerApi = {
// 1.1 客户总量分析(按日期)
getCustomerSummaryByDate: (params: any) => {
return request.get({
url: '/crm/statistics-customer/get-customer-summary-by-date',
params
})
},
// 1.2 客户总量分析(按用户)
getCustomerSummaryByUser: (params: any) => {
return request.get({
url: '/crm/statistics-customer/get-customer-summary-by-user',
params
})
},
// 2.1 客户跟进次数分析(按日期)
getFollowupSummaryByDate: (params: any) => {
return request.get({
url: '/crm/statistics-customer/get-followup-summary-by-date',
params
})
},
// 2.2 客户跟进次数分析(按用户)
getFollowupSummaryByUser: (params: any) => {
return request.get({
url: '/crm/statistics-customer/get-followup-summary-by-user',
params
})
},
// 3.1 获取客户跟进方式统计数
getFollowupSummaryByType: (params: any) => {
return request.get({
url: '/crm/statistics-customer/get-followup-summary-by-type',
params
})
},
// 4.1 合同摘要信息(客户转化率页面)
getContractSummary: (params: any) => {
return request.get({
url: '/crm/statistics-customer/get-contract-summary',
params
})
},
// 5.1 获取客户成交周期(按日期)
getCustomerDealCycleByDate: (params: any) => {
return request.get({
url: '/crm/statistics-customer/get-customer-deal-cycle-by-date',
params
})
},
// 5.2 获取客户成交周期(按用户)
getCustomerDealCycleByUser: (params: any) => {
return request.get({
url: '/crm/statistics-customer/get-customer-deal-cycle-by-user',
params
})
}
}
/* stylelint-disable order/properties-order */
<template>
<div class="add-node-btn-box">
<div class="add-node-btn">
<el-popover placement="right-start" v-model="visible" width="auto">
<div class="add-node-popover-body">
<a class="add-node-popover-item approver" @click="addType(1)">
<div class="item-wrapper">
<span class="iconfont"></span>
</div>
<p>审批人</p>
</a>
<a class="add-node-popover-item notifier" @click="addType(2)">
<div class="item-wrapper">
<span class="iconfont"></span>
</div>
<p>抄送人</p>
</a>
<a class="add-node-popover-item condition" @click="addType(4)">
<div class="item-wrapper">
<span class="iconfont"></span>
</div>
<p>条件分支</p>
</a>
</div>
<template #reference>
<button class="btn" type="button">
<span class="iconfont"></span>
</button>
</template>
</el-popover>
</div>
</div>
</template>
<script setup>
import { ref } from 'vue'
let props = defineProps({
childNodeP: {
type: Object,
default: () => ({})
}
})
let emits = defineEmits(['update:childNodeP'])
let visible = ref(false)
const addType = (type) => {
visible.value = false
if (type != 4) {
var data
if (type == 1) {
data = {
nodeName: '审核人',
error: true,
type: 1,
settype: 1,
selectMode: 0,
selectRange: 0,
directorLevel: 1,
examineMode: 1,
noHanderAction: 1,
examineEndDirectorLevel: 0,
childNode: props.childNodeP,
nodeUserList: []
}
} else if (type == 2) {
data = {
nodeName: '抄送人',
type: 2,
ccSelfSelectFlag: 1,
childNode: props.childNodeP,
nodeUserList: []
}
}
emits('update:childNodeP', data)
} else {
emits('update:childNodeP', {
nodeName: '路由',
type: 4,
childNode: null,
conditionNodes: [
{
nodeName: '条件1',
error: true,
type: 3,
priorityLevel: 1,
conditionList: [],
nodeUserList: [],
childNode: props.childNodeP
},
{
nodeName: '条件2',
type: 3,
priorityLevel: 2,
conditionList: [],
nodeUserList: [],
childNode: null
}
]
})
}
}
</script>
<style scoped lang="scss">
.add-node-btn-box {
width: 240px;
display: inline-flex;
-ms-flex-negative: 0;
flex-shrink: 0;
-webkit-box-flex: 1;
-ms-flex-positive: 1;
position: relative;
&:before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: -1;
margin: auto;
width: 2px;
height: 100%;
background-color: #cacaca;
}
.add-node-btn {
user-select: none;
width: 240px;
padding: 20px 0 32px;
display: flex;
-webkit-box-pack: center;
justify-content: center;
flex-shrink: 0;
-webkit-box-flex: 1;
flex-grow: 1;
.btn {
outline: none;
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.1);
width: 30px;
height: 30px;
background: #3296fa;
border-radius: 50%;
position: relative;
border: none;
line-height: 30px;
-webkit-transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
.iconfont {
color: #fff;
font-size: 16px;
}
&:hover {
transform: scale(1.3);
box-shadow: 0 13px 27px 0 rgba(0, 0, 0, 0.1);
}
&:active {
transform: none;
background: #1e83e9;
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.1);
}
}
}
}
.add-node-popover-body {
display: flex;
.add-node-popover-item {
margin-right: 10px;
cursor: pointer;
text-align: center;
flex: 1;
color: #191f25 !important;
.item-wrapper {
user-select: none;
display: inline-block;
width: 80px;
height: 80px;
margin-bottom: 5px;
background: #fff;
border: 1px solid #e2e2e2;
border-radius: 50%;
transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
.iconfont {
font-size: 35px;
line-height: 80px;
}
}
&.approver {
.item-wrapper {
color: #ff943e;
}
}
&.notifier {
.item-wrapper {
color: #3296fa;
}
}
&.condition {
.item-wrapper {
color: #15bc83;
}
}
&:hover {
.item-wrapper {
background: #3296fa;
box-shadow: 0 10px 20px 0 rgba(50, 150, 250, 0.4);
}
.iconfont {
color: #fff;
}
}
&:active {
.item-wrapper {
box-shadow: none;
background: #eaeaea;
}
.iconfont {
color: inherit;
}
}
}
}
</style>
/**
* todo
*/
export const arrToStr = (arr?: [{ name: string }]) => {
if (arr) {
return arr
.map((item) => {
return item.name
})
.toString()
}
}
export const setApproverStr = (nodeConfig: any) => {
if (nodeConfig.settype == 1) {
if (nodeConfig.nodeUserList.length == 1) {
return nodeConfig.nodeUserList[0].name
} else if (nodeConfig.nodeUserList.length > 1) {
if (nodeConfig.examineMode == 1) {
return arrToStr(nodeConfig.nodeUserList)
} else if (nodeConfig.examineMode == 2) {
return nodeConfig.nodeUserList.length + '人会签'
}
}
} else if (nodeConfig.settype == 2) {
const level =
nodeConfig.directorLevel == 1 ? '直接主管' : '第' + nodeConfig.directorLevel + '级主管'
if (nodeConfig.examineMode == 1) {
return level
} else if (nodeConfig.examineMode == 2) {
return level + '会签'
}
} else if (nodeConfig.settype == 4) {
if (nodeConfig.selectRange == 1) {
return '发起人自选'
} else {
if (nodeConfig.nodeUserList.length > 0) {
if (nodeConfig.selectRange == 2) {
return '发起人自选'
} else {
return '发起人从' + nodeConfig.nodeUserList[0].name + '中自选'
}
} else {
return ''
}
}
} else if (nodeConfig.settype == 5) {
return '发起人自己'
} else if (nodeConfig.settype == 7) {
return '从直接主管到通讯录中级别最高的第' + nodeConfig.examineEndDirectorLevel + '个层级主管'
}
}
export const copyerStr = (nodeConfig: any) => {
if (nodeConfig.nodeUserList.length != 0) {
return arrToStr(nodeConfig.nodeUserList)
} else {
if (nodeConfig.ccSelfSelectFlag == 1) {
return '发起人自选'
}
}
}
export const conditionStr = (nodeConfig, index) => {
const { conditionList, nodeUserList } = nodeConfig.conditionNodes[index]
if (conditionList.length == 0) {
return index == nodeConfig.conditionNodes.length - 1 &&
nodeConfig.conditionNodes[0].conditionList.length != 0
? '其他条件进入此流程'
: '请设置条件'
} else {
let str = ''
for (let i = 0; i < conditionList.length; i++) {
const {
columnId,
columnType,
showType,
showName,
optType,
zdy1,
opt1,
zdy2,
opt2,
fixedDownBoxValue
} = conditionList[i]
if (columnId == 0) {
if (nodeUserList.length != 0) {
str += '发起人属于:'
str +=
nodeUserList
.map((item) => {
return item.name
})
.join('或') + ' 并且 '
}
}
if (columnType == 'String' && showType == '3') {
if (zdy1) {
str += showName + '属于:' + dealStr(zdy1, JSON.parse(fixedDownBoxValue)) + ' 并且 '
}
}
if (columnType == 'Double') {
if (optType != 6 && zdy1) {
const optTypeStr = ['', '<', '>', '≤', '=', '≥'][optType]
str += `${showName} ${optTypeStr} ${zdy1} 并且 `
} else if (optType == 6 && zdy1 && zdy2) {
str += `${zdy1} ${opt1} ${showName} ${opt2} ${zdy2} 并且 `
}
}
}
return str ? str.substring(0, str.length - 4) : '请设置条件'
}
}
export const dealStr = (str: string, obj) => {
const arr = []
const list = str.split(',')
for (const elem in obj) {
list.map((item) => {
if (item == elem) {
arr.push(obj[elem].value)
}
})
}
return arr.join('或')
}
export const removeEle = (arr, elem, key = 'id') => {
let includesIndex
arr.map((item, index) => {
if (item[key] == elem[key]) {
includesIndex = index
}
})
arr.splice(includesIndex, 1)
}
export const bgColors = ['87, 106, 149', '255, 148, 62', '50, 150, 250']
export const placeholderList = ['发起人', '审核人', '抄送人']
export const setTypes = [
{ value: 1, label: '指定成员' },
{ value: 2, label: '主管' },
{ value: 4, label: '发起人自选' },
{ value: 5, label: '发起人自己' },
{ value: 7, label: '连续多级主管' }
]
export const selectModes = [
{ value: 1, label: '选一个人' },
{ value: 2, label: '选多个人' }
]
export const selectRanges = [
{ value: 1, label: '全公司' },
{ value: 2, label: '指定成员' },
{ value: 3, label: '指定角色' }
]
export const optTypes = [
{ value: '1', label: '小于' },
{ value: '2', label: '大于' },
{ value: '3', label: '小于等于' },
{ value: '4', label: '等于' },
{ value: '5', label: '大于等于' },
{ value: '6', label: '介于两个数之间' }
]
...@@ -436,7 +436,7 @@ const initBpmnModeler = () => { ...@@ -436,7 +436,7 @@ const initBpmnModeler = () => {
// bpmnModeler.createDiagram() // bpmnModeler.createDiagram()
console.log(bpmnModeler, 'bpmnModeler111111') // console.log(bpmnModeler, 'bpmnModeler111111')
emit('init-finished', bpmnModeler) emit('init-finished', bpmnModeler)
initModelListeners() initModelListeners()
} }
...@@ -666,10 +666,10 @@ const previewProcessJson = () => { ...@@ -666,10 +666,10 @@ const previewProcessJson = () => {
} }
/* ------------------------------------------------ 芋道源码 methods ------------------------------------------------------ */ /* ------------------------------------------------ 芋道源码 methods ------------------------------------------------------ */
const processSave = async () => { const processSave = async () => {
console.log(bpmnModeler, 'bpmnModelerbpmnModelerbpmnModelerbpmnModeler') // console.log(bpmnModeler, 'bpmnModelerbpmnModelerbpmnModelerbpmnModeler')
const { err, xml } = await bpmnModeler.saveXML() const { err, xml } = await bpmnModeler.saveXML()
console.log(err, 'errerrerrerrerr') // console.log(err, 'errerrerrerrerr')
console.log(xml, 'xmlxmlxmlxmlxml') // console.log(xml, 'xmlxmlxmlxmlxml')
// 读取异常时抛出异常 // 读取异常时抛出异常
if (err) { if (err) {
// this.$modal.msgError('保存模型失败,请重试!') // this.$modal.msgError('保存模型失败,请重试!')
......
...@@ -115,19 +115,19 @@ const highlightDiagram = async () => { ...@@ -115,19 +115,19 @@ const highlightDiagram = async () => {
if (!task) { if (!task) {
return return
} }
//进行中的任务已经高亮过了,则不高亮后面的任务了 // 进行中的任务已经高亮过了,则不高亮后面的任务了
if (findProcessTask) { if (findProcessTask) {
removeTaskDefinitionKeyList.push(n.id) removeTaskDefinitionKeyList.push(n.id)
return return
} }
// 高亮任务 // 高亮任务
canvas.addMarker(n.id, getResultCss(task.result)) canvas.addMarker(n.id, getResultCss(task.status))
//标记是否高亮了进行中任务 //标记是否高亮了进行中任务
if (task.result === 1) { if (task.status === 1) {
findProcessTask = true findProcessTask = true
} }
// 如果非通过,就不走后面的线条了 // 如果非通过,就不走后面的线条了
if (task.result !== 2) { if (task.status !== 2) {
return return
} }
// 处理 outgoing 出线 // 处理 outgoing 出线
...@@ -194,6 +194,7 @@ const highlightDiagram = async () => { ...@@ -194,6 +194,7 @@ const highlightDiagram = async () => {
}) })
} else if (n.$type === 'bpmn:StartEvent') { } else if (n.$type === 'bpmn:StartEvent') {
// 开始节点 // 开始节点
canvas.addMarker(n.id, 'highlight')
n.outgoing?.forEach((nn) => { n.outgoing?.forEach((nn) => {
// outgoing 例如说【bpmn:SequenceFlow】连线 // outgoing 例如说【bpmn:SequenceFlow】连线
// 获得连线是否有指向目标。如果有,则进行高亮 // 获得连线是否有指向目标。如果有,则进行高亮
...@@ -205,10 +206,10 @@ const highlightDiagram = async () => { ...@@ -205,10 +206,10 @@ const highlightDiagram = async () => {
}) })
} else if (n.$type === 'bpmn:EndEvent') { } else if (n.$type === 'bpmn:EndEvent') {
// 结束节点 // 结束节点
if (!processInstance.value || processInstance.value.result === 1) { if (!processInstance.value || processInstance.value.status === 1) {
return return
} }
canvas.addMarker(n.id, getResultCss(processInstance.value.result)) canvas.addMarker(n.id, getResultCss(processInstance.value.status))
} else if (n.$type === 'bpmn:ServiceTask') { } else if (n.$type === 'bpmn:ServiceTask') {
//服务任务 //服务任务
if (activity.startTime > 0 && activity.endTime === 0) { if (activity.startTime > 0 && activity.endTime === 0) {
...@@ -223,39 +224,49 @@ const highlightDiagram = async () => { ...@@ -223,39 +224,49 @@ const highlightDiagram = async () => {
canvas.addMarker(out.id, getResultCss(2)) canvas.addMarker(out.id, getResultCss(2))
}) })
} }
} else if (n.$type === 'bpmn:SequenceFlow') {
let targetActivity = activityList.find((m: any) => m.key === n.targetRef.id)
if (targetActivity) {
canvas.addMarker(n.id, getActivityHighlightCss(targetActivity))
}
} }
}) })
if (!isEmpty(removeTaskDefinitionKeyList)) { if (!isEmpty(removeTaskDefinitionKeyList)) {
taskList.value = taskList.value.filter( taskList.value = taskList.value.filter(
(item) => !removeTaskDefinitionKeyList.includes(item.definitionKey) (item) => !removeTaskDefinitionKeyList.includes(item.taskDefinitionKey)
) )
} }
} }
const getActivityHighlightCss = (activity) => { const getActivityHighlightCss = (activity) => {
return activity.endTime ? 'highlight' : 'highlight-todo' return activity.endTime ? 'highlight' : 'highlight-todo'
} }
const getResultCss = (result) => {
if (result === 1) { const getResultCss = (status) => {
if (status === 1) {
// 审批中 // 审批中
return 'highlight-todo' return 'highlight-todo'
} else if (result === 2) { } else if (status === 2) {
// 已通过 // 已通过
return 'highlight' return 'highlight'
} else if (result === 3) { } else if (status === 3) {
// 不通过 // 不通过
return 'highlight-reject' return 'highlight-reject'
} else if (result === 4) { } else if (status === 4) {
// 已取消 // 已取消
return 'highlight-cancel' return 'highlight-cancel'
} else if (result === 5) { } else if (status === 5) {
// 退回 // 退回
return 'highlight-return' return 'highlight-return'
} else if (result === 6) { } else if (status === 6) {
// 委派 // 委派
return 'highlight-return' return 'highlight-todo'
} else if (result === 7 || result === 8 || result === 9) { } else if (status === 7) {
// 待后加签任务完成/待前加签任务完成/待前置任务完成 // 审批通过中
return 'highlight-return' return 'highlight-todo'
} else if (status === 0) {
// 待审批
return 'highlight-todo'
} }
return '' return ''
} }
...@@ -296,10 +307,10 @@ const elementHover = (element) => { ...@@ -296,10 +307,10 @@ const elementHover = (element) => {
!elementOverlayIds.value && (elementOverlayIds.value = {}) !elementOverlayIds.value && (elementOverlayIds.value = {})
!overlays.value && (overlays.value = bpmnModeler.get('overlays')) !overlays.value && (overlays.value = bpmnModeler.get('overlays'))
// 展示信息 // 展示信息
console.log(activityLists.value, 'activityLists.value') // console.log(activityLists.value, 'activityLists.value')
console.log(element.value, 'element.value') // console.log(element.value, 'element.value')
const activity = activityLists.value.find((m) => m.key === element.value.id) const activity = activityLists.value.find((m) => m.key === element.value.id)
console.log(activity, 'activityactivityactivityactivity') // console.log(activity, 'activityactivityactivityactivity')
if (!activity) { if (!activity) {
return return
} }
...@@ -313,15 +324,14 @@ const elementHover = (element) => { ...@@ -313,15 +324,14 @@ const elementHover = (element) => {
<p>部门:${processInstance.value.startUser.deptName}</p> <p>部门:${processInstance.value.startUser.deptName}</p>
<p>创建时间:${formatDate(processInstance.value.createTime)}` <p>创建时间:${formatDate(processInstance.value.createTime)}`
} else if (element.value.type === 'bpmn:UserTask') { } else if (element.value.type === 'bpmn:UserTask') {
// debugger
let task = taskList.value.find((m) => m.id === activity.taskId) // 找到活动对应的 taskId let task = taskList.value.find((m) => m.id === activity.taskId) // 找到活动对应的 taskId
if (!task) { if (!task) {
return return
} }
let optionData = getIntDictOptions(DICT_TYPE.BPM_PROCESS_INSTANCE_RESULT) let optionData = getIntDictOptions(DICT_TYPE.BPM_TASK_STATUS)
let dataResult = '' let dataResult = ''
optionData.forEach((element) => { optionData.forEach((element) => {
if (element.value == task.result) { if (element.value == task.status) {
dataResult = element.label dataResult = element.label
} }
}) })
...@@ -333,7 +343,7 @@ const elementHover = (element) => { ...@@ -333,7 +343,7 @@ const elementHover = (element) => {
// <p>部门:${task.assigneeUser.deptName}</p> // <p>部门:${task.assigneeUser.deptName}</p>
// <p>结果:${getIntDictOptions( // <p>结果:${getIntDictOptions(
// DICT_TYPE.BPM_PROCESS_INSTANCE_RESULT, // DICT_TYPE.BPM_PROCESS_INSTANCE_RESULT,
// task.result // task.status
// )}</p> // )}</p>
// <p>创建时间:${formatDate(task.createTime)}</p>` // <p>创建时间:${formatDate(task.createTime)}</p>`
if (task.endTime) { if (task.endTime) {
...@@ -351,29 +361,30 @@ const elementHover = (element) => { ...@@ -351,29 +361,30 @@ const elementHover = (element) => {
} }
console.log(html) console.log(html)
} else if (element.value.type === 'bpmn:EndEvent' && processInstance.value) { } else if (element.value.type === 'bpmn:EndEvent' && processInstance.value) {
let optionData = getIntDictOptions(DICT_TYPE.BPM_PROCESS_INSTANCE_RESULT) let optionData = getIntDictOptions(DICT_TYPE.BPM_TASK_STATUS)
let dataResult = '' let dataResult = ''
optionData.forEach((element) => { optionData.forEach((element) => {
if (element.value == processInstance.value.result) { if (element.value == processInstance.value.status) {
dataResult = element.label dataResult = element.label
} }
}) })
html = `<p>结果:${dataResult}</p>` html = `<p>结果:${dataResult}</p>`
// html = `<p>结果:${getIntDictOptions( // html = `<p>结果:${getIntDictOptions(
// DICT_TYPE.BPM_PROCESS_INSTANCE_RESULT, // DICT_TYPE.BPM_PROCESS_INSTANCE_RESULT,
// processInstance.value.result // processInstance.value.status
// )}</p>` // )}</p>`
if (processInstance.value.endTime) { if (processInstance.value.endTime) {
html += `<p>结束时间:${formatDate(processInstance.value.endTime)}</p>` html += `<p>结束时间:${formatDate(processInstance.value.endTime)}</p>`
} }
} }
console.log(html, 'html111111111111111') // console.log(html, 'html111111111111111')
elementOverlayIds.value[element.value.id] = toRaw(overlays.value)?.add(element.value, { elementOverlayIds.value[element.value.id] = toRaw(overlays.value)?.add(element.value, {
position: { left: 0, bottom: 0 }, position: { left: 0, bottom: 0 },
html: `<div class="element-overlays">${html}</div>` html: `<div class="element-overlays">${html}</div>`
}) })
} }
} }
// 流程图的元素被 out // 流程图的元素被 out
const elementOut = (element) => { const elementOut = (element) => {
toRaw(overlays.value).remove({ element }) toRaw(overlays.value).remove({ element })
...@@ -389,6 +400,7 @@ onMounted(() => { ...@@ -389,6 +400,7 @@ onMounted(() => {
// 初始模型的监听器 // 初始模型的监听器
initModelListeners() initModelListeners()
}) })
onBeforeUnmount(() => { onBeforeUnmount(() => {
// this.$once('hook:beforeDestroy', () => { // this.$once('hook:beforeDestroy', () => {
// }) // })
...@@ -427,7 +439,7 @@ watch( ...@@ -427,7 +439,7 @@ watch(
) )
</script> </script>
<style> <style lang="scss">
/** 处理中 */ /** 处理中 */
.highlight-todo.djs-connection > .djs-visual > path { .highlight-todo.djs-connection > .djs-visual > path {
stroke: #1890ff !important; stroke: #1890ff !important;
...@@ -501,6 +513,10 @@ watch( ...@@ -501,6 +513,10 @@ watch(
stroke: green !important; stroke: green !important;
} }
.djs-element.highlight > .djs-visual > path {
stroke: green !important;
}
/** 不通过 */ /** 不通过 */
.highlight-reject.djs-shape .djs-visual > :nth-child(1) { .highlight-reject.djs-shape .djs-visual > :nth-child(1) {
fill: red !important; fill: red !important;
...@@ -520,6 +536,7 @@ watch( ...@@ -520,6 +536,7 @@ watch(
.highlight-reject.djs-connection > .djs-visual > path { .highlight-reject.djs-connection > .djs-visual > path {
stroke: red !important; stroke: red !important;
marker-end: url(#sequenceflow-end-white-success) !important;
} }
.highlight-reject:not(.djs-connection) .djs-visual > :nth-child(1) { .highlight-reject:not(.djs-connection) .djs-visual > :nth-child(1) {
......
...@@ -332,6 +332,16 @@ ...@@ -332,6 +332,16 @@
"name": "multiinstance_condition", "name": "multiinstance_condition",
"isAttr": true, "isAttr": true,
"type": "String" "type": "String"
},
{
"name": "candidateStrategy",
"isAttr": true,
"type": "String"
},
{
"name": "candidateParam",
"isAttr": true,
"type": "String"
} }
] ]
}, },
......
...@@ -319,6 +319,16 @@ ...@@ -319,6 +319,16 @@
"name": "priority", "name": "priority",
"isAttr": true, "isAttr": true,
"type": "String" "type": "String"
},
{
"name": "candidateStrategy",
"isAttr": true,
"type": "String"
},
{
"name": "candidateParam",
"isAttr": true,
"type": "String"
} }
] ]
}, },
......
...@@ -319,6 +319,16 @@ ...@@ -319,6 +319,16 @@
"name": "priority", "name": "priority",
"isAttr": true, "isAttr": true,
"type": "String" "type": "String"
},
{
"name": "candidateStrategy",
"isAttr": true,
"type": "String"
},
{
"name": "candidateParam",
"isAttr": true,
"type": "String"
} }
] ]
}, },
......
...@@ -24,15 +24,10 @@ ...@@ -24,15 +24,10 @@
</el-collapse-item> </el-collapse-item>
<el-collapse-item name="condition" v-if="formVisible" key="form"> <el-collapse-item name="condition" v-if="formVisible" key="form">
<template #title><Icon icon="ep:list" />表单</template> <template #title><Icon icon="ep:list" />表单</template>
<!-- <element-form :id="elementId" :type="elementType" /> --> <element-form :id="elementId" :type="elementType" />
友情提示:使用
<router-link :to="{ path: '/bpm/manager/form' }"
><el-link type="danger">流程表单</el-link>
</router-link>
替代,提供更好的表单设计功能
</el-collapse-item> </el-collapse-item>
<el-collapse-item name="task" v-if="elementType.indexOf('Task') !== -1" key="task"> <el-collapse-item name="task" v-if="elementType.indexOf('Task') !== -1" key="task">
<template #title><Icon icon="ep:checked" />任务</template> <template #title><Icon icon="ep:checked" />任务(审批人)</template>
<element-task :id="elementId" :type="elementType" /> <element-task :id="elementId" :type="elementType" />
</el-collapse-item> </el-collapse-item>
<el-collapse-item <el-collapse-item
...@@ -40,7 +35,7 @@ ...@@ -40,7 +35,7 @@
v-if="elementType.indexOf('Task') !== -1" v-if="elementType.indexOf('Task') !== -1"
key="multiInstance" key="multiInstance"
> >
<template #title><Icon icon="ep:help-filled" />多实例</template> <template #title><Icon icon="ep:help-filled" />多实例(会签配置)</template>
<element-multi-instance :business-object="elementBusinessObject" :type="elementType" /> <element-multi-instance :business-object="elementBusinessObject" :type="elementType" />
</el-collapse-item> </el-collapse-item>
<el-collapse-item name="listeners" key="listeners"> <el-collapse-item name="listeners" key="listeners">
......
...@@ -68,13 +68,13 @@ const resetBaseInfo = () => { ...@@ -68,13 +68,13 @@ const resetBaseInfo = () => {
console.log(bpmnElement.value, 'bpmnElement') console.log(bpmnElement.value, 'bpmnElement')
bpmnElement.value = bpmnInstances()?.bpmnElement bpmnElement.value = bpmnInstances()?.bpmnElement
console.log(bpmnElement.value, 'resetBaseInfo11111111111') // console.log(bpmnElement.value, 'resetBaseInfo11111111111')
elementBaseInfo.value = bpmnElement.value.businessObject elementBaseInfo.value = bpmnElement.value.businessObject
needProps.value['type'] = bpmnElement.value.businessObject.$type needProps.value['type'] = bpmnElement.value.businessObject.$type
// elementBaseInfo.value['typess'] = bpmnElement.value.businessObject.$type // elementBaseInfo.value['typess'] = bpmnElement.value.businessObject.$type
// elementBaseInfo.value = JSON.parse(JSON.stringify(bpmnElement.value.businessObject)) // elementBaseInfo.value = JSON.parse(JSON.stringify(bpmnElement.value.businessObject))
console.log(elementBaseInfo.value, 'elementBaseInfo22222222222') // console.log(elementBaseInfo.value, 'elementBaseInfo22222222222')
} }
const handleKeyUpdate = (value) => { const handleKeyUpdate = (value) => {
// 校验 value 的值,只有 XML NCName 通过的情况下,才进行赋值。否则,会导致流程图报错,无法绘制的问题 // 校验 value 的值,只有 XML NCName 通过的情况下,才进行赋值。否则,会导致流程图报错,无法绘制的问题
...@@ -121,11 +121,11 @@ const updateBaseInfo = (key) => { ...@@ -121,11 +121,11 @@ const updateBaseInfo = (key) => {
// id: elementBaseInfo.value[key] // id: elementBaseInfo.value[key]
// // di: { id: `${elementBaseInfo.value[key]}_di` } // // di: { id: `${elementBaseInfo.value[key]}_di` }
// } // }
console.log(elementBaseInfo, 'elementBaseInfo11111111111') // console.log(elementBaseInfo, 'elementBaseInfo11111111111')
needProps.value = { ...elementBaseInfo.value, ...needProps.value } needProps.value = { ...elementBaseInfo.value, ...needProps.value }
if (key === 'id') { if (key === 'id') {
console.log('jinru') // console.log('jinru')
console.log(window, 'window') console.log(window, 'window')
console.log(bpmnElement.value, 'bpmnElement') console.log(bpmnElement.value, 'bpmnElement')
console.log(toRaw(bpmnElement.value), 'bpmnElement') console.log(toRaw(bpmnElement.value), 'bpmnElement')
...@@ -138,20 +138,19 @@ const updateBaseInfo = (key) => { ...@@ -138,20 +138,19 @@ const updateBaseInfo = (key) => {
bpmnInstances().modeling.updateProperties(toRaw(bpmnElement.value), attrObj) bpmnInstances().modeling.updateProperties(toRaw(bpmnElement.value), attrObj)
} }
} }
onMounted(() => { onMounted(() => {
// 针对上传的 bpmn 流程图时,需要延迟 1 秒的时间,保证 key 和 name 的更新 // 针对上传的 bpmn 流程图时,需要延迟 1 秒的时间,保证 key 和 name 的更新
setTimeout(() => { setTimeout(() => {
console.log(props.model, 'props.model')
handleKeyUpdate(props.model.key) handleKeyUpdate(props.model.key)
handleNameUpdate(props.model.name) handleNameUpdate(props.model.name)
console.log(props, 'propsssssssssssssssssssss') }, 110)
}, 1000)
}) })
watch( watch(
() => props.businessObject, () => props.businessObject,
(val) => { (val) => {
console.log(val, 'val11111111111111111111') // console.log(val, 'val11111111111111111111')
if (val) { if (val) {
// nextTick(() => { // nextTick(() => {
resetBaseInfo() resetBaseInfo()
...@@ -159,6 +158,18 @@ watch( ...@@ -159,6 +158,18 @@ watch(
} }
} }
) )
watch(
() => props.model?.key,
(val) => {
// 针对上传的 bpmn 流程图时,保证 key 和 name 的更新
if (val) {
handleKeyUpdate(props.model.key)
handleNameUpdate(props.model.name)
}
}
)
// watch( // watch(
// () => ({ ...props }), // () => ({ ...props }),
// (oldVal, newVal) => { // (oldVal, newVal) => {
......
...@@ -26,8 +26,16 @@ ...@@ -26,8 +26,16 @@
type="primary" type="primary"
preIcon="ep:plus" preIcon="ep:plus"
title="添加监听器" title="添加监听器"
size="small"
@click="openListenerForm(null)" @click="openListenerForm(null)"
/> />
<XButton
type="success"
preIcon="ep:select"
title="选择监听器"
size="small"
@click="openProcessListenerDialog"
/>
</div> </div>
<!-- 监听器 编辑/创建 部分 --> <!-- 监听器 编辑/创建 部分 -->
...@@ -240,11 +248,21 @@ ...@@ -240,11 +248,21 @@
</template> </template>
</el-dialog> </el-dialog>
</div> </div>
<!-- 选择弹窗 -->
<ProcessListenerDialog ref="processListenerDialogRef" @select="selectProcessListener" />
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ElMessageBox } from 'element-plus' import { ElMessageBox } from 'element-plus'
import { createListenerObject, updateElementExtensions } from '../../utils' import { createListenerObject, updateElementExtensions } from '../../utils'
import { initListenerType, initListenerForm, listenerType, fieldType } from './utilSelf' import {
initListenerType,
initListenerForm,
listenerType,
fieldType,
initListenerForm2
} from './utilSelf'
import ProcessListenerDialog from './ProcessListenerDialog.vue'
defineOptions({ name: 'ElementListeners' }) defineOptions({ name: 'ElementListeners' })
...@@ -284,6 +302,7 @@ const resetListenersList = () => { ...@@ -284,6 +302,7 @@ const resetListenersList = () => {
} }
// 打开 监听器详情 侧边栏 // 打开 监听器详情 侧边栏
const openListenerForm = (listener, index?) => { const openListenerForm = (listener, index?) => {
// debugger
if (listener) { if (listener) {
listenerForm.value = initListenerForm(listener) listenerForm.value = initListenerForm(listener)
editingListenerIndex.value = index editingListenerIndex.value = index
...@@ -321,6 +340,7 @@ const openListenerFieldForm = (field, index?) => { ...@@ -321,6 +340,7 @@ const openListenerFieldForm = (field, index?) => {
} }
// 保存监听器注入字段 // 保存监听器注入字段
const saveListenerFiled = async () => { const saveListenerFiled = async () => {
// debugger
let validateStatus = await listenerFieldFormRef.value.validate() let validateStatus = await listenerFieldFormRef.value.validate()
if (!validateStatus) return // 验证不通过直接返回 if (!validateStatus) return // 验证不通过直接返回
if (editingListenerFieldIndex.value === -1) { if (editingListenerFieldIndex.value === -1) {
...@@ -337,6 +357,7 @@ const saveListenerFiled = async () => { ...@@ -337,6 +357,7 @@ const saveListenerFiled = async () => {
} }
// 移除监听器字段 // 移除监听器字段
const removeListenerField = (index) => { const removeListenerField = (index) => {
// debugger
ElMessageBox.confirm('确认移除该字段吗?', '提示', { ElMessageBox.confirm('确认移除该字段吗?', '提示', {
confirmButtonText: '确 认', confirmButtonText: '确 认',
cancelButtonText: '取 消' cancelButtonText: '取 消'
...@@ -349,6 +370,7 @@ const removeListenerField = (index) => { ...@@ -349,6 +370,7 @@ const removeListenerField = (index) => {
} }
// 移除监听器 // 移除监听器
const removeListener = (index) => { const removeListener = (index) => {
debugger
ElMessageBox.confirm('确认移除该监听器吗?', '提示', { ElMessageBox.confirm('确认移除该监听器吗?', '提示', {
confirmButtonText: '确 认', confirmButtonText: '确 认',
cancelButtonText: '取 消' cancelButtonText: '取 消'
...@@ -365,6 +387,7 @@ const removeListener = (index) => { ...@@ -365,6 +387,7 @@ const removeListener = (index) => {
} }
// 保存监听器配置 // 保存监听器配置
const saveListenerConfig = async () => { const saveListenerConfig = async () => {
// debugger
let validateStatus = await listenerFormRef.value.validate() let validateStatus = await listenerFormRef.value.validate()
if (!validateStatus) return // 验证不通过直接返回 if (!validateStatus) return // 验证不通过直接返回
const listenerObject = createListenerObject(listenerForm.value, false, prefix) const listenerObject = createListenerObject(listenerForm.value, false, prefix)
...@@ -389,6 +412,28 @@ const saveListenerConfig = async () => { ...@@ -389,6 +412,28 @@ const saveListenerConfig = async () => {
listenerForm.value = {} listenerForm.value = {}
} }
// 打开监听器弹窗
const processListenerDialogRef = ref()
const openProcessListenerDialog = async () => {
processListenerDialogRef.value.open('execution')
}
const selectProcessListener = (listener) => {
const listenerForm = initListenerForm2(listener)
const listenerObject = createListenerObject(listenerForm, false, prefix)
bpmnElementListeners.value.push(listenerObject)
elementListenersList.value.push(listenerForm)
// 保存其他配置
otherExtensionList.value =
bpmnElement.value.businessObject?.extensionElements?.values?.filter(
(ex) => ex.$type !== `${prefix}:ExecutionListener`
) ?? []
updateElementExtensions(
bpmnElement.value,
otherExtensionList.value.concat(bpmnElementListeners.value)
)
}
watch( watch(
() => props.id, () => props.id,
(val) => { (val) => {
......
<!-- 执行器选择 -->
<template>
<Dialog title="请选择监听器" v-model="dialogVisible" width="1024px">
<ContentWrap>
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
<el-table-column label="名字" align="center" prop="name" />
<el-table-column label="类型" align="center" prop="type">
<template #default="scope">
<dict-tag :type="DICT_TYPE.BPM_PROCESS_LISTENER_TYPE" :value="scope.row.type" />
</template>
</el-table-column>
<el-table-column label="事件" align="center" prop="event" />
<el-table-column label="值类型" align="center" prop="valueType">
<template #default="scope">
<dict-tag
:type="DICT_TYPE.BPM_PROCESS_LISTENER_VALUE_TYPE"
:value="scope.row.valueType"
/>
</template>
</el-table-column>
<el-table-column label="值" align="center" prop="value" />
<el-table-column label="操作" align="center">
<template #default="scope">
<el-button link type="primary" @click="select(scope.row)"> 选择 </el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<Pagination
:total="total"
v-model:page="queryParams.pageNo"
v-model:limit="queryParams.pageSize"
@pagination="getList"
/>
</ContentWrap>
</Dialog>
</template>
<script setup lang="ts">
import { ProcessListenerApi, ProcessListenerVO } from '@/api/bpm/processListener'
import { DICT_TYPE } from '@/utils/dict'
import { CommonStatusEnum } from '@/utils/constants'
/** BPM 流程 表单 */
defineOptions({ name: 'ProcessListenerDialog' })
const { t } = useI18n() // 国际化
const message = useMessage() // 消息弹窗
const dialogVisible = ref(false) // 弹窗的是否展示
const loading = ref(true) // 列表的加载中
const list = ref<ProcessListenerVO[]>([]) // 列表的数据
const total = ref(0) // 列表的总页数
const queryParams = reactive({
pageNo: 1,
pageSize: 10,
type: undefined,
status: CommonStatusEnum.ENABLE
})
/** 打开弹窗 */
const open = async (type: string) => {
dialogVisible.value = true
loading.value = true
try {
queryParams.pageNo = 1
queryParams.type = type
const data = await ProcessListenerApi.getProcessListenerPage(queryParams)
list.value = data.list
total.value = data.total
} finally {
loading.value = false
}
}
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
/** 提交表单 */
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
const select = async (row) => {
dialogVisible.value = false
// 发送操作成功的事件
emit('select', row)
}
</script>
...@@ -39,6 +39,13 @@ ...@@ -39,6 +39,13 @@
title="添加监听器" title="添加监听器"
@click="openListenerForm(null)" @click="openListenerForm(null)"
/> />
<XButton
type="success"
preIcon="ep:select"
title="选择监听器"
size="small"
@click="openProcessListenerDialog"
/>
</div> </div>
<!-- 监听器 编辑/创建 部分 --> <!-- 监听器 编辑/创建 部分 -->
...@@ -286,11 +293,22 @@ ...@@ -286,11 +293,22 @@
</template> </template>
</el-dialog> </el-dialog>
</div> </div>
<!-- 选择弹窗 -->
<ProcessListenerDialog ref="processListenerDialogRef" @select="selectProcessListener" />
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ElMessageBox } from 'element-plus' import { ElMessageBox } from 'element-plus'
import { createListenerObject, updateElementExtensions } from '../../utils' import { createListenerObject, updateElementExtensions } from '../../utils'
import { initListenerForm, initListenerType, eventType, listenerType, fieldType } from './utilSelf' import {
initListenerForm,
initListenerType,
eventType,
listenerType,
fieldType,
initListenerForm2
} from './utilSelf'
import ProcessListenerDialog from '@/components/bpmnProcessDesigner/package/penal/listeners/ProcessListenerDialog.vue'
defineOptions({ name: 'UserTaskListeners' }) defineOptions({ name: 'UserTaskListeners' })
...@@ -437,6 +455,28 @@ const removeListenerField = (field, index) => { ...@@ -437,6 +455,28 @@ const removeListenerField = (field, index) => {
.catch(() => console.info('操作取消')) .catch(() => console.info('操作取消'))
} }
// 打开监听器弹窗
const processListenerDialogRef = ref()
const openProcessListenerDialog = async () => {
processListenerDialogRef.value.open('task')
}
const selectProcessListener = (listener) => {
const listenerForm = initListenerForm2(listener)
const listenerObject = createListenerObject(listenerForm, true, prefix)
bpmnElementListeners.value.push(listenerObject)
elementListenersList.value.push(listenerForm)
// 保存其他配置
otherExtensionList.value =
bpmnElement.value.businessObject?.extensionElements?.values?.filter(
(ex) => ex.$type !== `${prefix}:TaskListener`
) ?? []
updateElementExtensions(
bpmnElement.value,
otherExtensionList.value.concat(bpmnElementListeners.value)
)
}
watch( watch(
() => props.id, () => props.id,
(val) => { (val) => {
......
...@@ -40,6 +40,33 @@ export function initListenerType(listener) { ...@@ -40,6 +40,33 @@ export function initListenerType(listener) {
} }
} }
/** 将 ProcessListenerDO 转换成 initListenerForm 想同的 Form 对象 */
export function initListenerForm2(processListener) {
if (processListener.valueType === 'class') {
return {
listenerType: 'classListener',
class: processListener.value,
event: processListener.event,
fields: []
}
} else if (processListener.valueType === 'expression') {
return {
listenerType: 'expressionListener',
expression: processListener.value,
event: processListener.event,
fields: []
}
} else if (processListener.valueType === 'delegateExpression') {
return {
listenerType: 'delegateExpressionListener',
delegateExpression: processListener.value,
event: processListener.event,
fields: []
}
}
throw new Error('未知的监听器类型')
}
export const listenerType = { export const listenerType = {
classListener: 'Java 类', classListener: 'Java 类',
expressionListener: '表达式', expressionListener: '表达式',
......
<template> <template>
<div class="panel-tab__content"> <div class="panel-tab__content">
<el-form label-width="90px"> <el-form label-width="90px">
<el-form-item label="回路特性"> <el-form-item label="快捷配置">
<el-button size="small" @click="changeConfig('依次审批')">依次审批</el-button>
<el-button size="small" @click="changeConfig('会签')">会签</el-button>
<el-button size="small" @click="changeConfig('或签')">或签</el-button>
</el-form-item>
<el-form-item label="会签类型">
<el-select v-model="loopCharacteristics" @change="changeLoopCharacteristicsType"> <el-select v-model="loopCharacteristics" @change="changeLoopCharacteristicsType">
<el-option label="并行多重事件" value="ParallelMultiInstance" /> <el-option label="并行多重事件" value="ParallelMultiInstance" />
<el-option label="时序多重事件" value="SequentialMultiInstance" /> <el-option label="时序多重事件" value="SequentialMultiInstance" />
<el-option label="循环事件" value="StandardLoop" />
<el-option label="无" value="Null" /> <el-option label="无" value="Null" />
</el-select> </el-select>
</el-form-item> </el-form-item>
...@@ -15,7 +19,7 @@ ...@@ -15,7 +19,7 @@
loopCharacteristics === 'SequentialMultiInstance' loopCharacteristics === 'SequentialMultiInstance'
" "
> >
<el-form-item label="循环基数" key="loopCardinality"> <el-form-item label="循环数量" key="loopCardinality">
<el-input <el-input
v-model="loopInstanceForm.loopCardinality" v-model="loopInstanceForm.loopCardinality"
clearable clearable
...@@ -25,7 +29,8 @@ ...@@ -25,7 +29,8 @@
<el-form-item label="集合" key="collection" v-show="false"> <el-form-item label="集合" key="collection" v-show="false">
<el-input v-model="loopInstanceForm.collection" clearable @change="updateLoopBase" /> <el-input v-model="loopInstanceForm.collection" clearable @change="updateLoopBase" />
</el-form-item> </el-form-item>
<el-form-item label="元素变量" key="elementVariable"> <!-- add by 芋艿:由于「元素变量」暂时用不到,所以这里 display 为 none -->
<el-form-item label="元素变量" key="elementVariable" style="display: none">
<el-input v-model="loopInstanceForm.elementVariable" clearable @change="updateLoopBase" /> <el-input v-model="loopInstanceForm.elementVariable" clearable @change="updateLoopBase" />
</el-form-item> </el-form-item>
<el-form-item label="完成条件" key="completionCondition"> <el-form-item label="完成条件" key="completionCondition">
...@@ -35,7 +40,8 @@ ...@@ -35,7 +40,8 @@
@change="updateLoopCondition" @change="updateLoopCondition"
/> />
</el-form-item> </el-form-item>
<el-form-item label="异步状态" key="async"> <!-- add by 芋艿:由于「异步状态」暂时用不到,所以这里 display 为 none -->
<el-form-item label="异步状态" key="async" style="display: none">
<el-checkbox <el-checkbox
v-model="loopInstanceForm.asyncBefore" v-model="loopInstanceForm.asyncBefore"
label="异步前" label="异步前"
...@@ -124,6 +130,7 @@ const getElementLoop = (businessObject) => { ...@@ -124,6 +130,7 @@ const getElementLoop = (businessObject) => {
businessObject.loopCharacteristics.extensionElements.values[0].body businessObject.loopCharacteristics.extensionElements.values[0].body
} }
} }
const changeLoopCharacteristicsType = (type) => { const changeLoopCharacteristicsType = (type) => {
// this.loopInstanceForm = { ...this.defaultLoopInstanceForm }; // 切换类型取消原表单配置 // this.loopInstanceForm = { ...this.defaultLoopInstanceForm }; // 切换类型取消原表单配置
// 取消多实例配置 // 取消多实例配置
...@@ -160,6 +167,7 @@ const changeLoopCharacteristicsType = (type) => { ...@@ -160,6 +167,7 @@ const changeLoopCharacteristicsType = (type) => {
loopCharacteristics: toRaw(multiLoopInstance.value) loopCharacteristics: toRaw(multiLoopInstance.value)
}) })
} }
// 循环基数 // 循环基数
const updateLoopCardinality = (cardinality) => { const updateLoopCardinality = (cardinality) => {
let loopCardinality = null let loopCardinality = null
...@@ -176,6 +184,7 @@ const updateLoopCardinality = (cardinality) => { ...@@ -176,6 +184,7 @@ const updateLoopCardinality = (cardinality) => {
} }
) )
} }
// 完成条件 // 完成条件
const updateLoopCondition = (condition) => { const updateLoopCondition = (condition) => {
let completionCondition = null let completionCondition = null
...@@ -192,6 +201,7 @@ const updateLoopCondition = (condition) => { ...@@ -192,6 +201,7 @@ const updateLoopCondition = (condition) => {
} }
) )
} }
// 重试周期 // 重试周期
const updateLoopTimeCycle = (timeCycle) => { const updateLoopTimeCycle = (timeCycle) => {
const extensionElements = bpmnInstances().moddle.create('bpmn:ExtensionElements', { const extensionElements = bpmnInstances().moddle.create('bpmn:ExtensionElements', {
...@@ -209,6 +219,7 @@ const updateLoopTimeCycle = (timeCycle) => { ...@@ -209,6 +219,7 @@ const updateLoopTimeCycle = (timeCycle) => {
} }
) )
} }
// 直接更新的基础信息 // 直接更新的基础信息
const updateLoopBase = () => { const updateLoopBase = () => {
bpmnInstances().modeling.updateModdleProperties( bpmnInstances().modeling.updateModdleProperties(
...@@ -220,6 +231,7 @@ const updateLoopBase = () => { ...@@ -220,6 +231,7 @@ const updateLoopBase = () => {
} }
) )
} }
// 各异步状态 // 各异步状态
const updateLoopAsync = (key) => { const updateLoopAsync = (key) => {
const { asyncBefore, asyncAfter } = loopInstanceForm.value const { asyncBefore, asyncAfter } = loopInstanceForm.value
...@@ -238,6 +250,20 @@ const updateLoopAsync = (key) => { ...@@ -238,6 +250,20 @@ const updateLoopAsync = (key) => {
) )
} }
const changeConfig = (config) => {
if (config === '依次审批') {
changeLoopCharacteristicsType('SequentialMultiInstance')
updateLoopCardinality('1')
updateLoopCondition('${ nrOfCompletedInstances >= nrOfInstances }')
} else if (config === '会签') {
changeLoopCharacteristicsType('ParallelMultiInstance')
updateLoopCondition('${ nrOfCompletedInstances >= nrOfInstances }')
} else if (config === '或签') {
changeLoopCharacteristicsType('ParallelMultiInstance')
updateLoopCondition('${ nrOfCompletedInstances > 0 }')
}
}
onBeforeUnmount(() => { onBeforeUnmount(() => {
multiLoopInstance.value = null multiLoopInstance.value = null
bpmnElement.value = null bpmnElement.value = null
......
<template> <template>
<div class="panel-tab__content"> <div class="panel-tab__content">
<el-form size="small" label-width="90px"> <el-form size="small" label-width="90px">
<el-form-item label="异步延续"> <!-- add by 芋艿:由于「异步延续」暂时用不到,所以这里 display 为 none -->
<el-form-item label="异步延续" style="display: none">
<el-checkbox <el-checkbox
v-model="taskConfigForm.asyncBefore" v-model="taskConfigForm.asyncBefore"
label="异步前" label="异步前"
......
<!-- 表达式选择 -->
<template>
<Dialog title="请选择表达式" v-model="dialogVisible" width="1024px">
<ContentWrap>
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
<el-table-column label="名字" align="center" prop="name" />
<el-table-column label="表达式" align="center" prop="expression" />
<el-table-column label="操作" align="center">
<template #default="scope">
<el-button link type="primary" @click="select(scope.row)"> 选择 </el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<Pagination
:total="total"
v-model:page="queryParams.pageNo"
v-model:limit="queryParams.pageSize"
@pagination="getList"
/>
</ContentWrap>
</Dialog>
</template>
<script setup lang="ts">
import { CommonStatusEnum } from '@/utils/constants'
import { ProcessExpressionApi, ProcessExpressionVO } from '@/api/bpm/processExpression'
/** BPM 流程 表单 */
defineOptions({ name: 'ProcessExpressionDialog' })
const { t } = useI18n() // 国际化
const message = useMessage() // 消息弹窗
const dialogVisible = ref(false) // 弹窗的是否展示
const loading = ref(true) // 列表的加载中
const list = ref<ProcessExpressionVO[]>([]) // 列表的数据
const total = ref(0) // 列表的总页数
const queryParams = reactive({
pageNo: 1,
pageSize: 10,
type: undefined,
status: CommonStatusEnum.ENABLE
})
/** 打开弹窗 */
const open = async (type: string) => {
dialogVisible.value = true
loading.value = true
try {
queryParams.pageNo = 1
queryParams.type = type
const data = await ProcessExpressionApi.getProcessExpressionPage(queryParams)
list.value = data.list
total.value = data.total
} finally {
loading.value = false
}
}
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
/** 提交表单 */
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
const select = async (row) => {
dialogVisible.value = false
// 发送操作成功的事件
emit('select', row)
}
</script>
<template> <template>
<div style="margin-top: 16px"> <el-form label-width="100px">
<!-- <el-form-item label="处理用户">--> <el-form-item label="规则类型" prop="candidateStrategy">
<!-- <el-select v-model="userTaskForm.assignee" @change="updateElementTask('assignee')">--> <el-select
<!-- <el-option v-for="ak in mockData" :key="'ass-' + ak" :label="`用户${ak}`" :value="`user${ak}`" />--> v-model="userTaskForm.candidateStrategy"
<!-- </el-select>--> clearable
<!-- </el-form-item>--> style="width: 100%"
<!-- <el-form-item label="候选用户">--> @change="changeCandidateStrategy"
<!-- <el-select v-model="userTaskForm.candidateUsers" multiple collapse-tags @change="updateElementTask('candidateUsers')">--> >
<!-- <el-option v-for="uk in mockData" :key="'user-' + uk" :label="`用户${uk}`" :value="`user${uk}`" />--> <el-option
<!-- </el-select>--> v-for="dict in getIntDictOptions(DICT_TYPE.BPM_TASK_CANDIDATE_STRATEGY)"
<!-- </el-form-item>--> :key="dict.value"
<!-- <el-form-item label="候选分组">--> :label="dict.label"
<!-- <el-select v-model="userTaskForm.candidateGroups" multiple collapse-tags @change="updateElementTask('candidateGroups')">--> :value="dict.value"
<!-- <el-option v-for="gk in mockData" :key="'ass-' + gk" :label="`分组${gk}`" :value="`group${gk}`" />--> />
<!-- </el-select>--> </el-select>
<!-- </el-form-item>-->
<el-form-item label="到期时间">
<el-input v-model="userTaskForm.dueDate" clearable @change="updateElementTask('dueDate')" />
</el-form-item> </el-form-item>
<el-form-item label="跟踪时间"> <el-form-item
<el-input v-if="userTaskForm.candidateStrategy == 10"
v-model="userTaskForm.followUpDate" label="指定角色"
prop="candidateParam"
>
<el-select
v-model="userTaskForm.candidateParam"
clearable clearable
@change="updateElementTask('followUpDate')" multiple
style="width: 100%"
@change="updateElementTask"
>
<el-option v-for="item in roleOptions" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-form-item>
<el-form-item
v-if="userTaskForm.candidateStrategy == 20 || userTaskForm.candidateStrategy == 21"
label="指定部门"
prop="candidateParam"
span="24"
>
<el-tree-select
ref="treeRef"
v-model="userTaskForm.candidateParam"
:data="deptTreeOptions"
:props="defaultProps"
empty-text="加载中,请稍后"
multiple
node-key="id"
show-checkbox
@change="updateElementTask"
/> />
</el-form-item> </el-form-item>
<el-form-item label="优先级"> <el-form-item
<el-input v-model="userTaskForm.priority" clearable @change="updateElementTask('priority')" /> v-if="userTaskForm.candidateStrategy == 22"
label="指定岗位"
prop="candidateParam"
span="24"
>
<el-select
v-model="userTaskForm.candidateParam"
clearable
multiple
style="width: 100%"
@change="updateElementTask"
>
<el-option v-for="item in postOptions" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-form-item>
<el-form-item
v-if="userTaskForm.candidateStrategy == 30"
label="指定用户"
prop="candidateParam"
span="24"
>
<el-select
v-model="userTaskForm.candidateParam"
clearable
multiple
style="width: 100%"
@change="updateElementTask"
>
<el-option
v-for="item in userOptions"
:key="item.id"
:label="item.nickname"
:value="item.id"
/>
</el-select>
</el-form-item>
<el-form-item
v-if="userTaskForm.candidateStrategy === 40"
label="指定用户组"
prop="candidateParam"
>
<el-select
v-model="userTaskForm.candidateParam"
clearable
multiple
style="width: 100%"
@change="updateElementTask"
>
<el-option
v-for="item in userGroupOptions"
:key="item.id"
:label="item.name"
:value="item.id"
/>
</el-select>
</el-form-item> </el-form-item>
友情提示:任务的分配规则,使用 <el-form-item
<router-link target="_blank" :to="{ path: '/bpm/manager/model' }" v-if="userTaskForm.candidateStrategy === 60"
><el-link type="danger">流程模型</el-link> label="流程表达式"
</router-link> prop="candidateParam"
下的【分配规则】替代,提供指定角色、部门负责人、部门成员、岗位、工作组、自定义脚本等 7 >
种维护的任务分配维度,更加灵活! <el-input
</div> type="textarea"
v-model="userTaskForm.candidateParam[0]"
clearable
style="width: 72%"
@change="updateElementTask"
/>
<el-button class="ml-5px" size="small" type="success" @click="openProcessExpressionDialog"
>选择表达式</el-button
>
<!-- 选择弹窗 -->
<ProcessExpressionDialog ref="processExpressionDialogRef" @select="selectProcessExpression" />
</el-form-item>
</el-form>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
import { defaultProps, handleTree } from '@/utils/tree'
import * as RoleApi from '@/api/system/role'
import * as DeptApi from '@/api/system/dept'
import * as PostApi from '@/api/system/post'
import * as UserApi from '@/api/system/user'
import * as UserGroupApi from '@/api/bpm/userGroup'
import ProcessExpressionDialog from './ProcessExpressionDialog.vue'
defineOptions({ name: 'UserTask' }) defineOptions({ name: 'UserTask' })
const props = defineProps({ const props = defineProps({
id: String, id: String,
type: String type: String
}) })
const defaultTaskForm = ref({ const userTaskForm = ref({
assignee: '', candidateStrategy: undefined, // 分配规则
candidateUsers: [], candidateParam: [] // 分配选项
candidateGroups: [],
dueDate: '',
followUpDate: '',
priority: ''
}) })
const userTaskForm = ref<any>({})
// const mockData=ref([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
const bpmnElement = ref() const bpmnElement = ref()
const bpmnInstances = () => (window as any)?.bpmnInstances const bpmnInstances = () => (window as any)?.bpmnInstances
const roleOptions = ref<RoleApi.RoleVO[]>([]) // 角色列表
const deptTreeOptions = ref() // 部门树
const postOptions = ref<PostApi.PostVO[]>([]) // 岗位列表
const userOptions = ref<UserApi.UserVO[]>([]) // 用户列表
const userGroupOptions = ref<UserGroupApi.UserGroupVO[]>([]) // 用户组列表
const resetTaskForm = () => { const resetTaskForm = () => {
for (let key in defaultTaskForm.value) { const businessObject = bpmnElement.value.businessObject
let value if (!businessObject) {
if (key === 'candidateUsers' || key === 'candidateGroups') { return
value = bpmnElement.value?.businessObject[key] }
? bpmnElement.value.businessObject[key].split(',') if (businessObject.candidateStrategy != undefined) {
: [] userTaskForm.value.candidateStrategy = parseInt(businessObject.candidateStrategy) as any
} else {
userTaskForm.value.candidateStrategy = undefined
}
if (businessObject.candidateParam && businessObject.candidateParam.length > 0) {
if (userTaskForm.value.candidateStrategy === 60) {
// 特殊:流程表达式,只有一个 input 输入框
userTaskForm.value.candidateParam = [businessObject.candidateParam]
} else { } else {
value = bpmnElement.value?.businessObject[key] || defaultTaskForm.value[key] userTaskForm.value.candidateParam = businessObject.candidateParam
.split(',')
.map((item) => +item)
} }
userTaskForm.value[key] = value
}
}
const updateElementTask = (key) => {
const taskAttr = Object.create(null)
if (key === 'candidateUsers' || key === 'candidateGroups') {
taskAttr[key] =
userTaskForm.value[key] && userTaskForm.value[key].length
? userTaskForm.value[key].join()
: null
} else { } else {
taskAttr[key] = userTaskForm.value[key] || null userTaskForm.value.candidateParam = []
} }
bpmnInstances().modeling.updateProperties(toRaw(bpmnElement.value), taskAttr) }
/** 更新 candidateStrategy 字段时,需要清空 candidateParam,并触发 bpmn 图更新 */
const changeCandidateStrategy = () => {
userTaskForm.value.candidateParam = []
updateElementTask()
}
/** 选中某个 options 时候,更新 bpmn 图 */
const updateElementTask = () => {
bpmnInstances().modeling.updateProperties(toRaw(bpmnElement.value), {
candidateStrategy: userTaskForm.value.candidateStrategy,
candidateParam: userTaskForm.value.candidateParam.join(',')
})
}
// 打开监听器弹窗
const processExpressionDialogRef = ref()
const openProcessExpressionDialog = async () => {
processExpressionDialogRef.value.open()
}
const selectProcessExpression = (expression) => {
userTaskForm.value.candidateParam = [expression.expression]
} }
watch( watch(
...@@ -92,6 +211,21 @@ watch( ...@@ -92,6 +211,21 @@ watch(
}, },
{ immediate: true } { immediate: true }
) )
onMounted(async () => {
// 获得角色列表
roleOptions.value = await RoleApi.getSimpleRoleList()
// 获得部门列表
const deptOptions = await DeptApi.getSimpleDeptList()
deptTreeOptions.value = handleTree(deptOptions, 'id')
// 获得岗位列表
postOptions.value = await PostApi.getSimplePostList()
// 获得用户列表
userOptions.value = await UserApi.getSimpleUserList()
// 获得用户组列表
userGroupOptions.value = await UserGroupApi.getUserGroupSimpleList()
})
onBeforeUnmount(() => { onBeforeUnmount(() => {
bpmnElement.value = null bpmnElement.value = null
}) })
......
...@@ -2,6 +2,7 @@ import { toRaw } from 'vue' ...@@ -2,6 +2,7 @@ import { toRaw } from 'vue'
const bpmnInstances = () => (window as any)?.bpmnInstances const bpmnInstances = () => (window as any)?.bpmnInstances
// 创建监听器实例 // 创建监听器实例
export function createListenerObject(options, isTask, prefix) { export function createListenerObject(options, isTask, prefix) {
debugger
const listenerObj = Object.create(null) const listenerObj = Object.create(null)
listenerObj.event = options.event listenerObj.event = options.event
isTask && (listenerObj.id = options.id) // 任务监听器特有的 id 字段 isTask && (listenerObj.id = options.id) // 任务监听器特有的 id 字段
......
...@@ -243,7 +243,7 @@ const remainingRouter: AppRouteRecordRaw[] = [ ...@@ -243,7 +243,7 @@ const remainingRouter: AppRouteRecordRaw[] = [
}, },
children: [ children: [
{ {
path: '/manager/form/edit', path: 'manager/form/edit',
component: () => import('@/views/bpm/form/editor/index.vue'), component: () => import('@/views/bpm/form/editor/index.vue'),
name: 'BpmFormEditor', name: 'BpmFormEditor',
meta: { meta: {
...@@ -255,7 +255,7 @@ const remainingRouter: AppRouteRecordRaw[] = [ ...@@ -255,7 +255,7 @@ const remainingRouter: AppRouteRecordRaw[] = [
} }
}, },
{ {
path: '/manager/model/edit', path: 'manager/model/edit',
component: () => import('@/views/bpm/model/editor/index.vue'), component: () => import('@/views/bpm/model/editor/index.vue'),
name: 'BpmModelEditor', name: 'BpmModelEditor',
meta: { meta: {
...@@ -267,42 +267,31 @@ const remainingRouter: AppRouteRecordRaw[] = [ ...@@ -267,42 +267,31 @@ const remainingRouter: AppRouteRecordRaw[] = [
} }
}, },
{ {
path: '/manager/definition', path: 'manager/simple/workflow/model/edit',
component: () => import('@/views/bpm/definition/index.vue'), component: () => import('@/views/bpm/simpleWorkflow/index.vue'),
name: 'BpmProcessDefinition', name: 'SimpleWorkflowDesignEditor',
meta: { meta: {
noCache: true, noCache: true,
hidden: true, hidden: true,
canTo: true, canTo: true,
title: '流程定义', title: '仿钉钉设计流程',
activeMenu: '/bpm/manager/model' activeMenu: '/bpm/manager/model'
} }
}, },
{ {
path: '/manager/task-assign-rule', path: 'manager/definition',
component: () => import('@/views/bpm/taskAssignRule/index.vue'), component: () => import('@/views/bpm/definition/index.vue'),
name: 'BpmTaskAssignRuleList', name: 'BpmProcessDefinition',
meta: {
noCache: true,
hidden: true,
canTo: true,
title: '任务分配规则'
}
},
{
path: '/process-instance/create',
component: () => import('@/views/bpm/processInstance/create/index.vue'),
name: 'BpmProcessInstanceCreate',
meta: { meta: {
noCache: true, noCache: true,
hidden: true, hidden: true,
canTo: true, canTo: true,
title: '发起流程', title: '流程定义',
activeMenu: 'bpm/processInstance/create' activeMenu: '/bpm/manager/model'
} }
}, },
{ {
path: '/process-instance/detail', path: 'process-instance/detail',
component: () => import('@/views/bpm/processInstance/detail/index.vue'), component: () => import('@/views/bpm/processInstance/detail/index.vue'),
name: 'BpmProcessInstanceDetail', name: 'BpmProcessInstanceDetail',
meta: { meta: {
...@@ -310,11 +299,11 @@ const remainingRouter: AppRouteRecordRaw[] = [ ...@@ -310,11 +299,11 @@ const remainingRouter: AppRouteRecordRaw[] = [
hidden: true, hidden: true,
canTo: true, canTo: true,
title: '流程详情', title: '流程详情',
activeMenu: 'bpm/processInstance/detail' activeMenu: '/bpm/task/my'
} }
}, },
{ {
path: '/bpm/oa/leave/create', path: 'oa/leave/create',
component: () => import('@/views/bpm/oa/leave/create.vue'), component: () => import('@/views/bpm/oa/leave/create.vue'),
name: 'OALeaveCreate', name: 'OALeaveCreate',
meta: { meta: {
...@@ -326,7 +315,7 @@ const remainingRouter: AppRouteRecordRaw[] = [ ...@@ -326,7 +315,7 @@ const remainingRouter: AppRouteRecordRaw[] = [
} }
}, },
{ {
path: '/bpm/oa/leave/detail', path: 'oa/leave/detail',
component: () => import('@/views/bpm/oa/leave/detail.vue'), component: () => import('@/views/bpm/oa/leave/detail.vue'),
name: 'OALeaveDetail', name: 'OALeaveDetail',
meta: { meta: {
......
import { store } from '../index'
import { defineStore } from 'pinia'
export const useWorkFlowStore = defineStore('simpleWorkflow', {
state: () => ({
tableId: '',
isTried: false,
promoterDrawer: false,
flowPermission1: {},
approverDrawer: false,
approverConfig1: {},
copyerDrawer: false,
copyerConfig1: {},
conditionDrawer: false,
conditionsConfig1: {
conditionNodes: []
}
}),
actions: {
setTableId(payload) {
this.tableId = payload
},
setIsTried(payload) {
this.isTried = payload
},
setPromoter(payload) {
this.promoterDrawer = payload
},
setFlowPermission(payload) {
this.flowPermission1 = payload
},
setApprover(payload) {
this.approverDrawer = payload
},
setApproverConfig(payload) {
this.approverConfig1 = payload
},
setCopyer(payload) {
this.copyerDrawer = payload
},
setCopyerConfig(payload) {
this.copyerConfig1 = payload
},
setCondition(payload) {
this.conditionDrawer = payload
},
setConditionsConfig(payload) {
this.conditionsConfig1 = payload
}
}
})
export const useWorkFlowStoreWithOut = () => {
return useWorkFlowStore(store)
}
...@@ -136,13 +136,13 @@ export enum DICT_TYPE { ...@@ -136,13 +136,13 @@ export enum DICT_TYPE {
INFRA_FILE_STORAGE = 'infra_file_storage', INFRA_FILE_STORAGE = 'infra_file_storage',
// ========== BPM 模块 ========== // ========== BPM 模块 ==========
BPM_MODEL_CATEGORY = 'bpm_model_category',
BPM_MODEL_FORM_TYPE = 'bpm_model_form_type', BPM_MODEL_FORM_TYPE = 'bpm_model_form_type',
BPM_TASK_ASSIGN_RULE_TYPE = 'bpm_task_assign_rule_type', BPM_TASK_CANDIDATE_STRATEGY = 'bpm_task_candidate_strategy',
BPM_PROCESS_INSTANCE_STATUS = 'bpm_process_instance_status', BPM_PROCESS_INSTANCE_STATUS = 'bpm_process_instance_status',
BPM_PROCESS_INSTANCE_RESULT = 'bpm_process_instance_result', BPM_TASK_STATUS = 'bpm_task_status',
BPM_TASK_ASSIGN_SCRIPT = 'bpm_task_assign_script',
BPM_OA_LEAVE_TYPE = 'bpm_oa_leave_type', BPM_OA_LEAVE_TYPE = 'bpm_oa_leave_type',
BPM_PROCESS_LISTENER_TYPE = 'bpm_process_listener_type',
BPM_PROCESS_LISTENER_VALUE_TYPE = 'bpm_process_listener_value_type',
// ========== PAY 模块 ========== // ========== PAY 模块 ==========
PAY_CHANNEL_CODE = 'pay_channel_code', // 支付渠道编码类型 PAY_CHANNEL_CODE = 'pay_channel_code', // 支付渠道编码类型
...@@ -157,7 +157,7 @@ export enum DICT_TYPE { ...@@ -157,7 +157,7 @@ export enum DICT_TYPE {
MP_AUTO_REPLY_REQUEST_MATCH = 'mp_auto_reply_request_match', // 自动回复请求匹配类型 MP_AUTO_REPLY_REQUEST_MATCH = 'mp_auto_reply_request_match', // 自动回复请求匹配类型
MP_MESSAGE_TYPE = 'mp_message_type', // 消息类型 MP_MESSAGE_TYPE = 'mp_message_type', // 消息类型
// ========== MALL - 会员模块 ========== // ========== Member 会员模块 ==========
MEMBER_POINT_BIZ_TYPE = 'member_point_biz_type', // 积分的业务类型 MEMBER_POINT_BIZ_TYPE = 'member_point_biz_type', // 积分的业务类型
MEMBER_EXPERIENCE_BIZ_TYPE = 'member_experience_biz_type', // 会员经验业务类型 MEMBER_EXPERIENCE_BIZ_TYPE = 'member_experience_biz_type', // 会员经验业务类型
......
...@@ -28,7 +28,7 @@ export const decodeFields = (fields: string[]) => { ...@@ -28,7 +28,7 @@ export const decodeFields = (fields: string[]) => {
return rule return rule
} }
// 设置表单的 Conf 和 Fields // 设置表单的 Conf 和 Fields,适用 FcDesigner 场景
export const setConfAndFields = (designerRef: object, conf: string, fields: string) => { export const setConfAndFields = (designerRef: object, conf: string, fields: string) => {
// @ts-ignore // @ts-ignore
designerRef.value.setOption(JSON.parse(conf)) designerRef.value.setOption(JSON.parse(conf))
...@@ -36,19 +36,22 @@ export const setConfAndFields = (designerRef: object, conf: string, fields: stri ...@@ -36,19 +36,22 @@ export const setConfAndFields = (designerRef: object, conf: string, fields: stri
designerRef.value.setRule(decodeFields(fields)) designerRef.value.setRule(decodeFields(fields))
} }
// 设置表单的 Conf 和 Fields // 设置表单的 Conf 和 Fields,适用 form-create 场景
export const setConfAndFields2 = ( export const setConfAndFields2 = (
detailPreview: object, detailPreview: object,
conf: string, conf: string,
fields: string, fields: string,
value?: object value?: object
) => { ) => {
if (isRef(detailPreview)) {
detailPreview = detailPreview.value
}
// @ts-ignore // @ts-ignore
detailPreview.value.option = JSON.parse(conf) detailPreview.option = JSON.parse(conf)
// @ts-ignore // @ts-ignore
detailPreview.value.rule = decodeFields(fields) detailPreview.rule = decodeFields(fields)
if (value) { if (value) {
// @ts-ignore // @ts-ignore
detailPreview.value.value = value detailPreview.value = value
} }
} }
...@@ -175,18 +175,18 @@ export function formatPast2(ms: number): string { ...@@ -175,18 +175,18 @@ export function formatPast2(ms: number): string {
const minute = Math.floor(ms / (60 * 1000) - day * 24 * 60 - hour * 60) const minute = Math.floor(ms / (60 * 1000) - day * 24 * 60 - hour * 60)
const second = Math.floor(ms / 1000 - day * 24 * 60 * 60 - hour * 60 * 60 - minute * 60) const second = Math.floor(ms / 1000 - day * 24 * 60 * 60 - hour * 60 * 60 - minute * 60)
if (day > 0) { if (day > 0) {
return day + '天' + hour + '小时' + minute + '分钟' return day + ' 天' + hour + ' 小时 ' + minute + ' 分钟'
} }
if (hour > 0) { if (hour > 0) {
return hour + '小时' + minute + '分钟' return hour + ' 小时 ' + minute + ' 分钟'
} }
if (minute > 0) { if (minute > 0) {
return minute + '分钟' return minute + ' 分钟'
} }
if (second > 0) { if (second > 0) {
return second + '秒' return second + ' 秒'
} else { } else {
return 0 + '秒' return 0 + ' 秒'
} }
} }
......
...@@ -291,10 +291,16 @@ const doSocialLogin = async (type: number) => { ...@@ -291,10 +291,16 @@ const doSocialLogin = async (type: number) => {
await getTenantId() await getTenantId()
// 如果获取不到,则需要弹出提示,进行处理 // 如果获取不到,则需要弹出提示,进行处理
if (!authUtil.getTenantId()) { if (!authUtil.getTenantId()) {
await message.prompt('请输入租户名称', t('common.reminder')).then(async ({ value }) => { try {
const res = await LoginApi.getTenantIdByName(value) const data = await message.prompt('请输入租户名称', t('common.reminder'))
if (data?.action !== 'confirm') throw 'cancel'
const res = await LoginApi.getTenantIdByName(data.value)
authUtil.setTenantId(res) authUtil.setTenantId(res)
}) } catch (error) {
if (error === 'cancel') return
} finally {
loginLoading.value = false
}
} }
} }
// 计算 redirectUri // 计算 redirectUri
......
<template>
<Dialog :title="dialogTitle" v-model="dialogVisible">
<el-form
ref="formRef"
:model="formData"
:rules="formRules"
label-width="100px"
v-loading="formLoading"
>
<el-form-item label="分类名" prop="name">
<el-input v-model="formData.name" placeholder="请输入分类名" />
</el-form-item>
<el-form-item label="分类标志" prop="code">
<el-input v-model="formData.code" placeholder="请输入分类标志" />
</el-form-item>
<el-form-item label="分类状态" prop="status">
<el-radio-group v-model="formData.status">
<el-radio
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)"
:key="dict.value"
:label="dict.value"
>
{{ dict.label }}
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="分类排序" prop="sort">
<el-input-number
v-model="formData.sort"
placeholder="请输入分类排序"
class="!w-1/1"
:precision="0"
/>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button>
<el-button @click="dialogVisible = false">取 消</el-button>
</template>
</Dialog>
</template>
<script setup lang="ts">
import { getIntDictOptions, DICT_TYPE } from '@/utils/dict'
import { CategoryApi, CategoryVO } from '@/api/bpm/category'
/** BPM 流程分类 表单 */
defineOptions({ name: 'CategoryForm' })
const { t } = useI18n() // 国际化
const message = useMessage() // 消息弹窗
const dialogVisible = ref(false) // 弹窗的是否展示
const dialogTitle = ref('') // 弹窗的标题
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
const formType = ref('') // 表单的类型:create - 新增;update - 修改
const formData = ref({
id: undefined,
name: undefined,
code: undefined,
status: undefined,
sort: undefined
})
const formRules = reactive({
name: [{ required: true, message: '分类名不能为空', trigger: 'blur' }],
code: [{ required: true, message: '分类标志不能为空', trigger: 'blur' }],
status: [{ required: true, message: '分类状态不能为空', trigger: 'blur' }],
sort: [{ required: true, message: '分类排序不能为空', trigger: 'blur' }]
})
const formRef = ref() // 表单 Ref
/** 打开弹窗 */
const open = async (type: string, id?: number) => {
dialogVisible.value = true
dialogTitle.value = t('action.' + type)
formType.value = type
resetForm()
// 修改时,设置数据
if (id) {
formLoading.value = true
try {
formData.value = await CategoryApi.getCategory(id)
} finally {
formLoading.value = false
}
}
}
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
/** 提交表单 */
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
const submitForm = async () => {
// 校验表单
await formRef.value.validate()
// 提交请求
formLoading.value = true
try {
const data = formData.value as unknown as CategoryVO
if (formType.value === 'create') {
await CategoryApi.createCategory(data)
message.success(t('common.createSuccess'))
} else {
await CategoryApi.updateCategory(data)
message.success(t('common.updateSuccess'))
}
dialogVisible.value = false
// 发送操作成功的事件
emit('success')
} finally {
formLoading.value = false
}
}
/** 重置表单 */
const resetForm = () => {
formData.value = {
id: undefined,
name: undefined,
code: undefined,
status: undefined,
sort: undefined
}
formRef.value?.resetFields()
}
</script>
<template>
<ContentWrap>
<!-- 搜索工作栏 -->
<el-form
class="-mb-15px"
:model="queryParams"
ref="queryFormRef"
:inline="true"
label-width="68px"
>
<el-form-item label="分类名" prop="name">
<el-input
v-model="queryParams.name"
placeholder="请输入分类名"
clearable
@keyup.enter="handleQuery"
class="!w-240px"
/>
</el-form-item>
<el-form-item label="分类标志" prop="code">
<el-input
v-model="queryParams.code"
placeholder="请输入分类标志"
clearable
@keyup.enter="handleQuery"
class="!w-240px"
/>
</el-form-item>
<el-form-item label="分类状态" prop="status">
<el-select
v-model="queryParams.status"
placeholder="请选择分类状态"
clearable
class="!w-240px"
>
<el-option
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
<el-form-item label="创建时间" prop="createTime">
<el-date-picker
v-model="queryParams.createTime"
value-format="YYYY-MM-DD HH:mm:ss"
type="daterange"
start-placeholder="开始日期"
end-placeholder="结束日期"
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
class="!w-240px"
/>
</el-form-item>
<el-form-item>
<el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
<el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
<el-button
type="primary"
plain
@click="openForm('create')"
v-hasPermi="['bpm:category:create']"
>
<Icon icon="ep:plus" class="mr-5px" /> 新增
</el-button>
</el-form-item>
</el-form>
</ContentWrap>
<!-- 列表 -->
<ContentWrap>
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
<el-table-column label="分类编号" align="center" prop="id" />
<el-table-column label="分类名" align="center" prop="name" />
<el-table-column label="分类标志" align="center" prop="code" />
<el-table-column label="分类描述" align="center" prop="description" />
<el-table-column label="分类状态" align="center" prop="status">
<template #default="scope">
<dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="scope.row.status" />
</template>
</el-table-column>
<el-table-column label="分类排序" align="center" prop="sort" />
<el-table-column
label="创建时间"
align="center"
prop="createTime"
:formatter="dateFormatter"
width="180px"
/>
<el-table-column label="操作" align="center">
<template #default="scope">
<el-button
link
type="primary"
@click="openForm('update', scope.row.id)"
v-hasPermi="['bpm:category:update']"
>
编辑
</el-button>
<el-button
link
type="danger"
@click="handleDelete(scope.row.id)"
v-hasPermi="['bpm:category:delete']"
>
删除
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<Pagination
:total="total"
v-model:page="queryParams.pageNo"
v-model:limit="queryParams.pageSize"
@pagination="getList"
/>
</ContentWrap>
<!-- 表单弹窗:添加/修改 -->
<CategoryForm ref="formRef" @success="getList" />
</template>
<script setup lang="ts">
import { getIntDictOptions, DICT_TYPE } from '@/utils/dict'
import { dateFormatter } from '@/utils/formatTime'
import download from '@/utils/download'
import { CategoryApi, CategoryVO } from '@/api/bpm/category'
import CategoryForm from './CategoryForm.vue'
/** BPM 流程分类 列表 */
defineOptions({ name: 'BpmCategory' })
const message = useMessage() // 消息弹窗
const { t } = useI18n() // 国际化
const loading = ref(true) // 列表的加载中
const list = ref<CategoryVO[]>([]) // 列表的数据
const total = ref(0) // 列表的总页数
const queryParams = reactive({
pageNo: 1,
pageSize: 10,
name: undefined,
code: undefined,
status: undefined,
createTime: []
})
const queryFormRef = ref() // 搜索的表单
const exportLoading = ref(false) // 导出的加载中
/** 查询列表 */
const getList = async () => {
loading.value = true
try {
const data = await CategoryApi.getCategoryPage(queryParams)
list.value = data.list
total.value = data.total
} finally {
loading.value = false
}
}
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.pageNo = 1
getList()
}
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value.resetFields()
handleQuery()
}
/** 添加/修改操作 */
const formRef = ref()
const openForm = (type: string, id?: number) => {
formRef.value.open(type, id)
}
/** 删除按钮操作 */
const handleDelete = async (id: number) => {
try {
// 删除的二次确认
await message.delConfirm()
// 发起删除
await CategoryApi.deleteCategory(id)
message.success(t('common.delSuccess'))
// 刷新列表
await getList()
} catch {}
}
/** 初始化 **/
onMounted(() => {
getList()
})
</script>
...@@ -11,11 +11,7 @@ ...@@ -11,11 +11,7 @@
</el-button> </el-button>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="定义分类" align="center" prop="category" width="100"> <el-table-column label="定义分类" align="center" prop="categoryName" width="100" />
<template #default="scope">
<dict-tag :type="DICT_TYPE.BPM_MODEL_CATEGORY" :value="scope.row.category" />
</template>
</el-table-column>
<el-table-column label="表单信息" align="center" prop="formType" width="200"> <el-table-column label="表单信息" align="center" prop="formType" width="200">
<template #default="scope"> <template #default="scope">
<el-button <el-button
...@@ -57,18 +53,6 @@ ...@@ -57,18 +53,6 @@
width="300" width="300"
show-overflow-tooltip show-overflow-tooltip
/> />
<el-table-column label="操作" align="center" width="150" fixed="right">
<template #default="scope">
<el-button
link
type="primary"
@click="handleAssignRule(scope.row)"
v-hasPermi="['bpm:task-assign-rule:query']"
>
分配规则
</el-button>
</template>
</el-table-column>
</el-table> </el-table>
<!-- 分页 --> <!-- 分页 -->
<Pagination <Pagination
...@@ -88,8 +72,8 @@ ...@@ -88,8 +72,8 @@
<Dialog title="流程图" v-model="bpmnDetailVisible" width="800"> <Dialog title="流程图" v-model="bpmnDetailVisible" width="800">
<MyProcessViewer <MyProcessViewer
key="designer" key="designer"
v-model="bpmnXML" v-model="bpmnXml"
:value="bpmnXML as any" :value="bpmnXml as any"
v-bind="bpmnControlForm" v-bind="bpmnControlForm"
:prefix="bpmnControlForm.prefix" :prefix="bpmnControlForm.prefix"
/> />
...@@ -97,7 +81,6 @@ ...@@ -97,7 +81,6 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { DICT_TYPE } from '@/utils/dict'
import { dateFormatter } from '@/utils/formatTime' import { dateFormatter } from '@/utils/formatTime'
import { MyProcessViewer } from '@/components/bpmnProcessDesigner/package' import { MyProcessViewer } from '@/components/bpmnProcessDesigner/package'
import * as DefinitionApi from '@/api/bpm/definition' import * as DefinitionApi from '@/api/bpm/definition'
...@@ -129,16 +112,6 @@ const getList = async () => { ...@@ -129,16 +112,6 @@ const getList = async () => {
} }
} }
/** 点击任务分配按钮 */
const handleAssignRule = (row) => {
push({
name: 'BpmTaskAssignRuleList',
query: {
modelId: row.id
}
})
}
/** 流程表单的详情按钮操作 */ /** 流程表单的详情按钮操作 */
const formDetailVisible = ref(false) const formDetailVisible = ref(false)
const formDetailPreview = ref({ const formDetailPreview = ref({
...@@ -160,12 +133,12 @@ const handleFormDetail = async (row) => { ...@@ -160,12 +133,12 @@ const handleFormDetail = async (row) => {
/** 流程图的详情按钮操作 */ /** 流程图的详情按钮操作 */
const bpmnDetailVisible = ref(false) const bpmnDetailVisible = ref(false)
const bpmnXML = ref(null) const bpmnXml = ref(null)
const bpmnControlForm = ref({ const bpmnControlForm = ref({
prefix: 'flowable' prefix: 'flowable'
}) })
const handleBpmnDetail = async (row) => { const handleBpmnDetail = async (row) => {
bpmnXML.value = await DefinitionApi.getProcessDefinitionBpmnXML(row.id) bpmnXml.value = (await DefinitionApi.getProcessDefinition(row.id))?.bpmnXml
bpmnDetailVisible.value = true bpmnDetailVisible.value = true
} }
......
...@@ -13,8 +13,8 @@ ...@@ -13,8 +13,8 @@
<el-form-item label="描述"> <el-form-item label="描述">
<el-input v-model="formData.description" placeholder="请输入描述" type="textarea" /> <el-input v-model="formData.description" placeholder="请输入描述" type="textarea" />
</el-form-item> </el-form-item>
<el-form-item label="成员" prop="memberUserIds"> <el-form-item label="成员" prop="userIds">
<el-select v-model="formData.memberUserIds" multiple placeholder="请选择成员"> <el-select v-model="formData.userIds" multiple placeholder="请选择成员">
<el-option <el-option
v-for="user in userList" v-for="user in userList"
:key="user.id" :key="user.id"
...@@ -60,13 +60,13 @@ const formData = ref({ ...@@ -60,13 +60,13 @@ const formData = ref({
id: undefined, id: undefined,
name: undefined, name: undefined,
description: undefined, description: undefined,
memberUserIds: undefined, userIds: undefined,
status: CommonStatusEnum.ENABLE status: CommonStatusEnum.ENABLE
}) })
const formRules = reactive({ const formRules = reactive({
name: [{ required: true, message: '组名不能为空', trigger: 'blur' }], name: [{ required: true, message: '组名不能为空', trigger: 'blur' }],
description: [{ required: true, message: '描述不能为空', trigger: 'blur' }], description: [{ required: true, message: '描述不能为空', trigger: 'blur' }],
memberUserIds: [{ required: true, message: '成员不能为空', trigger: 'blur' }], userIds: [{ required: true, message: '成员不能为空', trigger: 'blur' }],
status: [{ required: true, message: '状态不能为空', trigger: 'blur' }] status: [{ required: true, message: '状态不能为空', trigger: 'blur' }]
}) })
const formRef = ref() // 表单 Ref const formRef = ref() // 表单 Ref
...@@ -124,7 +124,7 @@ const resetForm = () => { ...@@ -124,7 +124,7 @@ const resetForm = () => {
id: undefined, id: undefined,
name: undefined, name: undefined,
description: undefined, description: undefined,
memberUserIds: undefined, userIds: undefined,
status: CommonStatusEnum.ENABLE status: CommonStatusEnum.ENABLE
} }
formRef.value?.resetFields() formRef.value?.resetFields()
......
...@@ -63,7 +63,7 @@ ...@@ -63,7 +63,7 @@
<el-table-column label="描述" align="center" prop="description" /> <el-table-column label="描述" align="center" prop="description" />
<el-table-column label="成员" align="center"> <el-table-column label="成员" align="center">
<template #default="scope"> <template #default="scope">
<span v-for="userId in scope.row.memberUserIds" :key="userId" class="pr-5px"> <span v-for="userId in scope.row.userIds" :key="userId" class="pr-5px">
{{ userList.find((user) => user.id === userId)?.nickname }} {{ userList.find((user) => user.id === userId)?.nickname }}
</span> </span>
</template> </template>
......
...@@ -43,13 +43,16 @@ ...@@ -43,13 +43,16 @@
style="width: 100%" style="width: 100%"
> >
<el-option <el-option
v-for="dict in getIntDictOptions(DICT_TYPE.BPM_MODEL_CATEGORY)" v-for="category in categoryList"
:key="dict.value" :key="category.code"
:label="dict.label" :label="category.name"
:value="dict.value" :value="category.code"
/> />
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item v-if="formData.id" label="流程图标" prop="icon">
<UploadImg v-model="formData.icon" :limit="1" height="128px" width="128px" />
</el-form-item>
<el-form-item label="流程描述" prop="description"> <el-form-item label="流程描述" prop="description">
<el-input v-model="formData.description" clearable type="textarea" /> <el-input v-model="formData.description" clearable type="textarea" />
</el-form-item> </el-form-item>
...@@ -126,6 +129,7 @@ import { DICT_TYPE, getIntDictOptions } from '@/utils/dict' ...@@ -126,6 +129,7 @@ import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
import { ElMessageBox } from 'element-plus' import { ElMessageBox } from 'element-plus'
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 } from '@/api/bpm/category'
defineOptions({ name: 'ModelForm' }) defineOptions({ name: 'ModelForm' })
...@@ -140,20 +144,23 @@ const formData = ref({ ...@@ -140,20 +144,23 @@ const formData = ref({
formType: 10, formType: 10,
name: '', name: '',
category: undefined, category: undefined,
icon: undefined,
description: '', description: '',
formId: '', formId: '',
formCustomCreatePath: '', formCustomCreatePath: '',
formCustomViewPath: '' formCustomViewPath: ''
}) })
const formRules = reactive({ const formRules = reactive({
category: [{ required: true, message: '参数分类不能为空', trigger: 'blur' }],
name: [{ required: true, message: '参数名称不能为空', trigger: 'blur' }], name: [{ required: true, message: '参数名称不能为空', trigger: 'blur' }],
key: [{ required: true, message: '参数键名不能为空', trigger: 'blur' }], key: [{ required: true, message: '参数键名不能为空', trigger: 'blur' }],
category: [{ required: true, message: '参数分类不能为空', trigger: 'blur' }],
icon: [{ required: true, message: '参数图标不能为空', trigger: 'blur' }],
value: [{ required: true, message: '参数键值不能为空', trigger: 'blur' }], value: [{ required: true, message: '参数键值不能为空', trigger: 'blur' }],
visible: [{ required: true, message: '是否可见不能为空', trigger: 'blur' }] visible: [{ required: true, message: '是否可见不能为空', trigger: 'blur' }]
}) })
const formRef = ref() // 表单 Ref const formRef = ref() // 表单 Ref
const formList = ref([]) // 流程表单的下拉框的数据 const formList = ref([]) // 流程表单的下拉框的数据
const categoryList = ref([]) // 流程分类列表
/** 打开弹窗 */ /** 打开弹窗 */
const open = async (type: string, id?: number) => { const open = async (type: string, id?: number) => {
...@@ -171,7 +178,9 @@ const open = async (type: string, id?: number) => { ...@@ -171,7 +178,9 @@ const open = async (type: string, id?: number) => {
} }
} }
// 获得流程表单的下拉框的数据 // 获得流程表单的下拉框的数据
formList.value = await FormApi.getSimpleFormList() formList.value = await FormApi.getFormSimpleList()
// 查询流程分类列表
categoryList.value = await CategoryApi.getCategorySimpleList()
} }
defineExpose({ open }) // 提供 open 方法,用于打开弹窗 defineExpose({ open }) // 提供 open 方法,用于打开弹窗
...@@ -190,11 +199,10 @@ const submitForm = async () => { ...@@ -190,11 +199,10 @@ const submitForm = async () => {
await ModelApi.createModel(data) await ModelApi.createModel(data)
// 提示,引导用户做后续的操作 // 提示,引导用户做后续的操作
await ElMessageBox.alert( await ElMessageBox.alert(
'<strong>新建模型成功!</strong>后续需要执行如下 4 个步骤:' + '<strong>新建模型成功!</strong>后续需要执行如下 3 个步骤:' +
'<div>1. 点击【修改流程】按钮,配置流程的分类、表单信息</div>' + '<div>1. 点击【修改流程】按钮,配置流程的分类、表单信息</div>' +
'<div>2. 点击【设计流程】按钮,绘制流程图</div>' + '<div>2. 点击【设计流程】按钮,绘制流程图</div>' +
'<div>3. 点击【分配规则】按钮,设置每个用户任务的审批人</div>' + '<div>3. 点击【发布流程】按钮,完成流程的最终发布</div>' +
'<div>4. 点击【发布流程】按钮,完成流程的最终发布</div>' +
'另外,每次流程修改后,都需要点击【发布流程】按钮,才能正式生效!!!', '另外,每次流程修改后,都需要点击【发布流程】按钮,才能正式生效!!!',
'重要提示', '重要提示',
{ {
...@@ -220,6 +228,7 @@ const resetForm = () => { ...@@ -220,6 +228,7 @@ const resetForm = () => {
formType: 10, formType: 10,
name: '', name: '',
category: undefined, category: undefined,
icon: '',
description: '', description: '',
formId: '', formId: '',
formCustomCreatePath: '', formCustomCreatePath: '',
......
...@@ -109,6 +109,7 @@ const submitFormSuccess = async (response: any) => { ...@@ -109,6 +109,7 @@ const submitFormSuccess = async (response: any) => {
} }
// 提示成功 // 提示成功
message.success('导入流程成功!请点击【设计流程】按钮,进行编辑保存后,才可以进行【发布流程】') message.success('导入流程成功!请点击【设计流程】按钮,进行编辑保存后,才可以进行【发布流程】')
dialogVisible.value = false
// 发送操作成功的事件 // 发送操作成功的事件
emit('success') emit('success')
} }
......
...@@ -89,11 +89,11 @@ onMounted(async () => { ...@@ -89,11 +89,11 @@ onMounted(async () => {
} }
// 查询模型 // 查询模型
const data = await ModelApi.getModel(modelId) const data = await ModelApi.getModel(modelId)
xmlString.value = data.bpmnXml
model.value = { model.value = {
...data, ...data,
bpmnXml: undefined // 清空 bpmnXml 属性 bpmnXml: undefined // 清空 bpmnXml 属性
} }
xmlString.value = data.bpmnXml
}) })
</script> </script>
<style lang="scss"> <style lang="scss">
......
...@@ -36,10 +36,10 @@ ...@@ -36,10 +36,10 @@
class="!w-240px" class="!w-240px"
> >
<el-option <el-option
v-for="dict in getIntDictOptions(DICT_TYPE.BPM_MODEL_CATEGORY)" v-for="category in categoryList"
:key="dict.value" :key="category.code"
:label="dict.label" :label="category.name"
:value="dict.value" :value="category.code"
/> />
</el-select> </el-select>
</el-form-item> </el-form-item>
...@@ -72,11 +72,12 @@ ...@@ -72,11 +72,12 @@
</el-button> </el-button>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="流程分类" align="center" prop="category" width="100"> <el-table-column label="流程图标" align="center" prop="icon" width="100">
<template #default="scope"> <template #default="scope">
<dict-tag :type="DICT_TYPE.BPM_MODEL_CATEGORY" :value="scope.row.category" /> <el-image :src="scope.row.icon" class="w-32px h-32px" />
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="流程分类" align="center" prop="categoryName" width="100" />
<el-table-column label="表单信息" align="center" prop="formType" width="200"> <el-table-column label="表单信息" align="center" prop="formType" width="200">
<template #default="scope"> <template #default="scope">
<el-button <el-button
...@@ -164,10 +165,10 @@ ...@@ -164,10 +165,10 @@
<el-button <el-button
link link
type="primary" type="primary"
@click="handleAssignRule(scope.row)" @click="handleSimpleDesign(scope.row.id)"
v-hasPermi="['bpm:task-assign-rule:query']" v-hasPermi="['bpm:model:update']"
> >
分配规则 仿钉钉设计流程
</el-button> </el-button>
<el-button <el-button
link link
...@@ -229,7 +230,6 @@ ...@@ -229,7 +230,6 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
import { dateFormatter, formatDate } from '@/utils/formatTime' import { dateFormatter, formatDate } from '@/utils/formatTime'
import { MyProcessViewer } from '@/components/bpmnProcessDesigner/package' import { MyProcessViewer } from '@/components/bpmnProcessDesigner/package'
import * as ModelApi from '@/api/bpm/model' import * as ModelApi from '@/api/bpm/model'
...@@ -237,6 +237,7 @@ import * as FormApi from '@/api/bpm/form' ...@@ -237,6 +237,7 @@ import * as FormApi from '@/api/bpm/form'
import ModelForm from './ModelForm.vue' import ModelForm from './ModelForm.vue'
import ModelImportForm from '@/views/bpm/model/ModelImportForm.vue' import ModelImportForm from '@/views/bpm/model/ModelImportForm.vue'
import { setConfAndFields2 } from '@/utils/formCreate' import { setConfAndFields2 } from '@/utils/formCreate'
import { CategoryApi } from '@/api/bpm/category'
defineOptions({ name: 'BpmModel' }) defineOptions({ name: 'BpmModel' })
...@@ -255,6 +256,7 @@ const queryParams = reactive({ ...@@ -255,6 +256,7 @@ const queryParams = reactive({
category: undefined category: undefined
}) })
const queryFormRef = ref() // 搜索的表单 const queryFormRef = ref() // 搜索的表单
const categoryList = ref([]) // 流程分类列表
/** 查询列表 */ /** 查询列表 */
const getList = async () => { const getList = async () => {
...@@ -334,6 +336,15 @@ const handleDesign = (row) => { ...@@ -334,6 +336,15 @@ const handleDesign = (row) => {
}) })
} }
const handleSimpleDesign = (row) => {
push({
name: 'SimpleWorkflowDesignEditor',
query: {
modelId: row.id
}
})
}
/** 发布流程 */ /** 发布流程 */
const handleDeploy = async (row) => { const handleDeploy = async (row) => {
try { try {
...@@ -347,16 +358,6 @@ const handleDeploy = async (row) => { ...@@ -347,16 +358,6 @@ const handleDeploy = async (row) => {
} catch {} } catch {}
} }
/** 点击任务分配按钮 */
const handleAssignRule = (row) => {
push({
name: 'BpmTaskAssignRuleList',
query: {
modelId: row.id
}
})
}
/** 跳转到指定流程定义列表 */ /** 跳转到指定流程定义列表 */
const handleDefinitionList = (row) => { const handleDefinitionList = (row) => {
push({ push({
...@@ -400,7 +401,9 @@ const handleBpmnDetail = async (row) => { ...@@ -400,7 +401,9 @@ const handleBpmnDetail = async (row) => {
} }
/** 初始化 **/ /** 初始化 **/
onMounted(() => { onMounted(async () => {
getList() await getList()
// 查询流程分类列表
categoryList.value = await CategoryApi.getCategorySimpleList()
}) })
</script> </script>
...@@ -37,6 +37,36 @@ ...@@ -37,6 +37,36 @@
<el-form-item label="原因" prop="reason"> <el-form-item label="原因" prop="reason">
<el-input v-model="formData.reason" placeholder="请输请假原因" type="textarea" /> <el-input v-model="formData.reason" placeholder="请输请假原因" type="textarea" />
</el-form-item> </el-form-item>
<el-col v-if="startUserSelectTasks.length > 0">
<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>
<el-form-item> <el-form-item>
<el-button :disabled="formLoading" type="primary" @click="submitForm">确 定</el-button> <el-button :disabled="formLoading" type="primary" @click="submitForm">确 定</el-button>
</el-form-item> </el-form-item>
...@@ -46,10 +76,15 @@ ...@@ -46,10 +76,15 @@
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict' import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
import * as LeaveApi from '@/api/bpm/leave' import * as LeaveApi from '@/api/bpm/leave'
import { useTagsViewStore } from '@/store/modules/tagsView' import { useTagsViewStore } from '@/store/modules/tagsView'
import * as DefinitionApi from '@/api/bpm/definition'
import * as UserApi from '@/api/system/user'
defineOptions({ name: 'BpmOALeaveCreate' }) defineOptions({ name: 'BpmOALeaveCreate' })
const message = useMessage() // 消息弹窗 const message = useMessage() // 消息弹窗
const { delView } = useTagsViewStore() // 视图操作
const { push, currentRoute } = useRouter() // 路由
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用 const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
const formData = ref({ const formData = ref({
type: undefined, type: undefined,
...@@ -64,18 +99,34 @@ const formRules = reactive({ ...@@ -64,18 +99,34 @@ const formRules = reactive({
endTime: [{ required: true, message: '请假结束时间不能为空', trigger: 'change' }] endTime: [{ required: true, message: '请假结束时间不能为空', trigger: 'change' }]
}) })
const formRef = ref() // 表单 Ref const formRef = ref() // 表单 Ref
const { delView } = useTagsViewStore() // 视图操作
const { push, currentRoute } = useRouter() // 路由 // 指定审批人
const processDefineKey = 'oa_leave' // 流程定义 Key
const startUserSelectTasks = ref([]) // 发起人需要选择审批人的用户任务列表
const startUserSelectAssignees = ref({}) // 发起人选择审批人的数据
const startUserSelectAssigneesFormRef = ref() // 发起人选择审批人的表单 Ref
const startUserSelectAssigneesFormRules = ref({}) // 发起人选择审批人的表单 Rules
const userList = ref<any[]>([]) // 用户列表
/** 提交表单 */ /** 提交表单 */
const submitForm = async () => { const submitForm = async () => {
// 校验表单 // 校验表单
if (!formRef) return if (!formRef) return
const valid = await formRef.value.validate() const valid = await formRef.value.validate()
if (!valid) return if (!valid) return
// 校验指定审批人
if (startUserSelectTasks.value?.length > 0) {
await startUserSelectAssigneesFormRef.value.validate()
}
// 提交请求 // 提交请求
formLoading.value = true formLoading.value = true
try { try {
const data = formData.value as unknown as LeaveApi.LeaveVO const data = { ...formData.value } as unknown as LeaveApi.LeaveVO
// 设置指定审批人
if (startUserSelectTasks.value?.length > 0) {
data.startUserSelectAssignees = startUserSelectAssignees.value
}
await LeaveApi.createLeave(data) await LeaveApi.createLeave(data)
message.success('发起成功') message.success('发起成功')
// 关闭当前 Tab // 关闭当前 Tab
...@@ -85,4 +136,29 @@ const submitForm = async () => { ...@@ -85,4 +136,29 @@ const submitForm = async () => {
formLoading.value = false formLoading.value = false
} }
} }
/** 初始化 */
onMounted(async () => {
const processDefinitionDetail = await DefinitionApi.getProcessDefinition(
undefined,
processDefineKey
)
if (!processDefinitionDetail) {
message.error('OA 请假的流程模型未配置,请检查!')
return
}
startUserSelectTasks.value = processDefinitionDetail.startUserSelectTasks
// 设置指定审批人
if (startUserSelectTasks.value?.length > 0) {
// 设置校验规则
for (const userTask of startUserSelectTasks.value) {
startUserSelectAssignees.value[userTask.id] = []
startUserSelectAssigneesFormRules.value[userTask.id] = [
{ required: true, message: '请选择审批人', trigger: 'blur' }
]
}
// 加载用户列表
userList.value = await UserApi.getSimpleUserList()
}
})
</script> </script>
...@@ -36,10 +36,15 @@ ...@@ -36,10 +36,15 @@
value-format="YYYY-MM-DD HH:mm:ss" value-format="YYYY-MM-DD HH:mm:ss"
/> />
</el-form-item> </el-form-item>
<el-form-item label="结果" prop="result"> <el-form-item label="审批结果" prop="result">
<el-select v-model="queryParams.result" class="!w-240px" clearable placeholder="请选择结果"> <el-select
v-model="queryParams.result"
class="!w-240px"
clearable
placeholder="请选择审批结果"
>
<el-option <el-option
v-for="dict in getIntDictOptions(DICT_TYPE.BPM_PROCESS_INSTANCE_RESULT)" v-for="dict in getIntDictOptions(DICT_TYPE.BPM_PROCESS_INSTANCE_STATUS)"
:key="dict.value" :key="dict.value"
:label="dict.label" :label="dict.label"
:value="dict.value" :value="dict.value"
...@@ -78,7 +83,7 @@ ...@@ -78,7 +83,7 @@
<el-table-column align="center" label="申请编号" prop="id" /> <el-table-column align="center" label="申请编号" prop="id" />
<el-table-column align="center" label="状态" prop="result"> <el-table-column align="center" label="状态" prop="result">
<template #default="scope"> <template #default="scope">
<dict-tag :type="DICT_TYPE.BPM_PROCESS_INSTANCE_RESULT" :value="scope.row.result" /> <dict-tag :type="DICT_TYPE.BPM_PROCESS_INSTANCE_STATUS" :value="scope.row.result" />
</template> </template>
</el-table-column> </el-table-column>
<el-table-column <el-table-column
...@@ -166,7 +171,7 @@ const queryParams = reactive({ ...@@ -166,7 +171,7 @@ const queryParams = reactive({
pageNo: 1, pageNo: 1,
pageSize: 10, pageSize: 10,
type: undefined, type: undefined,
result: undefined, status: undefined,
reason: undefined, reason: undefined,
createTime: [] createTime: []
}) })
...@@ -221,7 +226,7 @@ const cancelLeave = async (row) => { ...@@ -221,7 +226,7 @@ const cancelLeave = async (row) => {
inputErrorMessage: '取消原因不能为空' inputErrorMessage: '取消原因不能为空'
}) })
// 发起取消 // 发起取消
await ProcessInstanceApi.cancelProcessInstance(row.id, value) await ProcessInstanceApi.cancelProcessInstanceByStartUser(row.id, value)
message.success('取消成功') message.success('取消成功')
// 刷新列表 // 刷新列表
await getList() await getList()
......
<template>
<Dialog :title="dialogTitle" v-model="dialogVisible">
<el-form
ref="formRef"
:model="formData"
:rules="formRules"
label-width="100px"
v-loading="formLoading"
>
<el-form-item label="名字" prop="name">
<el-input v-model="formData.name" placeholder="请输入名字" />
</el-form-item>
<el-form-item label="状态" prop="status">
<el-radio-group v-model="formData.status">
<el-radio
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)"
:key="dict.value"
:label="dict.value"
>
{{ dict.label }}
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="表达式" prop="expression">
<el-input type="textarea" v-model="formData.expression" placeholder="请输入表达式" />
</el-form-item>
</el-form>
<template #footer>
<el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button>
<el-button @click="dialogVisible = false">取 消</el-button>
</template>
</Dialog>
</template>
<script setup lang="ts">
import { getIntDictOptions, DICT_TYPE } from '@/utils/dict'
import { ProcessExpressionApi, ProcessExpressionVO } from '@/api/bpm/processExpression'
import { CommonStatusEnum } from '@/utils/constants'
/** BPM 流程 表单 */
defineOptions({ name: 'ProcessExpressionForm' })
const { t } = useI18n() // 国际化
const message = useMessage() // 消息弹窗
const dialogVisible = ref(false) // 弹窗的是否展示
const dialogTitle = ref('') // 弹窗的标题
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
const formType = ref('') // 表单的类型:create - 新增;update - 修改
const formData = ref({
id: undefined,
name: undefined,
status: undefined,
expression: undefined
})
const formRules = reactive({
name: [{ required: true, message: '名字不能为空', trigger: 'blur' }],
status: [{ required: true, message: '状态不能为空', trigger: 'blur' }],
expression: [{ required: true, message: '表达式不能为空', trigger: 'blur' }]
})
const formRef = ref() // 表单 Ref
/** 打开弹窗 */
const open = async (type: string, id?: number) => {
dialogVisible.value = true
dialogTitle.value = t('action.' + type)
formType.value = type
resetForm()
// 修改时,设置数据
if (id) {
formLoading.value = true
try {
formData.value = await ProcessExpressionApi.getProcessExpression(id)
} finally {
formLoading.value = false
}
}
}
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
/** 提交表单 */
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
const submitForm = async () => {
// 校验表单
await formRef.value.validate()
// 提交请求
formLoading.value = true
try {
const data = formData.value as unknown as ProcessExpressionVO
if (formType.value === 'create') {
await ProcessExpressionApi.createProcessExpression(data)
message.success(t('common.createSuccess'))
} else {
await ProcessExpressionApi.updateProcessExpression(data)
message.success(t('common.updateSuccess'))
}
dialogVisible.value = false
// 发送操作成功的事件
emit('success')
} finally {
formLoading.value = false
}
}
/** 重置表单 */
const resetForm = () => {
formData.value = {
id: undefined,
name: undefined,
status: CommonStatusEnum.ENABLE,
expression: undefined
}
formRef.value?.resetFields()
}
</script>
<template>
<ContentWrap>
<!-- 搜索工作栏 -->
<el-form
class="-mb-15px"
:model="queryParams"
ref="queryFormRef"
:inline="true"
label-width="68px"
>
<el-form-item label="名字" prop="name">
<el-input
v-model="queryParams.name"
placeholder="请输入名字"
clearable
@keyup.enter="handleQuery"
class="!w-240px"
/>
</el-form-item>
<el-form-item label="状态" prop="status">
<el-select v-model="queryParams.status" placeholder="请选择状态" clearable class="!w-240px">
<el-option
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
<el-form-item label="创建时间" prop="createTime">
<el-date-picker
v-model="queryParams.createTime"
value-format="YYYY-MM-DD HH:mm:ss"
type="daterange"
start-placeholder="开始日期"
end-placeholder="结束日期"
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
class="!w-240px"
/>
</el-form-item>
<el-form-item>
<el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
<el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
<el-button
type="primary"
plain
@click="openForm('create')"
v-hasPermi="['bpm:process-expression:create']"
>
<Icon icon="ep:plus" class="mr-5px" /> 新增
</el-button>
</el-form-item>
</el-form>
</ContentWrap>
<!-- 列表 -->
<ContentWrap>
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
<el-table-column label="编号" align="center" prop="id" />
<el-table-column label="名字" align="center" prop="name" />
<el-table-column label="状态" align="center" prop="status">
<template #default="scope">
<dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="scope.row.status" />
</template>
</el-table-column>
<el-table-column label="表达式" align="center" prop="expression" />
<el-table-column
label="创建时间"
align="center"
prop="createTime"
:formatter="dateFormatter"
width="180px"
/>
<el-table-column label="操作" align="center">
<template #default="scope">
<el-button
link
type="primary"
@click="openForm('update', scope.row.id)"
v-hasPermi="['bpm:process-expression:update']"
>
编辑
</el-button>
<el-button
link
type="danger"
@click="handleDelete(scope.row.id)"
v-hasPermi="['bpm:process-expression:delete']"
>
删除
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<Pagination
:total="total"
v-model:page="queryParams.pageNo"
v-model:limit="queryParams.pageSize"
@pagination="getList"
/>
</ContentWrap>
<!-- 表单弹窗:添加/修改 -->
<ProcessExpressionForm ref="formRef" @success="getList" />
</template>
<script setup lang="ts">
import { getIntDictOptions, DICT_TYPE } from '@/utils/dict'
import { dateFormatter } from '@/utils/formatTime'
import { ProcessExpressionApi, ProcessExpressionVO } from '@/api/bpm/processExpression'
import ProcessExpressionForm from './ProcessExpressionForm.vue'
/** BPM 流程表达式列表 */
defineOptions({ name: 'BpmProcessExpression' })
const message = useMessage() // 消息弹窗
const { t } = useI18n() // 国际化
const loading = ref(true) // 列表的加载中
const list = ref<ProcessExpressionVO[]>([]) // 列表的数据
const total = ref(0) // 列表的总页数
const queryParams = reactive({
pageNo: 1,
pageSize: 10,
name: undefined,
status: undefined,
createTime: []
})
const queryFormRef = ref() // 搜索的表单
const exportLoading = ref(false) // 导出的加载中
/** 查询列表 */
const getList = async () => {
loading.value = true
try {
const data = await ProcessExpressionApi.getProcessExpressionPage(queryParams)
list.value = data.list
total.value = data.total
} finally {
loading.value = false
}
}
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.pageNo = 1
getList()
}
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value.resetFields()
handleQuery()
}
/** 添加/修改操作 */
const formRef = ref()
const openForm = (type: string, id?: number) => {
formRef.value.open(type, id)
}
/** 删除按钮操作 */
const handleDelete = async (id: number) => {
try {
// 删除的二次确认
await message.delConfirm()
// 发起删除
await ProcessExpressionApi.deleteProcessExpression(id)
message.success(t('common.delSuccess'))
// 刷新列表
await getList()
} catch {}
}
/** 初始化 **/
onMounted(() => {
getList()
})
</script>
...@@ -33,21 +33,18 @@ const bpmnControlForm = ref({ ...@@ -33,21 +33,18 @@ const bpmnControlForm = ref({
prefix: 'flowable' prefix: 'flowable'
}) })
const activityList = ref([]) // 任务列表 const activityList = ref([]) // 任务列表
// const bpmnXML = computed(() => { // TODO 芋艿:不晓得为啊哈不能这么搞
// if (!props.processInstance || !props.processInstance.processDefinition) {
// return
// }
// return DefinitionApi.getProcessDefinitionBpmnXML(props.processInstance.processDefinition.id)
// })
/** 初始化 */ /** 只有 loading 完成时,才去加载流程列表 */
onMounted(async () => { watch(
if (props.id) { () => props.loading,
activityList.value = await ActivityApi.getActivityList({ async (value) => {
processInstanceId: props.id if (value && props.id) {
}) activityList.value = await ActivityApi.getActivityList({
processInstanceId: props.id
})
}
} }
}) )
</script> </script>
<style> <style>
.box-card { .box-card {
......
...@@ -3,25 +3,44 @@ ...@@ -3,25 +3,44 @@
<template #header> <template #header>
<span class="el-icon-picture-outline">审批记录</span> <span class="el-icon-picture-outline">审批记录</span>
</template> </template>
<el-col :offset="4" :span="16"> <el-col :offset="3" :span="17">
<div class="block"> <div class="block">
<el-timeline> <el-timeline>
<el-timeline-item <el-timeline-item
v-if="processInstance.endTime"
:type="getProcessInstanceTimelineItemType(processInstance)"
>
<p style="font-weight: 700">
结束流程:在 {{ formatDate(processInstance?.endTime) }} 结束
<dict-tag
:type="DICT_TYPE.BPM_PROCESS_INSTANCE_STATUS"
:value="processInstance.status"
/>
</p>
</el-timeline-item>
<el-timeline-item
v-for="(item, index) in tasks" v-for="(item, index) in tasks"
:key="index" :key="index"
:icon="getTimelineItemIcon(item)" :type="getTaskTimelineItemType(item)"
:type="getTimelineItemType(item)"
> >
<p style="font-weight: 700"> <p style="font-weight: 700">
任务:{{ item.name }} 审批任务:{{ item.name }}
<dict-tag :type="DICT_TYPE.BPM_PROCESS_INSTANCE_RESULT" :value="item.result" /> <dict-tag :type="DICT_TYPE.BPM_TASK_STATUS" :value="item.status" />
<el-button <el-button
style="margin-left: 5px" class="ml-10px"
v-if="!isEmpty(item.children)" v-if="!isEmpty(item.children)"
@click="openChildrenTask(item)" @click="openChildrenTask(item)"
size="small"
>
<Icon icon="ep:memo" /> 子任务
</el-button>
<el-button
class="ml-10px"
size="small"
v-if="item.formId > 0"
@click="handleFormDetail(item)"
> >
<Icon icon="ep:memo" /> <Icon icon="ep:document" /> 查看表单
子任务
</el-button> </el-button>
</p> </p>
<el-card :body-style="{ padding: '10px' }"> <el-card :body-style="{ padding: '10px' }">
...@@ -45,84 +64,112 @@ ...@@ -45,84 +64,112 @@
<label v-if="item.durationInMillis" style="font-weight: normal; color: #8a909c"> <label v-if="item.durationInMillis" style="font-weight: normal; color: #8a909c">
{{ formatPast2(item?.durationInMillis) }} {{ formatPast2(item?.durationInMillis) }}
</label> </label>
<p v-if="item.reason"> <p v-if="item.reason"> 审批建议:{{ item.reason }} </p>
<el-tag :type="getTimelineItemType(item)">{{ item.reason }}</el-tag>
</p>
</el-card> </el-card>
</el-timeline-item> </el-timeline-item>
<el-timeline-item type="success">
<p style="font-weight: 700">
发起流程:【{{ processInstance.startUser?.nickname }}】在
{{ formatDate(processInstance?.startTime) }} 发起【 {{ processInstance.name }} 】流程
</p>
</el-timeline-item>
</el-timeline> </el-timeline>
</div> </div>
</el-col> </el-col>
<!-- 子任务 -->
<ProcessInstanceChildrenTaskList ref="processInstanceChildrenTaskList" />
</el-card> </el-card>
<!-- 弹窗:子任务 -->
<TaskSignList ref="taskSignListRef" @success="refresh" />
<!-- 弹窗:表单 -->
<Dialog title="表单详情" v-model="taskFormVisible" width="600">
<form-create
ref="fApi"
v-model="taskForm.value"
:option="taskForm.option"
:rule="taskForm.rule"
/>
</Dialog>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { formatDate, formatPast2 } from '@/utils/formatTime' import { formatDate, formatPast2 } from '@/utils/formatTime'
import { propTypes } from '@/utils/propTypes' import { propTypes } from '@/utils/propTypes'
import { DICT_TYPE } from '@/utils/dict' import { DICT_TYPE } from '@/utils/dict'
import { isEmpty } from '@/utils/is' import { isEmpty } from '@/utils/is'
import ProcessInstanceChildrenTaskList from './ProcessInstanceChildrenTaskList.vue' import TaskSignList from './dialog/TaskSignList.vue'
import type { ApiAttrs } from '@form-create/element-ui/types/config'
import { setConfAndFields2 } from '@/utils/formCreate'
defineOptions({ name: 'BpmProcessInstanceTaskList' }) defineOptions({ name: 'BpmProcessInstanceTaskList' })
defineProps({ defineProps({
loading: propTypes.bool, // 是否加载中 loading: propTypes.bool, // 是否加载中
processInstance: propTypes.object, // 流程实例
tasks: propTypes.arrayOf(propTypes.object) // 流程任务的数组 tasks: propTypes.arrayOf(propTypes.object) // 流程任务的数组
}) })
/** 获得任务对应的 icon */ /** 获得流程实例对应的颜色 */
const getTimelineItemIcon = (item) => { const getProcessInstanceTimelineItemType = (item: any) => {
if (item.result === 1) { if (item.status === 2) {
return 'el-icon-time' return 'success'
}
if (item.result === 2) {
return 'el-icon-check'
}
if (item.result === 3) {
return 'el-icon-close'
} }
if (item.result === 4) { if (item.status === 3) {
return 'el-icon-remove-outline' return 'danger'
} }
if (item.result === 5) { if (item.status === 4) {
return 'el-icon-back' return 'warning'
} }
return '' return ''
} }
/** 获得任务对应的颜色 */ /** 获得任务对应的颜色 */
const getTimelineItemType = (item) => { const getTaskTimelineItemType = (item: any) => {
if (item.result === 1) { if ([0, 1, 6, 7].includes(item.status)) {
return 'primary' return 'primary'
} }
if (item.result === 2) { if (item.status === 2) {
return 'success' return 'success'
} }
if (item.result === 3) { if (item.status === 3) {
return 'danger' return 'danger'
} }
if (item.result === 4) { if (item.status === 4) {
return 'info' return 'info'
} }
if (item.result === 5) { if (item.status === 5) {
return 'warning'
}
if (item.result === 6) {
return 'default'
}
if (item.result === 7 || item.result === 8) {
return 'warning' return 'warning'
} }
return '' return ''
} }
/** /** 子任务 */
* 子任务 const taskSignListRef = ref()
*/ const openChildrenTask = (item: any) => {
const processInstanceChildrenTaskList = ref() taskSignListRef.value.open(item)
}
/** 查看表单 */
const fApi = ref<ApiAttrs>() // form-create 的 API 操作类
const taskForm = ref({
rule: [],
option: {},
value: {}
}) // 流程任务的表单详情
const taskFormVisible = ref(false)
const handleFormDetail = async (row) => {
// 设置表单
setConfAndFields2(taskForm, row.formConf, row.formFields, row.formVariables)
// 弹窗打开
taskFormVisible.value = true
// 隐藏提交、重置按钮,设置禁用只读
await nextTick()
fApi.value.fapi.btn.show(false)
fApi.value?.fapi?.resetBtn.show(false)
fApi.value?.fapi?.disabled(true)
}
const openChildrenTask = (item) => { /** 刷新数据 */
processInstanceChildrenTaskList.value.open(item) const emit = defineEmits(['refresh']) // 定义 success 事件,用于操作成功后的回调
const refresh = () => {
emit('refresh')
} }
</script> </script>
<!-- TODO @kyle:需要在讨论下;可能直接选人更合适 -->
<template>
<Dialog v-model="dialogVisible" title="修改任务规则" width="600">
<el-form ref="formRef" :model="formData" :rules="formRules" label-width="80px">
<el-form-item label="任务名称" prop="taskName">
<el-input v-model="formData.taskName" disabled placeholder="请输入任务名称" />
</el-form-item>
<el-form-item label="任务标识" prop="taskKey">
<el-input v-model="formData.taskKey" disabled placeholder="请输入任务标识" />
</el-form-item>
<el-form-item label="流程名称" prop="processInstanceName">
<el-input v-model="formData.processInstanceName" disabled placeholder="请输入流程名称" />
</el-form-item>
<el-form-item label="流程标识" prop="processInstanceKey">
<el-input v-model="formData.processInstanceKey" disabled placeholder="请输入流程标识" />
</el-form-item>
<el-form-item label="规则类型" prop="type">
<el-select v-model="formData.type" clearable style="width: 100%">
<el-option
v-for="dict in getIntDictOptions(DICT_TYPE.BPM_TASK_ASSIGN_RULE_TYPE)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
<el-form-item v-if="formData.type === 10" label="指定角色" prop="roleIds">
<el-select v-model="formData.roleIds" clearable multiple style="width: 100%">
<el-option
v-for="item in roleOptions"
:key="item.id"
:label="item.name"
:value="item.id"
/>
</el-select>
</el-form-item>
<el-form-item
v-if="formData.type === 20 || formData.type === 21"
label="指定部门"
prop="deptIds"
span="24"
>
<el-tree-select
ref="treeRef"
v-model="formData.deptIds"
:data="deptTreeOptions"
:props="defaultProps"
empty-text="加载中,请稍后"
multiple
node-key="id"
show-checkbox
/>
</el-form-item>
<el-form-item v-if="formData.type === 22" label="指定岗位" prop="postIds" span="24">
<el-select v-model="formData.postIds" clearable multiple style="width: 100%">
<el-option
v-for="item in postOptions"
:key="parseInt(item.id)"
:label="item.name"
:value="parseInt(item.id)"
/>
</el-select>
</el-form-item>
<el-form-item
v-if="formData.type === 30 || formData.type === 31 || formData.type === 32"
label="指定用户"
prop="userIds"
span="24"
>
<el-select v-model="formData.userIds" clearable multiple style="width: 100%">
<el-option
v-for="item in userOptions"
:key="parseInt(item.id)"
:label="item.nickname"
:value="parseInt(item.id)"
/>
</el-select>
</el-form-item>
<el-form-item v-if="formData.type === 40" label="指定用户组" prop="userGroupIds">
<el-select v-model="formData.userGroupIds" clearable multiple style="width: 100%">
<el-option
v-for="item in userGroupOptions"
:key="parseInt(item.id)"
:label="item.name"
:value="parseInt(item.id)"
/>
</el-select>
</el-form-item>
<el-form-item v-if="formData.type === 50" label="指定脚本" prop="scripts">
<el-select v-model="formData.scripts" clearable multiple style="width: 100%">
<el-option
v-for="dict in taskAssignScriptDictDatas"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
<el-form-item label="抄送原因" prop="reason">
<el-input v-model="formData.reason" placeholder="请输入抄送原因" type="textarea" />
</el-form-item>
</el-form>
<!-- 操作按钮 -->
<template #footer>
<el-button :disabled="formLoading" type="primary" @click="submitForm">确 定</el-button>
<el-button @click="dialogVisible = false">取 消</el-button>
</template>
</Dialog>
</template>
<script lang="ts" setup>
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
import { defaultProps, handleTree } from '@/utils/tree'
import * as ProcessInstanceApi from '@/api/bpm/processInstance'
import * as RoleApi from '@/api/system/role'
import * as DeptApi from '@/api/system/dept'
import * as PostApi from '@/api/system/post'
import * as UserApi from '@/api/system/user'
import * as UserGroupApi from '@/api/bpm/userGroup'
const { t } = useI18n() // 国际化
const message = useMessage() // 消息弹窗
const dialogVisible = ref(false) // 弹窗的是否展示
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
const formData = ref({
type: Number(undefined),
taskName: '',
taskKey: '',
processInstanceName: '',
processInstanceKey: '',
startUserId: '',
options: [],
roleIds: [],
deptIds: [],
postIds: [],
userIds: [],
userGroupIds: [],
scripts: [],
reason: ''
})
const formRules = reactive({
type: [{ required: true, message: '规则类型不能为空', trigger: 'change' }],
roleIds: [{ required: true, message: '指定角色不能为空', trigger: 'change' }],
deptIds: [{ required: true, message: '指定部门不能为空', trigger: 'change' }],
postIds: [{ required: true, message: '指定岗位不能为空', trigger: 'change' }],
userIds: [{ required: true, message: '指定用户不能为空', trigger: 'change' }],
userGroupIds: [{ required: true, message: '指定用户组不能为空', trigger: 'change' }],
scripts: [{ required: true, message: '指定脚本不能为空', trigger: 'change' }],
reason: [{ required: true, message: '抄送原因不能为空', trigger: 'change' }]
})
const formRef = ref() // 表单 Ref
const roleOptions = ref<RoleApi.RoleVO[]>([]) // 角色列表
const deptOptions = ref<DeptApi.DeptVO[]>([]) // 部门列表
const deptTreeOptions = ref() // 部门树
const postOptions = ref<PostApi.PostVO[]>([]) // 岗位列表
const userOptions = ref<UserApi.UserVO[]>([]) // 用户列表
const userGroupOptions = ref<UserGroupApi.UserGroupVO[]>([]) // 用户组列表
const taskAssignScriptDictDatas = getIntDictOptions(DICT_TYPE.BPM_TASK_ASSIGN_SCRIPT)
/** 打开弹窗 */
const open = async (row) => {
// 1. 先重置表单
resetForm()
// 2. 再设置表单
if (row != null) {
formData.value.type = undefined as unknown as number
formData.value.taskName = row.name
formData.value.taskKey = row.id
formData.value.processInstanceName = row.processInstance.name
formData.value.processInstanceKey = row.processInstance.id
formData.value.startUserId = row.processInstance.startUserId
}
// 打开弹窗
dialogVisible.value = true
// 获得角色列表
roleOptions.value = await RoleApi.getSimpleRoleList()
// 获得部门列表
deptOptions.value = await DeptApi.getSimpleDeptList()
deptTreeOptions.value = handleTree(deptOptions.value, 'id')
// 获得岗位列表
postOptions.value = await PostApi.getSimplePostList()
// 获得用户列表
userOptions.value = await UserApi.getSimpleUserList()
// 获得用户组列表
userGroupOptions.value = await UserGroupApi.getSimpleUserGroupList()
}
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
/** 提交表单 */
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
const submitForm = async () => {
// 校验表单
if (!formRef) return
const valid = await formRef.value.validate()
if (!valid) return
// 构建表单
const form = {
...formData.value
}
// 将 roleIds 等选项赋值到 options 中
if (form.type === 10) {
form.options = form.roleIds
} else if (form.type === 20 || form.type === 21) {
form.options = form.deptIds
} else if (form.type === 22) {
form.options = form.postIds
} else if (form.type === 30 || form.type === 31 || form.type === 32) {
form.options = form.userIds
} else if (form.type === 40) {
form.options = form.userGroupIds
} else if (form.type === 50) {
form.options = form.scripts
}
form.roleIds = undefined
form.deptIds = undefined
form.postIds = undefined
form.userIds = undefined
form.userGroupIds = undefined
form.scripts = undefined
// 提交请求
formLoading.value = true
try {
const data = form as unknown as ProcessInstanceApi.ProcessInstanceCCVO
await ProcessInstanceApi.createProcessInstanceCC(data)
console.log(data)
message.success(t('common.createSuccess'))
dialogVisible.value = false
// 发送操作成功的事件
emit('success')
} finally {
formLoading.value = false
}
}
/** 重置表单 */
const resetForm = () => {
formRef.value?.resetFields()
}
</script>
...@@ -37,10 +37,12 @@ const dialogVisible = ref(false) // 弹窗的是否展示 ...@@ -37,10 +37,12 @@ const dialogVisible = ref(false) // 弹窗的是否展示
const formLoading = ref(false) // 表单的加载中 const formLoading = ref(false) // 表单的加载中
const formData = ref({ const formData = ref({
id: '', id: '',
delegateUserId: undefined delegateUserId: undefined,
reason: ''
}) })
const formRules = ref({ const formRules = ref({
delegateUserId: [{ required: true, message: '接收人不能为空', trigger: 'change' }] delegateUserId: [{ required: true, message: '接收人不能为空', trigger: 'change' }],
reason: [{ required: true, message: '委派理由不能为空', trigger: 'blur' }]
}) })
const formRef = ref() // 表单 Ref const formRef = ref() // 表单 Ref
...@@ -79,7 +81,8 @@ const submitForm = async () => { ...@@ -79,7 +81,8 @@ const submitForm = async () => {
const resetForm = () => { const resetForm = () => {
formData.value = { formData.value = {
id: '', id: '',
delegateUserId: undefined delegateUserId: undefined,
reason: ''
} }
formRef.value?.resetFields() formRef.value?.resetFields()
} }
......
<template> <template>
<Dialog v-model="dialogVisible" title="回退" width="500"> <Dialog v-model="dialogVisible" title="回退任务" width="500">
<el-form <el-form
ref="formRef" ref="formRef"
v-loading="formLoading" v-loading="formLoading"
...@@ -7,13 +7,13 @@ ...@@ -7,13 +7,13 @@
:rules="formRules" :rules="formRules"
label-width="110px" label-width="110px"
> >
<el-form-item label="退回节点" prop="targetDefinitionKey"> <el-form-item label="退回节点" prop="targetTaskDefinitionKey">
<el-select v-model="formData.targetDefinitionKey" clearable style="width: 100%"> <el-select v-model="formData.targetTaskDefinitionKey" clearable style="width: 100%">
<el-option <el-option
v-for="item in returnList" v-for="item in returnList"
:key="item.definitionKey" :key="item.taskDefinitionKey"
:label="item.name" :label="item.name"
:value="item.definitionKey" :value="item.taskDefinitionKey"
/> />
</el-select> </el-select>
</el-form-item> </el-form-item>
...@@ -35,19 +35,19 @@ const dialogVisible = ref(false) // 弹窗的是否展示 ...@@ -35,19 +35,19 @@ const dialogVisible = ref(false) // 弹窗的是否展示
const formLoading = ref(false) // 表单的加载中 const formLoading = ref(false) // 表单的加载中
const formData = ref({ const formData = ref({
id: '', id: '',
targetDefinitionKey: undefined, targetTaskDefinitionKey: undefined,
reason: '' reason: ''
}) })
const formRules = ref({ const formRules = ref({
targetDefinitionKey: [{ required: true, message: '必须选择回退节点', trigger: 'change' }], targetTaskDefinitionKey: [{ required: true, message: '必须选择回退节点', trigger: 'change' }],
reason: [{ required: true, message: '回退理由不能为空', trigger: 'blur' }] reason: [{ required: true, message: '回退理由不能为空', trigger: 'blur' }]
}) })
const formRef = ref() // 表单 Ref const formRef = ref() // 表单 Ref
const returnList = ref([]) const returnList = ref([] as any)
/** 打开弹窗 */ /** 打开弹窗 */
const open = async (id: string) => { const open = async (id: string) => {
returnList.value = await TaskApi.getReturnList({ taskId: id }) returnList.value = await TaskApi.getTaskListByReturn(id)
if (returnList.value.length === 0) { if (returnList.value.length === 0) {
message.warning('当前没有可回退的节点') message.warning('当前没有可回退的节点')
return false return false
...@@ -82,7 +82,7 @@ const submitForm = async () => { ...@@ -82,7 +82,7 @@ const submitForm = async () => {
const resetForm = () => { const resetForm = () => {
formData.value = { formData.value = {
id: '', id: '',
targetDefinitionKey: undefined, targetTaskDefinitionKey: undefined,
reason: '' reason: ''
} }
formRef.value?.resetFields() formRef.value?.resetFields()
......
...@@ -7,8 +7,8 @@ ...@@ -7,8 +7,8 @@
:rules="formRules" :rules="formRules"
label-width="110px" label-width="110px"
> >
<el-form-item label="加签处理人" prop="userIdList"> <el-form-item label="加签处理人" prop="userIds">
<el-select v-model="formData.userIdList" multiple clearable style="width: 100%"> <el-select v-model="formData.userIds" multiple clearable style="width: 100%">
<el-option <el-option
v-for="item in userList" v-for="item in userList"
:key="item.id" :key="item.id"
...@@ -36,18 +36,19 @@ ...@@ -36,18 +36,19 @@
import * as TaskApi from '@/api/bpm/task' import * as TaskApi from '@/api/bpm/task'
import * as UserApi from '@/api/system/user' import * as UserApi from '@/api/system/user'
const message = useMessage() // 消息弹窗 defineOptions({ name: 'TaskSignCreateForm' })
defineOptions({ name: 'BpmTaskUpdateAssigneeForm' })
const message = useMessage() // 消息弹窗
const dialogVisible = ref(false) // 弹窗的是否展示 const dialogVisible = ref(false) // 弹窗的是否展示
const formLoading = ref(false) // 表单的加载中 const formLoading = ref(false) // 表单的加载中
const formData = ref({ const formData = ref({
id: '', id: '',
userIdList: [], userIds: [],
type: '' type: '',
reason: ''
}) })
const formRules = ref({ const formRules = ref({
userIdList: [{ required: true, message: '加签处理人不能为空', trigger: 'change' }], userIds: [{ required: true, message: '加签处理人不能为空', trigger: 'change' }],
reason: [{ required: true, message: '加签理由不能为空', trigger: 'change' }] reason: [{ required: true, message: '加签理由不能为空', trigger: 'change' }]
}) })
...@@ -75,7 +76,7 @@ const submitForm = async (type: string) => { ...@@ -75,7 +76,7 @@ const submitForm = async (type: string) => {
formLoading.value = true formLoading.value = true
formData.value.type = type formData.value.type = type
try { try {
await TaskApi.taskAddSign(formData.value) await TaskApi.signCreateTask(formData.value)
message.success('加签成功') message.success('加签成功')
dialogVisible.value = false dialogVisible.value = false
// 发送操作成功的事件 // 发送操作成功的事件
...@@ -89,8 +90,9 @@ const submitForm = async (type: string) => { ...@@ -89,8 +90,9 @@ const submitForm = async (type: string) => {
const resetForm = () => { const resetForm = () => {
formData.value = { formData.value = {
id: '', id: '',
userIdList: [], userIds: [],
type: '' type: '',
reason: ''
} }
formRef.value?.resetFields() formRef.value?.resetFields()
} }
......
...@@ -9,8 +9,10 @@ ...@@ -9,8 +9,10 @@
> >
<el-form-item label="减签任务" prop="id"> <el-form-item label="减签任务" prop="id">
<el-radio-group v-model="formData.id"> <el-radio-group v-model="formData.id">
<el-radio-button v-for="item in subTaskList" :key="item.id" :label="item.id"> <el-radio-button v-for="item in childrenTaskList" :key="item.id" :label="item.id">
{{ item.name }}({{ item.assigneeUser.deptName }}{{ item.assigneeUser.nickname }}--审批) {{ item.name }}
({{ item.assigneeUser?.deptName || item.ownerUser?.deptName }} -
{{ item.assigneeUser?.nickname || item.ownerUser?.nickname }})
</el-radio-button> </el-radio-button>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
...@@ -24,10 +26,12 @@ ...@@ -24,10 +26,12 @@
</template> </template>
</Dialog> </Dialog>
</template> </template>
<script lang="ts" name="TaskRollbackDialogForm" setup> <script lang="ts" setup>
import * as TaskApi from '@/api/bpm/task' import * as TaskApi from '@/api/bpm/task'
import { isEmpty } from '@/utils/is' import { isEmpty } from '@/utils/is'
defineOptions({ name: 'TaskSignDeleteForm' })
const message = useMessage() // 消息弹窗 const message = useMessage() // 消息弹窗
const dialogVisible = ref(false) // 弹窗的是否展示 const dialogVisible = ref(false) // 弹窗的是否展示
const formLoading = ref(false) // 表单的加载中 const formLoading = ref(false) // 表单的加载中
...@@ -41,11 +45,11 @@ const formRules = ref({ ...@@ -41,11 +45,11 @@ const formRules = ref({
}) })
const formRef = ref() // 表单 Ref const formRef = ref() // 表单 Ref
const subTaskList = ref([]) const childrenTaskList = ref([])
/** 打开弹窗 */ /** 打开弹窗 */
const open = async (id: string) => { const open = async (id: string) => {
subTaskList.value = await TaskApi.getChildrenTaskList(id) childrenTaskList.value = await TaskApi.getChildrenTaskList(id)
if (isEmpty(subTaskList.value)) { if (isEmpty(childrenTaskList.value)) {
message.warning('当前没有可减签的任务') message.warning('当前没有可减签的任务')
return false return false
} }
...@@ -64,7 +68,7 @@ const submitForm = async () => { ...@@ -64,7 +68,7 @@ const submitForm = async () => {
// 提交请求 // 提交请求
formLoading.value = true formLoading.value = true
try { try {
await TaskApi.taskSubSign(formData.value) await TaskApi.signDeleteTask(formData.value)
message.success('减签成功') message.success('减签成功')
dialogVisible.value = false dialogVisible.value = false
// 发送操作成功的事件 // 发送操作成功的事件
......
<template> <template>
<el-drawer v-model="drawerVisible" title="子任务" size="70%"> <el-drawer v-model="drawerVisible" title="子任务" size="880px">
<!-- 当前任务 --> <!-- 当前任务 -->
<template #header> <template #header>
<h4>{{ baseTask.name }} 】审批人:{{ baseTask.assigneeUser?.nickname }}</h4> <h4>{{ parentTask.name }} 】审批人:{{ parentTask?.assigneeUser?.nickname }}</h4>
<el-button <el-button
style="margin-left: 5px" style="margin-left: 5px"
v-if="isSubSignButtonVisible(baseTask)" v-if="isSignDeleteButtonVisible(parentTask)"
type="danger" type="danger"
plain plain
@click="handleSubSign(baseTask)" @click="handleSignDelete(parentTask)"
> >
<Icon icon="ep:remove" /> 减签 <Icon icon="ep:remove" /> 减签
</el-button> </el-button>
</template> </template>
<!-- 子任务列表 --> <!-- 子任务列表 -->
<el-table :data="baseTask.children" style="width: 100%" row-key="id" border> <el-table :data="parentTask.children" style="width: 100%" row-key="id" border>
<el-table-column prop="assigneeUser.nickname" label="审批人" /> <el-table-column prop="assigneeUser.nickname" label="审批人" min-width="100">
<el-table-column prop="assigneeUser.deptName" label="所在部门" />
<el-table-column label="审批状态" prop="result">
<template #default="scope"> <template #default="scope">
<dict-tag :type="DICT_TYPE.BPM_PROCESS_INSTANCE_RESULT" :value="scope.row.result" /> {{ scope.row.assigneeUser?.nickname || scope.row.ownerUser?.nickname }}
</template>
</el-table-column>
<el-table-column prop="assigneeUser.deptName" label="所在部门" min-width="100">
<template #default="scope">
{{ scope.row.assigneeUser?.deptName || scope.row.ownerUser?.deptName }}
</template>
</el-table-column>
<el-table-column label="审批状态" prop="status" width="120">
<template #default="scope">
<dict-tag :type="DICT_TYPE.BPM_TASK_STATUS" :value="scope.row.status" />
</template> </template>
</el-table-column> </el-table-column>
<el-table-column <el-table-column
...@@ -36,61 +44,63 @@ ...@@ -36,61 +44,63 @@
width="180" width="180"
:formatter="dateFormatter" :formatter="dateFormatter"
/> />
<el-table-column label="操作" prop="operation"> <el-table-column label="操作" prop="operation" width="90">
<template #default="scope"> <template #default="scope">
<el-button <el-button
v-if="isSubSignButtonVisible(scope.row)" v-if="isSignDeleteButtonVisible(scope.row)"
type="danger" type="danger"
plain plain
@click="handleSubSign(scope.row)" size="small"
@click="handleSignDelete(scope.row)"
> >
<Icon icon="ep:remove" /> 减签 <Icon icon="ep:remove" /> 减签
</el-button> </el-button>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
<!-- 减签 --> <!-- 减签 -->
<TaskSubSignDialogForm ref="taskSubSignDialogForm" /> <TaskSignDeleteForm ref="taskSignDeleteFormRef" @success="handleSignDeleteSuccess" />
</el-drawer> </el-drawer>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { isEmpty } from '@/utils/is' import { isEmpty } from '@/utils/is'
import { DICT_TYPE } from '@/utils/dict' import { DICT_TYPE } from '@/utils/dict'
import { dateFormatter } from '@/utils/formatTime' import { dateFormatter } from '@/utils/formatTime'
import TaskSubSignDialogForm from './TaskSubSignDialogForm.vue' import TaskSignDeleteForm from './TaskSignDeleteForm.vue'
defineOptions({ name: 'ProcessInstanceChildrenTaskList' }) defineOptions({ name: 'TaskSignList' })
const message = useMessage() // 消息弹窗 const message = useMessage() // 消息弹窗
const drawerVisible = ref(false) // 抽屉的是否展示 const drawerVisible = ref(false) // 抽屉的是否展示
const parentTask = ref({} as any)
const baseTask = ref<object>({})
/** 打开弹窗 */ /** 打开弹窗 */
const open = async (task: any) => { const open = async (task: any) => {
if (isEmpty(task.children)) { if (isEmpty(task.children)) {
message.warning('该任务没有子任务') message.warning('该任务没有子任务')
return return
} }
baseTask.value = task parentTask.value = task
// 展开抽屉 // 展开抽屉
drawerVisible.value = true drawerVisible.value = true
} }
defineExpose({ open }) // 提供 openModal 方法,用于打开弹窗 defineExpose({ open }) // 提供 openModal 方法,用于打开弹窗
/** 发起减签 */ /** 发起减签 */
const taskSubSignDialogForm = ref() const taskSignDeleteFormRef = ref()
const handleSubSign = (item) => { const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
taskSubSignDialogForm.value.open(item.id) const handleSignDelete = (item: any) => {
// TODO @海洋:减签后,需要刷新下界面哈 taskSignDeleteFormRef.value.open(item.id)
}
const handleSignDeleteSuccess = () => {
emit('success')
// 关闭抽屉
drawerVisible.value = false
} }
/** 是否显示减签按钮 */ /** 是否显示减签按钮 */
const isSubSignButtonVisible = (task: any) => { const isSignDeleteButtonVisible = (task: any) => {
if (task && task.children && !isEmpty(task.children)) { return task && task.children && !isEmpty(task.children)
// 有子任务,且子任务有任意一个是 待处理 和 待前置任务完成 则显示减签按钮
const subTask = task.children.find((item) => item.result === 1 || item.result === 9)
return !isEmpty(subTask)
}
return false
} }
</script> </script>
<template> <template>
<Dialog v-model="dialogVisible" title="转派审批人" width="500"> <Dialog v-model="dialogVisible" title="转派任务" width="500">
<el-form <el-form
ref="formRef" ref="formRef"
v-loading="formLoading" v-loading="formLoading"
...@@ -17,6 +17,9 @@ ...@@ -17,6 +17,9 @@
/> />
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="转派理由" prop="reason">
<el-input v-model="formData.reason" clearable placeholder="请输入转派理由" />
</el-form-item>
</el-form> </el-form>
<template #footer> <template #footer>
<el-button :disabled="formLoading" type="primary" @click="submitForm">确 定</el-button> <el-button :disabled="formLoading" type="primary" @click="submitForm">确 定</el-button>
...@@ -28,16 +31,18 @@ ...@@ -28,16 +31,18 @@
import * as TaskApi from '@/api/bpm/task' import * as TaskApi from '@/api/bpm/task'
import * as UserApi from '@/api/system/user' import * as UserApi from '@/api/system/user'
defineOptions({ name: 'BpmTaskUpdateAssigneeForm' }) defineOptions({ name: 'TaskTransferForm' })
const dialogVisible = ref(false) // 弹窗的是否展示 const dialogVisible = ref(false) // 弹窗的是否展示
const formLoading = ref(false) // 表单的加载中 const formLoading = ref(false) // 表单的加载中
const formData = ref({ const formData = ref({
id: '', id: '',
assigneeUserId: undefined assigneeUserId: undefined,
reason: ''
}) })
const formRules = ref({ const formRules = ref({
assigneeUserId: [{ required: true, message: '新审批人不能为空', trigger: 'change' }] assigneeUserId: [{ required: true, message: '新审批人不能为空', trigger: 'change' }],
reason: [{ required: true, message: '转派理由不能为空', trigger: 'blur' }]
}) })
const formRef = ref() // 表单 Ref const formRef = ref() // 表单 Ref
...@@ -63,7 +68,7 @@ const submitForm = async () => { ...@@ -63,7 +68,7 @@ const submitForm = async () => {
// 提交请求 // 提交请求
formLoading.value = true formLoading.value = true
try { try {
await TaskApi.updateTaskAssignee(formData.value) await TaskApi.transferTask(formData.value)
dialogVisible.value = false dialogVisible.value = false
// 发送操作成功的事件 // 发送操作成功的事件
emit('success') emit('success')
...@@ -76,7 +81,8 @@ const submitForm = async () => { ...@@ -76,7 +81,8 @@ const submitForm = async () => {
const resetForm = () => { const resetForm = () => {
formData.value = { formData.value = {
id: '', id: '',
assigneeUserId: undefined assigneeUserId: undefined,
reason: ''
} }
formRef.value?.resetFields() formRef.value?.resetFields()
} }
......
...@@ -36,15 +36,20 @@ ...@@ -36,15 +36,20 @@
class="!w-240px" class="!w-240px"
> >
<el-option <el-option
v-for="dict in getIntDictOptions(DICT_TYPE.BPM_MODEL_CATEGORY)" v-for="category in categoryList"
:key="dict.value" :key="category.code"
:label="dict.label" :label="category.name"
:value="dict.value" :value="category.code"
/> />
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="状态" prop="status"> <el-form-item label="流程状态" prop="status">
<el-select v-model="queryParams.status" placeholder="请选择状态" clearable class="!w-240px"> <el-select
v-model="queryParams.status"
placeholder="请选择流程状态"
clearable
class="!w-240px"
>
<el-option <el-option
v-for="dict in getIntDictOptions(DICT_TYPE.BPM_PROCESS_INSTANCE_STATUS)" v-for="dict in getIntDictOptions(DICT_TYPE.BPM_PROCESS_INSTANCE_STATUS)"
:key="dict.value" :key="dict.value"
...@@ -53,17 +58,7 @@ ...@@ -53,17 +58,7 @@
/> />
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="结果" prop="result"> <el-form-item label="发起时间" prop="createTime">
<el-select v-model="queryParams.result" placeholder="请选择结果" clearable class="!w-240px">
<el-option
v-for="dict in getIntDictOptions(DICT_TYPE.BPM_PROCESS_INSTANCE_RESULT)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
<el-form-item label="提交时间" 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"
...@@ -81,7 +76,7 @@ ...@@ -81,7 +76,7 @@
type="primary" type="primary"
plain plain
v-hasPermi="['bpm:process-instance:query']" v-hasPermi="['bpm:process-instance:query']"
@click="handleCreate" @click="handleCreate()"
> >
<Icon icon="ep:plus" class="mr-5px" /> 发起流程 <Icon icon="ep:plus" class="mr-5px" /> 发起流程
</el-button> </el-button>
...@@ -92,34 +87,23 @@ ...@@ -92,34 +87,23 @@
<!-- 列表 --> <!-- 列表 -->
<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="300px" /> <el-table-column label="流程名称" align="center" prop="name" min-width="200px" fixed="left" />
<el-table-column label="流程名称" align="center" prop="name" /> <el-table-column
<el-table-column label="流程分类" align="center" prop="category"> label="流程分类"
<template #default="scope"> align="center"
<dict-tag :type="DICT_TYPE.BPM_MODEL_CATEGORY" :value="scope.row.category" /> prop="categoryName"
</template> min-width="100"
</el-table-column> fixed="left"
<el-table-column label="当前审批任务" align="center" prop="tasks"> />
<template #default="scope"> <el-table-column label="流程状态" prop="status" width="120">
<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="状态" prop="status">
<template #default="scope"> <template #default="scope">
<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>
</el-table-column> </el-table-column>
<el-table-column label="结果" prop="result">
<template #default="scope">
<dict-tag :type="DICT_TYPE.BPM_PROCESS_INSTANCE_RESULT" :value="scope.row.result" />
</template>
</el-table-column>
<el-table-column <el-table-column
label="提交时间" label="发起时间"
align="center" align="center"
prop="createTime" prop="startTime"
width="180" width="180"
:formatter="dateFormatter" :formatter="dateFormatter"
/> />
...@@ -130,7 +114,20 @@ ...@@ -130,7 +114,20 @@
width="180" width="180"
:formatter="dateFormatter" :formatter="dateFormatter"
/> />
<el-table-column label="操作" align="center"> <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" prop="id" min-width="320px" />
<el-table-column label="操作" align="center" fixed="right" width="180">
<template #default="scope"> <template #default="scope">
<el-button <el-button
link link
...@@ -143,12 +140,15 @@ ...@@ -143,12 +140,15 @@
<el-button <el-button
link link
type="primary" type="primary"
v-if="scope.row.result === 1" v-if="scope.row.status === 1"
v-hasPermi="['bpm:process-instance:query']" v-hasPermi="['bpm:process-instance:query']"
@click="handleCancel(scope.row)" @click="handleCancel(scope.row)"
> >
取消 取消
</el-button> </el-button>
<el-button link type="primary" v-else @click="handleCreate(scope.row.id)">
重新发起
</el-button>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
...@@ -163,11 +163,12 @@ ...@@ -163,11 +163,12 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict' import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
import { dateFormatter } from '@/utils/formatTime' import { dateFormatter, formatPast2 } from '@/utils/formatTime'
import { ElMessageBox } from 'element-plus' import { ElMessageBox } from 'element-plus'
import * as ProcessInstanceApi from '@/api/bpm/processInstance' import * as ProcessInstanceApi from '@/api/bpm/processInstance'
import { CategoryApi } from '@/api/bpm/category'
defineOptions({ name: 'BpmProcessInstance' }) defineOptions({ name: 'BpmProcessInstanceMy' })
const router = useRouter() // 路由 const router = useRouter() // 路由
const message = useMessage() // 消息弹窗 const message = useMessage() // 消息弹窗
...@@ -183,16 +184,16 @@ const queryParams = reactive({ ...@@ -183,16 +184,16 @@ const queryParams = reactive({
processDefinitionId: undefined, processDefinitionId: undefined,
category: undefined, category: undefined,
status: undefined, status: undefined,
result: undefined,
createTime: [] createTime: []
}) })
const queryFormRef = ref() // 搜索的表单 const queryFormRef = ref() // 搜索的表单
const categoryList = ref([]) // 流程分类列表
/** 查询列表 */ /** 查询列表 */
const getList = async () => { const getList = async () => {
loading.value = true loading.value = true
try { try {
const data = await ProcessInstanceApi.getMyProcessInstancePage(queryParams) const data = await ProcessInstanceApi.getProcessInstanceMyPage(queryParams)
list.value = data.list list.value = data.list
total.value = data.total total.value = data.total
} finally { } finally {
...@@ -213,9 +214,10 @@ const resetQuery = () => { ...@@ -213,9 +214,10 @@ const resetQuery = () => {
} }
/** 发起流程操作 **/ /** 发起流程操作 **/
const handleCreate = () => { const handleCreate = (id) => {
router.push({ router.push({
name: 'BpmProcessInstanceCreate' name: 'BpmProcessInstanceCreate',
query: { processInstanceId: id }
}) })
} }
...@@ -239,14 +241,20 @@ const handleCancel = async (row) => { ...@@ -239,14 +241,20 @@ const handleCancel = async (row) => {
inputErrorMessage: '取消原因不能为空' inputErrorMessage: '取消原因不能为空'
}) })
// 发起取消 // 发起取消
await ProcessInstanceApi.cancelProcessInstance(row.id, value) await ProcessInstanceApi.cancelProcessInstanceByStartUser(row.id, value)
message.success('取消成功') message.success('取消成功')
// 刷新列表 // 刷新列表
await getList() await getList()
} }
/** 初始化 **/ /** 激活时 **/
onMounted(() => { onActivated(() => {
getList() getList()
}) })
/** 初始化 **/
onMounted(async () => {
await getList()
categoryList.value = await CategoryApi.getCategorySimpleList()
})
</script> </script>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment