Skip to content

Commit 92314ee

Browse files
committed
feat: 自定义选择审批人逻辑TODO,选择用户弹窗绘制与选择交互逻辑处理
1 parent 0fbd982 commit 92314ee

File tree

4 files changed

+228
-26
lines changed

4 files changed

+228
-26
lines changed
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
<template>
2+
<Dialog v-model="dialogVisible" title="人员选择" width="900">
3+
<el-row>
4+
<el-col :span="6">
5+
<el-tree
6+
ref="treeRef"
7+
:data="deptList"
8+
:expand-on-click-node="false"
9+
:props="defaultProps"
10+
default-expand-all
11+
highlight-current
12+
node-key="id"
13+
@node-click="handleNodeClick"
14+
/>
15+
</el-col>
16+
<el-col :span="17" :offset="1">
17+
<el-transfer
18+
v-model="selectedUserList"
19+
filterable
20+
filter-placeholder="搜索成员"
21+
:data="userList"
22+
:props="{ label: 'nickname', key: 'id' }"
23+
/>
24+
</el-col>
25+
</el-row>
26+
<template #footer>
27+
<el-button :disabled="formLoading" type="primary" @click="submitForm">确 定</el-button>
28+
<el-button @click="dialogVisible = false">取 消</el-button>
29+
</template>
30+
</Dialog>
31+
</template>
32+
<script lang="ts" setup>
33+
import { defaultProps, handleTree } from '@/utils/tree'
34+
import * as DeptApi from '@/api/system/dept'
35+
import * as UserApi from '@/api/system/user'
36+
37+
defineOptions({ name: 'UserSelectForm' })
38+
const emit = defineEmits<{
39+
confirm: [userList: any[]]
40+
}>()
41+
const { t } = useI18n() // 国际
42+
const deptList = ref<Tree[]>([]) // 部门树形结构化
43+
const userList: any = ref([]) // 用户列表
44+
const message = useMessage() // 消息弹窗
45+
const selectedUserList: any = ref([]) // 选中的用户列表
46+
const dialogVisible = ref(false) // 弹窗的是否展示
47+
const formLoading = ref(false) // 表单的加载中
48+
49+
/** 打开弹窗 */
50+
const open = async () => {
51+
resetForm()
52+
deptList.value = handleTree(await DeptApi.getSimpleDeptList())
53+
await getUserList()
54+
// 修改时,设置数据
55+
dialogVisible.value = true
56+
}
57+
58+
/* 获取用户列表 */
59+
const getUserList = async (deptId?) => {
60+
try {
61+
// @ts-ignore
62+
const data = await UserApi.getUserPage({ pageSize: 100, pageNo: 1, deptId })
63+
userList.value = data.list
64+
} finally {
65+
}
66+
}
67+
68+
const submitForm = async () => {
69+
// 提交请求
70+
formLoading.value = true
71+
try {
72+
message.success(t('common.updateSuccess'))
73+
dialogVisible.value = false
74+
// 发送操作成功的事件
75+
emit('confirm', selectedUserList.value)
76+
} finally {
77+
formLoading.value = false
78+
}
79+
}
80+
const resetForm = () => {
81+
deptList.value = []
82+
userList.value = []
83+
selectedUserList.value = []
84+
}
85+
86+
/** 处理部门被点击 */
87+
const handleNodeClick = async (row: { [key: string]: any }) => {
88+
getUserList(row.id)
89+
}
90+
91+
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
92+
</script>

src/views/bpm/processInstance/create/ProcessDefinitionDetail.vue

Lines changed: 26 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
:option="detailForm.option"
2121
@submit="submitForm"
2222
>
23-
<template #type-startUserSelect>
23+
<!-- <template #type-startUserSelect>
2424
<el-col :span="24">
2525
<el-card class="mb-10px">
2626
<template #header>指定审批人</template>
@@ -51,7 +51,7 @@
5151
</el-form>
5252
</el-card>
5353
</el-col>
54-
</template>
54+
</template> -->
5555
</form-create>
5656
</el-col>
5757

