|
|
@@ -46,6 +46,15 @@
|
|
|
<el-icon><Plus /></el-icon>
|
|
|
加签
|
|
|
</el-button>
|
|
|
+ <el-button
|
|
|
+ v-if="canApprove"
|
|
|
+ class="btn-ghost"
|
|
|
+ :loading="acting"
|
|
|
+ @click="openTransfer"
|
|
|
+ >
|
|
|
+ <el-icon><Position /></el-icon>
|
|
|
+ 转交
|
|
|
+ </el-button>
|
|
|
<el-button
|
|
|
v-if="canCopyRestart"
|
|
|
class="btn-main"
|
|
|
@@ -103,8 +112,6 @@
|
|
|
v-model="addSignUserIds"
|
|
|
multiple
|
|
|
filterable
|
|
|
- collapse-tags
|
|
|
- collapse-tags-tooltip
|
|
|
placeholder="选择加签用户"
|
|
|
style="width: 100%"
|
|
|
:loading="userLoading"
|
|
|
@@ -132,6 +139,51 @@
|
|
|
</el-button>
|
|
|
</template>
|
|
|
</el-dialog>
|
|
|
+
|
|
|
+ <el-dialog
|
|
|
+ v-model="transferVisible"
|
|
|
+ title="转交"
|
|
|
+ width="520px"
|
|
|
+ class="dialog-form-layout"
|
|
|
+ align-center
|
|
|
+ append-to-body
|
|
|
+ modal-class="protocol-dialog-mask"
|
|
|
+ destroy-on-close
|
|
|
+ :close-on-click-modal="false"
|
|
|
+ >
|
|
|
+ <el-form label-position="top">
|
|
|
+ <el-form-item label="转交给">
|
|
|
+ <el-select
|
|
|
+ v-model="transferUserId"
|
|
|
+ filterable
|
|
|
+ clearable
|
|
|
+ placeholder="选择用户"
|
|
|
+ style="width: 100%"
|
|
|
+ :loading="userLoading"
|
|
|
+ >
|
|
|
+ <el-option
|
|
|
+ v-for="u in transferUserOptions"
|
|
|
+ :key="u.id"
|
|
|
+ :label="`${u.nickName}(${u.userNo})`"
|
|
|
+ :value="u.id"
|
|
|
+ />
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="说明(可选)">
|
|
|
+ <el-input v-model="transferComment" type="textarea" :rows="2" />
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+ <template #footer>
|
|
|
+ <el-button class="btn-ghost" @click="transferVisible = false">
|
|
|
+ <el-icon><Close /></el-icon>
|
|
|
+ 取消
|
|
|
+ </el-button>
|
|
|
+ <el-button class="btn-main" type="primary" :loading="acting" @click="submitTransfer">
|
|
|
+ <el-icon><Check /></el-icon>
|
|
|
+ 确定转交
|
|
|
+ </el-button>
|
|
|
+ </template>
|
|
|
+ </el-dialog>
|
|
|
</section>
|
|
|
</div>
|
|
|
</template>
|
|
|
@@ -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<string[]>([])
|
|
|
const addSignComment = ref('')
|
|
|
+ const transferVisible = ref(false)
|
|
|
+ const transferUserId = ref('')
|
|
|
+ const transferComment = ref('')
|
|
|
const userLoading = ref(false)
|
|
|
const userOptions = ref<Array<{ id: string; nickName: string; userNo: string }>>([])
|
|
|
|
|
|
@@ -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()
|
|
|
}
|