feat: 新增退款凭证
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 4m27s

This commit is contained in:
sexygoat
2026-05-27 14:58:37 +08:00
parent fe0093972f
commit 6654972ddb
14 changed files with 521 additions and 47 deletions

View File

@@ -22,6 +22,14 @@
</div>
</ElCard>
<ElImageViewer
v-if="voucherPreviewVisible"
:url-list="refundVoucherUrls"
:initial-index="voucherPreviewIndex"
hide-on-click-modal
@close="voucherPreviewVisible = false"
/>
<!-- 审批通过对话框 -->
<ElDialog
v-model="approveDialogVisible"
@@ -162,13 +170,13 @@
</template>
<script setup lang="ts">
import { ref, onMounted, computed, h } from 'vue'
import { ref, onMounted, computed, h, reactive } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { ElCard, ElButton, ElIcon, ElMessage, ElTag } from 'element-plus'
import { ElCard, ElImageViewer, ElIcon, ElMessage, ElTag } from 'element-plus'
import { ArrowLeft, Loading } from '@element-plus/icons-vue'
import DetailPage from '@/components/common/DetailPage.vue'
import type { DetailSection } from '@/components/common/DetailPage.vue'
import { RefundService } from '@/api/modules'
import { RefundService, StorageService } from '@/api/modules'
import type {
Refund,
RefundStatus,
@@ -177,7 +185,6 @@
ResubmitRefundRequest
} from '@/types/api'
import { formatDateTime, yuanToFen } from '@/utils/business/format'
import { RoutesAlias } from '@/router/routesAlias'
defineOptions({ name: 'RefundDetail' })
@@ -186,6 +193,9 @@
const loading = ref(false)
const refund = ref<Refund | null>(null)
const refundVoucherUrls = ref<string[]>([])
const voucherPreviewVisible = ref(false)
const voucherPreviewIndex = ref(0)
const pageTitle = computed(() => `退款详情`)
@@ -250,6 +260,14 @@
return statusMap[status] || '-'
}
const getVoucherKeys = (data: Refund): string[] => {
const voucherKey = data.refund_voucher_key || ''
return voucherKey
.split(',')
.map((key) => key.trim())
.filter(Boolean)
}
const detailSections = computed((): DetailSection[] => [
{
title: '退款信息',
@@ -287,6 +305,24 @@
render: (data) =>
h(ElTag, { type: getStatusType(data.status) }, () => getStatusText(data.status))
},
{
label: '退款凭证',
fullWidth: true,
render: () =>
refundVoucherUrls.value.length
? h(
'div',
{ class: 'refund-voucher-list' },
refundVoucherUrls.value.map((url, index) =>
h('img', {
class: 'refund-voucher-image',
src: url,
onClick: () => handlePreviewRefundVoucher(index)
})
)
)
: h('span', '-')
},
{
label: '资产重置',
render: (data) =>
@@ -354,6 +390,7 @@
const res = await RefundService.getRefundById(id)
if (res.code === 0) {
refund.value = res.data
await loadRefundVoucherUrls(res.data)
}
} catch (error) {
console.error(error)
@@ -367,6 +404,26 @@
router.back()
}
const loadRefundVoucherUrls = async (data: Refund) => {
const fileKeys = getVoucherKeys(data)
refundVoucherUrls.value = []
if (!fileKeys.length) return
try {
const res = await StorageService.batchDownloadUrls({ file_keys: fileKeys })
if (res.code === 0) {
refundVoucherUrls.value = fileKeys.map((key) => res.data.urls[key]).filter(Boolean)
}
} catch (error) {
console.error('获取退款凭证失败:', error)
}
}
const handlePreviewRefundVoucher = (index: number) => {
voucherPreviewIndex.value = index
voucherPreviewVisible.value = true
}
onMounted(() => {
const id = route.params.id as string
if (id) {
@@ -395,7 +452,8 @@
const handleApproveRefund = async () => {
if (!approveFormRef.value || !refund.value) return
await approveFormRef.value.validate(async (valid) => {
const refundId = refund.value.id
await approveFormRef.value.validate(async (valid: boolean) => {
if (valid) {
approveLoading.value = true
try {
@@ -404,10 +462,10 @@
remark: approveForm.remark || undefined
}
await RefundService.approveRefund(refund.value.id, data)
await RefundService.approveRefund(refundId, data)
ElMessage.success('审批通过成功')
approveDialogVisible.value = false
await fetchRefundDetail(refund.value.id)
await fetchRefundDetail(refundId)
} catch (error) {
console.error(error)
} finally {
@@ -420,14 +478,15 @@
const handleRejectRefund = async () => {
if (!rejectFormRef.value || !refund.value) return
await rejectFormRef.value.validate(async (valid) => {
const refundId = refund.value.id
await rejectFormRef.value.validate(async (valid: boolean) => {
if (valid) {
rejectLoading.value = true
try {
await RefundService.rejectRefund(refund.value.id, rejectForm)
await RefundService.rejectRefund(refundId, rejectForm)
ElMessage.success('审批拒绝成功')
rejectDialogVisible.value = false
await fetchRefundDetail(refund.value.id)
await fetchRefundDetail(refundId)
} catch (error) {
console.error(error)
} finally {
@@ -440,7 +499,8 @@
const handleResubmitRefund = async () => {
if (!resubmitFormRef.value || !refund.value) return
await resubmitFormRef.value.validate(async (valid) => {
const refundId = refund.value.id
await resubmitFormRef.value.validate(async (valid: boolean) => {
if (valid) {
resubmitLoading.value = true
try {
@@ -450,10 +510,10 @@
refund_reason: resubmitForm.refund_reason || undefined
}
await RefundService.resubmitRefund(refund.value.id, data)
await RefundService.resubmitRefund(refundId, data)
ElMessage.success('重新提交成功')
resubmitDialogVisible.value = false
await fetchRefundDetail(refund.value.id)
await fetchRefundDetail(refundId)
} catch (error) {
console.error(error)
} finally {
@@ -495,5 +555,21 @@
font-size: 32px;
}
}
:deep(.refund-voucher-list) {
display: flex;
flex-wrap: wrap;
gap: 12px;
line-height: 1;
}
:deep(.refund-voucher-image) {
width: 96px;
height: 96px;
cursor: zoom-in;
object-fit: cover;
border: 1px solid var(--el-border-color);
border-radius: 6px;
}
}
</style>