@@ -61,7 +61,7 @@
6161
ref="timelineRef"
6262
:activity-nodes="activityNodes"
6363
:show-status-icon="false"
64-
candidateField="candidateUserList"
64+
:startUserSelectTasks="startUserSelectTasks"
6565
/>
6666
</el-col>
6767
</el-row>
@@ -104,7 +104,8 @@ import type { ApiAttrs } from '@form-create/element-ui/types/config'
104104
import { useTagsViewStore } from '@/store/modules/tagsView'
105105
import * as ProcessInstanceApi from '@/api/bpm/processInstance'
106106
import * as DefinitionApi from '@/api/bpm/definition'
107-
import * as UserApi from '@/api/system/user'
107+
// import * as UserApi from '@/api/system/user'
108+
import { activityNodes as aN, startUserSelectTasks as sUs } from './mock'
108109
109110
defineOptions({ name: 'ProcessDefinitionDetail' })
110111
const props = defineProps<{
@@ -125,7 +126,7 @@ const startUserSelectAssigneesFormRef = ref() // 发起人选择审批人的表
125126
const startUserSelectTasks: any = ref([]) // 发起人需要选择审批人的用户任务列表
126127
const startUserSelectAssignees = ref({}) // 发起人选择审批人的数据
127128
const startUserSelectAssigneesFormRules = ref({}) // 发起人选择审批人的表单 Rules
128-
const userList = ref<any[]>([]) // 用户列表
129+
// const userList = ref<any[]>([]) // 用户列表
129130
const bpmnXML: any = ref(null) // BPMN 数据
130131
/** 当前的Tab */
131132
const activeTab = ref('form')
@@ -163,25 +164,25 @@ const initProcessInfo = async (row: any, formVariables?: any) => {
163164
if (processDefinitionDetail) {
164165
bpmnXML.value = processDefinitionDetail.bpmnXml
165166
startUserSelectTasks.value = processDefinitionDetail.startUserSelectTasks
166-
167+
startUserSelectTasks.value = sUs
167168
// 设置指定审批人
168-
if (startUserSelectTasks.value?.length > 0) {
169-
detailForm.value.rule.push({
170-
type: 'startUserSelect',
171-
props: {
172-
title: '指定审批人'
173-
}
174-
})
175-
// 设置校验规则
176-
for (const userTask of startUserSelectTasks.value) {
177-
startUserSelectAssignees.value[userTask.id] = []
178-
startUserSelectAssigneesFormRules.value[userTask.id] = [
179-
{ required: true, message: '请选择审批人', trigger: 'blur' }
180-
]
181-
}
182-
// 加载用户列表
183-
userList.value = await UserApi.getSimpleUserList()
184-
}
169+
// if (startUserSelectTasks.value?.length > 0) {
170+
// detailForm.value.rule.push({
171+
// type: 'startUserSelect',
172+
// props: {
173+
// title: '指定审批人'
174+
// }
175+
// })
176+
// // 设置校验规则
177+
// for (const userTask of startUserSelectTasks.value) {
178+
// startUserSelectAssignees.value[userTask.id] = []
179+
// startUserSelectAssigneesFormRules.value[userTask.id] = [
180+
// { required: true, message: '请选择审批人', trigger: 'blur' }
181+
// ]
182+
// }
183+
// // 加载用户列表
184+
// userList.value = await UserApi.getSimpleUserList()
185+
// }
185186
}
186187
// 情况二:业务表单
187188
} else if (row.formCustomCreatePath) {
@@ -205,6 +206,7 @@ const getApprovalDetail = async (row: any) => {
205206
}
206207
// 获取审批节点,显示 Timeline 的数据
207208
activityNodes.value = data.activityNodes
209+
activityNodes.value = aN
208210
} finally {
209211
}
210212
}
@@ -250,7 +252,7 @@ defineExpose({ initProcessInfo })
250252
$wrap-padding-height: 20px;
251253
$wrap-margin-height: 15px;
252254
$button-height: 51px;
253-
$process-header-height: 194px;
255+
$process-header-height: 105px;
254256
255257
.processInstance-wrap-main {
256258
height: calc(
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
import { ProcessInstanceVO, User, ApprovalTaskInfo, ApprovalNodeInfo } from '@/api/bpm/processInstance';
2+
import { NodeType } from '@/components/SimpleProcessDesignerV2/src/consts'
3+
4+
const users: User[] = [
5+
{ id: 1, nickname: 'Alice', avatar: 'https://picsum.photos/200?r=1' },
6+
{ id: 2, nickname: 'Bob', avatar: 'https://picsum.photos/200?r=2' },
7+
{ id: 3, nickname: 'Charlie', avatar: 'https://picsum.photos/200?r=3' },
8+
{ id: 4, nickname: 'David', avatar: 'https://picsum.photos/200?r=4' }
9+
];
10+
11+
const approvalTask1: ApprovalTaskInfo = {
12+
id: 1,
13+
ownerUser: users[0], // Alice is the owner (initiator)
14+
assigneeUser: users[1], // Bob is the assignee
15+
status: 1, // In Progress
16+
reason: 'Please review and approve the request.'
17+
};
18+
19+
const approvalTask2: ApprovalTaskInfo = {
20+
id: 2,
21+
ownerUser: users[1], // Bob is the owner (approver)
22+
assigneeUser: users[2], // Charlie is the assignee
23+
status: 0, // Pending approval
24+
reason: 'Awaiting Bob’s decision.'
25+
};
26+
27+
const approvalTask3: ApprovalTaskInfo = {
28+
id: 3,
29+
ownerUser: users[2], // Charlie is the owner (approver)
30+
assigneeUser: users[3], // David is the assignee
31+
status: 0, // Pending approval
32+
reason: 'Awaiting Charlie’s decision.'
33+
};
34+
35+
const approvalNode1: ApprovalNodeInfo = {
36+
id: 101,
37+
name: 'Start Review',
38+
nodeType: NodeType.START_USER_NODE,
39+
status: 1, // In Progress
40+
startTime: new Date('2024-11-01T10:00:00Z'),
41+
tasks: [approvalTask1]
42+
};
43+
44+
const approvalNode2: ApprovalNodeInfo = {
45+
id: 102,
46+
name: 'First Review',
47+
nodeType: NodeType.USER_TASK_NODE,
48+
status: 0, // Pending approval
49+
startTime: new Date('2024-11-01T11:00:00Z'),
50+
tasks: [approvalTask2],
51+
candidateUsers: [users[2], users[3]] // Candidate users: Charlie and David
52+
};
53+
54+
const approvalNode3: ApprovalNodeInfo = {
55+
id: 103,
56+
name: 'Second Review',
57+
nodeType: NodeType.USER_TASK_NODE,
58+
status: 0, // Pending approval
59+
startTime: new Date('2024-11-01T12:00:00Z'),
60+
tasks: [approvalTask3],
61+
candidateUsers: [users[1], users[3]] // Candidate users: Bob and David
62+
};
63+
64+
const processInstance: ProcessInstanceVO = {
65+
id: 1001,
66+
name: 'Request Approval Process',
67+
processDefinitionId: 'proc-2024-001',
68+
category: 'Approval Process',
69+
result: 0, // Ongoing
70+
tasks: [{ id: '1', name: 'Start Review' }, { id: '2', name: 'First Review' }, { id: '3', name: 'Second Review' }],
71+
fields: ['field1', 'field2'],
72+
status: 1, // In Progress
73+
remark: 'This is a sample approval process.',
74+
businessKey: 'BUS-12345',
75+
createTime: '2024-11-01T09:00:00Z',
76+
endTime: '',
77+
processDefinition: undefined // Not populated in this example
78+
};
79+
80+
// 模拟的 activityNodes 数据,传递给 ProcessInstanceTimeline 组件
81+
const activityNodes: ApprovalNodeInfo[] = [approvalNode1, approvalNode2, approvalNode3];
82+
83+
export { processInstance, activityNodes, users };
84+
85+
86+
export const startUserSelectTasks = users.map(user => ({id: user.id,name:user.nickname}))

src/views/bpm/processInstance/detail/ProcessInstanceTimeline.vue

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,15 @@
3636
{{ getApprovalNodeTime(activity) }}
3737
</div>
3838
</div>
39-
<div class="flex items-center flex-wrap mt-1 gap2">
39+
<!-- 需要自定义选择审批人 -->
40+
<div
41+
v-if="startUserSelectTasks?.length > 0 && activity.nodeType === NodeType.USER_TASK_NODE"
42+
>
43+
<el-button class="!px-8px" @click="handleSelectUser">
44+
<Icon icon="fa:user-plus" />
45+
</el-button>
46+
</div>
47+
<div v-else class="flex items-center flex-wrap mt-1 gap2">
4048
<!-- 情况一:遍历每个审批节点下的【进行中】task 任务 -->
4149
<div v-for="(task, idx) in activity.tasks" :key="idx" class="flex flex-col pr-2 gap2">
4250
<div
@@ -121,6 +129,8 @@
121129
</div>
122130
</el-timeline-item>
123131
</el-timeline>
132+
<!-- 用户选择弹窗 -->
133+
<UserSelectForm ref="userSelectFormRef" @confirm="handleUserSelectConfirm" />
124134
</template>
125135

126136
<script lang="ts" setup>
@@ -141,9 +151,11 @@ withDefaults(
141151
defineProps<{
142152
activityNodes: ProcessInstanceApi.ApprovalNodeInfo[] // 审批节点信息
143153
showStatusIcon?: boolean // 是否显示头像右下角状态图标
154+
startUserSelectTasks?: any[] // 发起人需要选择审批人的用户任务列表
144155
}>(),
145156
{
146-
showStatusIcon: true // 默认值为 true
157+
showStatusIcon: true, // 默认值为 true
158+
startUserSelectTasks: () => [] // 默认值为空数组
147159
}
148160
)
149161
@@ -241,4 +253,14 @@ const getApprovalNodeTime = (node: ProcessInstanceApi.ApprovalNodeInfo) => {
241253
return `${formatDate(node.startTime)}`
242254
}
243255
}
256+
257+
// 选择自定义审批人
258+
const userSelectFormRef = ref()
259+
const handleSelectUser = () => {
260+
userSelectFormRef.value.open()
261+
}
262+
// 选择完成
263+
const handleUserSelectConfirm = (userList) => {
264+
console.log('[ userList ] >', userList)
265+
}
244266
</script>

0 commit comments

Comments
 (0)