41 lines
3.2 KiB
Vue
41 lines
3.2 KiB
Vue
<script setup lang="ts">
|
||
import { onBeforeUnmount, ref, watch } from 'vue'
|
||
import { useAccessPreviewStore } from '@/core/access/accessPreview'
|
||
import { getApiErrorDetails, type ApiErrorDetails } from '@/shared/api/http'
|
||
import ApiErrorMeta from '@/shared/components/ApiErrorMeta.vue'
|
||
import { getTripOperationsFinance, type TripFinanceResult } from '../api/tripOperationsFinance'
|
||
const props = defineProps<{ tripNo: string }>()
|
||
const access = useAccessPreviewStore(), result = ref<TripFinanceResult | null>(null), loading = ref(false), error = ref<ApiErrorDetails | null>(null)
|
||
let sequence = 0
|
||
async function load() {
|
||
if (!access.can('platform:finance:customer-arap:list') || !access.can('platform:fleet:operations:list') || loading.value) return
|
||
const current = ++sequence, tripNo = props.tripNo; loading.value = true; error.value = null
|
||
try { const data = await getTripOperationsFinance(tripNo); if (current === sequence) result.value = data }
|
||
catch (cause) { if (current === sequence) { result.value = null; error.value = getApiErrorDetails(cause, '财务读取失败') } }
|
||
finally { if (current === sequence) loading.value = false }
|
||
}
|
||
watch(() => props.tripNo, () => { sequence++; result.value = null; error.value = null; loading.value = false })
|
||
onBeforeUnmount(() => { sequence++ })
|
||
</script>
|
||
<template>
|
||
<span v-if="!access.can('platform:finance:customer-arap:list')">无应收读取权限</span>
|
||
<template v-else>
|
||
<el-button v-if="!result" class="density-action" :loading="loading" @click="load">{{ error ? '重试财务读取' : '查看应收/实收' }}</el-button>
|
||
<span v-if="error">{{ error.message }}<ApiErrorMeta :details="error" /></span>
|
||
<template v-if="result">
|
||
<div v-for="m in result.finance.totals" :key="m.currency">{{ m.currency }} 应收 {{ m.receivableAmount.toFixed(2) }} / 实收 {{ !result.receiptsVisible ? '无读取权限' : m.receivedAmount == null ? '待分配' : m.receivedAmount.toFixed(2) }}</div>
|
||
<span v-if="!result.finance.totals.length || result.finance.status === 'PENDING_ALLOCATION' || result.attributionStatus === 'PENDING_ALLOCATION'">完整金额待分配</span>
|
||
<el-popover trigger="click" :width="620" placement="left">
|
||
<div class="finance-source-details"><p>{{ result.attributionReason }}</p><p>{{ result.finance.reason }}</p>
|
||
<p>排车来源:{{ result.dispatchNos.join(' / ') || '—' }}</p>
|
||
<div v-for="s in result.finance.sources" :key="`${s.customerId}:${s.businessNo}:${s.sourceNo}:${s.feeItemName}:${s.sourceVersion}`">{{ s.businessNo }} · {{ s.sourceNo || '—' }} · {{ s.feeItemName }} · 版本 {{ s.sourceVersion }} · {{ s.currency }} {{ s.amount.toFixed(2) }}</div>
|
||
<div v-for="d in result.finance.documents" :key="d.billNo">{{ d.billNo }} · {{ d.currency }} {{ d.receivedAmount == null ? '待分配' : d.receivedAmount.toFixed(2) }} · {{ d.reason }}</div>
|
||
</div><template #reference><el-button class="density-action">金额来源</el-button></template>
|
||
</el-popover>
|
||
<el-button class="density-action" :loading="loading" @click="load">刷新</el-button>
|
||
</template>
|
||
</template>
|
||
</template>
|
||
|
||
<style scoped>.finance-source-details { max-height: 50vh; overflow: auto; }</style>
|