Merge branch 'feature/bpm' of https://gitee.com/yudaocode/yudao-ui-admin-vue3 into feature/bpm
commit
30052b05f5
|
@ -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 @@
|
||||||
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[] = [
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
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,
|
||||||
|
|
|
@ -64,7 +64,11 @@ const designerConfig = ref({
|
||||||
switchType: [], // 是否可以切换组件类型,或者可以相互切换的字段
|
switchType: [], // 是否可以切换组件类型,或者可以相互切换的字段
|
||||||
autoActive: true, // 是否自动选中拖入的组件
|
autoActive: true, // 是否自动选中拖入的组件
|
||||||
useTemplate: false, // 是否生成vue2语法的模板组件
|
useTemplate: false, // 是否生成vue2语法的模板组件
|
||||||
formOptions: {}, // 定义表单配置默认值
|
formOptions: {
|
||||||
|
form: {
|
||||||
|
labelWidth: '100px' // 设置默认的 label 宽度为 100px
|
||||||
|
}
|
||||||
|
}, // 定义表单配置默认值
|
||||||
fieldReadonly: false, // 配置field是否可以编辑
|
fieldReadonly: false, // 配置field是否可以编辑
|
||||||
hiddenDragMenu: false, // 隐藏拖拽操作按钮
|
hiddenDragMenu: false, // 隐藏拖拽操作按钮
|
||||||
hiddenDragBtn: false, // 隐藏拖拽按钮
|
hiddenDragBtn: false, // 隐藏拖拽按钮
|
||||||
|
|
|
@ -1,216 +1,95 @@
|
||||||
<template>
|
<template>
|
||||||
<doc-alert title="流程设计器(BPMN)" url="https://doc.iocoder.cn/bpm/model-designer-dingding/" />
|
|
||||||
<doc-alert
|
|
||||||
title="流程设计器(钉钉、飞书)"
|
|
||||||
url="https://doc.iocoder.cn/bpm/model-designer-bpmn/"
|
|
||||||
/>
|
|
||||||
<doc-alert title="选择审批人、发起人自选" url="https://doc.iocoder.cn/bpm/assignee/" />
|
|
||||||
<doc-alert title="会签、或签、依次审批" url="https://doc.iocoder.cn/bpm/multi-instance/" />
|
|
||||||
|
|
||||||
<ContentWrap>
|
<ContentWrap>
|
||||||
<!-- 搜索工作栏 -->
|
<div class="flex justify-between pl-20px items-center">
|
||||||
<el-form
|
<h3 class="font-extrabold">流程模型</h3>
|
||||||
class="-mb-15px"
|
<!-- 搜索工作栏 -->
|
||||||
:model="queryParams"
|
<el-form
|
||||||
ref="queryFormRef"
|
v-if="!isCategorySorting"
|
||||||
:inline="true"
|
class="-mb-15px flex mr-10px"
|
||||||
label-width="68px"
|
:model="queryParams"
|
||||||
>
|
ref="queryFormRef"
|
||||||
<el-form-item label="流程标识" prop="key">
|
:inline="true"
|
||||||
<el-input
|
label-width="68px"
|
||||||
v-model="queryParams.key"
|
@submit.prevent
|
||||||
placeholder="请输入流程标识"
|
>
|
||||||
clearable
|
<el-form-item align="right" prop="key" class="ml-auto">
|
||||||
@keyup.enter="handleQuery"
|
<el-input
|
||||||
class="!w-240px"
|
v-model="queryParams.key"
|
||||||
/>
|
placeholder="搜索流程"
|
||||||
</el-form-item>
|
clearable
|
||||||
<el-form-item label="流程名称" prop="name">
|
@keyup.enter="handleQuery"
|
||||||
<el-input
|
class="!w-240px"
|
||||||
v-model="queryParams.name"
|
>
|
||||||
placeholder="请输入流程名称"
|
<template #prefix>
|
||||||
clearable
|
<Icon icon="ep:search" class="mx-10px" />
|
||||||
@keyup.enter="handleQuery"
|
</template>
|
||||||
class="!w-240px"
|
</el-input>
|
||||||
/>
|
</el-form-item>
|
||||||
</el-form-item>
|
<el-form-item>
|
||||||
<el-form-item label="流程分类" prop="category">
|
<el-button type="primary" @click="openForm('create')" v-hasPermi="['bpm:model:create']">
|
||||||
<el-select
|
<Icon icon="ep:plus" class="mr-5px" /> 新建模型
|
||||||
v-model="queryParams.category"
|
</el-button>
|
||||||
placeholder="请选择流程分类"
|
</el-form-item>
|
||||||
clearable
|
|
||||||
class="!w-240px"
|
|
||||||
>
|
|
||||||
<el-option
|
|
||||||
v-for="category in categoryList"
|
|
||||||
:key="category.code"
|
|
||||||
:label="category.name"
|
|
||||||
:value="category.code"
|
|
||||||
/>
|
|
||||||
</el-select>
|
|
||||||
</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:model:create']"
|
|
||||||
>
|
|
||||||
<Icon icon="ep:plus" class="mr-5px" /> 新建
|
|
||||||
</el-button>
|
|
||||||
</el-form-item>
|
|
||||||
</el-form>
|
|
||||||
</ContentWrap>
|
|
||||||
|
|
||||||
<!-- 列表 -->
|
<el-form-item>
|
||||||
<ContentWrap>
|
<el-dropdown @command="(command) => handleCommand(command)" placement="bottom-end">
|
||||||
<el-table v-loading="loading" :data="list">
|
<el-button class="w-30px" plain>
|
||||||
<el-table-column label="流程名称" align="center" prop="name" min-width="200" />
|
<Icon icon="ep:setting" />
|
||||||
<el-table-column label="流程图标" align="center" prop="icon" min-width="100">
|
</el-button>
|
||||||
<template #default="scope">
|
|
||||||
<el-image :src="scope.row.icon" class="h-32px w-32px" />
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column label="可见范围" align="center" prop="startUserIds" min-width="100">
|
|
||||||
<template #default="scope">
|
|
||||||
<el-text v-if="!scope.row.startUsers || scope.row.startUsers.length === 0">
|
|
||||||
全部可见
|
|
||||||
</el-text>
|
|
||||||
<el-text v-else-if="scope.row.startUsers.length == 1">
|
|
||||||
{{ scope.row.startUsers[0].nickname }}
|
|
||||||
</el-text>
|
|
||||||
<el-text v-else>
|
|
||||||
<el-tooltip
|
|
||||||
class="box-item"
|
|
||||||
effect="dark"
|
|
||||||
placement="top"
|
|
||||||
:content="scope.row.startUsers.map((user: any) => user.nickname).join('、')"
|
|
||||||
>
|
|
||||||
{{ scope.row.startUsers[0].nickname }}等 {{ scope.row.startUsers.length }} 人可见
|
|
||||||
</el-tooltip>
|
|
||||||
</el-text>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column label="流程分类" align="center" prop="categoryName" min-width="100" />
|
|
||||||
<el-table-column label="表单信息" align="center" prop="formType" min-width="200">
|
|
||||||
<template #default="scope">
|
|
||||||
<el-button
|
|
||||||
v-if="scope.row.formType === 10"
|
|
||||||
type="primary"
|
|
||||||
link
|
|
||||||
@click="handleFormDetail(scope.row)"
|
|
||||||
>
|
|
||||||
<span>{{ scope.row.formName }}</span>
|
|
||||||
</el-button>
|
|
||||||
<el-button
|
|
||||||
v-else-if="scope.row.formType === 20"
|
|
||||||
type="primary"
|
|
||||||
link
|
|
||||||
@click="handleFormDetail(scope.row)"
|
|
||||||
>
|
|
||||||
<span>{{ scope.row.formCustomCreatePath }}</span>
|
|
||||||
</el-button>
|
|
||||||
<label v-else>暂无表单</label>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column label="最后发布" align="center" prop="deploymentTime" min-width="250">
|
|
||||||
<template #default="scope">
|
|
||||||
<span v-if="scope.row.processDefinition">
|
|
||||||
{{ formatDate(scope.row.processDefinition.deploymentTime) }}
|
|
||||||
</span>
|
|
||||||
<el-tag v-if="scope.row.processDefinition" class="ml-10px">
|
|
||||||
v{{ scope.row.processDefinition.version }}
|
|
||||||
</el-tag>
|
|
||||||
<el-tag v-else type="warning">未部署</el-tag>
|
|
||||||
<el-tag
|
|
||||||
v-if="scope.row.processDefinition?.suspensionState === 2"
|
|
||||||
type="warning"
|
|
||||||
class="ml-10px"
|
|
||||||
>
|
|
||||||
已停用
|
|
||||||
</el-tag>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column label="操作" align="center" width="200" fixed="right">
|
|
||||||
<template #default="scope">
|
|
||||||
<el-button
|
|
||||||
link
|
|
||||||
type="primary"
|
|
||||||
@click="openForm('update', scope.row.id)"
|
|
||||||
v-hasPermi="['bpm:model:update']"
|
|
||||||
:disabled="!isManagerUser(scope.row)"
|
|
||||||
>
|
|
||||||
修改
|
|
||||||
</el-button>
|
|
||||||
<el-button
|
|
||||||
link
|
|
||||||
class="!ml-5px"
|
|
||||||
type="primary"
|
|
||||||
@click="handleDesign(scope.row)"
|
|
||||||
v-hasPermi="['bpm:model:update']"
|
|
||||||
:disabled="!isManagerUser(scope.row)"
|
|
||||||
>
|
|
||||||
设计
|
|
||||||
</el-button>
|
|
||||||
<el-button
|
|
||||||
link
|
|
||||||
class="!ml-5px"
|
|
||||||
type="primary"
|
|
||||||
@click="handleDeploy(scope.row)"
|
|
||||||
v-hasPermi="['bpm:model:deploy']"
|
|
||||||
:disabled="!isManagerUser(scope.row)"
|
|
||||||
>
|
|
||||||
发布
|
|
||||||
</el-button>
|
|
||||||
<el-dropdown
|
|
||||||
class="!align-middle ml-5px"
|
|
||||||
@command="(command) => handleCommand(command, scope.row)"
|
|
||||||
v-hasPermi="['bpm:process-definition:query', 'bpm:model:update', 'bpm:model:delete']"
|
|
||||||
>
|
|
||||||
<el-button type="primary" link>更多</el-button>
|
|
||||||
<template #dropdown>
|
<template #dropdown>
|
||||||
<el-dropdown-menu>
|
<el-dropdown-menu>
|
||||||
<el-dropdown-item
|
<el-dropdown-item command="handleAddCategory">
|
||||||
command="handleDefinitionList"
|
<Icon icon="ep:circle-plus" :size="13" class="mr-5px" />
|
||||||
v-if="checkPermi(['bpm:process-definition:query'])"
|
新建分类
|
||||||
>
|
|
||||||
历史
|
|
||||||
</el-dropdown-item>
|
</el-dropdown-item>
|
||||||
<el-dropdown-item
|
<el-dropdown-item command="handleSort">
|
||||||
command="handleChangeState"
|
<Icon icon="fa:sort-amount-desc" :size="13" class="mr-5px" />
|
||||||
v-if="checkPermi(['bpm:model:update']) && scope.row.processDefinition"
|
分类排序
|
||||||
:disabled="!isManagerUser(scope.row)"
|
|
||||||
>
|
|
||||||
{{ scope.row.processDefinition.suspensionState === 1 ? '停用' : '启用' }}
|
|
||||||
</el-dropdown-item>
|
|
||||||
<el-dropdown-item
|
|
||||||
type="danger"
|
|
||||||
command="handleDelete"
|
|
||||||
v-if="checkPermi(['bpm:model:delete'])"
|
|
||||||
:disabled="!isManagerUser(scope.row)"
|
|
||||||
>
|
|
||||||
删除
|
|
||||||
</el-dropdown-item>
|
</el-dropdown-item>
|
||||||
</el-dropdown-menu>
|
</el-dropdown-menu>
|
||||||
</template>
|
</template>
|
||||||
</el-dropdown>
|
</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>
|
</template>
|
||||||
</el-table-column>
|
</draggable>
|
||||||
</el-table>
|
</div>
|
||||||
<!-- 分页 -->
|
|
||||||
<Pagination
|
|
||||||
:total="total"
|
|
||||||
v-model:page="queryParams.pageNo"
|
|
||||||
v-model:limit="queryParams.pageSize"
|
|
||||||
@pagination="getList"
|
|
||||||
/>
|
|
||||||
</ContentWrap>
|
</ContentWrap>
|
||||||
|
|
||||||
<!-- 表单弹窗:添加/修改流程 -->
|
<!-- 表单弹窗:添加/修改流程 -->
|
||||||
<ModelForm ref="formRef" @success="getList" />
|
<ModelForm ref="formRef" @success="getList" />
|
||||||
|
<!-- 表单弹窗:添加/修改分类 -->
|
||||||
|
<CategoryForm ref="categoryFormRef" @success="getList" />
|
||||||
<!-- 弹窗:表单详情 -->
|
<!-- 弹窗:表单详情 -->
|
||||||
<Dialog title="表单详情" v-model="formDetailVisible" width="800">
|
<Dialog title="表单详情" v-model="formDetailVisible" width="800">
|
||||||
<form-create :rule="formDetailPreview.rule" :option="formDetailPreview.option" />
|
<form-create :rule="formDetailPreview.rule" :option="formDetailPreview.option" />
|
||||||
|
@ -218,26 +97,20 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { formatDate } from '@/utils/formatTime'
|
import draggable from 'vuedraggable'
|
||||||
import * as ModelApi from '@/api/bpm/model'
|
|
||||||
import * as FormApi from '@/api/bpm/form'
|
|
||||||
import ModelForm from './ModelForm.vue'
|
|
||||||
import { setConfAndFields2 } from '@/utils/formCreate'
|
|
||||||
import { CategoryApi } from '@/api/bpm/category'
|
import { CategoryApi } from '@/api/bpm/category'
|
||||||
import { BpmModelType } from '@/utils/constants'
|
import * as ModelApi from '@/api/bpm/model'
|
||||||
import { checkPermi } from '@/utils/permission'
|
import ModelForm from './ModelForm.vue'
|
||||||
import { useUserStoreWithOut } from '@/store/modules/user'
|
import CategoryForm from '../category/CategoryForm.vue'
|
||||||
|
import { groupBy, cloneDeep } from 'lodash-es'
|
||||||
|
import CategoryDraggableModel from './CategoryDraggableModel.vue'
|
||||||
|
|
||||||
defineOptions({ name: 'BpmModel' })
|
defineOptions({ name: 'BpmModel' })
|
||||||
|
|
||||||
const message = useMessage() // 消息弹窗
|
const categoryDraggableModelRef = ref()
|
||||||
const { t } = useI18n() // 国际化
|
const categoryFormRef = ref()
|
||||||
const { push } = useRouter() // 路由
|
|
||||||
const userStore = useUserStoreWithOut() // 用户信息缓存
|
|
||||||
|
|
||||||
const loading = ref(true) // 列表的加载中
|
const loading = ref(true) // 列表的加载中
|
||||||
const total = ref(0) // 列表的总页数
|
const isCategorySorting = ref(false) // 是否正处于排序状态
|
||||||
const list = ref([]) // 列表的数据
|
|
||||||
const queryParams = reactive({
|
const queryParams = reactive({
|
||||||
pageNo: 1,
|
pageNo: 1,
|
||||||
pageSize: 10,
|
pageSize: 10,
|
||||||
|
@ -246,18 +119,26 @@ const queryParams = reactive({
|
||||||
category: undefined
|
category: undefined
|
||||||
})
|
})
|
||||||
const queryFormRef = ref() // 搜索的表单
|
const queryFormRef = ref() // 搜索的表单
|
||||||
const categoryList = 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 getList = async () => {
|
const getAllModel = async () => {
|
||||||
loading.value = true
|
// TODO 芋艿:这里需要一个不分页查全部的流程模型接口
|
||||||
try {
|
const data = await ModelApi.getModelPage(queryParams)
|
||||||
const data = await ModelApi.getModelPage(queryParams)
|
const groupedData = groupBy(data.list, 'categoryName')
|
||||||
list.value = data.list
|
Object.keys(groupedData).forEach((key) => {
|
||||||
total.value = data.total
|
const category = categoryGroup.value.find((item) => item.name === key)
|
||||||
} finally {
|
if (category) {
|
||||||
loading.value = false
|
category.modelList = groupedData[key]
|
||||||
}
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 搜索按钮操作 */
|
/** 搜索按钮操作 */
|
||||||
|
@ -266,139 +147,82 @@ const handleQuery = () => {
|
||||||
getList()
|
getList()
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 重置按钮操作 */
|
|
||||||
const resetQuery = () => {
|
|
||||||
queryFormRef.value.resetFields()
|
|
||||||
handleQuery()
|
|
||||||
}
|
|
||||||
|
|
||||||
/** '更多'操作按钮 */
|
|
||||||
const handleCommand = (command: string, row: any) => {
|
|
||||||
switch (command) {
|
|
||||||
case 'handleDefinitionList':
|
|
||||||
handleDefinitionList(row)
|
|
||||||
break
|
|
||||||
case 'handleDelete':
|
|
||||||
handleDelete(row)
|
|
||||||
break
|
|
||||||
case 'handleChangeState':
|
|
||||||
handleChangeState(row)
|
|
||||||
break
|
|
||||||
default:
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 添加/修改操作 */
|
/** 添加/修改操作 */
|
||||||
const formRef = ref()
|
const formRef = ref()
|
||||||
const openForm = (type: string, id?: number) => {
|
const openForm = (type: string, id?: number) => {
|
||||||
formRef.value.open(type, id)
|
formRef.value.open(type, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 删除按钮操作 */
|
|
||||||
const handleDelete = async (row: any) => {
|
|
||||||
try {
|
|
||||||
// 删除的二次确认
|
|
||||||
await message.delConfirm()
|
|
||||||
// 发起删除
|
|
||||||
await ModelApi.deleteModel(row.id)
|
|
||||||
message.success(t('common.delSuccess'))
|
|
||||||
// 刷新列表
|
|
||||||
await getList()
|
|
||||||
} catch {}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 更新状态操作 */
|
|
||||||
const handleChangeState = async (row: any) => {
|
|
||||||
const state = row.processDefinition.suspensionState
|
|
||||||
const newState = state === 1 ? 2 : 1
|
|
||||||
try {
|
|
||||||
// 修改状态的二次确认
|
|
||||||
const id = row.id
|
|
||||||
debugger
|
|
||||||
const statusState = state === 1 ? '停用' : '启用'
|
|
||||||
const content = '是否确认' + statusState + '流程名字为"' + row.name + '"的数据项?'
|
|
||||||
await message.confirm(content)
|
|
||||||
// 发起修改状态
|
|
||||||
await ModelApi.updateModelState(id, newState)
|
|
||||||
message.success(statusState + '成功')
|
|
||||||
// 刷新列表
|
|
||||||
await getList()
|
|
||||||
} catch {}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 设计流程 */
|
|
||||||
const handleDesign = (row: any) => {
|
|
||||||
if (row.type == BpmModelType.BPMN) {
|
|
||||||
push({
|
|
||||||
name: 'BpmModelEditor',
|
|
||||||
query: {
|
|
||||||
modelId: row.id
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
push({
|
|
||||||
name: 'SimpleWorkflowDesignEditor',
|
|
||||||
query: {
|
|
||||||
modelId: row.id
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 发布流程 */
|
|
||||||
const handleDeploy = async (row: any) => {
|
|
||||||
try {
|
|
||||||
// 删除的二次确认
|
|
||||||
await message.confirm('是否部署该流程!!')
|
|
||||||
// 发起部署
|
|
||||||
await ModelApi.deployModel(row.id)
|
|
||||||
message.success(t('部署成功'))
|
|
||||||
// 刷新列表
|
|
||||||
await getList()
|
|
||||||
} catch {}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 跳转到指定流程定义列表 */
|
|
||||||
const handleDefinitionList = (row) => {
|
|
||||||
push({
|
|
||||||
name: 'BpmProcessDefinition',
|
|
||||||
query: {
|
|
||||||
key: row.key
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 流程表单的详情按钮操作 */
|
/** 流程表单的详情按钮操作 */
|
||||||
const formDetailVisible = ref(false)
|
const formDetailVisible = ref(false)
|
||||||
const formDetailPreview = ref({
|
const formDetailPreview = ref({
|
||||||
rule: [],
|
rule: [],
|
||||||
option: {}
|
option: {}
|
||||||
})
|
})
|
||||||
const handleFormDetail = async (row: any) => {
|
|
||||||
if (row.formType == 10) {
|
/** 右上角设置按钮 */
|
||||||
// 设置表单
|
const handleCommand = (command: string) => {
|
||||||
const data = await FormApi.getForm(row.formId)
|
switch (command) {
|
||||||
setConfAndFields2(formDetailPreview, data.conf, data.fields)
|
case 'handleAddCategory':
|
||||||
// 弹窗打开
|
handleAddCategory()
|
||||||
formDetailVisible.value = true
|
break
|
||||||
} else {
|
case 'handleSort':
|
||||||
await push({
|
handleSort()
|
||||||
path: row.formCustomCreatePath
|
break
|
||||||
})
|
default:
|
||||||
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 判断是否可以操作 */
|
// 新建分类
|
||||||
const isManagerUser = (row: any) => {
|
const handleAddCategory = () => {
|
||||||
const userId = userStore.getUser.id
|
categoryFormRef.value.open('create')
|
||||||
return row.managerUserIds && row.managerUserIds.includes(userId)
|
}
|
||||||
|
// 分类排序
|
||||||
|
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 () => {
|
onMounted(async () => {
|
||||||
await getList()
|
getList()
|
||||||
// 查询流程分类列表
|
|
||||||
categoryList.value = await CategoryApi.getCategorySimpleList()
|
|
||||||
})
|
})
|
||||||
</script>
|
</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>
|
||||||
|
|
|
@ -1,228 +0,0 @@
|
||||||
<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>
|
|
|
@ -0,0 +1,404 @@
|
||||||
|
<template>
|
||||||
|
<doc-alert title="流程设计器(BPMN)" url="https://doc.iocoder.cn/bpm/model-designer-dingding/" />
|
||||||
|
<doc-alert
|
||||||
|
title="流程设计器(钉钉、飞书)"
|
||||||
|
url="https://doc.iocoder.cn/bpm/model-designer-bpmn/"
|
||||||
|
/>
|
||||||
|
<doc-alert title="选择审批人、发起人自选" url="https://doc.iocoder.cn/bpm/assignee/" />
|
||||||
|
<doc-alert title="会签、或签、依次审批" url="https://doc.iocoder.cn/bpm/multi-instance/" />
|
||||||
|
|
||||||
|
<ContentWrap>
|
||||||
|
<!-- 搜索工作栏 -->
|
||||||
|
<el-form
|
||||||
|
class="-mb-15px"
|
||||||
|
:model="queryParams"
|
||||||
|
ref="queryFormRef"
|
||||||
|
:inline="true"
|
||||||
|
label-width="68px"
|
||||||
|
>
|
||||||
|
<el-form-item label="流程标识" prop="key">
|
||||||
|
<el-input
|
||||||
|
v-model="queryParams.key"
|
||||||
|
placeholder="请输入流程标识"
|
||||||
|
clearable
|
||||||
|
@keyup.enter="handleQuery"
|
||||||
|
class="!w-240px"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<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="category">
|
||||||
|
<el-select
|
||||||
|
v-model="queryParams.category"
|
||||||
|
placeholder="请选择流程分类"
|
||||||
|
clearable
|
||||||
|
class="!w-240px"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="category in categoryList"
|
||||||
|
:key="category.code"
|
||||||
|
:label="category.name"
|
||||||
|
:value="category.code"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</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:model:create']"
|
||||||
|
>
|
||||||
|
<Icon icon="ep:plus" class="mr-5px" /> 新建
|
||||||
|
</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</ContentWrap>
|
||||||
|
|
||||||
|
<!-- 列表 -->
|
||||||
|
<ContentWrap>
|
||||||
|
<el-table v-loading="loading" :data="list">
|
||||||
|
<el-table-column label="流程名称" align="center" prop="name" min-width="200" />
|
||||||
|
<el-table-column label="流程图标" align="center" prop="icon" min-width="100">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-image :src="scope.row.icon" class="h-32px w-32px" />
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="可见范围" align="center" prop="startUserIds" min-width="100">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-text v-if="!scope.row.startUsers || scope.row.startUsers.length === 0">
|
||||||
|
全部可见
|
||||||
|
</el-text>
|
||||||
|
<el-text v-else-if="scope.row.startUsers.length == 1">
|
||||||
|
{{ scope.row.startUsers[0].nickname }}
|
||||||
|
</el-text>
|
||||||
|
<el-text v-else>
|
||||||
|
<el-tooltip
|
||||||
|
class="box-item"
|
||||||
|
effect="dark"
|
||||||
|
placement="top"
|
||||||
|
:content="scope.row.startUsers.map((user: any) => user.nickname).join('、')"
|
||||||
|
>
|
||||||
|
{{ scope.row.startUsers[0].nickname }}等 {{ scope.row.startUsers.length }} 人可见
|
||||||
|
</el-tooltip>
|
||||||
|
</el-text>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="流程分类" align="center" prop="categoryName" min-width="100" />
|
||||||
|
<el-table-column label="表单信息" align="center" prop="formType" min-width="200">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button
|
||||||
|
v-if="scope.row.formType === 10"
|
||||||
|
type="primary"
|
||||||
|
link
|
||||||
|
@click="handleFormDetail(scope.row)"
|
||||||
|
>
|
||||||
|
<span>{{ scope.row.formName }}</span>
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
v-else-if="scope.row.formType === 20"
|
||||||
|
type="primary"
|
||||||
|
link
|
||||||
|
@click="handleFormDetail(scope.row)"
|
||||||
|
>
|
||||||
|
<span>{{ scope.row.formCustomCreatePath }}</span>
|
||||||
|
</el-button>
|
||||||
|
<label v-else>暂无表单</label>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="最后发布" align="center" prop="deploymentTime" min-width="250">
|
||||||
|
<template #default="scope">
|
||||||
|
<span v-if="scope.row.processDefinition">
|
||||||
|
{{ formatDate(scope.row.processDefinition.deploymentTime) }}
|
||||||
|
</span>
|
||||||
|
<el-tag v-if="scope.row.processDefinition" class="ml-10px">
|
||||||
|
v{{ scope.row.processDefinition.version }}
|
||||||
|
</el-tag>
|
||||||
|
<el-tag v-else type="warning">未部署</el-tag>
|
||||||
|
<el-tag
|
||||||
|
v-if="scope.row.processDefinition?.suspensionState === 2"
|
||||||
|
type="warning"
|
||||||
|
class="ml-10px"
|
||||||
|
>
|
||||||
|
已停用
|
||||||
|
</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="操作" align="center" width="200" fixed="right">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button
|
||||||
|
link
|
||||||
|
type="primary"
|
||||||
|
@click="openForm('update', scope.row.id)"
|
||||||
|
v-hasPermi="['bpm:model:update']"
|
||||||
|
:disabled="!isManagerUser(scope.row)"
|
||||||
|
>
|
||||||
|
修改
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
link
|
||||||
|
class="!ml-5px"
|
||||||
|
type="primary"
|
||||||
|
@click="handleDesign(scope.row)"
|
||||||
|
v-hasPermi="['bpm:model:update']"
|
||||||
|
:disabled="!isManagerUser(scope.row)"
|
||||||
|
>
|
||||||
|
设计
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
link
|
||||||
|
class="!ml-5px"
|
||||||
|
type="primary"
|
||||||
|
@click="handleDeploy(scope.row)"
|
||||||
|
v-hasPermi="['bpm:model:deploy']"
|
||||||
|
:disabled="!isManagerUser(scope.row)"
|
||||||
|
>
|
||||||
|
发布
|
||||||
|
</el-button>
|
||||||
|
<el-dropdown
|
||||||
|
class="!align-middle ml-5px"
|
||||||
|
@command="(command) => handleCommand(command, scope.row)"
|
||||||
|
v-hasPermi="['bpm:process-definition:query', 'bpm:model:update', 'bpm:model:delete']"
|
||||||
|
>
|
||||||
|
<el-button type="primary" link>更多</el-button>
|
||||||
|
<template #dropdown>
|
||||||
|
<el-dropdown-menu>
|
||||||
|
<el-dropdown-item
|
||||||
|
command="handleDefinitionList"
|
||||||
|
v-if="checkPermi(['bpm:process-definition:query'])"
|
||||||
|
>
|
||||||
|
历史
|
||||||
|
</el-dropdown-item>
|
||||||
|
<el-dropdown-item
|
||||||
|
command="handleChangeState"
|
||||||
|
v-if="checkPermi(['bpm:model:update']) && scope.row.processDefinition"
|
||||||
|
:disabled="!isManagerUser(scope.row)"
|
||||||
|
>
|
||||||
|
{{ scope.row.processDefinition.suspensionState === 1 ? '停用' : '启用' }}
|
||||||
|
</el-dropdown-item>
|
||||||
|
<el-dropdown-item
|
||||||
|
type="danger"
|
||||||
|
command="handleDelete"
|
||||||
|
v-if="checkPermi(['bpm:model:delete'])"
|
||||||
|
:disabled="!isManagerUser(scope.row)"
|
||||||
|
>
|
||||||
|
删除
|
||||||
|
</el-dropdown-item>
|
||||||
|
</el-dropdown-menu>
|
||||||
|
</template>
|
||||||
|
</el-dropdown>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
<!-- 分页 -->
|
||||||
|
<Pagination
|
||||||
|
:total="total"
|
||||||
|
v-model:page="queryParams.pageNo"
|
||||||
|
v-model:limit="queryParams.pageSize"
|
||||||
|
@pagination="getList"
|
||||||
|
/>
|
||||||
|
</ContentWrap>
|
||||||
|
|
||||||
|
<!-- 表单弹窗:添加/修改流程 -->
|
||||||
|
<ModelForm ref="formRef" @success="getList" />
|
||||||
|
|
||||||
|
<!-- 弹窗:表单详情 -->
|
||||||
|
<Dialog title="表单详情" v-model="formDetailVisible" width="800">
|
||||||
|
<form-create :rule="formDetailPreview.rule" :option="formDetailPreview.option" />
|
||||||
|
</Dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { formatDate } from '@/utils/formatTime'
|
||||||
|
import * as ModelApi from '@/api/bpm/model'
|
||||||
|
import * as FormApi from '@/api/bpm/form'
|
||||||
|
import ModelForm from './ModelForm.vue'
|
||||||
|
import { setConfAndFields2 } from '@/utils/formCreate'
|
||||||
|
import { CategoryApi } from '@/api/bpm/category'
|
||||||
|
import { BpmModelType } from '@/utils/constants'
|
||||||
|
import { checkPermi } from '@/utils/permission'
|
||||||
|
import { useUserStoreWithOut } from '@/store/modules/user'
|
||||||
|
|
||||||
|
defineOptions({ name: 'BpmModel' })
|
||||||
|
|
||||||
|
const message = useMessage() // 消息弹窗
|
||||||
|
const { t } = useI18n() // 国际化
|
||||||
|
const { push } = useRouter() // 路由
|
||||||
|
const userStore = useUserStoreWithOut() // 用户信息缓存
|
||||||
|
|
||||||
|
const loading = ref(true) // 列表的加载中
|
||||||
|
const total = ref(0) // 列表的总页数
|
||||||
|
const list = ref([]) // 列表的数据
|
||||||
|
const queryParams = reactive({
|
||||||
|
pageNo: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
key: undefined,
|
||||||
|
name: undefined,
|
||||||
|
category: undefined
|
||||||
|
})
|
||||||
|
const queryFormRef = ref() // 搜索的表单
|
||||||
|
const categoryList = ref([]) // 流程分类列表
|
||||||
|
|
||||||
|
/** 查询列表 */
|
||||||
|
const getList = async () => {
|
||||||
|
loading.value = true
|
||||||
|
try {
|
||||||
|
const data = await ModelApi.getModelPage(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 handleCommand = (command: string, row: any) => {
|
||||||
|
switch (command) {
|
||||||
|
case 'handleDefinitionList':
|
||||||
|
handleDefinitionList(row)
|
||||||
|
break
|
||||||
|
case 'handleDelete':
|
||||||
|
handleDelete(row)
|
||||||
|
break
|
||||||
|
case 'handleChangeState':
|
||||||
|
handleChangeState(row)
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 添加/修改操作 */
|
||||||
|
const formRef = ref()
|
||||||
|
const openForm = (type: string, id?: number) => {
|
||||||
|
formRef.value.open(type, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 删除按钮操作 */
|
||||||
|
const handleDelete = async (row: any) => {
|
||||||
|
try {
|
||||||
|
// 删除的二次确认
|
||||||
|
await message.delConfirm()
|
||||||
|
// 发起删除
|
||||||
|
await ModelApi.deleteModel(row.id)
|
||||||
|
message.success(t('common.delSuccess'))
|
||||||
|
// 刷新列表
|
||||||
|
await getList()
|
||||||
|
} catch {}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 更新状态操作 */
|
||||||
|
const handleChangeState = async (row: any) => {
|
||||||
|
const state = row.processDefinition.suspensionState
|
||||||
|
const newState = state === 1 ? 2 : 1
|
||||||
|
try {
|
||||||
|
// 修改状态的二次确认
|
||||||
|
const id = row.id
|
||||||
|
debugger
|
||||||
|
const statusState = state === 1 ? '停用' : '启用'
|
||||||
|
const content = '是否确认' + statusState + '流程名字为"' + row.name + '"的数据项?'
|
||||||
|
await message.confirm(content)
|
||||||
|
// 发起修改状态
|
||||||
|
await ModelApi.updateModelState(id, newState)
|
||||||
|
message.success(statusState + '成功')
|
||||||
|
// 刷新列表
|
||||||
|
await getList()
|
||||||
|
} catch {}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 设计流程 */
|
||||||
|
const handleDesign = (row: any) => {
|
||||||
|
if (row.type == BpmModelType.BPMN) {
|
||||||
|
push({
|
||||||
|
name: 'BpmModelEditor',
|
||||||
|
query: {
|
||||||
|
modelId: row.id
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
push({
|
||||||
|
name: 'SimpleWorkflowDesignEditor',
|
||||||
|
query: {
|
||||||
|
modelId: row.id
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 发布流程 */
|
||||||
|
const handleDeploy = async (row: any) => {
|
||||||
|
try {
|
||||||
|
// 删除的二次确认
|
||||||
|
await message.confirm('是否部署该流程!!')
|
||||||
|
// 发起部署
|
||||||
|
await ModelApi.deployModel(row.id)
|
||||||
|
message.success(t('部署成功'))
|
||||||
|
// 刷新列表
|
||||||
|
await getList()
|
||||||
|
} catch {}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 跳转到指定流程定义列表 */
|
||||||
|
const handleDefinitionList = (row) => {
|
||||||
|
push({
|
||||||
|
name: 'BpmProcessDefinition',
|
||||||
|
query: {
|
||||||
|
key: row.key
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 流程表单的详情按钮操作 */
|
||||||
|
const formDetailVisible = ref(false)
|
||||||
|
const formDetailPreview = ref({
|
||||||
|
rule: [],
|
||||||
|
option: {}
|
||||||
|
})
|
||||||
|
const handleFormDetail = async (row: any) => {
|
||||||
|
if (row.formType == 10) {
|
||||||
|
// 设置表单
|
||||||
|
const data = await FormApi.getForm(row.formId)
|
||||||
|
setConfAndFields2(formDetailPreview, data.conf, data.fields)
|
||||||
|
// 弹窗打开
|
||||||
|
formDetailVisible.value = true
|
||||||
|
} else {
|
||||||
|
await push({
|
||||||
|
path: row.formCustomCreatePath
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 判断是否可以操作 */
|
||||||
|
const isManagerUser = (row: any) => {
|
||||||
|
const userId = userStore.getUser.id
|
||||||
|
return row.managerUserIds && row.managerUserIds.includes(userId)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 初始化 **/
|
||||||
|
onMounted(async () => {
|
||||||
|
await getList()
|
||||||
|
// 查询流程分类列表
|
||||||
|
categoryList.value = await CategoryApi.getCategorySimpleList()
|
||||||
|
})
|
||||||
|
</script>
|
|
@ -1,8 +1,5 @@
|
||||||
<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' })
|
||||||
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({
|
||||||
|
|
|
@ -391,7 +391,7 @@
|
||||||
</div>
|
</div>
|
||||||
</el-popover>
|
</el-popover>
|
||||||
|
|
||||||
<!--【撤消】按钮 这个对应发起人的取消, 只有发起人可以取消 -->
|
<!--【取消】按钮 这个对应发起人的取消, 只有发起人可以取消 -->
|
||||||
<el-popover
|
<el-popover
|
||||||
:visible="popOverVisible.cancel"
|
:visible="popOverVisible.cancel"
|
||||||
placement="top-start"
|
placement="top-start"
|
||||||
|
@ -403,7 +403,7 @@
|
||||||
>
|
>
|
||||||
<template #reference>
|
<template #reference>
|
||||||
<div @click="openPopover('cancel')" class="hover-bg-gray-100 rounded-xl p-6px">
|
<div @click="openPopover('cancel')" class="hover-bg-gray-100 rounded-xl p-6px">
|
||||||
<Icon :size="14" icon="fa:mail-reply" /> 撤消
|
<Icon :size="14" icon="fa:mail-reply" /> 取消
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<div class="flex flex-col flex-1 pt-20px px-20px" v-loading="formLoading">
|
<div class="flex flex-col flex-1 pt-20px px-20px" v-loading="formLoading">
|
||||||
|
@ -415,20 +415,20 @@
|
||||||
:rules="genericRule"
|
:rules="genericRule"
|
||||||
label-width="100px"
|
label-width="100px"
|
||||||
>
|
>
|
||||||
<el-form-item label="撤消理由" prop="cancelReason">
|
<el-form-item label="取消理由" prop="cancelReason">
|
||||||
<span class="text-#878c93 text-12px"> 撤消后,该审批流程将自动结束</span>
|
<span class="text-#878c93 text-12px"> 取消后,该审批流程将自动结束</span>
|
||||||
<el-input
|
<el-input
|
||||||
v-model="genericForm.cancelReason"
|
v-model="genericForm.cancelReason"
|
||||||
clearable
|
clearable
|
||||||
placeholder="请输入撤消理由"
|
placeholder="请输入取消理由"
|
||||||
type="textarea"
|
type="textarea"
|
||||||
:rows="3"
|
:rows="3"
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item>
|
<el-form-item>
|
||||||
<el-button :disabled="formLoading" type="primary" @click="handleCancel()"
|
<el-button :disabled="formLoading" type="primary" @click="handleCancel()">
|
||||||
>撤消</el-button
|
取消
|
||||||
>
|
</el-button>
|
||||||
<el-button @click="popOverVisible.cancel = false"> 取消 </el-button>
|
<el-button @click="popOverVisible.cancel = false"> 取消 </el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
|
@ -499,7 +499,7 @@ const formRef = ref()
|
||||||
const genericRule = reactive({
|
const genericRule = reactive({
|
||||||
reason: [{ required: true, message: '审批意见不能为空', trigger: 'blur' }],
|
reason: [{ required: true, message: '审批意见不能为空', trigger: 'blur' }],
|
||||||
returnReason: [{ required: true, message: '退回理由不能为空', trigger: 'blur' }],
|
returnReason: [{ required: true, message: '退回理由不能为空', trigger: 'blur' }],
|
||||||
cancelReason: [{ required: true, message: '撤消理由不能为空', trigger: 'blur' }],
|
cancelReason: [{ required: true, message: '取消理由不能为空', trigger: 'blur' }],
|
||||||
copyUserIds: [{ required: true, message: '抄送人不能为空', trigger: 'change' }],
|
copyUserIds: [{ required: true, message: '抄送人不能为空', trigger: 'change' }],
|
||||||
assigneeUserId: [{ required: true, message: '新审批人不能为空', trigger: 'change' }],
|
assigneeUserId: [{ required: true, message: '新审批人不能为空', trigger: 'change' }],
|
||||||
delegateUserId: [{ required: true, message: '接收人不能为空', trigger: 'change' }],
|
delegateUserId: [{ required: true, message: '接收人不能为空', trigger: 'change' }],
|
||||||
|
|
|
@ -1,9 +1,6 @@
|
||||||
<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
|
||||||
|
@ -26,14 +23,6 @@
|
||||||
<p style="font-weight: 700">
|
<p style="font-weight: 700">
|
||||||
审批任务:{{ item.name }}
|
审批任务:{{ item.name }}
|
||||||
<dict-tag :type="DICT_TYPE.BPM_TASK_STATUS" :value="item.status" />
|
<dict-tag :type="DICT_TYPE.BPM_TASK_STATUS" :value="item.status" />
|
||||||
<el-button
|
|
||||||
class="ml-10px"
|
|
||||||
v-if="!isEmpty(item.children)"
|
|
||||||
@click="openChildrenTask(item)"
|
|
||||||
size="small"
|
|
||||||
>
|
|
||||||
<Icon icon="ep:memo" /> 子任务
|
|
||||||
</el-button>
|
|
||||||
<el-button
|
<el-button
|
||||||
class="ml-10px"
|
class="ml-10px"
|
||||||
size="small"
|
size="small"
|
||||||
|
@ -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 @@
|
||||||
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' })
|
||||||
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) => {
|
||||||
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({
|
||||||
|
|
|
@ -17,42 +17,21 @@
|
||||||
<div class="flex flex-col pr-2">
|
<div class="flex flex-col pr-2">
|
||||||
<div class="position-relative" v-if="task.assigneeUser || task.ownerUser">
|
<div class="position-relative" v-if="task.assigneeUser || task.ownerUser">
|
||||||
<!-- 信息:头像 -->
|
<!-- 信息:头像 -->
|
||||||
<el-tooltip
|
<el-avatar
|
||||||
:content="task.reason"
|
|
||||||
placement="bottom"
|
|
||||||
v-if="task.assigneeUser && task.assigneeUser.avatar"
|
v-if="task.assigneeUser && task.assigneeUser.avatar"
|
||||||
effect="light"
|
:size="36"
|
||||||
>
|
:src="task.assigneeUser.avatar"
|
||||||
<el-avatar :size="36" :src="task.assigneeUser.avatar" />
|
/>
|
||||||
</el-tooltip>
|
<el-avatar v-else-if="task.assigneeUser && task.assigneeUser.nickname">
|
||||||
<el-tooltip
|
{{ task.assigneeUser.nickname.substring(0, 1) }}
|
||||||
:content="task.reason"
|
</el-avatar>
|
||||||
placement="bottom"
|
<el-avatar
|
||||||
v-else-if="task.assigneeUser && task.assigneeUser.nickname"
|
|
||||||
effect="light"
|
|
||||||
>
|
|
||||||
<el-avatar>
|
|
||||||
{{ task.assigneeUser.nickname.substring(0, 1) }}
|
|
||||||
</el-avatar>
|
|
||||||
</el-tooltip>
|
|
||||||
<el-tooltip
|
|
||||||
:content="task.reason"
|
|
||||||
placement="bottom"
|
|
||||||
v-else-if="task.ownerUser && task.ownerUser.avatar"
|
v-else-if="task.ownerUser && task.ownerUser.avatar"
|
||||||
effect="light"
|
:src="task.ownerUser.avatar"
|
||||||
>
|
/>
|
||||||
<el-avatar :src="task.ownerUser.avatar" />
|
<el-avatar v-else-if="task.ownerUser && task.ownerUser.nickname">
|
||||||
</el-tooltip>
|
{{ task.ownerUser.nickname.substring(0, 1) }}
|
||||||
<el-tooltip
|
</el-avatar>
|
||||||
:content="task.reason"
|
|
||||||
placement="bottom"
|
|
||||||
v-else-if="task.ownerUser && task.ownerUser.nickname"
|
|
||||||
effect="light"
|
|
||||||
>
|
|
||||||
<el-avatar>
|
|
||||||
{{ task.ownerUser.nickname.substring(0, 1) }}
|
|
||||||
</el-avatar>
|
|
||||||
</el-tooltip>
|
|
||||||
<!-- 信息:任务 ICON -->
|
<!-- 信息:任务 ICON -->
|
||||||
<div
|
<div
|
||||||
class="position-absolute top-26px left-26px bg-#fff rounded-full flex items-center p-2px"
|
class="position-absolute top-26px left-26px bg-#fff rounded-full flex items-center p-2px"
|
||||||
|
@ -78,16 +57,12 @@
|
||||||
>
|
>
|
||||||
{{ task.ownerUser.nickname }}
|
{{ task.ownerUser.nickname }}
|
||||||
</div>
|
</div>
|
||||||
<!--
|
<div
|
||||||
<div v-if="task.reason" class="text-#a5a5a5 my-4px text-12px flex items-center w-100%">
|
v-if="task.reason && activity.nodeType === NodeType.USER_TASK_NODE"
|
||||||
<div
|
class="text-#a5a5a5 text-13px mt-1"
|
||||||
:title="task.reason"
|
>
|
||||||
class="text-truncate w-200px border-1px border-#a5a5a5 border-dashed rounded py-5px px-15px text-#2d2d2d"
|
审批意见:{{ task.reason }}
|
||||||
>
|
|
||||||
{{ task.reason }}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
-->
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -131,20 +106,6 @@
|
||||||
>
|
>
|
||||||
{{ getApprovalNodeTime(activity) }}
|
{{ getApprovalNodeTime(activity) }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- TODO @jason:审批意见,要展示哈。 -->
|
|
||||||
<!-- <div class="color-#a1a6ae text-12px mb-10px"> {{ activity.assigneeUser.nickname }}</div>
|
|
||||||
<div v-if="activity.opinion" class="text-#a5a5a5 text-12px w-100%">
|
|
||||||
<div class="mb-5px">审批意见:</div>
|
|
||||||
<div
|
|
||||||
class="w-100% border-1px border-#a5a5a5 border-dashed rounded py-5px px-15px text-#2d2d2d"
|
|
||||||
>
|
|
||||||
{{ activity.opinion }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div v-if="activity.createTime" class="text-#a5a5a5 text-13px">
|
|
||||||
{{ formatDate(activity.createTime) }}
|
|
||||||
</div> -->
|
|
||||||
</div>
|
</div>
|
||||||
</el-timeline-item>
|
</el-timeline-item>
|
||||||
</el-timeline>
|
</el-timeline>
|
||||||
|
@ -219,8 +180,11 @@ const getApprovalNodeColor = (taskStatus: number) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const getApprovalNodeTime = (node: ProcessInstanceApi.ApprovalNodeInfo) => {
|
const getApprovalNodeTime = (node: ProcessInstanceApi.ApprovalNodeInfo) => {
|
||||||
|
if (node.nodeType === NodeType.START_USER_NODE && node.startTime) {
|
||||||
|
return `发起时间:${formatDate(node.startTime)}`
|
||||||
|
}
|
||||||
if (node.endTime) {
|
if (node.endTime) {
|
||||||
return `结束时间:${formatDate(node.endTime)}`
|
return `审批时间:${formatDate(node.endTime)}`
|
||||||
}
|
}
|
||||||
if (node.startTime) {
|
if (node.startTime) {
|
||||||
return `创建时间:${formatDate(node.startTime)}`
|
return `创建时间:${formatDate(node.startTime)}`
|
||||||
|
|
|
@ -1,89 +0,0 @@
|
||||||
<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>
|
|
|
@ -1,90 +0,0 @@
|
||||||
<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>
|
|
|
@ -1,99 +0,0 @@
|
||||||
<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 = () => {
|
||||||
}
|
}
|
||||||
formRef.value?.resetFields()
|
formRef.value?.resetFields()
|
||||||
}
|
}
|
||||||
|
// TODO @jason:新界面搞完,可以删除
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -1,89 +0,0 @@
|
||||||
<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>
|
|
|
@ -1,216 +1,163 @@
|
||||||
<template>
|
<template>
|
||||||
<ContentWrap>
|
<ContentWrap :bodyStyle="{ padding: '10px 20px 0' }" class="position-relative">
|
||||||
<!-- 审批信息 -->
|
<div class="processInstance-wrap-main">
|
||||||
<el-card
|
<el-scrollbar>
|
||||||
v-for="(item, index) in runningTasks"
|
<img
|
||||||
:key="index"
|
class="position-absolute right-20px"
|
||||||
v-loading="processInstanceLoading"
|
width="150"
|
||||||
class="box-card"
|
:src="auditIcons[processInstance.status]"
|
||||||
>
|
alt=""
|
||||||
<template #header>
|
|
||||||
<span class="el-icon-picture-outline">审批任务【{{ item.name }}】</span>
|
|
||||||
</template>
|
|
||||||
<el-col :offset="6" :span="16">
|
|
||||||
<el-form
|
|
||||||
:ref="'form' + index"
|
|
||||||
:model="auditForms[index]"
|
|
||||||
:rules="auditRule"
|
|
||||||
label-width="100px"
|
|
||||||
>
|
|
||||||
<el-form-item v-if="processInstance && processInstance.name" label="流程名">
|
|
||||||
{{ processInstance.name }}
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item v-if="processInstance && processInstance.startUser" label="流程发起人">
|
|
||||||
{{ processInstance?.startUser.nickname }}
|
|
||||||
<el-tag size="small" type="info">{{ processInstance?.startUser.deptName }}</el-tag>
|
|
||||||
</el-form-item>
|
|
||||||
<el-card v-if="runningTasks[index].formId > 0" class="mb-15px !-mt-10px">
|
|
||||||
<template #header>
|
|
||||||
<span class="el-icon-picture-outline">
|
|
||||||
填写表单【{{ runningTasks[index]?.formName }}】
|
|
||||||
</span>
|
|
||||||
</template>
|
|
||||||
<form-create
|
|
||||||
v-model="approveForms[index].value"
|
|
||||||
v-model:api="approveFormFApis[index]"
|
|
||||||
:option="approveForms[index].option"
|
|
||||||
:rule="approveForms[index].rule"
|
|
||||||
/>
|
|
||||||
</el-card>
|
|
||||||
<el-form-item label="审批建议" prop="reason">
|
|
||||||
<el-input
|
|
||||||
v-model="auditForms[index].reason"
|
|
||||||
placeholder="请输入审批建议"
|
|
||||||
type="textarea"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="抄送人" prop="copyUserIds">
|
|
||||||
<el-select v-model="auditForms[index].copyUserIds" multiple placeholder="请选择抄送人">
|
|
||||||
<el-option
|
|
||||||
v-for="itemx in userOptions"
|
|
||||||
:key="itemx.id"
|
|
||||||
:label="itemx.nickname"
|
|
||||||
:value="itemx.id"
|
|
||||||
/>
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
</el-form>
|
|
||||||
<div style="margin-bottom: 20px; margin-left: 10%; font-size: 14px">
|
|
||||||
<!-- TODO @jason:建议搞个 if 来判断,替代现有的 !item.buttonsSetting || item.buttonsSetting[OpsButtonType.APPROVE]?.enable -->
|
|
||||||
<el-button
|
|
||||||
type="success"
|
|
||||||
v-if="!item.buttonsSetting || item.buttonsSetting[OperationButtonType.APPROVE]?.enable"
|
|
||||||
@click="handleAudit(item, true)"
|
|
||||||
>
|
|
||||||
<Icon icon="ep:select" />
|
|
||||||
<!-- TODO @jason:这个也是类似哈,搞个方法来生成名字 -->
|
|
||||||
{{
|
|
||||||
item.buttonsSetting?.[OperationButtonType.APPROVE]?.displayName ||
|
|
||||||
OPERATION_BUTTON_NAME.get(OperationButtonType.APPROVE)
|
|
||||||
}}
|
|
||||||
</el-button>
|
|
||||||
<el-button
|
|
||||||
v-if="!item.buttonsSetting || item.buttonsSetting[OperationButtonType.REJECT]?.enable"
|
|
||||||
type="danger"
|
|
||||||
@click="handleAudit(item, false)"
|
|
||||||
>
|
|
||||||
<Icon icon="ep:close" />
|
|
||||||
{{
|
|
||||||
item.buttonsSetting?.[OperationButtonType.REJECT].displayName ||
|
|
||||||
OPERATION_BUTTON_NAME.get(OperationButtonType.REJECT)
|
|
||||||
}}
|
|
||||||
</el-button>
|
|
||||||
<el-button
|
|
||||||
v-if="!item.buttonsSetting || item.buttonsSetting[OperationButtonType.TRANSFER]?.enable"
|
|
||||||
type="primary"
|
|
||||||
@click="openTaskUpdateAssigneeForm(item.id)"
|
|
||||||
>
|
|
||||||
<Icon icon="ep:edit" />
|
|
||||||
{{
|
|
||||||
item.buttonsSetting?.[OperationButtonType.TRANSFER]?.displayName ||
|
|
||||||
OPERATION_BUTTON_NAME.get(OperationButtonType.TRANSFER)
|
|
||||||
}}
|
|
||||||
</el-button>
|
|
||||||
<el-button
|
|
||||||
v-if="!item.buttonsSetting || item.buttonsSetting[OperationButtonType.DELEGATE]?.enable"
|
|
||||||
type="primary"
|
|
||||||
@click="handleDelegate(item)"
|
|
||||||
>
|
|
||||||
<Icon icon="ep:position" />
|
|
||||||
{{
|
|
||||||
item.buttonsSetting?.[OperationButtonType.DELEGATE]?.displayName ||
|
|
||||||
OPERATION_BUTTON_NAME.get(OperationButtonType.DELEGATE)
|
|
||||||
}}
|
|
||||||
</el-button>
|
|
||||||
<el-button
|
|
||||||
v-if="!item.buttonsSetting || item.buttonsSetting[OperationButtonType.ADD_SIGN]?.enable"
|
|
||||||
type="primary"
|
|
||||||
@click="handleSign(item)"
|
|
||||||
>
|
|
||||||
<Icon icon="ep:plus" />
|
|
||||||
{{
|
|
||||||
item.buttonsSetting?.[OperationButtonType.ADD_SIGN]?.displayName ||
|
|
||||||
OPERATION_BUTTON_NAME.get(OperationButtonType.ADD_SIGN)
|
|
||||||
}}
|
|
||||||
</el-button>
|
|
||||||
<el-button
|
|
||||||
v-if="!item.buttonsSetting || item.buttonsSetting[OperationButtonType.RETURN]?.enable"
|
|
||||||
type="warning"
|
|
||||||
@click="handleBack(item)"
|
|
||||||
>
|
|
||||||
<Icon icon="ep:back" />
|
|
||||||
{{
|
|
||||||
item.buttonsSetting?.[OperationButtonType.RETURN]?.displayName ||
|
|
||||||
OPERATION_BUTTON_NAME.get(OperationButtonType.RETURN)
|
|
||||||
}}
|
|
||||||
</el-button>
|
|
||||||
</div>
|
|
||||||
</el-col>
|
|
||||||
</el-card>
|
|
||||||
|
|
||||||
<!-- 申请信息 -->
|
|
||||||
<el-card v-loading="processInstanceLoading" class="box-card">
|
|
||||||
<template #header>
|
|
||||||
<span class="el-icon-document">申请信息【{{ processInstance.name }}】</span>
|
|
||||||
</template>
|
|
||||||
<!-- 情况一:流程表单 -->
|
|
||||||
<el-col v-if="processInstance?.processDefinition?.formType === 10" :offset="6" :span="16">
|
|
||||||
<form-create
|
|
||||||
v-model="detailForm.value"
|
|
||||||
v-model:api="fApi"
|
|
||||||
:option="detailForm.option"
|
|
||||||
:rule="detailForm.rule"
|
|
||||||
/>
|
/>
|
||||||
</el-col>
|
<div class="text-#878c93 h-15px">编号:{{ id }}</div>
|
||||||
<!-- 情况二:业务表单 -->
|
<el-divider class="!my-8px" />
|
||||||
<div v-if="processInstance?.processDefinition?.formType === 20">
|
<div class="flex items-center gap-5 mb-10px h-40px">
|
||||||
<BusinessFormComponent :id="processInstance.businessKey" />
|
<div class="text-26px font-bold mb-5px">{{ processInstance.name }}</div>
|
||||||
</div>
|
<dict-tag
|
||||||
</el-card>
|
v-if="processInstance.status"
|
||||||
|
:type="DICT_TYPE.BPM_PROCESS_INSTANCE_STATUS"
|
||||||
|
:value="processInstance.status"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- 审批记录 -->
|
<div class="flex items-center gap-5 mb-10px text-13px h-35px">
|
||||||
<ProcessInstanceTaskList
|
<div
|
||||||
:loading="tasksLoad"
|
class="bg-gray-100 h-35px rounded-3xl flex items-center p-8px gap-2 dark:color-gray-600"
|
||||||
:process-instance="processInstance"
|
>
|
||||||
:tasks="tasks"
|
<el-avatar
|
||||||
@refresh="getTaskList"
|
:size="28"
|
||||||
/>
|
v-if="processInstance?.startUser?.avatar"
|
||||||
|
:src="processInstance?.startUser?.avatar"
|
||||||
|
/>
|
||||||
|
<el-avatar :size="28" v-else-if="processInstance?.startUser?.nickname">
|
||||||
|
{{ processInstance?.startUser?.nickname.substring(0, 1) }}
|
||||||
|
</el-avatar>
|
||||||
|
{{ processInstance?.startUser?.nickname }}
|
||||||
|
</div>
|
||||||
|
<div class="text-#878c93"> {{ formatDate(processInstance.startTime) }} 提交 </div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- 高亮流程图 -->
|
<el-tabs v-model="activeTab">
|
||||||
<ProcessInstanceBpmnViewer :id="`${id}`" :loading="processInstanceLoading" />
|
<!-- 表单信息 -->
|
||||||
|
<el-tab-pane label="审批详情" name="form">
|
||||||
|
<div class="form-scroll-area">
|
||||||
|
<el-scrollbar>
|
||||||
|
<el-row>
|
||||||
|
<el-col :span="17" class="!flex !flex-col formCol">
|
||||||
|
<!-- 表单信息 -->
|
||||||
|
<div
|
||||||
|
v-loading="processInstanceLoading"
|
||||||
|
class="form-box flex flex-col mb-30px flex-1"
|
||||||
|
>
|
||||||
|
<!-- 情况一:流程表单 -->
|
||||||
|
<el-col v-if="processDefinition?.formType === 10">
|
||||||
|
<form-create
|
||||||
|
v-model="detailForm.value"
|
||||||
|
v-model:api="fApi"
|
||||||
|
:option="detailForm.option"
|
||||||
|
:rule="detailForm.rule"
|
||||||
|
/>
|
||||||
|
</el-col>
|
||||||
|
<!-- 情况二:业务表单 -->
|
||||||
|
<div v-if="processDefinition?.formType === 20">
|
||||||
|
<BusinessFormComponent :id="processInstance.businessKey" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="7">
|
||||||
|
<!-- 审批记录时间线 -->
|
||||||
|
<ProcessInstanceTimeline ref="timelineRef" :approve-nodes="approveNodes" />
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</el-scrollbar>
|
||||||
|
</div>
|
||||||
|
</el-tab-pane>
|
||||||
|
|
||||||
<!-- 弹窗:转派审批人 -->
|
<!-- 流程图 -->
|
||||||
<TaskTransferForm ref="taskTransferFormRef" @success="getDetail" />
|
<el-tab-pane label="流程图" name="diagram">
|
||||||
<!-- 弹窗:退回节点 -->
|
<div class="form-scroll-area">
|
||||||
<TaskReturnForm ref="taskReturnFormRef" @success="getDetail" />
|
<ProcessInstanceBpmnViewer
|
||||||
<!-- 弹窗:委派,将任务委派给别人处理,处理完成后,会重新回到原审批人手中-->
|
:id="`${id}`"
|
||||||
<TaskDelegateForm ref="taskDelegateForm" @success="getDetail" />
|
:loading="processInstanceLoading"
|
||||||
<!-- 弹窗:加签,当前任务审批人为A,向前加签选了一个C,则需要C先审批,然后再是A审批,向后加签B,A审批完,需要B再审批完,才算完成这个任务节点 -->
|
:show-header="false"
|
||||||
<TaskSignCreateForm ref="taskSignCreateFormRef" @success="getDetail" />
|
/>
|
||||||
|
</div>
|
||||||
|
</el-tab-pane>
|
||||||
|
|
||||||
|
<!-- 流转记录 -->
|
||||||
|
<el-tab-pane label="流转记录" name="record">
|
||||||
|
<div class="form-scroll-area">
|
||||||
|
<el-scrollbar>
|
||||||
|
<ProcessInstanceTaskList
|
||||||
|
:loading="tasksLoad"
|
||||||
|
:process-instance="processInstance"
|
||||||
|
:tasks="tasks"
|
||||||
|
:show-header="false"
|
||||||
|
/>
|
||||||
|
</el-scrollbar>
|
||||||
|
</div>
|
||||||
|
</el-tab-pane>
|
||||||
|
|
||||||
|
<!-- 流转评论 TODO 待开发 -->
|
||||||
|
<el-tab-pane label="流转评论" name="comment" v-if="false">
|
||||||
|
<div class="form-scroll-area">
|
||||||
|
<el-scrollbar> 流转评论 </el-scrollbar>
|
||||||
|
</div>
|
||||||
|
</el-tab-pane>
|
||||||
|
</el-tabs>
|
||||||
|
|
||||||
|
<div class="b-t-solid border-t-1px border-[var(--el-border-color)]">
|
||||||
|
<!-- 操作栏按钮 -->
|
||||||
|
<ProcessInstanceOperationButton
|
||||||
|
ref="operationButtonRef"
|
||||||
|
:process-instance="processInstance"
|
||||||
|
:process-definition="processDefinition"
|
||||||
|
:userOptions="userOptions"
|
||||||
|
@success="refresh"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</el-scrollbar>
|
||||||
|
</div>
|
||||||
</ContentWrap>
|
</ContentWrap>
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { useUserStore } from '@/store/modules/user'
|
import { formatDate } from '@/utils/formatTime'
|
||||||
|
import { DICT_TYPE } from '@/utils/dict'
|
||||||
import { setConfAndFields2 } from '@/utils/formCreate'
|
import { setConfAndFields2 } from '@/utils/formCreate'
|
||||||
import type { ApiAttrs } from '@form-create/element-ui/types/config'
|
import type { ApiAttrs } from '@form-create/element-ui/types/config'
|
||||||
import * as DefinitionApi from '@/api/bpm/definition'
|
|
||||||
import * as ProcessInstanceApi from '@/api/bpm/processInstance'
|
import * as ProcessInstanceApi from '@/api/bpm/processInstance'
|
||||||
import * as TaskApi from '@/api/bpm/task'
|
import * as TaskApi from '@/api/bpm/task'
|
||||||
import ProcessInstanceBpmnViewer from './ProcessInstanceBpmnViewer.vue'
|
import ProcessInstanceBpmnViewer from './ProcessInstanceBpmnViewer.vue'
|
||||||
import ProcessInstanceTaskList from './ProcessInstanceTaskList.vue'
|
import ProcessInstanceTaskList from './ProcessInstanceTaskList.vue'
|
||||||
import TaskReturnForm from './dialog/TaskReturnForm.vue'
|
import ProcessInstanceOperationButton from './ProcessInstanceOperationButton.vue'
|
||||||
import TaskDelegateForm from './dialog/TaskDelegateForm.vue'
|
import ProcessInstanceTimeline from './ProcessInstanceTimeline.vue'
|
||||||
import TaskTransferForm from './dialog/TaskTransferForm.vue'
|
|
||||||
import TaskSignCreateForm from './dialog/TaskSignCreateForm.vue'
|
|
||||||
import { registerComponent } from '@/utils/routerHelper'
|
|
||||||
import { isEmpty } from '@/utils/is'
|
|
||||||
import * as UserApi from '@/api/system/user'
|
import * as UserApi from '@/api/system/user'
|
||||||
import {
|
import { FieldPermissionType } from '@/components/SimpleProcessDesignerV2/src/consts'
|
||||||
OperationButtonType,
|
import audit1 from '@/assets/svgs/bpm/audit1.svg'
|
||||||
OPERATION_BUTTON_NAME
|
import audit2 from '@/assets/svgs/bpm/audit2.svg'
|
||||||
} from '@/components/SimpleProcessDesignerV2/src/consts'
|
import audit3 from '@/assets/svgs/bpm/audit3.svg'
|
||||||
|
import audit4 from '@/assets/svgs/bpm/audit4.svg'
|
||||||
|
|
||||||
defineOptions({ name: 'BpmProcessInstanceDetail' })
|
defineOptions({ name: 'BpmProcessInstanceDetail' })
|
||||||
|
const props = defineProps<{
|
||||||
const { query } = useRoute() // 查询参数
|
id: string // 流程实例的编号
|
||||||
|
taskId?: string // 任务编号
|
||||||
|
activityId?: string //流程活动编号,用于抄送查看
|
||||||
|
}>()
|
||||||
const message = useMessage() // 消息弹窗
|
const message = useMessage() // 消息弹窗
|
||||||
const { proxy } = getCurrentInstance() as any
|
|
||||||
|
|
||||||
const userId = useUserStore().getUser.id // 当前登录的编号
|
|
||||||
const id = query.id as unknown as string // 流程实例的编号
|
|
||||||
const processInstanceLoading = ref(false) // 流程实例的加载中
|
const processInstanceLoading = ref(false) // 流程实例的加载中
|
||||||
const processInstance = ref<any>({}) // 流程实例
|
const processInstance = ref<any>({}) // 流程实例
|
||||||
const bpmnXml = ref('') // BPMN XML
|
const processDefinition = ref<any>({}) // 流程定义
|
||||||
|
const timelineRef = ref()
|
||||||
|
// 操作按钮组件 ref
|
||||||
|
const operationButtonRef = ref()
|
||||||
const tasksLoad = ref(true) // 任务的加载中
|
const tasksLoad = ref(true) // 任务的加载中
|
||||||
const tasks = ref<any[]>([]) // 任务列表
|
const tasks = ref<any[]>([]) // 任务列表
|
||||||
// ========== 审批信息 ==========
|
const auditIcons = {
|
||||||
const runningTasks = ref<any[]>([]) // 运行中的任务
|
1: audit1,
|
||||||
const auditForms = ref<any[]>([]) // 审批任务的表单
|
2: audit2,
|
||||||
const auditRule = reactive({
|
3: audit3,
|
||||||
reason: [{ required: true, message: '审批建议不能为空', trigger: 'blur' }]
|
4: audit4
|
||||||
})
|
}
|
||||||
const approveForms = ref<any[]>([]) // 审批通过时,额外的补充信息
|
|
||||||
const approveFormFApis = ref<ApiAttrs[]>([]) // approveForms 的 fAPi
|
|
||||||
|
|
||||||
// ========== 申请信息 ==========
|
// ========== 申请信息 ==========
|
||||||
const fApi = ref<ApiAttrs>() //
|
const fApi = ref<ApiAttrs>() //
|
||||||
|
@ -220,163 +167,106 @@ const detailForm = ref({
|
||||||
value: {}
|
value: {}
|
||||||
}) // 流程实例的表单详情
|
}) // 流程实例的表单详情
|
||||||
|
|
||||||
/** 监听 approveFormFApis,实现它对应的 form-create 初始化后,隐藏掉对应的表单提交按钮 */
|
|
||||||
watch(
|
|
||||||
() => approveFormFApis.value,
|
|
||||||
(value) => {
|
|
||||||
value?.forEach((api) => {
|
|
||||||
api.btn.show(false)
|
|
||||||
api.resetBtn.show(false)
|
|
||||||
})
|
|
||||||
},
|
|
||||||
{
|
|
||||||
deep: true
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
/** 处理审批通过和不通过的操作 */
|
|
||||||
const handleAudit = async (task, pass) => {
|
|
||||||
// 1.1 获得对应表单
|
|
||||||
const index = runningTasks.value.indexOf(task)
|
|
||||||
const auditFormRef = proxy.$refs['form' + index][0]
|
|
||||||
// 1.2 校验表单
|
|
||||||
const elForm = unref(auditFormRef)
|
|
||||||
if (!elForm) return
|
|
||||||
let valid = await elForm.validate()
|
|
||||||
if (!valid) return
|
|
||||||
// 校验申请表单(可编辑字段)
|
|
||||||
// TODO @jason:之前这里是 if (!fApi.value) return;针对业务表单的情况下,会导致没办法审核,可能要看下。我这里改了点,看看是不是还有别的地方兼容性
|
|
||||||
if (fApi.value) {
|
|
||||||
valid = await fApi.value.validate()
|
|
||||||
if (!valid) return
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2.1 提交审批
|
|
||||||
const data = {
|
|
||||||
id: task.id,
|
|
||||||
reason: auditForms.value[index].reason,
|
|
||||||
copyUserIds: auditForms.value[index].copyUserIds
|
|
||||||
}
|
|
||||||
if (pass) {
|
|
||||||
// 审批通过,并且有额外的 approveForm 表单,需要校验 + 拼接到 data 表单里提交
|
|
||||||
const formCreateApi = approveFormFApis.value[index]
|
|
||||||
if (formCreateApi) {
|
|
||||||
await formCreateApi.validate()
|
|
||||||
data.variables = approveForms.value[index].value
|
|
||||||
}
|
|
||||||
// 获取表单可编辑字段的值
|
|
||||||
if (fApi.value) {
|
|
||||||
data.variables = getWritableValueOfForm(task.fieldsPermission)
|
|
||||||
}
|
|
||||||
|
|
||||||
await TaskApi.approveTask(data)
|
|
||||||
message.success('审批通过成功')
|
|
||||||
} else {
|
|
||||||
await TaskApi.rejectTask(data)
|
|
||||||
message.success('审批不通过成功')
|
|
||||||
}
|
|
||||||
// 2.2 加载最新数据
|
|
||||||
getDetail()
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 转派审批人 */
|
|
||||||
const taskTransferFormRef = ref()
|
|
||||||
const openTaskUpdateAssigneeForm = (id: string) => {
|
|
||||||
taskTransferFormRef.value.open(id)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 处理审批退回的操作 */
|
|
||||||
const taskDelegateForm = ref()
|
|
||||||
const handleDelegate = async (task) => {
|
|
||||||
taskDelegateForm.value.open(task.id)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 处理审批退回的操作 */
|
|
||||||
const taskReturnFormRef = ref()
|
|
||||||
const handleBack = async (task: any) => {
|
|
||||||
taskReturnFormRef.value.open(task.id)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 处理审批加签的操作 */
|
|
||||||
const taskSignCreateFormRef = ref()
|
|
||||||
const handleSign = async (task: any) => {
|
|
||||||
taskSignCreateFormRef.value.open(task.id)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 获得详情 */
|
/** 获得详情 */
|
||||||
const getDetail = async () => {
|
const getDetail = () => {
|
||||||
// 1. 获得流程任务列表(审批记录)。 需要先获取任务,表单的权限设置需要根据任务来设置
|
// 1. 获取审批详情
|
||||||
await getTaskList()
|
getApprovalDetail()
|
||||||
// 2. 获得流程实例相关
|
// 2. 获得流程任务列表
|
||||||
getProcessInstance()
|
getTaskList()
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 加载流程实例 */
|
/** 加载流程实例 */
|
||||||
const BusinessFormComponent = ref(null) // 异步组件
|
const BusinessFormComponent = ref<any>(null) // 异步组件
|
||||||
const getProcessInstance = async () => {
|
/** 获取审批详情 */
|
||||||
|
const getApprovalDetail = async () => {
|
||||||
|
processInstanceLoading.value = true
|
||||||
try {
|
try {
|
||||||
processInstanceLoading.value = true
|
const param = {
|
||||||
const data = await ProcessInstanceApi.getProcessInstance(id)
|
processInstanceId: props.id,
|
||||||
|
activityId: props.activityId,
|
||||||
|
taskId: props.taskId
|
||||||
|
}
|
||||||
|
const data = await ProcessInstanceApi.getApprovalDetail(param)
|
||||||
if (!data) {
|
if (!data) {
|
||||||
|
message.error('查询不到审批详情信息!')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!data.processDefinition || !data.processInstance) {
|
||||||
message.error('查询不到流程信息!')
|
message.error('查询不到流程信息!')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
processInstance.value = data
|
processInstance.value = data.processInstance
|
||||||
|
processDefinition.value = data.processDefinition
|
||||||
|
|
||||||
// 设置表单信息
|
// 设置表单信息
|
||||||
const processDefinition = data.processDefinition
|
if (processDefinition.value.formType === 10) {
|
||||||
if (processDefinition.formType === 10) {
|
// 获取表单字段权限
|
||||||
|
const formFieldsPermission = data.formFieldsPermission
|
||||||
|
|
||||||
if (detailForm.value.rule.length > 0) {
|
if (detailForm.value.rule.length > 0) {
|
||||||
detailForm.value.value = data.formVariables
|
// 避免刷新 form-create 显示不了,
|
||||||
|
detailForm.value.value = processInstance.value.formVariables
|
||||||
} else {
|
} else {
|
||||||
setConfAndFields2(
|
setConfAndFields2(
|
||||||
detailForm,
|
detailForm,
|
||||||
processDefinition.formConf,
|
processDefinition.value.formConf,
|
||||||
processDefinition.formFields,
|
processDefinition.value.formFields,
|
||||||
data.formVariables
|
processInstance.value.formVariables
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
nextTick().then(() => {
|
nextTick().then(() => {
|
||||||
fApi.value?.btn.show(false)
|
fApi.value?.btn.show(false)
|
||||||
fApi.value?.resetBtn.show(false)
|
fApi.value?.resetBtn.show(false)
|
||||||
|
//@ts-ignore
|
||||||
fApi.value?.disabled(true)
|
fApi.value?.disabled(true)
|
||||||
// 设置表单权限。后续需要改造成。只处理一个运行中的任务
|
// 设置表单字段权限
|
||||||
if (runningTasks.value.length > 0) {
|
if (formFieldsPermission) {
|
||||||
const task = runningTasks.value.at(0)
|
Object.keys(data.formFieldsPermission).forEach((item) => {
|
||||||
if (task.fieldsPermission) {
|
setFieldPermission(item, formFieldsPermission[item])
|
||||||
Object.keys(task.fieldsPermission).forEach((item) => {
|
})
|
||||||
setFieldPermission(item, task.fieldsPermission[item])
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
} else {
|
|
||||||
// 注意:data.processDefinition.formCustomViewPath 是组件的全路径,例如说:/crm/contract/detail/index.vue
|
|
||||||
BusinessFormComponent.value = registerComponent(data.processDefinition.formCustomViewPath)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 加载流程图
|
// 获取审批节点,显示 Timeline 的数据
|
||||||
bpmnXml.value = (
|
approveNodes.value = data.approveNodes
|
||||||
await DefinitionApi.getProcessDefinition(processDefinition.id as number)
|
|
||||||
)?.bpmnXml
|
// 获取待办任务显示操作按钮
|
||||||
|
operationButtonRef.value?.loadTodoTask(data.todoTask)
|
||||||
} finally {
|
} finally {
|
||||||
processInstanceLoading.value = false
|
processInstanceLoading.value = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 审批节点信息
|
||||||
|
const approveNodes = ref<ProcessInstanceApi.ApprovalNodeInfo[]>([])
|
||||||
|
/**
|
||||||
|
* 设置表单权限
|
||||||
|
*/
|
||||||
|
const setFieldPermission = (field: string, permission: string) => {
|
||||||
|
if (permission === FieldPermissionType.READ) {
|
||||||
|
//@ts-ignore
|
||||||
|
fApi.value?.disabled(true, field)
|
||||||
|
}
|
||||||
|
if (permission === FieldPermissionType.WRITE) {
|
||||||
|
//@ts-ignore
|
||||||
|
fApi.value?.disabled(false, field)
|
||||||
|
}
|
||||||
|
if (permission === FieldPermissionType.NONE) {
|
||||||
|
//@ts-ignore
|
||||||
|
fApi.value?.hidden(true, field)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** 加载任务列表 */
|
/** 加载任务列表 */
|
||||||
const getTaskList = async () => {
|
const getTaskList = async () => {
|
||||||
runningTasks.value = []
|
|
||||||
auditForms.value = []
|
|
||||||
approveForms.value = []
|
|
||||||
approveFormFApis.value = []
|
|
||||||
try {
|
try {
|
||||||
// 获得未取消的任务
|
// 获得未取消的任务
|
||||||
tasksLoad.value = true
|
tasksLoad.value = true
|
||||||
const data = await TaskApi.getTaskListByProcessInstanceId(id)
|
const data = await TaskApi.getTaskListByProcessInstanceId(props.id)
|
||||||
tasks.value = []
|
tasks.value = []
|
||||||
// 1.1 移除已取消的审批
|
// 1.1 移除已取消的审批
|
||||||
data.forEach((task) => {
|
data.forEach((task: any) => {
|
||||||
if (task.status !== 4) {
|
if (task.status !== 4) {
|
||||||
tasks.value.push(task)
|
tasks.value.push(task)
|
||||||
}
|
}
|
||||||
|
@ -395,77 +285,21 @@ const getTaskList = async () => {
|
||||||
return b.createTime - a.createTime
|
return b.createTime - a.createTime
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// 获得需要自己审批的任务
|
|
||||||
loadRunningTask(tasks.value)
|
|
||||||
} finally {
|
} finally {
|
||||||
tasksLoad.value = false
|
tasksLoad.value = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 设置 runningTasks 中的任务
|
* 操作成功后刷新
|
||||||
*/
|
*/
|
||||||
const loadRunningTask = (tasks) => {
|
const refresh = () => {
|
||||||
tasks.forEach((task) => {
|
// 重新获取详情
|
||||||
if (!isEmpty(task.children)) {
|
getDetail()
|
||||||
loadRunningTask(task.children)
|
|
||||||
}
|
|
||||||
// 2.1 只有待处理才需要
|
|
||||||
if (task.status !== 1 && task.status !== 6) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// 2.2 自己不是处理人
|
|
||||||
if (!task.assigneeUser || task.assigneeUser.id !== userId) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2.3 添加到处理任务
|
|
||||||
runningTasks.value.push({ ...task })
|
|
||||||
auditForms.value.push({
|
|
||||||
reason: '',
|
|
||||||
copyUserIds: []
|
|
||||||
})
|
|
||||||
|
|
||||||
// 2.4 处理 approve 表单
|
|
||||||
if (task.formId && task.formConf) {
|
|
||||||
const approveForm = {}
|
|
||||||
setConfAndFields2(approveForm, task.formConf, task.formFields, task.formVariables)
|
|
||||||
approveForms.value.push(approveForm)
|
|
||||||
} else {
|
|
||||||
approveForms.value.push({}) // 占位,避免为空
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** 当前的Tab */
|
||||||
* 设置表单权限
|
const activeTab = ref('form')
|
||||||
*/
|
|
||||||
const setFieldPermission = (field: string, permission: string) => {
|
|
||||||
if (permission === '1') {
|
|
||||||
fApi.value?.disabled(true, field)
|
|
||||||
}
|
|
||||||
if (permission === '2') {
|
|
||||||
fApi.value?.disabled(false, field)
|
|
||||||
}
|
|
||||||
if (permission === '3') {
|
|
||||||
fApi.value?.hidden(true, field)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* 获取可以编辑字段的值
|
|
||||||
*/
|
|
||||||
const getWritableValueOfForm = (fieldsPermission: Object) => {
|
|
||||||
const fieldsValue = {}
|
|
||||||
if (fieldsPermission && fApi.value) {
|
|
||||||
Object.keys(fieldsPermission).forEach((item) => {
|
|
||||||
if (fieldsPermission[item] === '2') {
|
|
||||||
fieldsValue[item] = fApi.value.getValue(item)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return fieldsValue
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 初始化 */
|
/** 初始化 */
|
||||||
const userOptions = ref<UserApi.UserVO[]>([]) // 用户列表
|
const userOptions = ref<UserApi.UserVO[]>([]) // 用户列表
|
||||||
|
@ -475,3 +309,38 @@ onMounted(async () => {
|
||||||
userOptions.value = await UserApi.getSimpleUserList()
|
userOptions.value = await UserApi.getSimpleUserList()
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
$wrap-padding-height: 20px;
|
||||||
|
$wrap-margin-height: 15px;
|
||||||
|
$button-height: 51px;
|
||||||
|
$process-header-height: 194px;
|
||||||
|
|
||||||
|
.processInstance-wrap-main {
|
||||||
|
height: calc(
|
||||||
|
100vh - var(--top-tool-height) - var(--tags-view-height) - var(--app-footer-height) - 35px
|
||||||
|
);
|
||||||
|
max-height: calc(
|
||||||
|
100vh - var(--top-tool-height) - var(--tags-view-height) - var(--app-footer-height) - 35px
|
||||||
|
);
|
||||||
|
overflow: auto;
|
||||||
|
|
||||||
|
.form-scroll-area {
|
||||||
|
height: calc(
|
||||||
|
100vh - var(--top-tool-height) - var(--tags-view-height) - var(--app-footer-height) - 35px -
|
||||||
|
$process-header-height - 40px
|
||||||
|
);
|
||||||
|
max-height: calc(
|
||||||
|
100vh - var(--top-tool-height) - var(--tags-view-height) - var(--app-footer-height) - 35px -
|
||||||
|
$process-header-height - 40px
|
||||||
|
);
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-box {
|
||||||
|
:deep(.el-card) {
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
|
@ -1,341 +0,0 @@
|
||||||
<template>
|
|
||||||
<ContentWrap :bodyStyle="{ padding: '10px 20px 0' }" class="position-relative">
|
|
||||||
<div class="processInstance-wrap-main">
|
|
||||||
<el-scrollbar>
|
|
||||||
<img
|
|
||||||
class="position-absolute right-20px"
|
|
||||||
width="150"
|
|
||||||
:src="auditIcons[processInstance.status]"
|
|
||||||
alt=""
|
|
||||||
/>
|
|
||||||
<div class="text-#878c93 h-15px">编号:{{ id }}</div>
|
|
||||||
<el-divider class="!my-8px" />
|
|
||||||
<div class="flex items-center gap-5 mb-10px h-40px">
|
|
||||||
<div class="text-26px font-bold mb-5px">{{ processInstance.name }}</div>
|
|
||||||
<dict-tag
|
|
||||||
v-if="processInstance.status"
|
|
||||||
:type="DICT_TYPE.BPM_PROCESS_INSTANCE_STATUS"
|
|
||||||
:value="processInstance.status"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="flex items-center gap-5 mb-10px text-13px h-35px">
|
|
||||||
<div class="bg-gray-100 h-35px rounded-3xl flex items-center p-8px gap-2 dark:color-gray-600">
|
|
||||||
<el-avatar
|
|
||||||
:size="28"
|
|
||||||
v-if="processInstance?.startUser?.avatar"
|
|
||||||
:src="processInstance?.startUser?.avatar"
|
|
||||||
/>
|
|
||||||
<el-avatar :size="28" v-else-if="processInstance?.startUser?.nickname">
|
|
||||||
{{ processInstance?.startUser?.nickname.substring(0, 1) }}
|
|
||||||
</el-avatar>
|
|
||||||
{{ processInstance?.startUser?.nickname }}
|
|
||||||
</div>
|
|
||||||
<div class="text-#878c93"> {{ formatDate(processInstance.startTime) }} 提交 </div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<el-tabs v-model="activeTab">
|
|
||||||
<!-- 表单信息 -->
|
|
||||||
<el-tab-pane label="审批详情" name="form">
|
|
||||||
<div class="form-scroll-area">
|
|
||||||
<el-scrollbar>
|
|
||||||
<el-row>
|
|
||||||
<el-col :span="18" class="!flex !flex-col formCol">
|
|
||||||
<!-- 表单信息 -->
|
|
||||||
<div
|
|
||||||
v-loading="processInstanceLoading"
|
|
||||||
class="form-box flex flex-col mb-30px flex-1"
|
|
||||||
>
|
|
||||||
<!-- 情况一:流程表单 -->
|
|
||||||
<el-col v-if="processDefinition?.formType === 10">
|
|
||||||
<form-create
|
|
||||||
v-model="detailForm.value"
|
|
||||||
v-model:api="fApi"
|
|
||||||
:option="detailForm.option"
|
|
||||||
:rule="detailForm.rule"
|
|
||||||
/>
|
|
||||||
</el-col>
|
|
||||||
<!-- 情况二:业务表单 -->
|
|
||||||
<div v-if="processDefinition?.formType === 20">
|
|
||||||
<BusinessFormComponent :id="processInstance.businessKey" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="6">
|
|
||||||
<!-- 审批记录时间线 -->
|
|
||||||
<ProcessInstanceTimeline ref="timelineRef" :approve-nodes="approveNodes" />
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
</el-scrollbar>
|
|
||||||
</div>
|
|
||||||
</el-tab-pane>
|
|
||||||
|
|
||||||
<!-- 流程图 -->
|
|
||||||
<el-tab-pane label="流程图" name="diagram">
|
|
||||||
<div class="form-scroll-area">
|
|
||||||
<ProcessInstanceBpmnViewer :id="`${id}`" :loading="processInstanceLoading" :show-header="false"/>
|
|
||||||
</div>
|
|
||||||
</el-tab-pane>
|
|
||||||
|
|
||||||
<!-- 流转记录 -->
|
|
||||||
<el-tab-pane label="流转记录" name="record">
|
|
||||||
<div class="form-scroll-area">
|
|
||||||
<el-scrollbar>
|
|
||||||
<ProcessInstanceTaskList
|
|
||||||
:loading="tasksLoad"
|
|
||||||
:process-instance="processInstance"
|
|
||||||
:tasks="tasks"
|
|
||||||
:show-header="false"
|
|
||||||
/>
|
|
||||||
</el-scrollbar>
|
|
||||||
</div>
|
|
||||||
</el-tab-pane>
|
|
||||||
|
|
||||||
<!-- 流转评论 TODO 待开发 -->
|
|
||||||
<el-tab-pane label="流转评论" name="comment">
|
|
||||||
<div class="form-scroll-area">
|
|
||||||
<el-scrollbar> 流转评论 </el-scrollbar>
|
|
||||||
</div>
|
|
||||||
</el-tab-pane>
|
|
||||||
</el-tabs>
|
|
||||||
|
|
||||||
<div class="b-t-solid border-t-1px border-[var(--el-border-color)]">
|
|
||||||
<!-- 操作栏按钮 -->
|
|
||||||
<ProcessInstanceOperationButton
|
|
||||||
ref="operationButtonRef"
|
|
||||||
:process-instance="processInstance"
|
|
||||||
:process-definition="processDefinition"
|
|
||||||
:userOptions="userOptions"
|
|
||||||
@success="refresh"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</el-scrollbar>
|
|
||||||
</div>
|
|
||||||
</ContentWrap>
|
|
||||||
</template>
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import { formatDate } from '@/utils/formatTime'
|
|
||||||
import { DICT_TYPE } from '@/utils/dict'
|
|
||||||
import { setConfAndFields2 } from '@/utils/formCreate'
|
|
||||||
import type { ApiAttrs } from '@form-create/element-ui/types/config'
|
|
||||||
import * as ProcessInstanceApi from '@/api/bpm/processInstance'
|
|
||||||
import * as TaskApi from '@/api/bpm/task'
|
|
||||||
import ProcessInstanceBpmnViewer from './ProcessInstanceBpmnViewer.vue'
|
|
||||||
import ProcessInstanceTaskList from './ProcessInstanceTaskList.vue'
|
|
||||||
import ProcessInstanceOperationButton from './ProcessInstanceOperationButton.vue'
|
|
||||||
import ProcessInstanceTimeline from './ProcessInstanceTimeline.vue'
|
|
||||||
import * as UserApi from '@/api/system/user'
|
|
||||||
import { FieldPermissionType } from '@/components/SimpleProcessDesignerV2/src/consts'
|
|
||||||
import audit1 from '@/assets/svgs/bpm/audit1.svg'
|
|
||||||
import audit2 from '@/assets/svgs/bpm/audit2.svg'
|
|
||||||
import audit3 from '@/assets/svgs/bpm/audit3.svg'
|
|
||||||
import audit4 from '@/assets/svgs/bpm/audit4.svg'
|
|
||||||
|
|
||||||
defineOptions({ name: 'BpmProcessInstanceDetail' })
|
|
||||||
const props = defineProps<{
|
|
||||||
id: string // 流程实例的编号
|
|
||||||
taskId?: string // 任务编号
|
|
||||||
activityId?: string //流程活动编号,用于抄送查看
|
|
||||||
}>()
|
|
||||||
const message = useMessage() // 消息弹窗
|
|
||||||
const processInstanceLoading = ref(false) // 流程实例的加载中
|
|
||||||
const processInstance = ref<any>({}) // 流程实例
|
|
||||||
const processDefinition = ref<any>({}) // 流程定义
|
|
||||||
const timelineRef = ref()
|
|
||||||
// 操作按钮组件 ref
|
|
||||||
const operationButtonRef = ref()
|
|
||||||
const tasksLoad = ref(true) // 任务的加载中
|
|
||||||
const tasks = ref<any[]>([]) // 任务列表
|
|
||||||
const auditIcons = {
|
|
||||||
1: audit1,
|
|
||||||
2: audit2,
|
|
||||||
3: audit3,
|
|
||||||
4: audit4
|
|
||||||
}
|
|
||||||
|
|
||||||
// ========== 申请信息 ==========
|
|
||||||
const fApi = ref<ApiAttrs>() //
|
|
||||||
const detailForm = ref({
|
|
||||||
rule: [],
|
|
||||||
option: {},
|
|
||||||
value: {}
|
|
||||||
}) // 流程实例的表单详情
|
|
||||||
|
|
||||||
/** 获得详情 */
|
|
||||||
const getDetail = async () => {
|
|
||||||
// 1. 获取审批详情
|
|
||||||
getApprovalDetail()
|
|
||||||
// 2. 获得流程任务列表
|
|
||||||
getTaskList()
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 加载流程实例 */
|
|
||||||
const BusinessFormComponent = ref<any>(null) // 异步组件
|
|
||||||
/** 获取审批详情 */
|
|
||||||
const getApprovalDetail = async () => {
|
|
||||||
processInstanceLoading.value = true
|
|
||||||
try {
|
|
||||||
const param = {
|
|
||||||
processInstanceId: props.id,
|
|
||||||
activityId: props.activityId,
|
|
||||||
taskId: props.taskId
|
|
||||||
}
|
|
||||||
const data = await ProcessInstanceApi.getApprovalDetail(param);
|
|
||||||
if (!data) {
|
|
||||||
message.error('查询不到审批详情信息!')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if(!data.processDefinition || !data.processInstance) {
|
|
||||||
message.error('查询不到流程信息!')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
processInstance.value = data.processInstance
|
|
||||||
processDefinition.value = data.processDefinition
|
|
||||||
|
|
||||||
// 设置表单信息
|
|
||||||
if (processDefinition.value.formType === 10) {
|
|
||||||
// 获取表单字段权限
|
|
||||||
const formFieldsPermission = data.formFieldsPermission
|
|
||||||
|
|
||||||
if (detailForm.value.rule.length > 0) { // 避免刷新 form-create 显示不了,
|
|
||||||
detailForm.value.value = processInstance.value.formVariables
|
|
||||||
} else {
|
|
||||||
setConfAndFields2(
|
|
||||||
detailForm,
|
|
||||||
processDefinition.value.formConf,
|
|
||||||
processDefinition.value.formFields,
|
|
||||||
processInstance.value.formVariables
|
|
||||||
)
|
|
||||||
}
|
|
||||||
nextTick().then(() => {
|
|
||||||
fApi.value?.btn.show(false)
|
|
||||||
fApi.value?.resetBtn.show(false)
|
|
||||||
//@ts-ignore
|
|
||||||
fApi.value?.disabled(true)
|
|
||||||
// 设置表单字段权限
|
|
||||||
if (formFieldsPermission) {
|
|
||||||
Object.keys(data.formFieldsPermission).forEach((item) => {
|
|
||||||
setFieldPermission(item, formFieldsPermission[item])
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取审批节点,显示 Timeline 的数据
|
|
||||||
approveNodes.value = data.approveNodes
|
|
||||||
|
|
||||||
// 获取待办任务显示操作按钮
|
|
||||||
operationButtonRef.value?.loadTodoTask(data.todoTask)
|
|
||||||
|
|
||||||
} finally {
|
|
||||||
processInstanceLoading.value = false
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// 审批节点信息
|
|
||||||
const approveNodes = ref<ProcessInstanceApi.ApprovalNodeInfo[]>([])
|
|
||||||
/**
|
|
||||||
* 设置表单权限
|
|
||||||
*/
|
|
||||||
const setFieldPermission = (field: string, permission: string) => {
|
|
||||||
if (permission === FieldPermissionType.READ) {
|
|
||||||
//@ts-ignore
|
|
||||||
fApi.value?.disabled(true, field)
|
|
||||||
}
|
|
||||||
if (permission === FieldPermissionType.WRITE) {
|
|
||||||
//@ts-ignore
|
|
||||||
fApi.value?.disabled(false, field)
|
|
||||||
}
|
|
||||||
if (permission === FieldPermissionType.NONE) {
|
|
||||||
//@ts-ignore
|
|
||||||
fApi.value?.hidden(true, field)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 加载任务列表 */
|
|
||||||
const getTaskList = async () => {
|
|
||||||
try {
|
|
||||||
// 获得未取消的任务
|
|
||||||
tasksLoad.value = true
|
|
||||||
const data = await TaskApi.getTaskListByProcessInstanceId(props.id)
|
|
||||||
tasks.value = []
|
|
||||||
// 1.1 移除已取消的审批
|
|
||||||
data.forEach((task: any) => {
|
|
||||||
if (task.status !== 4) {
|
|
||||||
tasks.value.push(task)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
// 1.2 排序,将未完成的排在前面,已完成的排在后面;
|
|
||||||
tasks.value.sort((a, b) => {
|
|
||||||
// 有已完成的情况,按照完成时间倒序
|
|
||||||
if (a.endTime && b.endTime) {
|
|
||||||
return b.endTime - a.endTime
|
|
||||||
} else if (a.endTime) {
|
|
||||||
return 1
|
|
||||||
} else if (b.endTime) {
|
|
||||||
return -1
|
|
||||||
// 都是未完成,按照创建时间倒序
|
|
||||||
} else {
|
|
||||||
return b.createTime - a.createTime
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} finally {
|
|
||||||
tasksLoad.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 操作成功后刷新
|
|
||||||
*/
|
|
||||||
const refresh = () => {
|
|
||||||
// 重新获取详情
|
|
||||||
getDetail()
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 当前的Tab */
|
|
||||||
const activeTab = ref('form')
|
|
||||||
|
|
||||||
/** 初始化 */
|
|
||||||
const userOptions = ref<UserApi.UserVO[]>([]) // 用户列表
|
|
||||||
onMounted(async () => {
|
|
||||||
getDetail()
|
|
||||||
// 获得用户列表
|
|
||||||
userOptions.value = await UserApi.getSimpleUserList()
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
$wrap-padding-height: 20px;
|
|
||||||
$wrap-margin-height: 15px;
|
|
||||||
$button-height: 51px;
|
|
||||||
$process-header-height: 194px;
|
|
||||||
|
|
||||||
.processInstance-wrap-main {
|
|
||||||
height: calc(
|
|
||||||
100vh - var(--top-tool-height) - var(--tags-view-height) - var(--app-footer-height) - 35px
|
|
||||||
);
|
|
||||||
max-height: calc(
|
|
||||||
100vh - var(--top-tool-height) - var(--tags-view-height) - var(--app-footer-height) - 35px
|
|
||||||
);
|
|
||||||
overflow: auto;
|
|
||||||
|
|
||||||
.form-scroll-area {
|
|
||||||
height: calc(
|
|
||||||
100vh - var(--top-tool-height) - var(--tags-view-height) - var(--app-footer-height) - 35px -
|
|
||||||
$process-header-height - 40px
|
|
||||||
);
|
|
||||||
max-height: calc(
|
|
||||||
100vh - var(--top-tool-height) - var(--tags-view-height) - var(--app-footer-height) - 35px -
|
|
||||||
$process-header-height - 40px
|
|
||||||
);
|
|
||||||
overflow: auto;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-box {
|
|
||||||
:deep(.el-card) {
|
|
||||||
border: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -49,7 +49,11 @@ const designerConfig = ref({
|
||||||
switchType: [], // 是否可以切换组件类型,或者可以相互切换的字段
|
switchType: [], // 是否可以切换组件类型,或者可以相互切换的字段
|
||||||
autoActive: true, // 是否自动选中拖入的组件
|
autoActive: true, // 是否自动选中拖入的组件
|
||||||
useTemplate: false, // 是否生成vue2语法的模板组件
|
useTemplate: false, // 是否生成vue2语法的模板组件
|
||||||
formOptions: {}, // 定义表单配置默认值
|
formOptions: {
|
||||||
|
form: {
|
||||||
|
labelWidth: '100px' // 设置默认的 label 宽度为 100px
|
||||||
|
}
|
||||||
|
}, // 定义表单配置默认值
|
||||||
fieldReadonly: false, // 配置field是否可以编辑
|
fieldReadonly: false, // 配置field是否可以编辑
|
||||||
hiddenDragMenu: false, // 隐藏拖拽操作按钮
|
hiddenDragMenu: false, // 隐藏拖拽操作按钮
|
||||||
hiddenDragBtn: false, // 隐藏拖拽按钮
|
hiddenDragBtn: false, // 隐藏拖拽按钮
|
||||||
|
|
Loading…
Reference in New Issue