!84 feat(bpm): 新增流程当前节点及办理人查询功能
Merge pull request !84 from wsm/feature/query_current_assignee
This commit is contained in:
@@ -20,6 +20,7 @@ enum Api {
|
|||||||
deleteSingleProcess = '/tasktask/taskTask/deleteSingleProcess',
|
deleteSingleProcess = '/tasktask/taskTask/deleteSingleProcess',
|
||||||
deletePendingTask = '/tasktask/taskTask/deletePendingTask',
|
deletePendingTask = '/tasktask/taskTask/deletePendingTask',
|
||||||
updateTitleAndDeadline = '/tasktask/taskTask/updateTitleAndDeadline',
|
updateTitleAndDeadline = '/tasktask/taskTask/updateTitleAndDeadline',
|
||||||
|
currentNodeInfo = '/act/task/currentNodeInfo',
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -202,6 +203,12 @@ export const updateTitleAndDeadline = (params) => {
|
|||||||
return defHttp.post({ url: Api.updateTitleAndDeadline, params }, { joinParamsToUrl: true, isTransformResponse: false });
|
return defHttp.post({ url: Api.updateTitleAndDeadline, params }, { joinParamsToUrl: true, isTransformResponse: false });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询流程当前节点及办理人信息
|
||||||
|
* @param params { procInstId: string }
|
||||||
|
*/
|
||||||
|
export const queryCurrentNodeInfo = (params) => defHttp.get({ url: Api.currentNodeInfo, params });
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 【用于评论功能】自定义文件上传-方法
|
* 【用于评论功能】自定义文件上传-方法
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
</BasicTable>
|
</BasicTable>
|
||||||
</div>
|
</div>
|
||||||
<BpmPictureModal @register="registerBpmModal" />
|
<BpmPictureModal @register="registerBpmModal" />
|
||||||
|
<CurrentNodeModal ref="currentNodeModalRef" />
|
||||||
<j-modal
|
<j-modal
|
||||||
title="修改流程信息"
|
title="修改流程信息"
|
||||||
:width="460"
|
:width="460"
|
||||||
@@ -57,6 +58,7 @@
|
|||||||
import { deleteSingleProcess, deletePendingTask, updateTitleAndDeadline, queryByBusinessId } from '/@/api/common/api';
|
import { deleteSingleProcess, deletePendingTask, updateTitleAndDeadline, queryByBusinessId } from '/@/api/common/api';
|
||||||
import { useMessage } from '/@/hooks/web/useMessage';
|
import { useMessage } from '/@/hooks/web/useMessage';
|
||||||
import TaskApprovalHistoryContent from '/@/views/super/bpm/process/personalOffice/myHandleTask/content/TaskApprovalHistoryContent.vue';
|
import TaskApprovalHistoryContent from '/@/views/super/bpm/process/personalOffice/myHandleTask/content/TaskApprovalHistoryContent.vue';
|
||||||
|
import CurrentNodeModal from './CurrentNodeModal.vue';
|
||||||
|
|
||||||
interface OpenParams {
|
interface OpenParams {
|
||||||
businessId?: string;
|
businessId?: string;
|
||||||
@@ -71,7 +73,7 @@
|
|||||||
const modalWidth = 'min(1280px, calc(100vw - 64px))';
|
const modalWidth = 'min(1280px, calc(100vw - 64px))';
|
||||||
const approvalHistoryModalWidth = 'min(1100px, calc(100vw - 96px))';
|
const approvalHistoryModalWidth = 'min(1100px, calc(100vw - 96px))';
|
||||||
// 按当前列宽合计收敛横向滚动距离,避免少量列被默认宽度撑得过大。
|
// 按当前列宽合计收敛横向滚动距离,避免少量列被默认宽度撑得过大。
|
||||||
const tableScrollX = 1680;
|
const tableScrollX = 1760;
|
||||||
// 固定表体高度,让横向滚动条停留在弹窗底部,而不是跟随少量数据行出现在中间。
|
// 固定表体高度,让横向滚动条停留在弹窗底部,而不是跟随少量数据行出现在中间。
|
||||||
const tableBodyHeight = 500;
|
const tableBodyHeight = 500;
|
||||||
const { createMessage } = useMessage();
|
const { createMessage } = useMessage();
|
||||||
@@ -86,6 +88,7 @@
|
|||||||
requireFinishDate: undefined as string | undefined,
|
requireFinishDate: undefined as string | undefined,
|
||||||
});
|
});
|
||||||
const [registerBpmModal, { openModal: bpmPicModal }] = useModal();
|
const [registerBpmModal, { openModal: bpmPicModal }] = useModal();
|
||||||
|
const currentNodeModalRef = ref();
|
||||||
|
|
||||||
const [registerTable, { setColumns, setTableData, reload, clearSelectedRowKeys }] = useTable({
|
const [registerTable, { setColumns, setTableData, reload, clearSelectedRowKeys }] = useTable({
|
||||||
rowKey: 'id',
|
rowKey: 'id',
|
||||||
@@ -110,7 +113,7 @@
|
|||||||
actionColumn: {
|
actionColumn: {
|
||||||
title: '操作',
|
title: '操作',
|
||||||
dataIndex: 'action',
|
dataIndex: 'action',
|
||||||
width: 320,
|
width: 400,
|
||||||
fixed: 'right',
|
fixed: 'right',
|
||||||
slots: { customRender: 'action' },
|
slots: { customRender: 'action' },
|
||||||
},
|
},
|
||||||
@@ -259,8 +262,21 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handlePreviewCurrentNode(record) {
|
||||||
|
const processInstId = record?.processInstId;
|
||||||
|
if (!processInstId) {
|
||||||
|
createMessage.error('流程尚未发起,无法查询');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
currentNodeModalRef.value?.open({ processInstId });
|
||||||
|
}
|
||||||
|
|
||||||
function getTableAction(record) {
|
function getTableAction(record) {
|
||||||
return [
|
return [
|
||||||
|
{
|
||||||
|
label: '当前节点',
|
||||||
|
onClick: handlePreviewCurrentNode.bind(null, record),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: '流程图',
|
label: '流程图',
|
||||||
onClick: handlePreviewPic.bind(null, record),
|
onClick: handlePreviewPic.bind(null, record),
|
||||||
|
|||||||
@@ -0,0 +1,196 @@
|
|||||||
|
<template>
|
||||||
|
<j-modal
|
||||||
|
title="当前办理人"
|
||||||
|
:width="520"
|
||||||
|
:visible="visible"
|
||||||
|
:footer="null"
|
||||||
|
@cancel="handleCancel"
|
||||||
|
cancelText="关闭"
|
||||||
|
>
|
||||||
|
<div v-if="loading" style="text-align: center; padding: 40px 0;">
|
||||||
|
<a-spin tip="查询中..." />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-else-if="errorMsg" style="text-align: center; padding: 40px 0;">
|
||||||
|
<a-result status="error" :title="errorMsg" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-else class="current-node-content">
|
||||||
|
<!-- 流程已结束 — 卡片布局,与进行中统一 -->
|
||||||
|
<div v-if="nodeInfo?.finished" class="task-card finished-card">
|
||||||
|
<div class="task-card-header">
|
||||||
|
<a-tag color="success">已结束</a-tag>
|
||||||
|
<span class="task-name">该流程已结束</span>
|
||||||
|
</div>
|
||||||
|
<div class="task-card-body">
|
||||||
|
<div v-if="nodeInfo?.endTime" class="info-row">
|
||||||
|
<span class="label">结束时间</span>
|
||||||
|
<span class="value">{{ nodeInfo.endTime }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="info-row">
|
||||||
|
<span class="label">状态</span>
|
||||||
|
<span class="value" style="color: #22c55e;">所有节点已完成</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 流程进行中 -->
|
||||||
|
<div v-else class="running-state">
|
||||||
|
<div
|
||||||
|
v-for="(task, index) in nodeInfo?.currentTasks || []"
|
||||||
|
:key="index"
|
||||||
|
class="task-card"
|
||||||
|
>
|
||||||
|
<div class="task-card-header">
|
||||||
|
<a-tag color="processing">进行中</a-tag>
|
||||||
|
<span class="task-name">{{ task.taskName }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="task-card-body">
|
||||||
|
<div class="info-row">
|
||||||
|
<span class="label">当前办理人</span>
|
||||||
|
<span class="value">
|
||||||
|
<a-avatar size="small" style="margin-right: 6px; background-color: #637d8f;">
|
||||||
|
{{ (task.assigneeName || '待签收')[0] }}
|
||||||
|
</a-avatar>
|
||||||
|
{{ task.assigneeName || '待签收(无具体办理人)' }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div v-if="task.deptNames?.length" class="info-row">
|
||||||
|
<span class="label">所属部门</span>
|
||||||
|
<span class="value">{{ task.deptNames.join('、') }}</span>
|
||||||
|
</div>
|
||||||
|
<div v-if="task.startTime" class="info-row">
|
||||||
|
<span class="label">转入时间</span>
|
||||||
|
<span class="value">{{ task.startTime }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="!nodeInfo?.currentTasks?.length" style="text-align: center; padding: 20px;">
|
||||||
|
暂无任务信息
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</j-modal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import JModal from '/@/components/Modal/src/JModal/JModal.vue';
|
||||||
|
import { queryCurrentNodeInfo } from '/@/api/common/api';
|
||||||
|
|
||||||
|
interface TaskInfo {
|
||||||
|
taskId?: string;
|
||||||
|
taskName?: string;
|
||||||
|
taskDefinitionKey?: string;
|
||||||
|
startTime?: string;
|
||||||
|
assignee?: string;
|
||||||
|
assigneeName?: string;
|
||||||
|
deptNames?: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface NodeInfo {
|
||||||
|
finished: boolean;
|
||||||
|
endTime?: string;
|
||||||
|
currentTasks?: TaskInfo[];
|
||||||
|
}
|
||||||
|
|
||||||
|
const visible = ref(false);
|
||||||
|
const loading = ref(false);
|
||||||
|
const errorMsg = ref('');
|
||||||
|
const nodeInfo = ref<NodeInfo | null>(null);
|
||||||
|
let currentProcessInstId = '';
|
||||||
|
|
||||||
|
async function open(params: { processInstId: string; title?: string }) {
|
||||||
|
currentProcessInstId = params.processInstId;
|
||||||
|
visible.value = true;
|
||||||
|
loading.value = true;
|
||||||
|
errorMsg.value = '';
|
||||||
|
nodeInfo.value = null;
|
||||||
|
|
||||||
|
if (!currentProcessInstId) {
|
||||||
|
loading.value = false;
|
||||||
|
errorMsg.value = '流程尚未发起,无法查询';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// defHttp.get 默认自动解包 Result 包装,res 即为后端返回的 result 对象
|
||||||
|
const res = await queryCurrentNodeInfo({ procInstId: currentProcessInstId });
|
||||||
|
nodeInfo.value = res as NodeInfo;
|
||||||
|
} catch {
|
||||||
|
errorMsg.value = '查询失败,请重试';
|
||||||
|
} finally {
|
||||||
|
loading.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleCancel() {
|
||||||
|
visible.value = false;
|
||||||
|
loading.value = false;
|
||||||
|
errorMsg.value = '';
|
||||||
|
nodeInfo.value = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({ open });
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.current-node-content {
|
||||||
|
padding: 8px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.finished-state {
|
||||||
|
padding: 16px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.task-card {
|
||||||
|
margin-bottom: 12px;
|
||||||
|
padding: 16px;
|
||||||
|
background: #f8fafc;
|
||||||
|
border: 1px solid #e5e7eb;
|
||||||
|
border-radius: 6px;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.task-card-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
|
||||||
|
.task-name {
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #1e293b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.task-card-body {
|
||||||
|
.info-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
font-size: 13px;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label {
|
||||||
|
width: 96px;
|
||||||
|
color: #64748b;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.value {
|
||||||
|
color: #334155;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
Reference in New Issue
Block a user