Commit 43b7dff0 by YunaiV

【功能调整】工作流:工作流程的流转记录,不区分父子任务

parent dcdce412
...@@ -53,11 +53,14 @@ ...@@ -53,11 +53,14 @@
/> />
<el-table-column <el-table-column
label="审批人" label="审批人"
prop="assigneeUser.nickname"
min-width="100" min-width="100"
align="center" align="center"
v-if="selectActivityType === 'bpmn:UserTask'" v-if="selectActivityType === 'bpmn:UserTask'"
/> >
<template #default="scope">
{{ scope.row.assigneeUser?.nickname || scope.row.ownerUser?.nickname }}
</template>
</el-table-column>
<el-table-column <el-table-column
label="发起人" label="发起人"
prop="assigneeUser.nickname" prop="assigneeUser.nickname"
...@@ -65,12 +68,11 @@ ...@@ -65,12 +68,11 @@
align="center" align="center"
v-else v-else
/> />
<el-table-column <el-table-column label="部门" min-width="100" align="center">
label="部门" <template #default="scope">
prop="assigneeUser.deptName" {{ scope.row.assigneeUser?.deptName || scope.row.ownerUser?.deptName }}
min-width="100" </template>
align="center" </el-table-column>
/>
<el-table-column <el-table-column
:formatter="dateFormatter" :formatter="dateFormatter"
align="center" align="center"
......
...@@ -292,8 +292,7 @@ const remainingRouter: AppRouteRecordRaw[] = [ ...@@ -292,8 +292,7 @@ const remainingRouter: AppRouteRecordRaw[] = [
}, },
{ {
path: 'process-instance/detail', path: 'process-instance/detail',
component: () => import('@/views/bpm/processInstance/detail/index_new.vue'), component: () => import('@/views/bpm/processInstance/detail/index.vue'),
//component: () => import('@/views/bpm/processInstance/detail/index.vue'),
name: 'BpmProcessInstanceDetail', name: 'BpmProcessInstanceDetail',
meta: { meta: {
noCache: true, noCache: true,
......
<template>
<ContentWrap>
<div class="flex justify-between pl-20px items-center">
<h3 class="font-extrabold">流程模型</h3>
<!-- 搜索工作栏 -->
<el-form
v-if="!isCategorySorting"
class="-mb-15px flex mr-10px"
:model="queryParams"
ref="queryFormRef"
:inline="true"
label-width="68px"
@submit.prevent
>
<el-form-item align="right" prop="key" class="ml-auto">
<el-input
v-model="queryParams.key"
placeholder="搜索流程"
clearable
@keyup.enter="handleQuery"
class="!w-240px"
>
<template #prefix>
<Icon icon="ep:search" class="mx-10px" />
</template>
</el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="openForm('create')" v-hasPermi="['bpm:model:create']">
<Icon icon="ep:plus" class="mr-5px" /> 新建模型
</el-button>
</el-form-item>
<el-form-item>
<el-dropdown @command="(command) => handleCommand(command)" placement="bottom-end">
<el-button class="w-30px" plain>
<Icon icon="ep:setting" />
</el-button>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item command="handleAddCategory">
<Icon icon="ep:circle-plus" :size="13" class="mr-5px" />
新建分类
</el-dropdown-item>
<el-dropdown-item command="handleSort">
<Icon icon="fa:sort-amount-desc" :size="13" class="mr-5px" />
分类排序
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</el-form-item>
</el-form>
<div class="mr-20px" v-else>
<el-button @click="cancelSort"> 取 消 </el-button>
<el-button type="primary" @click="saveSort"> 保存排序 </el-button>
</div>
</div>
<el-divider />
<!-- 分类卡片组 -->
<div class="px-15px">
<draggable
:disabled="!isCategorySorting"
v-model="categoryGroup"
item-key="id"
:animation="400"
>
<template #item="{ element }">
<ContentWrap
class="rounded-lg transition-all duration-300 ease-in-out hover:shadow-xl"
v-loading="loading"
:body-style="{ padding: 0 }"
:key="element.id"
>
<CategoryDraggableModel
ref="categoryDraggableModelRef"
:isCategorySorting="isCategorySorting"
:categoryInfo="element"
@success="getList"
/>
</ContentWrap>
</template>
</draggable>
</div>
</ContentWrap>
<!-- 表单弹窗:添加/修改流程 -->
<ModelForm ref="formRef" @success="getList" />
<!-- 表单弹窗:添加/修改分类 -->
<CategoryForm ref="categoryFormRef" @success="getList" />
<!-- 弹窗:表单详情 -->
<Dialog title="表单详情" v-model="formDetailVisible" width="800">
<form-create :rule="formDetailPreview.rule" :option="formDetailPreview.option" />
</Dialog>
</template>
<script lang="ts" setup>
import draggable from 'vuedraggable'
import { CategoryApi } from '@/api/bpm/category'
import * as ModelApi from '@/api/bpm/model'
import ModelForm from './ModelForm.vue'
import CategoryForm from '../category/CategoryForm.vue'
import { groupBy, cloneDeep } from 'lodash-es'
import CategoryDraggableModel from './CategoryDraggableModel.vue'
defineOptions({ name: 'BpmModel' })
const categoryDraggableModelRef = ref()
const categoryFormRef = ref()
const loading = ref(true) // 列表的加载中
const isCategorySorting = ref(false) // 是否正处于排序状态
const queryParams = reactive({
pageNo: 1,
pageSize: 10,
key: undefined,
name: undefined,
category: undefined
})
const queryFormRef = ref() // 搜索的表单
const categoryGroup: any = ref([]) // 按照category分组的数据
const originalData: any = ref([])
// 查询所有分类数据
const getAllCategory = async () => {
// TODO 芋艿:这里需要一个不分页查全部的流程分类接口
const data = await CategoryApi.getCategoryPage(queryParams)
categoryGroup.value = data.list.map((item) => ({ ...item, modelList: [] }))
}
/** 查询所有流程模型接口 */
const getAllModel = async () => {
// TODO 芋艿:这里需要一个不分页查全部的流程模型接口
const data = await ModelApi.getModelPage(queryParams)
const groupedData = groupBy(data.list, 'categoryName')
Object.keys(groupedData).forEach((key) => {
const category = categoryGroup.value.find((item) => item.name === key)
if (category) {
category.modelList = groupedData[key]
}
})
}
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.pageNo = 1
getList()
}
/** 添加/修改操作 */
const formRef = ref()
const openForm = (type: string, id?: number) => {
formRef.value.open(type, id)
}
/** 流程表单的详情按钮操作 */
const formDetailVisible = ref(false)
const formDetailPreview = ref({
rule: [],
option: {}
})
/** 右上角设置按钮 */
const handleCommand = (command: string) => {
switch (command) {
case 'handleAddCategory':
handleAddCategory()
break
case 'handleSort':
handleSort()
break
default:
break
}
}
// 新建分类
const handleAddCategory = () => {
categoryFormRef.value.open('create')
}
// 分类排序
const handleSort = () => {
// 保存初始数据
originalData.value = cloneDeep(categoryGroup.value)
isCategorySorting.value = true
}
// 取消排序
const cancelSort = () => {
// 恢复初始数据
categoryGroup.value = cloneDeep(originalData.value)
isCategorySorting.value = false
}
// 保存排序
const saveSort = () => {
// TODO 芋艿:这里需要一个保存分类排序接口
}
const getList = async () => {
loading.value = true
try {
await getAllCategory()
await getAllModel()
} finally {
loading.value = false
}
}
/** 初始化 **/
onMounted(async () => {
getList()
})
</script>
<style lang="scss" scoped>
:deep() {
.el-table--fit .el-table__inner-wrapper:before {
height: 0;
}
.el-card {
border-radius: 8px;
}
.el-form--inline .el-form-item {
margin-right: 10px;
}
.el-divider--horizontal {
margin-top: 6px;
}
}
</style>
<template> <template>
<el-card v-loading="loading" class="box-card"> <el-card v-loading="loading" class="box-card">
<template #header v-if="showHeader">
<span class="el-icon-picture-outline">流程图</span>
</template>
<MyProcessViewer key="designer" :xml="view.bpmnXml" :view="view" class="h-700px" /> <MyProcessViewer key="designer" :xml="view.bpmnXml" :view="view" class="h-700px" />
</el-card> </el-card>
</template> </template>
...@@ -16,8 +13,7 @@ defineOptions({ name: 'BpmProcessInstanceBpmnViewer' }) ...@@ -16,8 +13,7 @@ defineOptions({ name: 'BpmProcessInstanceBpmnViewer' })
const props = defineProps({ const props = defineProps({
loading: propTypes.bool.def(false), // 是否加载中 loading: propTypes.bool.def(false), // 是否加载中
id: propTypes.string, // 流程实例的编号 id: propTypes.string, // 流程实例的编号
bpmnXml: propTypes.string, // BPMN XML bpmnXml: propTypes.string // BPMN XML
showHeader: propTypes.bool.def(true), // 是否显示头
}) })
const view = ref({ const view = ref({
......
<template> <template>
<el-card v-loading="loading" class="box-card"> <el-card v-loading="loading" class="box-card">
<template #header v-if="showHeader"> <el-col>
<span class="el-icon-picture-outline">审批记录</span>
</template>
<el-col :offset="3" :span="17">
<div class="block"> <div class="block">
<el-timeline> <el-timeline>
<el-timeline-item <el-timeline-item
...@@ -28,14 +25,6 @@ ...@@ -28,14 +25,6 @@
<dict-tag :type="DICT_TYPE.BPM_TASK_STATUS" :value="item.status" /> <dict-tag :type="DICT_TYPE.BPM_TASK_STATUS" :value="item.status" />
<el-button <el-button
class="ml-10px" class="ml-10px"
v-if="!isEmpty(item.children)"
@click="openChildrenTask(item)"
size="small"
>
<Icon icon="ep:memo" /> 子任务
</el-button>
<el-button
class="ml-10px"
size="small" size="small"
v-if="item.formId > 0" v-if="item.formId > 0"
@click="handleFormDetail(item)" @click="handleFormDetail(item)"
...@@ -78,8 +67,6 @@ ...@@ -78,8 +67,6 @@
</el-col> </el-col>
</el-card> </el-card>
<!-- 弹窗:子任务 -->
<TaskSignList ref="taskSignListRef" @success="refresh" />
<!-- 弹窗:表单 --> <!-- 弹窗:表单 -->
<Dialog title="表单详情" v-model="taskFormVisible" width="600"> <Dialog title="表单详情" v-model="taskFormVisible" width="600">
<form-create <form-create
...@@ -94,8 +81,6 @@ ...@@ -94,8 +81,6 @@
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 TaskSignList from './dialog/TaskSignList.vue'
import type { ApiAttrs } from '@form-create/element-ui/types/config' import type { ApiAttrs } from '@form-create/element-ui/types/config'
import { setConfAndFields2 } from '@/utils/formCreate' import { setConfAndFields2 } from '@/utils/formCreate'
...@@ -104,8 +89,7 @@ defineOptions({ name: 'BpmProcessInstanceTaskList' }) ...@@ -104,8 +89,7 @@ defineOptions({ name: 'BpmProcessInstanceTaskList' })
defineProps({ defineProps({
loading: propTypes.bool, // 是否加载中 loading: propTypes.bool, // 是否加载中
processInstance: propTypes.object, // 流程实例 processInstance: propTypes.object, // 流程实例
tasks: propTypes.arrayOf(propTypes.object), // 流程任务的数组 tasks: propTypes.arrayOf(propTypes.object) // 流程任务的数组
showHeader: propTypes.bool.def(true), // 是否显示头
}) })
/** 获得流程实例对应的颜色 */ /** 获得流程实例对应的颜色 */
...@@ -142,12 +126,6 @@ const getTaskTimelineItemType = (item: any) => { ...@@ -142,12 +126,6 @@ const getTaskTimelineItemType = (item: any) => {
return '' return ''
} }
/** 子任务 */
const taskSignListRef = ref()
const openChildrenTask = (item: any) => {
taskSignListRef.value.open(item)
}
/** 查看表单 */ /** 查看表单 */
const fApi = ref<ApiAttrs>() // form-create 的 API 操作类 const fApi = ref<ApiAttrs>() // form-create 的 API 操作类
const taskForm = ref({ const taskForm = ref({
......
<template>
<Dialog v-model="dialogVisible" title="委派任务" width="500">
<el-form
ref="formRef"
v-loading="formLoading"
:model="formData"
:rules="formRules"
label-width="110px"
>
<el-form-item label="接收人" prop="delegateUserId">
<el-select v-model="formData.delegateUserId" clearable style="width: 100%">
<el-option
v-for="item in userList"
:key="item.id"
:label="item.nickname"
:value="item.id"
/>
</el-select>
</el-form-item>
<el-form-item label="委派理由" prop="reason">
<el-input v-model="formData.reason" clearable placeholder="请输入委派理由" />
</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 * as TaskApi from '@/api/bpm/task'
import * as UserApi from '@/api/system/user'
defineOptions({ name: 'BpmTaskDelegateForm' })
const dialogVisible = ref(false) // 弹窗的是否展示
const formLoading = ref(false) // 表单的加载中
const formData = ref({
id: '',
delegateUserId: undefined,
reason: ''
})
const formRules = ref({
delegateUserId: [{ required: true, message: '接收人不能为空', trigger: 'change' }],
reason: [{ required: true, message: '委派理由不能为空', trigger: 'blur' }]
})
const formRef = ref() // 表单 Ref
const userList = ref<any[]>([]) // 用户列表
/** 打开弹窗 */
const open = async (id: string) => {
dialogVisible.value = true
resetForm()
formData.value.id = id
// 获得用户列表
userList.value = await UserApi.getSimpleUserList()
}
defineExpose({ open }) // 提供 openModal 方法,用于打开弹窗
/** 提交表单 */
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
const submitForm = async () => {
// 校验表单
if (!formRef) return
const valid = await formRef.value.validate()
if (!valid) return
// 提交请求
formLoading.value = true
try {
await TaskApi.delegateTask(formData.value)
dialogVisible.value = false
// 发送操作成功的事件
emit('success')
} finally {
formLoading.value = false
}
}
/** 重置表单 */
const resetForm = () => {
formData.value = {
id: '',
delegateUserId: undefined,
reason: ''
}
formRef.value?.resetFields()
}
</script>
<template>
<Dialog v-model="dialogVisible" title="退回任务" width="500">
<el-form
ref="formRef"
v-loading="formLoading"
:model="formData"
:rules="formRules"
label-width="110px"
>
<el-form-item label="退回节点" prop="targetTaskDefinitionKey">
<el-select v-model="formData.targetTaskDefinitionKey" clearable style="width: 100%">
<el-option
v-for="item in returnList"
:key="item.taskDefinitionKey"
:label="item.name"
:value="item.taskDefinitionKey"
/>
</el-select>
</el-form-item>
<el-form-item label="退回理由" prop="reason">
<el-input v-model="formData.reason" clearable placeholder="请输入退回理由" />
</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" name="TaskRollbackDialogForm" setup>
import * as TaskApi from '@/api/bpm/task'
const message = useMessage() // 消息弹窗
const dialogVisible = ref(false) // 弹窗的是否展示
const formLoading = ref(false) // 表单的加载中
const formData = ref({
id: '',
targetTaskDefinitionKey: undefined,
reason: ''
})
const formRules = ref({
targetTaskDefinitionKey: [{ required: true, message: '必须选择退回节点', trigger: 'change' }],
reason: [{ required: true, message: '退回理由不能为空', trigger: 'blur' }]
})
const formRef = ref() // 表单 Ref
const returnList = ref([] as any)
/** 打开弹窗 */
const open = async (id: string) => {
returnList.value = await TaskApi.getTaskListByReturn(id)
if (returnList.value.length === 0) {
message.warning('当前没有可退回的节点')
return false
}
dialogVisible.value = true
resetForm()
formData.value.id = id
}
defineExpose({ open }) // 提供 openModal 方法,用于打开弹窗
/** 提交表单 */
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
const submitForm = async () => {
// 校验表单
if (!formRef) return
const valid = await formRef.value.validate()
if (!valid) return
// 提交请求
formLoading.value = true
try {
await TaskApi.returnTask(formData.value)
message.success('退回成功')
dialogVisible.value = false
// 发送操作成功的事件
emit('success')
} finally {
formLoading.value = false
}
}
/** 重置表单 */
const resetForm = () => {
formData.value = {
id: '',
targetTaskDefinitionKey: undefined,
reason: ''
}
formRef.value?.resetFields()
}
</script>
<template>
<Dialog v-model="dialogVisible" title="加签" width="500">
<el-form
ref="formRef"
v-loading="formLoading"
:model="formData"
:rules="formRules"
label-width="110px"
>
<el-form-item label="加签处理人" prop="userIds">
<el-select v-model="formData.userIds" multiple clearable style="width: 100%">
<el-option
v-for="item in userList"
:key="item.id"
:label="item.nickname"
:value="item.id"
/>
</el-select>
</el-form-item>
<el-form-item label="加签理由" prop="reason">
<el-input v-model="formData.reason" clearable placeholder="请输入加签理由" />
</el-form-item>
</el-form>
<template #footer>
<el-button :disabled="formLoading" type="primary" @click="submitForm('before')">
向前加签
</el-button>
<el-button :disabled="formLoading" type="primary" @click="submitForm('after')">
向后加签
</el-button>
<el-button @click="dialogVisible = false">取 消</el-button>
</template>
</Dialog>
</template>
<script lang="ts" setup>
import * as TaskApi from '@/api/bpm/task'
import * as UserApi from '@/api/system/user'
defineOptions({ name: 'TaskSignCreateForm' })
const message = useMessage() // 消息弹窗
const dialogVisible = ref(false) // 弹窗的是否展示
const formLoading = ref(false) // 表单的加载中
const formData = ref({
id: '',
userIds: [],
type: '',
reason: ''
})
const formRules = ref({
userIds: [{ required: true, message: '加签处理人不能为空', trigger: 'change' }],
reason: [{ required: true, message: '加签理由不能为空', trigger: 'change' }]
})
const formRef = ref() // 表单 Ref
const userList = ref<any[]>([]) // 用户列表
/** 打开弹窗 */
const open = async (id: string) => {
dialogVisible.value = true
resetForm()
formData.value.id = id
// 获得用户列表
userList.value = await UserApi.getSimpleUserList()
}
defineExpose({ open }) // 提供 openModal 方法,用于打开弹窗
/** 提交表单 */
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
const submitForm = async (type: string) => {
// 校验表单
if (!formRef) return
const valid = await formRef.value.validate()
if (!valid) return
// 提交请求
formLoading.value = true
formData.value.type = type
try {
await TaskApi.signCreateTask(formData.value)
message.success('加签成功')
dialogVisible.value = false
// 发送操作成功的事件
emit('success')
} finally {
formLoading.value = false
}
}
/** 重置表单 */
const resetForm = () => {
formData.value = {
id: '',
userIds: [],
type: '',
reason: ''
}
formRef.value?.resetFields()
}
</script>
...@@ -86,4 +86,5 @@ const resetForm = () => { ...@@ -86,4 +86,5 @@ const resetForm = () => {
} }
formRef.value?.resetFields() formRef.value?.resetFields()
} }
// TODO @jason:新界面搞完,可以删除
</script> </script>
...@@ -103,4 +103,5 @@ const handleSignDeleteSuccess = () => { ...@@ -103,4 +103,5 @@ const handleSignDeleteSuccess = () => {
const isSignDeleteButtonVisible = (task: any) => { const isSignDeleteButtonVisible = (task: any) => {
return task && task.children && !isEmpty(task.children) return task && task.children && !isEmpty(task.children)
} }
// TODO @jason:新界面搞完,可以删除
</script> </script>
<template>
<Dialog v-model="dialogVisible" title="转派任务" width="500">
<el-form
ref="formRef"
v-loading="formLoading"
:model="formData"
:rules="formRules"
label-width="110px"
>
<el-form-item label="新审批人" prop="assigneeUserId">
<el-select v-model="formData.assigneeUserId" clearable style="width: 100%">
<el-option
v-for="item in userList"
:key="item.id"
:label="item.nickname"
:value="item.id"
/>
</el-select>
</el-form-item>
<el-form-item label="转派理由" prop="reason">
<el-input v-model="formData.reason" clearable placeholder="请输入转派理由" />
</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 * as TaskApi from '@/api/bpm/task'
import * as UserApi from '@/api/system/user'
defineOptions({ name: 'TaskTransferForm' })
const dialogVisible = ref(false) // 弹窗的是否展示
const formLoading = ref(false) // 表单的加载中
const formData = ref({
id: '',
assigneeUserId: undefined,
reason: ''
})
const formRules = ref({
assigneeUserId: [{ required: true, message: '新审批人不能为空', trigger: 'change' }],
reason: [{ required: true, message: '转派理由不能为空', trigger: 'blur' }]
})
const formRef = ref() // 表单 Ref
const userList = ref<any[]>([]) // 用户列表
/** 打开弹窗 */
const open = async (id: string) => {
dialogVisible.value = true
resetForm()
formData.value.id = id
// 获得用户列表
userList.value = await UserApi.getSimpleUserList()
}
defineExpose({ open }) // 提供 openModal 方法,用于打开弹窗
/** 提交表单 */
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
const submitForm = async () => {
// 校验表单
if (!formRef) return
const valid = await formRef.value.validate()
if (!valid) return
// 提交请求
formLoading.value = true
try {
await TaskApi.transferTask(formData.value)
dialogVisible.value = false
// 发送操作成功的事件
emit('success')
} finally {
formLoading.value = false
}
}
/** 重置表单 */
const resetForm = () => {
formData.value = {
id: '',
assigneeUserId: undefined,
reason: ''
}
formRef.value?.resetFields()
}
</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