Files
shanxia2.0/miniprogram/pages/bx/advance/detail.vue
T

350 lines
11 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<view class="bx-page bx-page--form">
<view class="topbar" :style="safeAreaStyle">
<view class="topbar-left">
<view class="topbar-back" @click="goBack">
<text class="topbar-back-icon">‹</text>
</view>
</view>
<text class="topbar-title">备用金详情</text>
<view class="topbar-right" />
</view>
<template v-if="detail">
<view class="advance-ticket-shell">
<view class="advance-ticket-stack">
<view class="advance-ticket-top">
<text class="advance-ticket-code">{{ detail.applyCode || '—' }}</text>
<text
class="advance-ticket-status"
:class="ticketHeaderStatusToneClass(detail.status)"
>
{{ statusLabel(detail.status) }}
</text>
</view>
<view class="advance-ticket-tear">
<view class="advance-ticket-tear__line" />
</view>
<view class="advance-ticket-bottom">
<view class="advance-ticket-reason">{{ detail.reason || '—' }}</view>
<view class="advance-ticket-amount">{{ formatAmount(detail.amount) }}</view>
</view>
</view>
</view>
<view class="detail-tabs">
<view
class="detail-tab"
:class="{ active: detailTab === 0 }"
@click="detailTab = 0"
>
基础信息
</view>
<view
class="detail-tab"
:class="{ active: detailTab === 1 }"
@click="detailTab = 1"
>
审批信息
</view>
<view
class="detail-tab"
:class="{ active: detailTab === 2 }"
@click="detailTab = 2"
>
发放信息
</view>
</view>
<view v-if="detailTab === 0" class="page-padded advance-detail-stack">
<view class="detail-info-card">
<view class="adv-field">
<text class="adv-field__label">申请单号</text>
<text class="adv-field__value">{{ detail.applyCode || '—' }}</text>
</view>
<view class="adv-field">
<text class="adv-field__label">申请原因</text>
<text class="adv-field__value adv-field__value--multiline">{{ detail.reason || '—' }}</text>
</view>
<view class="adv-field">
<text class="adv-field__label">实际发放金额</text>
<text
class="adv-field__value"
:class="{ 'adv-field__value--accent': detail.actualAmount != null }"
>
{{
detail.actualAmount != null
? '¥ ' + formatAmount(detail.actualAmount)
: '—'
}}
</text>
</view>
<view class="adv-field">
<text class="adv-field__label">申请人</text>
<text class="adv-field__value">{{ detail.applicantName || '—' }}</text>
</view>
<view class="adv-field">
<text class="adv-field__label">当前节点</text>
<text class="adv-field__value">{{ detail.currentNode || '—' }}</text>
</view>
<view class="adv-field">
<text class="adv-field__label">申请时间</text>
<text class="adv-field__value">{{ formatApplyDateTime(detail) }}</text>
</view>
<view v-if="detail.remark" class="adv-field">
<text class="adv-field__label">备注</text>
<text class="adv-field__value adv-field__value--multiline">{{ detail.remark }}</text>
</view>
</view>
<view class="detail-info-card">
<view class="detail-info-card__title">申请附件</view>
<view v-if="attachments.length" class="att-grid">
<view
v-for="file in attachments"
:key="file.id"
class="att-img"
@click="previewAtt(file)"
>
<image
v-if="file.previewUrl"
:src="file.previewUrl"
mode="aspectFill"
/>
<view v-else class="detail-section-empty" style="border: none; padding: 8px">文件</view>
</view>
</view>
<view v-else class="detail-section-empty">暂无申请附件</view>
</view>
</view>
<view v-else-if="detailTab === 1" class="page-padded advance-detail-stack">
<view class="detail-info-card" style="padding: 14px 16px">
<template v-if="approvals.length">
<view v-for="(a, i) in approvals" :key="a.id ?? i" class="flow-step">
<view
class="flow-avatar"
:class="avatarClass(a)"
>
{{ (a.approverName || a.nodeName || a.nodeLabel || '审')[0] }}
</view>
<view class="flow-info">
<view class="flow-name">{{ a.nodeName || a.nodeLabel || a.approverName || '—' }}</view>
<view v-if="a.approverName" class="flow-role">{{ a.approverName }}</view>
<view v-if="a.approvalTime" class="flow-time">{{ formatDateTime(a.approvalTime) }}</view>
<view v-if="a.approvalOpinion" class="adv-flow-opinion">{{ a.approvalOpinion }}</view>
</view>
</view>
</template>
<view v-else class="detail-section-empty">暂无审批记录</view>
</view>
</view>
<view v-else class="page-padded advance-detail-stack">
<template v-if="isAdvanceIssued">
<view class="detail-info-card">
<view class="adv-field">
<text class="adv-field__label">发放时间</text>
<text class="adv-field__value">{{ formatDateTime(detail.issueTime) || '—' }}</text>
</view>
<view class="adv-field">
<text class="adv-field__label">发放操作人</text>
<text class="adv-field__value">{{ detail.issueUserName || '—' }}</text>
</view>
<view v-if="detail.issueRemark" class="adv-field">
<text class="adv-field__label">发放备注</text>
<text class="adv-field__value adv-field__value--multiline">{{ detail.issueRemark }}</text>
</view>
</view>
</template>
<view v-else class="detail-info-card" style="padding: 28px 16px">
<view class="detail-section-empty">尚未发放</view>
</view>
</view>
</template>
<view v-else class="page-body-loading">加载中...</view>
<view v-if="showActions" class="bottom-action-bar">
<template v-if="detail?.status === 'draft'">
<button
class="btn btn-primary"
style="flex: 1"
:disabled="actionLoading.submit"
@click="handleSubmit"
>
提交审批
</button>
<button
class="btn btn-danger"
style="flex: 1"
:disabled="actionLoading.delete"
@click="handleDelete"
>
删除
</button>
</template>
<template v-if="detail?.status === 'rejected'">
<button class="btn btn-primary" style="flex: 1" @click="handleResubmit">重新提交</button>
</template>
</view>
</view>
</template>
<script setup>
import { ref, reactive, computed } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { PAGE_PATHS } from '@/config/api'
import { advanceApi, attachmentApi } from '@/api/bx'
import {
statusLabel,
ticketHeaderStatusToneClass,
formatAmount,
formatDateTime,
formatApplyDateTime
} from '@/utils/bxFormat'
import { getSafeAreaStyle } from '@/utils/safeArea'
const safeAreaStyle = getSafeAreaStyle()
const detailId = ref('')
const detail = ref(null)
const detailTab = ref(0)
const approvals = ref([])
const attachments = ref([])
const actionLoading = reactive({ submit: false, delete: false })
const isAdvanceIssued = computed(() => {
const d = detail.value
return !!(d && (d.issueStatus === 'issued' || d.status === 'issued'))
})
const showActions = computed(() => {
if (!detail.value) return false
const status = detail.value.status
return status === 'draft' || status === 'rejected'
})
function goBack() {
uni.navigateBack({ fail: () => uni.redirectTo({ url: PAGE_PATHS.bxAdvanceApply }) })
}
function avatarClass(a) {
const s = a.approvalStatus || a.status
if (s === 'approved') return 'av-done'
if (s === 'pending') return 'av-current'
if (s === 'rejected') return 'av-reject'
return 'av-pending'
}
function previewAtt(file) {
if (!file.previewUrl) return
uni.previewImage({ urls: [file.previewUrl], current: file.previewUrl })
}
async function loadDetail() {
const id = detailId.value
if (!id) return
try {
const res = await advanceApi.getById(id)
detail.value = res.data
} catch (e) {
uni.showToast({ title: '加载详情失败', icon: 'none' })
return
}
try {
const histRes = await advanceApi.getApprovalHistory(id)
const hist = histRes.data || []
if (Array.isArray(hist) && hist.length) {
approvals.value = hist
} else {
const apprRes = await advanceApi.getApprovals(id)
const nodes = apprRes.data || []
approvals.value = nodes.slice().sort((a, b) => (a.nodeOrder ?? 0) - (b.nodeOrder ?? 0))
}
} catch (e) {
try {
const apprRes = await advanceApi.getApprovals(id)
const nodes = apprRes.data || []
approvals.value = nodes.slice().sort((a, b) => (a.nodeOrder ?? 0) - (b.nodeOrder ?? 0))
} catch (err) {
approvals.value = []
}
}
try {
const attRes = await attachmentApi.listByBiz('advance', id)
const list = (attRes.data || []).map((a) => ({ ...a, previewUrl: '' }))
await Promise.all(
list
.filter((a) => (a.fileType || '').startsWith('image/'))
.map(async (a) => {
try {
const pRes = await attachmentApi.preview(a.id)
a.previewUrl = pRes.data?.url || ''
} catch (e) {
// skip
}
})
)
attachments.value = list
} catch (e) {
attachments.value = []
}
}
async function handleSubmit() {
if (!detail.value) return
actionLoading.submit = true
try {
await advanceApi.submit(detail.value.id)
uni.showToast({ title: '提交成功', icon: 'success' })
await loadDetail()
} catch (e) {
// toasted
} finally {
actionLoading.submit = false
}
}
function handleDelete() {
if (!detail.value) return
uni.showModal({
title: '确认删除',
content: '确定要删除该备用金申请吗?删除后不可恢复。',
confirmText: '确定删除',
confirmColor: '#ef4444',
success: async (res) => {
if (!res.confirm) return
actionLoading.delete = true
try {
await advanceApi.delete(detail.value.id)
uni.showToast({ title: '删除成功', icon: 'success' })
setTimeout(() => goBack(), 500)
} catch (e) {
// toasted
} finally {
actionLoading.delete = false
}
}
})
}
function handleResubmit() {
if (!detail.value) return
uni.navigateTo({
url: `${PAGE_PATHS.bxAdvanceApplyForm}?id=${detail.value.id}`
})
}
onLoad((query) => {
detailId.value = query?.id ? String(query.id) : ''
loadDetail()
})
</script>
<style lang="scss">
@import '@/styles/bx-theme.scss';
</style>