fix(approval): 待办支持全部待办开关与按任务改派、keywords 与报送人术语

- 有 approval:instance:assign 的用户可切换全租户待办(scope=all)
- 详情抽屉展示待办人员并按任务 id 改派
- 查询参数 keyword 改为 keywords;发起人统一为报送人
This commit is contained in:
2026-10-30 22:28:05 +08:00
parent bc94f04243
commit 56a2b96b3d
5 changed files with 75 additions and 16 deletions
+17 -2
View File
@@ -9,12 +9,12 @@ export interface ApprovalQueryParam extends PageParam {
// 单号 // 单号
bizNo?: string; bizNo?: string;
// 关键词:单号、名称或标题、实施单位 // 关键词:单号、名称或标题、实施单位
keyword?: string; keywords?: string;
// 报送部门id // 报送部门id
reportOrgId?: number; reportOrgId?: number;
// 受理部门id // 受理部门id
acceptOrgId?: number; acceptOrgId?: number;
// 发起人id // 报送人id
reporterId?: number; reporterId?: number;
// 单据状态 // 单据状态
status?: string; status?: string;
@@ -93,6 +93,18 @@ export interface ApprovalConfirm {
handleTime?: string; handleTime?: string;
} }
/**
* 实例上的待办任务(PC 按任务改派)
*/
export interface ApprovalPendingTask {
id?: number;
nodeKey?: string;
nodeName?: string;
nodeType?: string;
assigneeId?: number;
assigneeName?: string;
}
/** /**
* 我的待处理任务 * 我的待处理任务
*/ */
@@ -144,6 +156,7 @@ export interface ApprovalInstance {
records?: ApprovalRecord[]; records?: ApprovalRecord[];
confirms?: ApprovalConfirm[]; confirms?: ApprovalConfirm[];
myTask?: ApprovalMyTask | null; myTask?: ApprovalMyTask | null;
pendingTasks?: ApprovalPendingTask[];
permissions?: Record<string, boolean>; permissions?: Record<string, boolean>;
} }
@@ -191,6 +204,8 @@ export interface ApprovalTodoParam extends PageParam {
status?: string; status?: string;
// 单据类型 // 单据类型
docType?: string; docType?: string;
// 查询范围:mine(默认本人待办)/all(全租户待办,需 assign 权限)
scope?: 'mine' | 'all';
// 关键词:单号、名称或标题、实施单位 // 关键词:单号、名称或标题、实施单位
keywords?: string; keywords?: string;
} }
@@ -29,7 +29,7 @@
<a-descriptions-item label="当前节点"> <a-descriptions-item label="当前节点">
{{ data.currentNodeName || '-' }} {{ data.currentNodeName || '-' }}
</a-descriptions-item> </a-descriptions-item>
<a-descriptions-item label="发起人"> <a-descriptions-item label="报送人">
{{ data.reporterName || '-' }} {{ data.reporterName || '-' }}
</a-descriptions-item> </a-descriptions-item>
<a-descriptions-item label="报送部门"> <a-descriptions-item label="报送部门">
@@ -131,7 +131,7 @@
prop: prop:
| 'docType' | 'docType'
| 'bizNo' | 'bizNo'
| 'keyword' | 'keywords'
| 'reportOrgId' | 'reportOrgId'
| 'acceptOrgId' | 'acceptOrgId'
| 'reporterId' | 'reporterId'
@@ -140,10 +140,10 @@
}[] = [ }[] = [
{ prop: 'docType', label: '单据类型' }, { prop: 'docType', label: '单据类型' },
{ prop: 'bizNo', label: '单号' }, { prop: 'bizNo', label: '单号' },
{ prop: 'keyword', label: '标题或名称' }, { prop: 'keywords', label: '标题或名称' },
{ prop: 'reportOrgId', label: '报送部门' }, { prop: 'reportOrgId', label: '报送部门' },
{ prop: 'acceptOrgId', label: '受理部门' }, { prop: 'acceptOrgId', label: '受理部门' },
{ prop: 'reporterId', label: '发起人' }, { prop: 'reporterId', label: '报送人' },
{ prop: 'status', label: '状态' } { prop: 'status', label: '状态' }
]; ];
@@ -167,7 +167,7 @@
const { form, resetFields } = useFormData<ApprovalQueryParam>({ const { form, resetFields } = useFormData<ApprovalQueryParam>({
docType: undefined, docType: undefined,
bizNo: '', bizNo: '',
keyword: '', keywords: '',
reportOrgId: undefined, reportOrgId: undefined,
acceptOrgId: undefined, acceptOrgId: undefined,
reporterId: undefined, reporterId: undefined,
@@ -182,9 +182,9 @@
// 组织下拉选项 // 组织下拉选项
const organizationOptions = ref<{ label: string; value: number }[]>([]); const organizationOptions = ref<{ label: string; value: number }[]>([]);
// 发起人下拉选项 // 报送人下拉选项
const userOptions = ref<{ label: string; value: number }[]>([]); const userOptions = ref<{ label: string; value: number }[]>([]);
// 发起人请求状态 // 报送人请求状态
const userLoading = ref(false); const userLoading = ref(false);
// 组织远程过滤 // 组织远程过滤
@@ -192,7 +192,7 @@
return (option?.label ?? '').toLowerCase().includes(input.toLowerCase()); return (option?.label ?? '').toLowerCase().includes(input.toLowerCase());
}; };
// 远程搜索发起人 // 远程搜索报送人
const searchUsers = (keywords?: string) => { const searchUsers = (keywords?: string) => {
userLoading.value = true; userLoading.value = true;
searchApprovalUsers({ keywords }) searchApprovalUsers({ keywords })
@@ -76,7 +76,7 @@
<a-descriptions-item label="当前节点"> <a-descriptions-item label="当前节点">
{{ detail.currentNodeName || '-' }} {{ detail.currentNodeName || '-' }}
</a-descriptions-item> </a-descriptions-item>
<a-descriptions-item label="发起人"> <a-descriptions-item label="报送人">
{{ detail.reporterName }} {{ detail.reporterName }}
</a-descriptions-item> </a-descriptions-item>
<a-descriptions-item label="报送部门"> <a-descriptions-item label="报送部门">
@@ -169,6 +169,14 @@
<a-descriptions-item label="节点"> <a-descriptions-item label="节点">
{{ detail.currentNodeName || '-' }} {{ detail.currentNodeName || '-' }}
</a-descriptions-item> </a-descriptions-item>
<a-descriptions-item label="待办人员">
<template v-if="pendingTasks.length">
<a-tag v-for="task in pendingTasks" :key="task.id" color="blue">
{{ task.nodeName }}·{{ task.assigneeName }}
</a-tag>
</template>
<span v-else>-</span>
</a-descriptions-item>
<a-descriptions-item label="存档确认"> <a-descriptions-item label="存档确认">
<template v-if="confirms.length"> <template v-if="confirms.length">
<a-tag <a-tag
@@ -270,6 +278,13 @@
@ok="submitAssign" @ok="submitAssign"
> >
<a-form layout="vertical"> <a-form layout="vertical">
<a-form-item v-if="pendingTasks.length" label="待办任务" required>
<a-select
v-model:value="assignTaskId"
:options="pendingTaskOptions"
placeholder="请选择需要改派的待办任务"
/>
</a-form-item>
<a-form-item label="改派对象" required> <a-form-item label="改派对象" required>
<a-select <a-select
v-model:value="assignForm.targetUserId" v-model:value="assignForm.targetUserId"
@@ -302,6 +317,7 @@
import type { import type {
ApprovalAttachment, ApprovalAttachment,
ApprovalInstance, ApprovalInstance,
ApprovalPendingTask,
ApprovalUser ApprovalUser
} from '@/api/approval/instance/model'; } from '@/api/approval/instance/model';
import { getInstance } from '@/api/approval/instance'; import { getInstance } from '@/api/approval/instance';
@@ -346,6 +362,9 @@
); );
const records = computed(() => detail.value?.records ?? []); const records = computed(() => detail.value?.records ?? []);
const confirms = computed(() => detail.value?.confirms ?? []); const confirms = computed(() => detail.value?.confirms ?? []);
const pendingTasks = computed<ApprovalPendingTask[]>(
() => detail.value?.pendingTasks ?? []
);
const load = async () => { const load = async () => {
if (!props.instanceId) { if (!props.instanceId) {
@@ -554,6 +573,13 @@
targetUserId: undefined, targetUserId: undefined,
reason: '' reason: ''
}); });
const assignTaskId = ref<number | undefined>(undefined);
const pendingTaskOptions = computed(() =>
pendingTasks.value.map((task) => ({
value: task.id as number,
label: `${task.nodeName || task.nodeKey || ''}·${task.assigneeName || ''}`
}))
);
let searchTimer: ReturnType<typeof setTimeout> | undefined; let searchTimer: ReturnType<typeof setTimeout> | undefined;
const doSearchUsers = async (keywords?: string) => { const doSearchUsers = async (keywords?: string) => {
@@ -585,14 +611,15 @@
const openAssign = () => { const openAssign = () => {
assignForm.targetUserId = undefined; assignForm.targetUserId = undefined;
assignForm.reason = ''; assignForm.reason = '';
assignTaskId.value = detail.value?.myTask?.id ?? pendingTasks.value[0]?.id;
assignVisible.value = true; assignVisible.value = true;
doSearchUsers(); doSearchUsers();
}; };
const submitAssign = async () => { const submitAssign = async () => {
const taskId = detail.value?.myTask?.id; const taskId = assignTaskId.value;
if (!taskId) { if (!taskId) {
message.error('待办任务不存在,请刷新'); message.error('请选择待办任务');
return; return;
} }
if (!assignForm.targetUserId) { if (!assignForm.targetUserId) {
@@ -613,8 +640,12 @@
message.success('改派成功'); message.success('改派成功');
assignVisible.value = false; assignVisible.value = false;
emit('done'); emit('done');
// 改派后待办不再属于当前用户,关闭抽屉 if (detail.value?.myTask?.id === taskId) {
updateVisible(false); // 改派的是当前用户的待办,关闭抽屉回到列表
updateVisible(false);
} else {
await load();
}
} catch (e: any) { } catch (e: any) {
message.error(e.message); message.error(e.message);
} finally { } finally {
+14 -1
View File
@@ -31,6 +31,13 @@
@pressEnter="reload" @pressEnter="reload"
@search="reload" @search="reload"
/> />
<a-checkbox
v-if="canAssign"
v-model:checked="allScope"
@change="reload"
>
全部待办
</a-checkbox>
<a-button @click="resetSearch">重置</a-button> <a-button @click="resetSearch">重置</a-button>
</a-space> </a-space>
</template> </template>
@@ -68,6 +75,7 @@
ColumnItem ColumnItem
} from 'ele-admin-pro/es/ele-pro-table/types'; } from 'ele-admin-pro/es/ele-pro-table/types';
import useSearch from '@/utils/use-search'; import useSearch from '@/utils/use-search';
import { hasPermission } from '@/utils/permission';
import type { import type {
ApprovalItem, ApprovalItem,
ApprovalTodoParam ApprovalTodoParam
@@ -82,6 +90,9 @@
const showDetail = ref(false); const showDetail = ref(false);
// 当前单据id // 当前单据id
const currentId = ref<number | null>(null); const currentId = ref<number | null>(null);
// 是否查看全租户待办(仅 assign 权限)
const allScope = ref(false);
const canAssign = hasPermission('approval:instance:assign');
// 搜索条件 // 搜索条件
const { where, resetFields } = useSearch<ApprovalTodoParam>({ const { where, resetFields } = useSearch<ApprovalTodoParam>({
@@ -122,7 +133,7 @@
ellipsis: true ellipsis: true
}, },
{ {
title: '发起人', title: '报送人',
dataIndex: 'reporterName', dataIndex: 'reporterName',
key: 'reporterName', key: 'reporterName',
width: 120, width: 120,
@@ -171,6 +182,7 @@
return pageApprovalTodo({ return pageApprovalTodo({
status: where.status, status: where.status,
keywords: where.keywords?.trim() || undefined, keywords: where.keywords?.trim() || undefined,
scope: allScope.value ? 'all' : undefined,
page, page,
limit limit
}); });
@@ -184,6 +196,7 @@
// 重置搜索 // 重置搜索
const resetSearch = () => { const resetSearch = () => {
resetFields(); resetFields();
allScope.value = false;
reload(); reload();
}; };