diff --git a/web-admin/src/api/oaApproval.ts b/web-admin/src/api/oaApproval.ts index e3a547cd..44031153 100644 --- a/web-admin/src/api/oaApproval.ts +++ b/web-admin/src/api/oaApproval.ts @@ -288,6 +288,17 @@ export function fetchAddSignOaTask( }) } +export function fetchTransferOaTask( + taskId: string, + data: { targetUserId: string; comment?: string } +) { + return request.post({ + url: `${BASE}/tasks/${taskId}/transfer`, + data, + showSuccessMessage: true + }) +} + export function fetchWithdrawOaInstance(instanceId: string) { return request.post({ url: `${BASE}/instances/${instanceId}/withdraw`, diff --git a/web-admin/src/views/oa-approval/instances/detail.vue b/web-admin/src/views/oa-approval/instances/detail.vue index 38aa8e93..62b3ac55 100644 --- a/web-admin/src/views/oa-approval/instances/detail.vue +++ b/web-admin/src/views/oa-approval/instances/detail.vue @@ -46,6 +46,15 @@ 加签 + + + 转交 + + + + + + + + + + + + + + + @@ -140,7 +192,7 @@ import { computed, onMounted, ref } from 'vue' import { useRoute, useRouter } from 'vue-router' import { ElMessage, ElMessageBox } from 'element-plus' - import { ArrowLeft, Check, Close, Plus, RefreshLeft, RefreshRight } from '@element-plus/icons-vue' + import { ArrowLeft, Check, Close, Plus, Position, RefreshLeft, RefreshRight } from '@element-plus/icons-vue' import { fetchUserList } from '@/api/admin' import { fetchAddSignOaTask, @@ -148,6 +200,7 @@ fetchOaInstanceDetail, fetchRejectOaTask, fetchReturnOaTask, + fetchTransferOaTask, fetchWithdrawOaInstance } from '@/api/oaApproval' import OaProgressChain from '@/components/oa-approval/OaProgressChain.vue' @@ -177,6 +230,9 @@ const addSignVisible = ref(false) const addSignUserIds = ref([]) const addSignComment = ref('') + const transferVisible = ref(false) + const transferUserId = ref('') + const transferComment = ref('') const userLoading = ref(false) const userOptions = ref>([]) @@ -186,29 +242,38 @@ const timelineItems = computed(() => { const list = detail.value?.progress if (!Array.isArray(list) || !list.length) return [] - return list.map((p: any, idx: number) => ({ - key: `${p.kind}-${p.nodeKey || p.taskId || idx}-${p.roundNo || 1}`, - title: p.title || '-', - status: p.status || 'info', - timestamp: p.timestamp || '', - assignee: p.assigneeUserName || p.assigneeUserId || '', - resolveSource: p.resolveSource || '', - commentText: p.commentText || '', - taskType: p.taskType || '', - kind: p.kind || '', - roundNo: p.roundNo || 1, - signMode: p.signMode || '', - people: Array.isArray(p.people) - ? p.people.map((x: any) => ({ - id: x.id || '', - nickName: x.nickName || x.id || '', - status: x.status || '', - timestamp: x.timestamp || '', - commentText: x.commentText || '', - taskId: x.taskId || '' - })) - : [] - })) + return list + .filter((p: any) => { + // 打回只保留审批人节点,过滤独立终态「已打回」行 + const kind = String(p?.kind || '') + const status = String(p?.status || '') + const title = String(p?.title || '').trim() + if (kind === 'terminal' && (status === 'returned' || title === '已打回')) return false + return true + }) + .map((p: any, idx: number) => ({ + key: `${p.kind}-${p.nodeKey || p.taskId || idx}-${p.roundNo || 1}`, + title: p.title || '-', + status: p.status || 'info', + timestamp: p.timestamp || '', + assignee: p.assigneeUserName || p.assigneeUserId || '', + resolveSource: p.resolveSource || '', + commentText: p.commentText || '', + taskType: p.taskType || '', + kind: p.kind || '', + roundNo: p.roundNo || 1, + signMode: p.signMode || '', + people: Array.isArray(p.people) + ? p.people.map((x: any) => ({ + id: x.id || '', + nickName: x.nickName || x.id || '', + status: x.status || '', + timestamp: x.timestamp || '', + commentText: x.commentText || '', + taskId: x.taskId || '' + })) + : [] + })) }) const currentUserId = computed(() => { @@ -230,6 +295,16 @@ const canApprove = computed(() => !!myPendingTask.value) + const transferUserOptions = computed(() => { + const me = currentUserId.value + const pendingIds = new Set( + (detail.value?.tasks || []) + .filter((t: any) => t.status === 'pending' && t.taskType === 'approval') + .map((t: any) => String(t.assigneeUserId || '')) + ) + return userOptions.value.filter((u) => u.id !== me && !pendingIds.has(u.id)) + }) + const canCopyRestart = computed(() => { if (!isInitiator.value || !detail.value) return false return ['returned', 'cancelled', 'rejected'].includes(String(detail.value.status || '')) @@ -254,7 +329,8 @@ done: '已结束', submitted: '已提交', not_reached: '未到达', - returned: '已打回' + returned: '已打回', + transferred: '已转交' } return map[s] || s } @@ -414,6 +490,33 @@ } } + async function openTransfer() { + transferUserId.value = '' + transferComment.value = '' + transferVisible.value = true + if (!userOptions.value.length) await loadUsers() + } + + async function submitTransfer() { + const task = myPendingTask.value + if (!task) return + if (!transferUserId.value) { + ElMessage.warning('请选择转交人') + return + } + acting.value = true + try { + await fetchTransferOaTask(task.id, { + targetUserId: transferUserId.value, + comment: transferComment.value || undefined + }) + transferVisible.value = false + await load() + } finally { + acting.value = false + } + } + function goBack() { router.back() }