Skip to content

Commit 0242c84

Browse files
YunaiVgitee-org
authored andcommitted
!267 【工作流】加签和减签
Merge pull request !267 from Youkehai/feature/addSignAndSubSign
2 parents 8c18ad9 + 62a9538 commit 0242c84

File tree

8 files changed

+380
-17
lines changed

8 files changed

+380
-17
lines changed

src/api/bpm/task/index.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,3 +58,24 @@ export const returnTask = async (data) => {
5858
export const delegateTask = async (data) => {
5959
return await request.put({ url: '/bpm/task/delegate', data })
6060
}
61+
62+
/**
63+
* 加签
64+
*/
65+
export const taskAddSign = async (data) => {
66+
return await request.put({ url: '/bpm/task/add-sign', data })
67+
}
68+
69+
/**
70+
* 获取减签任务列表
71+
*/
72+
export const getChildrenTaskList = async (id: string) => {
73+
return await request.get({ url: '/bpm/task/get-children-task-list?taskId=' + id })
74+
}
75+
76+
/**
77+
* 减签
78+
*/
79+
export const taskSubSign = async (data) => {
80+
return await request.put({ url: '/bpm/task/sub-sign', data })
81+
}

src/components/bpmnProcessDesigner/package/designer/ProcessViewer.vue

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,12 @@ const getResultCss = (result) => {
250250
} else if (result === 5) {
251251
// 退回
252252
return 'highlight-return'
253+
} else if (result === 6) {
254+
// 委派
255+
return 'highlight-return'
256+
} else if (result === 7 || result === 8 || result === 9) {
257+
// 待后加签任务完成/待前加签任务完成/待前置任务完成
258+
return 'highlight-return'
253259
}
254260
return ''
255261
}
@@ -362,7 +368,7 @@ const elementHover = (element) => {
362368
}
363369
}
364370
console.log(html, 'html111111111111111')
365-
elementOverlayIds.value[element.value.id] = toRaw(overlays.value).add(element.value, {
371+
elementOverlayIds.value[element.value.id] = toRaw(overlays.value)?.add(element.value, {
366372
position: { left: 0, bottom: 0 },
367373
html: `<div class="element-overlays">${html}</div>`
368374
})

src/utils/is.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ export const isObject = (val: any): val is Record<any, any> => {
1919
}
2020

2121
export const isEmpty = <T = unknown>(val: T): val is T => {
22+
if (val === null) {
23+
return true
24+
}
2225
if (isArray(val) || isString(val)) {
2326
return val.length === 0
2427
}
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
<template>
2+
<el-drawer v-model="drawerVisible" title="子任务" size="70%">
3+
<template #header>
4+
<h4>【{{ baseTask.name }} 】审批人:{{ baseTask.assigneeUser?.nickname }}</h4>
5+
<el-button style="margin-left: 5px" v-if="showSubSignButton(baseTask)" type="danger" plain @click="handleSubSign(baseTask)">
6+
<Icon icon="ep:remove" />
7+
减签
8+
</el-button>
9+
</template>
10+
<el-table :data="tableData" style="width: 100%" row-key="id" border>
11+
<el-table-column prop="assigneeUser.nickname" label="审批人" />
12+
<el-table-column prop="assigneeUser.deptName" label="所在部门" />
13+
<el-table-column label="审批状态" prop="result">
14+
<template #default="scope">
15+
<dict-tag :type="DICT_TYPE.BPM_PROCESS_INSTANCE_RESULT" :value="scope.row.result" />
16+
</template>
17+
</el-table-column>
18+
<el-table-column
19+
label="提交时间"
20+
align="center"
21+
prop="createTime"
22+
width="180"
23+
:formatter="dateFormatter"
24+
/>
25+
<el-table-column
26+
label="结束时间"
27+
align="center"
28+
prop="endTime"
29+
width="180"
30+
:formatter="dateFormatter"
31+
/>
32+
<el-table-column label="操作" prop="operation">
33+
<template #default="scope">
34+
<el-button
35+
v-if="showSubSignButton(scope.row)"
36+
type="danger"
37+
plain
38+
@click="handleSubSign(scope.row)"
39+
>
40+
<Icon icon="ep:remove" />
41+
减签
42+
</el-button>
43+
</template>
44+
</el-table-column>
45+
</el-table>
46+
<!-- 减签 -->
47+
<TaskSubSignDialogForm ref="taskSubSignDialogForm" />
48+
</el-drawer>
49+
</template>
50+
<script lang="ts" setup>
51+
import { isEmpty } from '@/utils/is'
52+
import { DICT_TYPE } from '@/utils/dict'
53+
import { dateFormatter } from '@/utils/formatTime'
54+
import TaskSubSignDialogForm from './TaskSubSignDialogForm.vue'
55+
56+
const message = useMessage() // 消息弹窗
57+
defineOptions({ name: 'ProcessInstancechildrenList' })
58+
59+
const drawerVisible = ref(false) // 抽屉的是否展示
60+
61+
const tableData = ref<any[]>([]) //表格数据
62+
const baseTask = ref<object>({})
63+
/** 打开弹窗 */
64+
const open = async (task: any) => {
65+
if (isEmpty(task.children)) {
66+
message.warning('该任务没有子任务')
67+
return
68+
}
69+
baseTask.value = task
70+
//设置表格数据
71+
tableData.value = task.children
72+
//展开抽屉
73+
drawerVisible.value = true
74+
}
75+
defineExpose({ open }) // 提供 openModal 方法,用于打开弹窗
76+
77+
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
78+
79+
/**
80+
* 减签
81+
*/
82+
const taskSubSignDialogForm = ref()
83+
const handleSubSign = (item) => {
84+
taskSubSignDialogForm.value.open(item.id)
85+
}
86+
87+
/**
88+
* 显示减签按钮
89+
* @param task
90+
*/
91+
const showSubSignButton = (task:any) => {
92+
if(!isEmpty(task.children)){
93+
//有子任务,且子任务有任意一个是 待处理 和 待前置任务完成 则显示减签按钮
94+
const subTask = task.children.find((item) => item.result === 1 || item.result === 9)
95+
return !isEmpty(subTask)
96+
}
97+
return false
98+
}
99+
</script>

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

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,18 @@
1212
:icon="getTimelineItemIcon(item)"
1313
:type="getTimelineItemType(item)"
1414
>
15-
<p style="font-weight: 700">任务:{{ item.name }}</p>
15+
<p style="font-weight: 700">
16+
任务:{{ item.name }}
17+
<dict-tag :type="DICT_TYPE.BPM_PROCESS_INSTANCE_RESULT" :value="item.result" />
18+
<el-button
19+
style="margin-left: 5px"
20+
v-if="!isEmpty(item.children)"
21+
@click="openChildrenTask(item)"
22+
>
23+
<Icon icon="ep:memo" />
24+
子任务
25+
</el-button>
26+
</p>
1627
<el-card :body-style="{ padding: '10px' }">
1728
<label v-if="item.assigneeUser" style="margin-right: 30px; font-weight: normal">
1829
审批人:{{ item.assigneeUser.nickname }}
@@ -42,11 +53,16 @@
4253
</el-timeline>
4354
</div>
4455
</el-col>
56+
<!-- 子任务 -->
57+
<ProcessInstanceChildrenTaskList ref="processInstanceChildrenTaskList" />
4558
</el-card>
4659
</template>
4760
<script lang="ts" setup>
4861
import { formatDate, formatPast2 } from '@/utils/formatTime'
4962
import { propTypes } from '@/utils/propTypes'
63+
import { DICT_TYPE } from '@/utils/dict'
64+
import { isEmpty } from '@/utils/is'
65+
import ProcessInstanceChildrenTaskList from './ProcessInstanceChildrenTaskList.vue'
5066
5167
defineOptions({ name: 'BpmProcessInstanceTaskList' })
5268
@@ -95,6 +111,18 @@ const getTimelineItemType = (item) => {
95111
if (item.result === 6) {
96112
return 'default'
97113
}
114+
if (item.result === 7 || item.result === 8) {
115+
return 'warning'
116+
}
98117
return ''
99118
}
119+
120+
/**
121+
* 子任务
122+
*/
123+
const processInstanceChildrenTaskList = ref()
124+
125+
const openChildrenTask = (item) => {
126+
processInstanceChildrenTaskList.value.open(item)
127+
}
100128
</script>
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
<template>
2+
<Dialog v-model="dialogVisible" title="加签" width="500">
3+
<el-form
4+
ref="formRef"
5+
v-loading="formLoading"
6+
:model="formData"
7+
:rules="formRules"
8+
label-width="110px"
9+
>
10+
<el-form-item label="加签处理人" prop="userIdList">
11+
<el-select v-model="formData.userIdList" multiple clearable style="width: 100%">
12+
<el-option
13+
v-for="item in userList"
14+
:key="item.id"
15+
:label="item.nickname"
16+
:value="item.id"
17+
/>
18+
</el-select>
19+
</el-form-item>
20+
<el-form-item label="加签理由" prop="reason">
21+
<el-input v-model="formData.reason" clearable placeholder="请输入加签理由" />
22+
</el-form-item>
23+
</el-form>
24+
<template #footer>
25+
<el-button :disabled="formLoading" type="primary" @click="submitForm('before')"
26+
>向前加签</el-button
27+
>
28+
<el-button :disabled="formLoading" type="primary" @click="submitForm('after')"
29+
>向后加签</el-button
30+
>
31+
<el-button @click="dialogVisible = false">取 消</el-button>
32+
</template>
33+
</Dialog>
34+
</template>
35+
<script lang="ts" setup>
36+
import * as TaskApi from '@/api/bpm/task'
37+
import * as UserApi from '@/api/system/user'
38+
39+
const message = useMessage() // 消息弹窗
40+
defineOptions({ name: 'BpmTaskUpdateAssigneeForm' })
41+
42+
const dialogVisible = ref(false) // 弹窗的是否展示
43+
const formLoading = ref(false) // 表单的加载中
44+
const formData = ref({
45+
id: '',
46+
userIdList: [],
47+
type: ''
48+
})
49+
const formRules = ref({
50+
userIdList: [{ required: true, message: '加签处理人不能为空', trigger: 'change' }],
51+
reason: [{ required: true, message: '加签理由不能为空', trigger: 'change' }]
52+
})
53+
54+
const formRef = ref() // 表单 Ref
55+
const userList = ref<any[]>([]) // 用户列表
56+
57+
/** 打开弹窗 */
58+
const open = async (id: string) => {
59+
dialogVisible.value = true
60+
resetForm()
61+
formData.value.id = id
62+
// 获得用户列表
63+
userList.value = await UserApi.getSimpleUserList()
64+
}
65+
defineExpose({ open }) // 提供 openModal 方法,用于打开弹窗
66+
67+
/** 提交表单 */
68+
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
69+
const submitForm = async (type: string) => {
70+
// 校验表单
71+
if (!formRef) return
72+
const valid = await formRef.value.validate()
73+
if (!valid) return
74+
// 提交请求
75+
formLoading.value = true
76+
formData.value.type = type
77+
try {
78+
await TaskApi.taskAddSign(formData.value)
79+
message.success('加签成功')
80+
dialogVisible.value = false
81+
// 发送操作成功的事件
82+
emit('success')
83+
} finally {
84+
formLoading.value = false
85+
}
86+
}
87+
88+
/** 重置表单 */
89+
const resetForm = () => {
90+
formData.value = {
91+
id: '',
92+
userIdList: [],
93+
type: ''
94+
}
95+
formRef.value?.resetFields()
96+
}
97+
</script>
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
<template>
2+
<Dialog v-model="dialogVisible" title="减签" width="500">
3+
<el-form
4+
ref="formRef"
5+
v-loading="formLoading"
6+
:model="formData"
7+
:rules="formRules"
8+
label-width="110px"
9+
>
10+
<el-form-item label="减签任务" prop="id">
11+
<el-radio-group v-model="formData.id">
12+
<el-radio-button v-for="item in subTaskList" :key="item.id" :label="item.id">
13+
{{ item.name }}({{ item.assigneeUser.deptName }}{{ item.assigneeUser.nickname }}--审批)
14+
</el-radio-button>
15+
</el-radio-group>
16+
</el-form-item>
17+
<el-form-item label="减签理由" prop="reason">
18+
<el-input v-model="formData.reason" clearable placeholder="请输入减签理由" />
19+
</el-form-item>
20+
</el-form>
21+
<template #footer>
22+
<el-button :disabled="formLoading" type="primary" @click="submitForm">确 定</el-button>
23+
<el-button @click="dialogVisible = false">取 消</el-button>
24+
</template>
25+
</Dialog>
26+
</template>
27+
<script lang="ts" name="TaskRollbackDialogForm" setup>
28+
import * as TaskApi from '@/api/bpm/task'
29+
import { isEmpty } from '@/utils/is'
30+
31+
const message = useMessage() // 消息弹窗
32+
const dialogVisible = ref(false) // 弹窗的是否展示
33+
const formLoading = ref(false) // 表单的加载中
34+
const formData = ref({
35+
id: '',
36+
reason: ''
37+
})
38+
const formRules = ref({
39+
id: [{ required: true, message: '必须选择减签任务', trigger: 'change' }],
40+
reason: [{ required: true, message: '减签理由不能为空', trigger: 'blur' }]
41+
})
42+
43+
const formRef = ref() // 表单 Ref
44+
const subTaskList = ref([])
45+
/** 打开弹窗 */
46+
const open = async (id: string) => {
47+
subTaskList.value = await TaskApi.getChildrenTaskList(id)
48+
if (isEmpty(subTaskList.value)) {
49+
message.warning('当前没有可减签的任务')
50+
return false
51+
}
52+
dialogVisible.value = true
53+
resetForm()
54+
}
55+
defineExpose({ open }) // 提供 openModal 方法,用于打开弹窗
56+
57+
/** 提交表单 */
58+
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
59+
const submitForm = async () => {
60+
// 校验表单
61+
if (!formRef) return
62+
const valid = await formRef.value.validate()
63+
if (!valid) return
64+
// 提交请求
65+
formLoading.value = true
66+
try {
67+
await TaskApi.taskSubSign(formData.value)
68+
message.success('减签成功')
69+
dialogVisible.value = false
70+
// 发送操作成功的事件
71+
emit('success')
72+
} finally {
73+
formLoading.value = false
74+
}
75+
}
76+
77+
/** 重置表单 */
78+
const resetForm = () => {
79+
formData.value = {
80+
id: '',
81+
reason: ''
82+
}
83+
formRef.value?.resetFields()
84+
}
85+
</script>

0 commit comments

Comments
 (0)