Skip to content

Commit 3783582

Browse files
authored
Merge pull request #102 from GoldenZqqq/feature/bpm
工作流发起与审核页面具体细节优化
2 parents 52a8c46 + 2719541 commit 3783582

File tree

11 files changed

+158
-81
lines changed

11 files changed

+158
-81
lines changed

src/assets/svgs/bpm/add-user.svg

Lines changed: 1 addition & 0 deletions
Loading
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Lines changed: 76 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,27 @@
11
<template>
2-
<Dialog v-model="dialogVisible" title="人员选择" width="900">
3-
<el-row>
2+
<Dialog v-model="dialogVisible" title="人员选择" width="800">
3+
<el-row class="gap2" v-loading="formLoading">
44
<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-
/>
5+
<ContentWrap class="h-1/1">
6+
<el-tree
7+
ref="treeRef"
8+
:data="deptList"
9+
:expand-on-click-node="false"
10+
:props="defaultProps"
11+
default-expand-all
12+
highlight-current
13+
node-key="id"
14+
@node-click="handleNodeClick"
15+
/>
16+
</ContentWrap>
1517
</el-col>
16-
<el-col :span="17" :offset="1">
18+
<el-col :span="17">
1719
<el-transfer
1820
v-model="selectedUserIdList"
1921
:titles="['未选', '已选']"
2022
filterable
2123
filter-placeholder="搜索成员"
22-
:data="userList"
24+
:data="transferUserList"
2325
:props="{ label: 'nickname', key: 'id' }"
2426
/>
2527
</el-col>
@@ -47,62 +49,87 @@ const emit = defineEmits<{
4749
}>()
4850
const { t } = useI18n() // 国际
4951
const message = useMessage() // 消息弹窗
50-
5152
const deptList = ref<Tree[]>([]) // 部门树形结构化
52-
const userList: any = ref([]) // 用户列表
53+
const allUserList = ref<UserApi.UserVO[]>([]) // 所有用户列表
54+
const filteredUserList = ref<UserApi.UserVO[]>([]) // 当前部门过滤后的用户列表
5355
const selectedUserIdList: any = ref([]) // 选中的用户列表
5456
const dialogVisible = ref(false) // 弹窗的是否展示
5557
const formLoading = ref(false) // 表单的加载中
56-
const activityId = ref() // 关联的主键编号 TODO @goldenzqqq:这个 activityId 有没可能不传递。在使用 @submitForm="xxx()" 时,传递的参数。目的是,更加解耦一些。
58+
const activityId = ref()
59+
60+
// 计算属性:合并已选择的用户和当前部门过滤后的用户
61+
const transferUserList = computed(() => {
62+
// 获取所有已选择的用户
63+
const selectedUsers = allUserList.value.filter((user: any) =>
64+
selectedUserIdList.value.includes(user.id)
65+
)
66+
67+
// 获取当前部门过滤后的未选择用户
68+
const filteredUnselectedUsers = filteredUserList.value.filter(
69+
(user: any) => !selectedUserIdList.value.includes(user.id)
70+
)
71+
72+
// 合并并去重
73+
return [...selectedUsers, ...filteredUnselectedUsers]
74+
})
5775
5876
/** 打开弹窗 */
5977
const open = async (id: number, selectedList?: any[]) => {
6078
activityId.value = id
61-
// 重置表单
6279
resetForm()
6380
64-
// 加载相关数据
6581
deptList.value = handleTree(await DeptApi.getSimpleDeptList())
66-
await getUserList()
67-
// 设置选中的用户列表
68-
selectedUserIdList.value = selectedList?.map((item: any) => item.id)
69-
70-
// 设置可见
82+
// 初始加载所有用户
83+
await getAllUserList()
84+
// 初始状态下,过滤列表等于所有用户列表
85+
filteredUserList.value = [...allUserList.value]
86+
selectedUserIdList.value = selectedList?.map((item: any) => item.id) || []
7187
dialogVisible.value = true
7288
}
89+
/** 获取所有用户列表 */
90+
const getAllUserList = async () => {
91+
try {
92+
// @ts-ignore
93+
const data = await UserApi.getSimpleUserList()
94+
allUserList.value = data
95+
} finally {
96+
}
97+
}
7398
74-
/** 获取用户列表 */
99+
/** 获取部门过滤后的用户列表 */
75100
const getUserList = async (deptId?: number) => {
101+
formLoading.value = true
76102
try {
77103
// @ts-ignore
78-
// TODO @芋艿:替换到 simple List
104+
// TODO @芋艿:替换到 simple List 暂不支持 deptId 过滤
79105
const data = await UserApi.getUserPage({ pageSize: 100, pageNo: 1, deptId })
80-
userList.value = data.list
106+
// 更新过滤后的用户列表
107+
filteredUserList.value = data.list
81108
} finally {
109+
formLoading.value = false
82110
}
83111
}
84112
85113
/** 提交选择 */
86114
const submitForm = async () => {
87-
// 提交请求
88-
formLoading.value = true
89115
try {
90116
message.success(t('common.updateSuccess'))
91117
dialogVisible.value = false
92-
const emitUserList = userList.value.filter((user: any) =>
118+
// 从所有用户列表中筛选出已选择的用户
119+
const emitUserList = allUserList.value.filter((user: any) =>
93120
selectedUserIdList.value.includes(user.id)
94121
)
95122
// 发送操作成功的事件
96123
emit('confirm', activityId.value, emitUserList)
97124
} finally {
98-
formLoading.value = false
99125
}
100126
}
101127
102128
/** 重置表单 */
103129
const resetForm = () => {
104130
deptList.value = []
105-
userList.value = []
131+
allUserList.value = []
132+
filteredUserList.value = []
106133
selectedUserIdList.value = []
107134
}
108135
@@ -113,3 +140,20 @@ const handleNodeClick = (row: { [key: string]: any }) => {
113140
114141
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
115142
</script>
143+
144+
<style lang="scss" scoped>
145+
:deep() {
146+
.el-transfer {
147+
display: flex;
148+
}
149+
.el-transfer__buttons {
150+
display: flex !important;
151+
flex-direction: column-reverse;
152+
justify-content: center;
153+
gap: 20px;
154+
.el-transfer__button:nth-child(2) {
155+
margin: 0;
156+
}
157+
}
158+
}
159+
</style>

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

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141
:ref="`category-${categoryCode}`"
4242
>
4343
<h3 class="text-18px font-bold mb-10px mt-5px">
44-
{{ getCategoryName(categoryCode) }}
44+
{{ getCategoryName(categoryCode as any) }}
4545
</h3>
4646
<div class="grid grid-cols-3 gap3">
4747
<el-tooltip
@@ -175,7 +175,17 @@ const handleQuery = () => {
175175
/** 流程定义的分组 */
176176
const processDefinitionGroup: any = computed(() => {
177177
if (!processDefinitionList.value?.length) return {}
178-
return groupBy(filteredProcessDefinitionList.value, 'category')
178+
const grouped = groupBy(filteredProcessDefinitionList.value, 'category')
179+
180+
const orderedGroup = {}
181+
// 按照 categoryList 的顺序重新组织数据
182+
categoryList.value.forEach((category: any) => {
183+
if (grouped[category.code]) {
184+
orderedGroup[category.code] = grouped[category.code]
185+
}
186+
})
187+
188+
return orderedGroup
179189
})
180190
181191
/** 左侧分类切换 */

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

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<template>
22
<el-card v-loading="loading" class="box-card">
3-
<MyProcessViewer key="designer" :xml="view.bpmnXml" :view="view" class="h-700px" />
3+
<MyProcessViewer key="designer" :xml="view.bpmnXml" :view="view" class="process-viewer" />
44
</el-card>
55
</template>
66
<script lang="ts" setup>
@@ -45,4 +45,9 @@ watch(
4545
width: 100%;
4646
margin-bottom: 20px;
4747
}
48+
49+
:deep(.process-viewer) {
50+
height: 100% !important;
51+
min-height: 500px;
52+
}
4853
</style>

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
:flow-node="simpleModel"
55
:tasks="tasks"
66
:process-instance="processInstance"
7+
class="process-viewer"
78
/>
89
</div>
910
</template>
@@ -151,3 +152,10 @@ const setSimpleModelNodeTaskStatus = (
151152
)
152153
}
153154
</script>
155+
156+
<style lang="scss" scoped>
157+
:deep(.process-viewer) {
158+
height: 100% !important;
159+
min-height: 500px;
160+
}
161+
</style>

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

Lines changed: 35 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,10 @@
1616
<img class="w-full h-full" :src="getApprovalNodeImg(activity.nodeType)" alt="" />
1717
<div
1818
v-if="showStatusIcon"
19-
class="position-absolute top-17px left-17px bg-#fff rounded-full flex items-center p-2px"
19+
class="position-absolute top-17px left-17px rounded-full flex items-center p-2px"
20+
:style="{ backgroundColor: getApprovalNodeColor(activity.status) }"
2021
>
21-
<el-icon :size="12" :color="getApprovalNodeColor(activity.status)">
22+
<el-icon :size="12" color="#fff">
2223
<component :is="getApprovalNodeIcon(activity.status, activity.nodeType)" />
2324
</el-icon>
2425
</div>
@@ -46,19 +47,22 @@
4647
"
4748
>
4849
<!-- && activity.nodeType === NodeType.USER_TASK_NODE -->
49-
<el-button
50-
class="!px-8px"
51-
@click="handleSelectUser(activity.id, customApproveUsers[activity.id])"
52-
>
53-
<Icon icon="fa:user-plus" />
54-
</el-button>
50+
51+
<el-tooltip content="添加用户" placement="left">
52+
<el-button
53+
class="!px-6px"
54+
@click="handleSelectUser(activity.id, customApproveUsers[activity.id])"
55+
>
56+
<img class="w-18px text-#ccc" src="@/assets/svgs/bpm/add-user.svg" alt="" />
57+
</el-button>
58+
</el-tooltip>
5559
<div
5660
v-for="(user, idx1) in customApproveUsers[activity.id]"
5761
:key="idx1"
58-
class="bg-gray-100 h-35px rounded-3xl flex items-center p-8px gap-2 dark:color-gray-600 position-relative"
62+
class="bg-gray-100 h-35px rounded-3xl flex items-center pr-8px dark:color-gray-600 position-relative"
5963
>
60-
<el-avatar :size="28" v-if="user.avatar" :src="user.avatar" />
61-
<el-avatar :size="28" v-else>
64+
<el-avatar class="!m-5px" :size="28" v-if="user.avatar" :src="user.avatar" />
65+
<el-avatar class="!m-5px" :size="28" v-else>
6266
{{ user.nickname.substring(0, 1) }}
6367
</el-avatar>
6468
{{ user.nickname }}
@@ -73,40 +77,39 @@
7377
>
7478
<!-- 信息:头像昵称 -->
7579
<div
76-
class="bg-gray-100 h-35px rounded-3xl flex items-center p-8px gap-2 dark:color-gray-600 position-relative"
80+
class="bg-gray-100 h-35px rounded-3xl flex items-center pr-8px dark:color-gray-600 position-relative"
7781
>
7882
<template v-if="task.assigneeUser?.avatar || task.assigneeUser?.nickname">
7983
<el-avatar
84+
class="!m-5px"
8085
:size="28"
8186
v-if="task.assigneeUser?.avatar"
8287
:src="task.assigneeUser?.avatar"
8388
/>
84-
<el-avatar :size="28" v-else>
89+
<el-avatar class="!m-5px" :size="28" v-else>
8590
{{ task.assigneeUser?.nickname.substring(0, 1) }}
8691
</el-avatar>
8792
{{ task.assigneeUser?.nickname }}
8893
</template>
8994
<template v-else-if="task.ownerUser?.avatar || task.ownerUser?.nickname">
9095
<el-avatar
96+
class="!m-5px"
9197
:size="28"
9298
v-if="task.ownerUser?.avatar"
9399
:src="task.ownerUser?.avatar"
94100
/>
95-
<el-avatar :size="28" v-else>
101+
<el-avatar class="!m-5px" :size="28" v-else>
96102
{{ task.ownerUser?.nickname.substring(0, 1) }}
97103
</el-avatar>
98104
{{ task.ownerUser?.nickname }}
99105
</template>
100106
<!-- 信息:任务 ICON -->
101107
<div
102-
v-if="onlyStatusIconShow.includes(task.status)"
103-
class="position-absolute top-22px left-26px bg-#fff rounded-full flex items-center p-2px"
108+
v-if="showStatusIcon && onlyStatusIconShow.includes(task.status)"
109+
class="position-absolute top-19px left-23px rounded-full flex items-center p-2px"
110+
:style="{ backgroundColor: statusIconMap2[task.status]?.color }"
104111
>
105-
<Icon
106-
:size="12"
107-
:icon="statusIconMap2[task.status]?.icon"
108-
:color="statusIconMap2[task.status]?.color"
109-
/>
112+
<Icon :size="12" :icon="statusIconMap2[task.status]?.icon" color="#FFFFFF" />
110113
</div>
111114
</div>
112115
</div>
@@ -126,23 +129,21 @@
126129
<div
127130
v-for="(user, idx1) in activity.candidateUsers"
128131
:key="idx1"
129-
class="bg-gray-100 h-35px rounded-3xl flex items-center p-8px gap-2 dark:color-gray-600 position-relative"
132+
class="bg-gray-100 h-35px rounded-3xl flex items-center pr-8px dark:color-gray-600 position-relative"
130133
>
131-
<el-avatar :size="28" v-if="user.avatar" :src="user.avatar" />
132-
<el-avatar :size="28" v-else>
134+
<el-avatar class="!m-5px" :size="28" v-if="user.avatar" :src="user.avatar" />
135+
<el-avatar class="!m-5px" :size="28" v-else>
133136
{{ user.nickname.substring(0, 1) }}
134137
</el-avatar>
135138
{{ user.nickname }}
136139

137140
<!-- 信息:任务 ICON -->
138141
<div
139-
class="position-absolute top-22px left-26px bg-#fff rounded-full flex items-center p-2px"
142+
v-if="showStatusIcon"
143+
class="position-absolute top-19px left-23px rounded-full flex items-center p-2px"
144+
:style="{ backgroundColor: statusIconMap2['-1']?.color }"
140145
>
141-
<Icon
142-
:size="12"
143-
:icon="statusIconMap2['-1']?.icon"
144-
:color="statusIconMap2['-1']?.color"
145-
/>
146+
<Icon :size="12" :icon="statusIconMap2['-1']?.icon" color="#FFFFFF" />
146147
</div>
147148
</div>
148149
</div>
@@ -184,7 +185,7 @@ const statusIconMap2 = {
184185
// 未开始
185186
'-1': { color: '#909398', icon: 'ep-clock' },
186187
// 待审批
187-
'0': { color: '#e5e7ec', icon: 'ep:loading' },
188+
'0': { color: '#00b32a', icon: 'ep:loading' },
188189
// 审批中
189190
'1': { color: '#448ef7', icon: 'ep:loading' },
190191
// 审批通过
@@ -204,7 +205,7 @@ const statusIconMap2 = {
204205
const statusIconMap = {
205206
// 审批未开始
206207
'-1': { color: '#909398', icon: Clock },
207-
'0': { color: '#e5e7ec', icon: Clock },
208+
'0': { color: '#00b32a', icon: Clock },
208209
// 审批中
209210
'1': { color: '#448ef7', icon: Loading },
210211
// 审批通过
@@ -223,9 +224,9 @@ const statusIconMap = {
223224
224225
const nodeTypeSvgMap = {
225226
// 结束节点
226-
[NodeType.END_EVENT_NODE]: { color: '#ffffff', svg: finishSvg },
227+
[NodeType.END_EVENT_NODE]: { color: '#909398', svg: finishSvg },
227228
// 发起人节点
228-
[NodeType.START_USER_NODE]: { color: '#ffffff', svg: starterSvg },
229+
[NodeType.START_USER_NODE]: { color: '#909398', svg: starterSvg },
229230
// 审批人节点
230231
[NodeType.USER_TASK_NODE]: { color: '#ff943e', svg: auditorSvg },
231232
// 抄送人节点

0 commit comments

Comments
 (0)