feat(oa): terminate on return and copy form to start a new instance
Align with DingTalk: returned/rejected/cancelled are terminal; progress shows return on the approver task only; done list includes returned; deprecate same-instance resubmit and source-instance linking. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
1 parent
8b2d8284c7
commit
c9711d0dce
20 files changed
+74586
-32336
No files matched your search
@@ -210,7 +210,6 @@ export function fetchStartOaInstance(data: {
|
||||
initiatorDeptId?: string
|
||||
initiatorSelectUserIds?: string[]
|
||||
relatedInstanceIds?: string[]
|
||||
sourceInstanceId?: string
|
||||
}) {
|
||||
return request.post({
|
||||
url: `${BASE}/instances`,
|
||||
@@ -269,17 +268,6 @@ export function fetchReturnOaTask(taskId: string, comment: string) {
|
||||
})
|
||||
}
|
||||
|
||||
export function fetchResubmitOaInstance(
|
||||
id: string,
|
||||
data: { formData?: Record<string, any>; initiatorSelectUserIds?: string[] }
|
||||
) {
|
||||
return request.post({
|
||||
url: `${BASE}/instances/${id}/resubmit`,
|
||||
data,
|
||||
showSuccessMessage: true
|
||||
})
|
||||
}
|
||||
|
||||
export function fetchAddSignOaTask(
|
||||
taskId: string,
|
||||
data: { userIds: string[]; comment?: string }
|
||||
|
||||
@@ -45,12 +45,16 @@
|
||||
查看
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="kind === 'mine' && row.status === 'cancelled' && row.appId"
|
||||
v-if="
|
||||
kind === 'mine' &&
|
||||
row.appId &&
|
||||
['returned', 'cancelled', 'rejected'].includes(row.status)
|
||||
"
|
||||
class="btn-op-item"
|
||||
@click="openResubmitFromCancelled(row)"
|
||||
@click="openCopyRestart(row)"
|
||||
>
|
||||
<el-icon><RefreshRight /></el-icon>
|
||||
修改并重新提交
|
||||
复制并重新发起
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
@@ -76,9 +80,9 @@
|
||||
const rows = ref<any[]>([])
|
||||
|
||||
function statusType(s: string) {
|
||||
if (s === 'pending' || s === 'running' || s === 'returned') return 'warning'
|
||||
if (s === 'pending' || s === 'running') return 'warning'
|
||||
if (s === 'approved' || s === 'completed' || s === 'done') return 'success'
|
||||
if (s === 'rejected' || s === 'cancelled') return 'danger'
|
||||
if (s === 'rejected' || s === 'cancelled' || s === 'returned') return 'danger'
|
||||
return 'info'
|
||||
}
|
||||
|
||||
@@ -92,7 +96,7 @@
|
||||
completed: '已完成',
|
||||
cancelled: '已撤销',
|
||||
done: '已结束',
|
||||
returned: '待修改'
|
||||
returned: '已打回'
|
||||
}
|
||||
return map[s] || s
|
||||
}
|
||||
@@ -103,11 +107,11 @@
|
||||
router.push({ path: `/system-manage/oa-approval/instances/${id}` })
|
||||
}
|
||||
|
||||
function openResubmitFromCancelled(row: any) {
|
||||
function openCopyRestart(row: any) {
|
||||
if (!row?.appId || !row?.id) return
|
||||
router.push({
|
||||
path: '/system-manage/oa-approval/start',
|
||||
query: { appId: row.appId, sourceInstanceId: row.id }
|
||||
query: { appId: row.appId, copyFromInstanceId: row.id }
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -47,14 +47,13 @@
|
||||
加签
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="detail?.status === 'returned' && isInitiator"
|
||||
v-if="canCopyRestart"
|
||||
class="btn-main"
|
||||
type="primary"
|
||||
:loading="acting"
|
||||
@click="openResubmit"
|
||||
@click="goCopyRestart"
|
||||
>
|
||||
<el-icon><Check /></el-icon>
|
||||
修改并再次提交
|
||||
<el-icon><RefreshRight /></el-icon>
|
||||
复制并重新发起
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="detail?.status === 'running' && isInitiator"
|
||||
@@ -69,16 +68,6 @@
|
||||
</div>
|
||||
|
||||
<div v-loading="loading" class="oa-detail__body">
|
||||
<p v-if="detail?.sourceInstanceId" class="oa-detail__link-tip">
|
||||
本单由已撤销审批重提,
|
||||
<el-button link type="primary" @click="goInstance(detail.sourceInstanceId)">查看原单</el-button>
|
||||
</p>
|
||||
<p v-if="detail?.resubmittedInstanceId" class="oa-detail__link-tip">
|
||||
已有重提单,
|
||||
<el-button link type="primary" @click="goInstance(detail.resubmittedInstanceId)">
|
||||
查看重提单
|
||||
</el-button>
|
||||
</p>
|
||||
<section class="oa-detail__section">
|
||||
<h3>表单内容</h3>
|
||||
<div class="oa-detail__form-wrap">
|
||||
@@ -96,18 +85,13 @@
|
||||
<h3>审批进度</h3>
|
||||
<div class="oa-detail__timeline-wrap">
|
||||
<el-timeline v-if="timelineItems.length">
|
||||
<template v-for="(item, idx) in timelineItems" :key="item.key">
|
||||
<div
|
||||
v-if="idx === 0 || item.roundNo !== timelineItems[idx - 1].roundNo"
|
||||
class="oa-tl__round"
|
||||
>
|
||||
第 {{ item.roundNo }} 轮
|
||||
</div>
|
||||
<el-timeline-item
|
||||
:type="timelineType(item.status)"
|
||||
:timestamp="item.timestamp"
|
||||
placement="top"
|
||||
>
|
||||
<el-timeline-item
|
||||
v-for="item in timelineItems"
|
||||
:key="item.key"
|
||||
:type="timelineType(item.status)"
|
||||
:timestamp="item.timestamp"
|
||||
placement="top"
|
||||
>
|
||||
<div class="oa-tl">
|
||||
<div class="oa-tl__title">
|
||||
{{ item.title }}
|
||||
@@ -115,7 +99,6 @@
|
||||
{{ statusText(item.status) }}
|
||||
</el-tag>
|
||||
<el-tag v-if="item.taskType === 'cc'" size="small" type="info">抄送</el-tag>
|
||||
<el-tag v-if="item.kind === 'return'" size="small" type="warning">打回</el-tag>
|
||||
</div>
|
||||
<div
|
||||
v-if="item.assignee && item.kind !== 'pending_node' && item.kind !== 'terminal'"
|
||||
@@ -128,8 +111,7 @@
|
||||
</div>
|
||||
<div v-if="item.commentText" class="oa-tl__comment">意见:{{ item.commentText }}</div>
|
||||
</div>
|
||||
</el-timeline-item>
|
||||
</template>
|
||||
</el-timeline-item>
|
||||
</el-timeline>
|
||||
<p v-else class="oa-detail__empty">暂无任务记录</p>
|
||||
</div>
|
||||
@@ -190,14 +172,13 @@
|
||||
import { computed, onMounted, ref } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { ArrowLeft, Check, Close, RefreshLeft, Plus } from '@element-plus/icons-vue'
|
||||
import { ArrowLeft, Check, Close, Plus, RefreshLeft, RefreshRight } from '@element-plus/icons-vue'
|
||||
import { fetchUserList } from '@/api/admin'
|
||||
import {
|
||||
fetchAddSignOaTask,
|
||||
fetchApproveOaTask,
|
||||
fetchOaInstanceDetail,
|
||||
fetchRejectOaTask,
|
||||
fetchResubmitOaInstance,
|
||||
fetchReturnOaTask,
|
||||
fetchWithdrawOaInstance
|
||||
} from '@/api/oaApproval'
|
||||
@@ -264,10 +245,15 @@
|
||||
|
||||
const canApprove = computed(() => !!myPendingTask.value)
|
||||
|
||||
const canCopyRestart = computed(() => {
|
||||
if (!isInitiator.value || !detail.value) return false
|
||||
return ['returned', 'cancelled', 'rejected'].includes(String(detail.value.status || ''))
|
||||
})
|
||||
|
||||
function statusType(s: string) {
|
||||
if (s === 'pending' || s === 'running' || s === 'returned') return 'warning'
|
||||
if (s === 'pending' || s === 'running') return 'warning'
|
||||
if (s === 'approved' || s === 'completed' || s === 'done' || s === 'submitted') return 'success'
|
||||
if (s === 'rejected' || s === 'cancelled') return 'danger'
|
||||
if (s === 'rejected' || s === 'cancelled' || s === 'returned') return 'danger'
|
||||
if (s === 'not_reached') return 'info'
|
||||
return 'info'
|
||||
}
|
||||
@@ -283,17 +269,15 @@
|
||||
done: '已结束',
|
||||
submitted: '已提交',
|
||||
not_reached: '未到达',
|
||||
returned: '待修改'
|
||||
returned: '已打回'
|
||||
}
|
||||
// 任务被打回后取消:展示「已取消」
|
||||
if (s === 'cancelled') return '已撤销'
|
||||
return map[s] || s
|
||||
}
|
||||
|
||||
function timelineType(s: string) {
|
||||
if (s === 'approved' || s === 'completed' || s === 'done' || s === 'submitted') return 'success'
|
||||
if (s === 'rejected' || s === 'cancelled') return 'danger'
|
||||
if (s === 'pending' || s === 'returned') return 'warning'
|
||||
if (s === 'rejected' || s === 'cancelled' || s === 'returned') return 'danger'
|
||||
if (s === 'pending') return 'warning'
|
||||
if (s === 'not_reached') return 'info'
|
||||
return 'primary'
|
||||
}
|
||||
@@ -395,26 +379,17 @@
|
||||
}
|
||||
}
|
||||
|
||||
async function openResubmit() {
|
||||
await ElMessageBox.confirm(
|
||||
'将使用当前表单内容再次提交并重新走审批。若需改字段,请先在下方确认表单后确定。',
|
||||
'再次提交',
|
||||
{ type: 'warning' }
|
||||
)
|
||||
acting.value = true
|
||||
try {
|
||||
await fetchResubmitOaInstance(String(route.params.id || ''), {
|
||||
formData: { ...(viewFormData.value || detail.value?.formDataJson || {}) }
|
||||
})
|
||||
await load()
|
||||
} finally {
|
||||
acting.value = false
|
||||
function goCopyRestart() {
|
||||
const appId = String(detail.value?.appId || '').trim()
|
||||
const id = String(route.params.id || '').trim()
|
||||
if (!appId || !id) {
|
||||
ElMessage.warning('无法复制:缺少应用或实例信息')
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
function goInstance(id: string) {
|
||||
if (!id) return
|
||||
router.push({ path: `/system-manage/oa-approval/instances/${id}` })
|
||||
router.push({
|
||||
path: '/system-manage/oa-approval/start',
|
||||
query: { appId, copyFromInstanceId: id }
|
||||
})
|
||||
}
|
||||
|
||||
async function loadUsers() {
|
||||
|
||||
@@ -184,7 +184,6 @@
|
||||
const formOption = ref<Record<string, any>>({ submitBtn: false, resetBtn: false })
|
||||
const fapi = ref<any>(null)
|
||||
const submitting = ref(false)
|
||||
const sourceInstanceId = ref('')
|
||||
const needInitiatorSelect = ref(false)
|
||||
const initiatorSelectUserIds = ref<string[]>([])
|
||||
const userLoading = ref(false)
|
||||
@@ -350,11 +349,9 @@
|
||||
formData: { ...formData.value },
|
||||
initiatorSelectUserIds: needInitiatorSelect.value
|
||||
? [...initiatorSelectUserIds.value]
|
||||
: undefined,
|
||||
sourceInstanceId: sourceInstanceId.value || undefined
|
||||
: undefined
|
||||
})
|
||||
dlgVisible.value = false
|
||||
sourceInstanceId.value = ''
|
||||
if (data?.id) {
|
||||
router.push({ path: `/system-manage/oa-approval/instances/${data.id}` })
|
||||
}
|
||||
@@ -370,15 +367,14 @@
|
||||
templates.value = tpl?.list || []
|
||||
groupOptions.value = groupsRes?.list || []
|
||||
const qAppId = String(route.query.appId || '').trim()
|
||||
const qSource = String(route.query.sourceInstanceId || '').trim()
|
||||
const qCopyFrom = String(route.query.copyFromInstanceId || '').trim()
|
||||
if (qAppId) {
|
||||
const row = templates.value.find((t: any) => t.id === qAppId)
|
||||
if (row) {
|
||||
sourceInstanceId.value = qSource
|
||||
await onPick(row)
|
||||
if (qSource) {
|
||||
if (qCopyFrom) {
|
||||
try {
|
||||
const old = await fetchOaInstanceDetail(qSource)
|
||||
const old = await fetchOaInstanceDetail(qCopyFrom)
|
||||
formData.value = { ...(old?.formDataJson || {}) }
|
||||
} catch {
|
||||
/* 预填失败仍可手工填 */
|
||||
|
||||
Reference in new issue
Block a user