381 lines
10 KiB
Vue
381 lines
10 KiB
Vue
<template>
|
||
<el-dialog
|
||
v-model="visible"
|
||
title="付款记录详情"
|
||
width="800px"
|
||
:close-on-click-modal="false"
|
||
@close="handleClose"
|
||
>
|
||
<div class="payment-detail-dialog" v-loading="loading">
|
||
<div v-if="paymentData" class="detail-content">
|
||
<!-- 基本信息 -->
|
||
<div class="section">
|
||
<div class="section-title">
|
||
<span class="border"></span>
|
||
<span class="title">基本信息</span>
|
||
</div>
|
||
<div class="info-grid">
|
||
<div class="info-item">
|
||
<label>创建日期:</label>
|
||
<span>{{ formatDate(paymentData.created_at) }}</span>
|
||
</div>
|
||
<div class="info-item">
|
||
<label>付款来源:</label>
|
||
<span>{{ paymentData.country_name || '—' }}</span>
|
||
</div>
|
||
<div class="info-item">
|
||
<label>汇款人:</label>
|
||
<span>{{ paymentData.remitter || '—' }}</span>
|
||
</div>
|
||
<div class="info-item">
|
||
<label>账户:</label>
|
||
<span>{{ paymentData.account_name || '—' }}</span>
|
||
</div>
|
||
<div class="info-item">
|
||
<label>币种:</label>
|
||
<span>{{ paymentData.currency_name || '—' }}</span>
|
||
</div>
|
||
<div class="info-item">
|
||
<label>金额:</label>
|
||
<span class="amount">{{ paymentData.amount }}</span>
|
||
</div>
|
||
<div class="info-item">
|
||
<label>付款水单:</label>
|
||
<div>
|
||
<p v-for="(item, index) in paymentData.voucher_file" :key="index">
|
||
<el-image
|
||
v-if="
|
||
/\.(png|jpe?g|gif|webp|bmp|svg)(\?|$)/i.test(
|
||
(item.file_full || '').split('?')[0]
|
||
)
|
||
"
|
||
:src="item.file_full"
|
||
:preview-src-list="item.file_full ? [item.file_full] : []"
|
||
style="width: 100px; height: 100px"
|
||
/>
|
||
<a
|
||
v-else
|
||
:href="item.file_full"
|
||
target="_blank"
|
||
rel="noopener"
|
||
style="
|
||
flex: 1;
|
||
white-space: nowrap;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
width: 200px;
|
||
"
|
||
>{{ item.file_name }}</a
|
||
>
|
||
</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 关联信息 -->
|
||
<div class="section" v-if="paymentData.payment_apply_id">
|
||
<div class="section-title">
|
||
<span class="border"></span>
|
||
<span class="title">关联信息</span>
|
||
</div>
|
||
<div class="info-grid">
|
||
<div class="info-item">
|
||
<label>付款申请ID:</label>
|
||
<span>{{ paymentData.payment_apply_id }}</span>
|
||
</div>
|
||
<div class="info-item">
|
||
<label>关联状态:</label>
|
||
<el-tag :type="getStatusType(paymentData.status)">
|
||
{{ getStatusText(paymentData.status) }}
|
||
</el-tag>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 收款记录关联 -->
|
||
<div
|
||
class="section"
|
||
v-if="paymentData.receipt_records && paymentData.receipt_records.length"
|
||
>
|
||
<div class="section-title">
|
||
<span class="border"></span>
|
||
<span class="title">关联收款记录</span>
|
||
</div>
|
||
<el-table :data="paymentData.receipt_records" style="margin-top: 12px">
|
||
<el-table-column label="收款记录ID" prop="id" min-width="120" />
|
||
<el-table-column label="创建日期" prop="created_at" min-width="140">
|
||
<template #default="scope">
|
||
{{ formatDate(scope.row.created_at) }}
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="金额" prop="amount" min-width="120">
|
||
<template #default="scope">
|
||
{{ formatAmount(scope.row.amount, scope.row.currency_name) }}
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="状态" prop="status" min-width="100">
|
||
<template #default="scope">
|
||
<el-tag :type="getStatusType(scope.row.status)">
|
||
{{ getStatusText(scope.row.status) }}
|
||
</el-tag>
|
||
</template>
|
||
</el-table-column>
|
||
</el-table>
|
||
</div>
|
||
|
||
<!-- 备注信息 -->
|
||
<div class="section" v-if="paymentData.remark">
|
||
<div class="section-title">
|
||
<span class="border"></span>
|
||
<span class="title">备注信息</span>
|
||
</div>
|
||
<div class="remark-content">
|
||
{{ paymentData.remark }}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 无数据状态 -->
|
||
<div v-else-if="!loading" class="no-data">
|
||
<i class="iconfont-sys"></i>
|
||
<span>暂无数据</span>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- <div class="dialog-footer">
|
||
<el-button @click="handleClose">关闭</el-button>
|
||
</div> -->
|
||
</el-dialog>
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
import { financeService } from '@/api/admin/finance'
|
||
|
||
interface PaymentRecord {
|
||
id: string | number
|
||
created_at: string
|
||
country_name: string
|
||
remitter: string
|
||
account_name: string
|
||
currency_name: string
|
||
amount: number
|
||
status: string
|
||
payment_apply_id?: string | number
|
||
receipt_records?: any[]
|
||
voucher_file?: any[]
|
||
submit_files?: string[]
|
||
remark?: string
|
||
}
|
||
|
||
interface Props {
|
||
modelValue: boolean
|
||
paymentId?: string | number
|
||
}
|
||
|
||
interface Emits {
|
||
(e: 'update:modelValue', value: boolean): void
|
||
}
|
||
|
||
const props = defineProps<Props>()
|
||
const emit = defineEmits<Emits>()
|
||
|
||
const visible = computed({
|
||
get: () => props.modelValue,
|
||
set: (value) => emit('update:modelValue', value)
|
||
})
|
||
|
||
const loading = ref(false)
|
||
const paymentData = ref<PaymentRecord | null>(null)
|
||
|
||
// 获取付款记录详情
|
||
const getPaymentDetail = async () => {
|
||
if (!props.paymentId) return
|
||
|
||
loading.value = true
|
||
try {
|
||
const res = await financeService.getPaymentDetail({ id: props.paymentId })
|
||
paymentData.value = res
|
||
} catch (error) {
|
||
console.error('获取付款记录详情失败:', error)
|
||
paymentData.value = null
|
||
} finally {
|
||
loading.value = false
|
||
}
|
||
}
|
||
|
||
// 关闭对话框
|
||
const handleClose = () => {
|
||
visible.value = false
|
||
paymentData.value = null
|
||
}
|
||
|
||
// 打印功能
|
||
const handlePrint = () => {
|
||
window.print()
|
||
}
|
||
|
||
// 格式化日期
|
||
const formatDate = (dateStr: string) => {
|
||
if (!dateStr) return '—'
|
||
try {
|
||
return new Date(dateStr).toLocaleString('zh-CN')
|
||
} catch {
|
||
return dateStr
|
||
}
|
||
}
|
||
|
||
// 格式化金额
|
||
const formatAmount = (amount: number, currency: string) => {
|
||
return `${currency || ''} ${amount?.toLocaleString() || 0}`
|
||
}
|
||
|
||
// 获取状态类型
|
||
const getStatusType = (status: string): 'success' | 'info' | 'warning' | 'danger' | 'primary' => {
|
||
const statusMap: Record<string, 'success' | 'info' | 'warning' | 'danger' | 'primary'> = {
|
||
available: 'success',
|
||
used: 'info',
|
||
pending: 'warning',
|
||
cancelled: 'danger',
|
||
bound: 'success',
|
||
unbound: 'info'
|
||
}
|
||
return statusMap[status] || 'info'
|
||
}
|
||
|
||
// 获取状态文本
|
||
const getStatusText = (status: string) => {
|
||
const statusMap: Record<string, string> = {
|
||
'available': '可用',
|
||
'used': '已使用',
|
||
'pending': '待处理',
|
||
'cancelled': '已取消',
|
||
'bound': '已绑定',
|
||
'unbound': '未绑定'
|
||
}
|
||
return statusMap[status] || status
|
||
}
|
||
|
||
// 监听对话框显示状态
|
||
watch(visible, (newVal) => {
|
||
if (newVal && props.paymentId) {
|
||
getPaymentDetail()
|
||
}
|
||
})
|
||
|
||
// 监听付款ID变化
|
||
watch(() => props.paymentId, (newId) => {
|
||
if (visible.value && newId) {
|
||
getPaymentDetail()
|
||
}
|
||
})
|
||
</script>
|
||
|
||
<style scoped lang="scss">
|
||
.payment-detail-dialog {
|
||
.detail-content {
|
||
.section {
|
||
margin-bottom: 24px;
|
||
|
||
&:last-child {
|
||
margin-bottom: 0;
|
||
}
|
||
|
||
.section-title {
|
||
display: flex;
|
||
align-items: center;
|
||
margin-bottom: 16px;
|
||
font-weight: 600;
|
||
color: #222;
|
||
|
||
.border {
|
||
width: 4px;
|
||
height: 16px;
|
||
background: #3d7bff;
|
||
margin-right: 8px;
|
||
border-radius: 2px;
|
||
}
|
||
|
||
.title {
|
||
font-size: 16px;
|
||
}
|
||
}
|
||
|
||
.info-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
||
gap: 16px;
|
||
|
||
.info-item {
|
||
display: flex;
|
||
align-items: center;
|
||
// padding: 8px 0;
|
||
|
||
label {
|
||
font-weight: 500;
|
||
color: #666;
|
||
min-width: 100px;
|
||
margin-right: 8px;
|
||
}
|
||
|
||
span {
|
||
color: #333;
|
||
flex: 1;
|
||
|
||
&.amount {
|
||
font-weight: 600;
|
||
color: #3d7bff;
|
||
font-size: 16px;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.remark-content {
|
||
padding: 12px;
|
||
background: #f8f9fa;
|
||
border-radius: 6px;
|
||
color: #666;
|
||
line-height: 1.6;
|
||
min-height: 60px;
|
||
}
|
||
}
|
||
}
|
||
|
||
.no-data {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
padding: 60px 0;
|
||
color: #999;
|
||
|
||
i {
|
||
font-size: 48px;
|
||
margin-bottom: 12px;
|
||
}
|
||
}
|
||
}
|
||
|
||
.dialog-footer {
|
||
display: flex;
|
||
justify-content: center;
|
||
gap: 12px;
|
||
}
|
||
|
||
// 打印样式
|
||
@media print {
|
||
.dialog-footer {
|
||
display: none;
|
||
}
|
||
|
||
.payment-detail-dialog {
|
||
.section-title {
|
||
.border {
|
||
background: #000 !important;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
</style>
|