修改计费失败原因
This commit is contained in:
@@ -65,6 +65,15 @@ export function listOrderBill(query) {
|
||||
})
|
||||
}
|
||||
|
||||
/** Order-list: billing fail reason popup */
|
||||
export function getOrderBillFailReason(billId) {
|
||||
return request({
|
||||
url: '/price/billing/orderBill/' + billId + '/failReason',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
/** Order-bill product check: package sales products missing from catalog */
|
||||
export function checkOrderBillProducts(data) {
|
||||
return request({
|
||||
|
||||
@@ -106,9 +106,6 @@
|
||||
<template #col-chargeWeight="{ row }">
|
||||
{{ formatWeight(row.chargeWeight) }}
|
||||
</template>
|
||||
<template #col-matchSource="{ row }">
|
||||
<el-tag size="small" :type="sourceTagType(row.matchSource)">{{ sourceLabel(row.matchSource) }}</el-tag>
|
||||
</template>
|
||||
<template #col-orderStatus="{ row }">
|
||||
{{ orderStatusLabel(row.orderStatus) }}
|
||||
</template>
|
||||
@@ -565,7 +562,6 @@ const columns = [
|
||||
{ label: '收货重', prop: 'receiveWeight', align: 'center', width: 90, slot: 'chargeWeight' },
|
||||
{ label: '应收总额', prop: 'incomeTotal', align: 'right', width: 120, slot: 'incomeTotal' },
|
||||
{ label: '应付总额', prop: 'payTotal', align: 'right', width: 120, slot: 'payTotal' },
|
||||
{ label: '计价来源', prop: 'matchSource', align: 'center', width: 130, slot: 'matchSource' },
|
||||
{ label: '订单状态', prop: 'orderStatus', align: 'center', width: 110, slot: 'orderStatus' },
|
||||
{ label: '计费状态', prop: 'billingFailStatus', align: 'center', width: 120, slot: 'billingFailStatus' },
|
||||
{ label: '客户状态', prop: 'customerActiveStatus', align: 'center', width: 100, slot: 'customerActiveStatus' },
|
||||
@@ -643,19 +639,6 @@ const searchColumns = [
|
||||
defaultTime: [new Date(2000, 0, 1, 0, 0, 0), new Date(2000, 0, 1, 23, 59, 59)]
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '\u8ba1\u8d39\u5931\u8d25',
|
||||
prop: 'billingFailStatuses',
|
||||
component: 'select',
|
||||
options: billingFailStatusOptions,
|
||||
placeholder: '\u8bf7\u9009\u62e9',
|
||||
props: {
|
||||
multiple: true,
|
||||
clearable: true,
|
||||
collapseTags: true,
|
||||
collapseTagsTooltip: true
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
function buildBillQuery(params) {
|
||||
|
||||
@@ -186,7 +186,13 @@
|
||||
{{ orderStatusLabel(row.orderStatus) }}
|
||||
</template>
|
||||
<template #col-billingFailStatus="{ row }">
|
||||
<el-tag size="small" :type="billingFailTagType(row.billingFailStatus)">{{ billingFailLabel(row.billingFailStatus) }}</el-tag>
|
||||
<el-tag
|
||||
size="small"
|
||||
:type="billingFailTagType(row.billingFailStatus)"
|
||||
class="billing-fail-tag"
|
||||
:class="{ 'is-clickable': Number(row.billingFailStatus) !== 0 }"
|
||||
@click="openFailReason(row)"
|
||||
>{{ billingFailLabel(row.billingFailStatus) }}</el-tag>
|
||||
</template>
|
||||
</basic-table>
|
||||
<el-dialog
|
||||
@@ -294,12 +300,92 @@
|
||||
:fields="ORDER_BILL_EXPORT_FIELDS"
|
||||
storage-key="exportFields:orderBill"
|
||||
/>
|
||||
<el-dialog
|
||||
v-model="failReasonVisible"
|
||||
title="计费失败详情"
|
||||
width="780px"
|
||||
append-to-body
|
||||
destroy-on-close
|
||||
class="fail-reason-dialog"
|
||||
>
|
||||
<div v-loading="failReasonLoading" class="fail-reason-body">
|
||||
<div v-if="failReason.failReasonDetail || failReason.errorMessage" class="fail-reason-banner">
|
||||
<div class="fail-reason-banner__badge">{{ failReason.billingFailStatusLabel || billingFailLabel(failReason.billingFailStatus) }}</div>
|
||||
<div class="fail-reason-text">{{ failReason.failReasonDetail || failReason.errorMessage }}</div>
|
||||
</div>
|
||||
<el-descriptions :column="2" border size="small" class="fail-reason-desc">
|
||||
<el-descriptions-item label="订单号">
|
||||
<span class="fr-val">{{ failReason.orderNo || '-' }}</span>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="转单号">
|
||||
<span class="fr-val">{{ failReason.transferNo || '-' }}</span>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="运单号">
|
||||
<span class="fr-val">{{ failReason.trackingNo || '-' }}</span>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="计费状态">
|
||||
<el-tag size="small" type="danger" effect="dark">{{ failReason.billingFailStatusLabel || billingFailLabel(failReason.billingFailStatus) }}</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label-class-name="fr-label-key" class-name="fr-cell-key" label="客户">
|
||||
<span :class="frValueClass(joinCodeName(failReason.customerCode, failReason.customerName), true)">{{ joinCodeName(failReason.customerCode, failReason.customerName) }}</span>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label-class-name="fr-label-key" class-name="fr-cell-key" label="产品">
|
||||
<span :class="frValueClass(joinCodeName(failReason.productCode, failReason.productName), true)">{{ joinCodeName(failReason.productCode, failReason.productName) }}</span>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label-class-name="fr-label-key" class-name="fr-cell-key" label="国家">
|
||||
<span :class="frValueClass(joinCodeName(failReason.countryCode, failReason.countryName), true)">{{ joinCodeName(failReason.countryCode, failReason.countryName) }}</span>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="分区">
|
||||
<span :class="frValueClass(joinCodeName(failReason.zoneCode, failReason.zoneName))">{{ joinCodeName(failReason.zoneCode, failReason.zoneName) }}</span>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="收件人邮编">
|
||||
<span :class="frValueClass(failReason.destPostcode || '-')">{{ failReason.destPostcode || '-' }}</span>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label-class-name="fr-label-key" class-name="fr-cell-key" label="套餐">
|
||||
<span :class="frValueClass(joinCodeName(failReason.priceSetCode, failReason.priceSetName), true)">{{ joinCodeName(failReason.priceSetCode, failReason.priceSetName) }}</span>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="套餐版本">
|
||||
<span :class="frValueClass(failReason.versionNo || '-')">{{ failReason.versionNo || '-' }}</span>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="重量段">
|
||||
<span :class="frValueClass(failReason.weightLabel || formatWeightRange(failReason.weightMin, failReason.weightMax))">{{ failReason.weightLabel || formatWeightRange(failReason.weightMin, failReason.weightMax) }}</span>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label-class-name="fr-label-key" class-name="fr-cell-key" label="计费重">
|
||||
<span :class="frValueClass(formatWeight(failReason.chargeWeight), true)">{{ formatWeight(failReason.chargeWeight) }}</span>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="原计费重">
|
||||
<span class="fr-val">{{ formatWeight(failReason.weight) }}</span>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="单价">
|
||||
<span class="fr-val fr-val-money">{{ formatMoney(failReason.freightRate, failReason.currency) }}</span>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="运费金额">
|
||||
<span class="fr-val fr-val-money">{{ formatMoney(failReason.freightAmount, failReason.currency) }}</span>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="应收总额">
|
||||
<span class="fr-val fr-val-money">{{ formatMoney(failReason.incomeTotal, failReason.currency) }}</span>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="计费时间">
|
||||
<span class="fr-val">{{ formatDateTime(failReason.bizDate) }}</span>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label-class-name="fr-label-key" class-name="fr-cell-key" label="收货时间">
|
||||
<span :class="frValueClass(formatDateTime(failReason.pickupDate), true)">{{ formatDateTime(failReason.pickupDate) }}</span>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="原始失败信息" :span="2">
|
||||
<span class="fr-val fr-val-raw">{{ failReason.errorMessage || '-' }}</span>
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</div>
|
||||
<template #footer>
|
||||
<el-button type="primary" @click="failReasonVisible = false">关闭</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="OrderBillQuery">
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { listOrderBill, localRebillAsync, pollLocalRebillTask, cancelLocalRebill, getLocalRebillTask } from '@/api/price/billing'
|
||||
import { listOrderBill, localRebillAsync, pollLocalRebillTask, cancelLocalRebill, getLocalRebillTask, getOrderBillFailReason } from '@/api/price/billing'
|
||||
import { exportAsyncFromTable } from '@/utils/excelExport'
|
||||
import ExcelExportFieldDialog from '@/components/ExcelExportFieldDialog/index.vue'
|
||||
import { ORDER_BILL_EXPORT_FIELDS } from '@/utils/exportFieldDefs'
|
||||
@@ -1068,6 +1154,133 @@ function orderStatusLabel(status) {
|
||||
return String(status)
|
||||
}
|
||||
|
||||
|
||||
const failReasonVisible = ref(false)
|
||||
const failReasonLoading = ref(false)
|
||||
const failReason = reactive({
|
||||
billId: '',
|
||||
orderNo: '',
|
||||
transferNo: '',
|
||||
trackingNo: '',
|
||||
customerCode: '',
|
||||
customerName: '',
|
||||
productCode: '',
|
||||
productName: '',
|
||||
countryCode: '',
|
||||
countryName: '',
|
||||
zoneCode: '',
|
||||
zoneName: '',
|
||||
destPostcode: '',
|
||||
priceSetCode: '',
|
||||
priceSetName: '',
|
||||
versionNo: '',
|
||||
weightLabel: '',
|
||||
weightMin: null,
|
||||
weightMax: null,
|
||||
chargeWeight: null,
|
||||
weight: null,
|
||||
freightRate: null,
|
||||
freightAmount: null,
|
||||
incomeTotal: null,
|
||||
currency: '',
|
||||
bizDate: null,
|
||||
pickupDate: null,
|
||||
billingFailStatus: null,
|
||||
billingFailStatusLabel: '',
|
||||
errorMessage: '',
|
||||
failReasonDetail: ''
|
||||
})
|
||||
|
||||
function joinCodeName(code, name) {
|
||||
if (!code && !name) return '-'
|
||||
if (!code) return name
|
||||
if (!name || name === code) return code
|
||||
return code + ' / ' + name
|
||||
}
|
||||
|
||||
function frValueClass(text, keyField) {
|
||||
const empty = text == null || text === '' || text === '-'
|
||||
if (empty) return 'fr-val fr-val-empty'
|
||||
return keyField ? 'fr-val fr-val-key' : 'fr-val'
|
||||
}
|
||||
|
||||
function formatWeightRange(min, max) {
|
||||
if (min == null && max == null) return '-'
|
||||
return (min == null ? '-' : formatWeight(min)) + ' ~ ' + (max == null ? '-' : formatWeight(max))
|
||||
}
|
||||
|
||||
function formatMoney(val, currency) {
|
||||
if (val == null || val === '') return '-'
|
||||
const num = Number(val)
|
||||
if (Number.isNaN(num)) return String(val)
|
||||
return (currency ? (currency + ' ') : '') + num.toFixed(4)
|
||||
}
|
||||
|
||||
function formatDateTime(val) {
|
||||
if (!val) return '-'
|
||||
const d = new Date(val)
|
||||
if (Number.isNaN(d.getTime())) return String(val)
|
||||
const pad = (n) => String(n).padStart(2, '0')
|
||||
return d.getFullYear() + '-' + pad(d.getMonth() + 1) + '-' + pad(d.getDate()) +
|
||||
' ' + pad(d.getHours()) + ':' + pad(d.getMinutes()) + ':' + pad(d.getSeconds())
|
||||
}
|
||||
|
||||
function applyFailReason(data, row) {
|
||||
const src = data || {}
|
||||
const base = row || {}
|
||||
failReason.billId = src.billId || base.billId || ''
|
||||
failReason.orderNo = src.orderNo || base.orderNo || ''
|
||||
failReason.transferNo = src.transferNo || base.transferNo || ''
|
||||
failReason.trackingNo = src.trackingNo || base.trackingNo || ''
|
||||
failReason.customerCode = src.customerCode || base.customerCode || ''
|
||||
failReason.customerName = src.customerName || base.customerName || ''
|
||||
failReason.productCode = src.productCode || base.productCode || ''
|
||||
failReason.productName = src.productName || base.productName || ''
|
||||
failReason.countryCode = src.countryCode || base.countryCode || ''
|
||||
failReason.countryName = src.countryName || base.countryName || ''
|
||||
failReason.zoneCode = src.zoneCode || ''
|
||||
failReason.zoneName = src.zoneName || ''
|
||||
failReason.destPostcode = src.destPostcode || base.destPostcode || ''
|
||||
failReason.priceSetCode = src.priceSetCode || base.priceSetCode || ''
|
||||
failReason.priceSetName = src.priceSetName || base.priceSetName || ''
|
||||
failReason.versionNo = src.versionNo || ''
|
||||
failReason.weightLabel = src.weightLabel || base.weightLabel || ''
|
||||
failReason.weightMin = src.weightMin != null ? src.weightMin : base.weightMin
|
||||
failReason.weightMax = src.weightMax != null ? src.weightMax : base.weightMax
|
||||
failReason.chargeWeight = src.chargeWeight != null ? src.chargeWeight : base.chargeWeight
|
||||
failReason.weight = src.weight != null ? src.weight : base.weight
|
||||
failReason.freightRate = src.freightRate != null ? src.freightRate : base.freightRate
|
||||
failReason.freightAmount = src.freightAmount != null ? src.freightAmount : base.freightAmount
|
||||
failReason.incomeTotal = src.incomeTotal != null ? src.incomeTotal : base.incomeTotal
|
||||
failReason.currency = src.currency || base.currency || ''
|
||||
failReason.bizDate = src.bizDate || base.bizDate || null
|
||||
failReason.pickupDate = src.pickupDate || base.pickupDate || null
|
||||
failReason.billingFailStatus = src.billingFailStatus != null ? src.billingFailStatus : base.billingFailStatus
|
||||
failReason.billingFailStatusLabel = src.billingFailStatusLabel || billingFailLabel(failReason.billingFailStatus)
|
||||
failReason.errorMessage = src.errorMessage || ''
|
||||
failReason.failReasonDetail = src.failReasonDetail || src.errorMessage || billingFailLabel(failReason.billingFailStatus)
|
||||
}
|
||||
|
||||
async function openFailReason(row) {
|
||||
if (!row || !row.billId) return
|
||||
const code = row.billingFailStatus == null ? 0 : Number(row.billingFailStatus)
|
||||
if (code === 0) return
|
||||
failReasonVisible.value = true
|
||||
failReasonLoading.value = true
|
||||
applyFailReason({}, row)
|
||||
failReason.errorMessage = ''
|
||||
failReason.failReasonDetail = ''
|
||||
try {
|
||||
const res = await getOrderBillFailReason(row.billId)
|
||||
applyFailReason((res && res.data) || {}, row)
|
||||
} catch (e) {
|
||||
failReason.errorMessage = (e && e.message) || '查询失败原因失败'
|
||||
failReason.failReasonDetail = failReason.errorMessage
|
||||
} finally {
|
||||
failReasonLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function billingFailLabel(status) {
|
||||
const code = status == null ? 0 : Number(status)
|
||||
const hit = billingFailStatusOptions.find(item => item.value === code)
|
||||
@@ -1222,4 +1435,74 @@ onBeforeUnmount(() => {
|
||||
color: var(--el-text-color-secondary);
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.billing-fail-tag.is-clickable {
|
||||
cursor: pointer;
|
||||
}
|
||||
.fail-reason-body {
|
||||
min-height: 180px;
|
||||
}
|
||||
.fail-reason-banner {
|
||||
margin-bottom: 14px;
|
||||
padding: 12px 14px;
|
||||
border-radius: 8px;
|
||||
border: 1px solid #f3a5a5;
|
||||
background: linear-gradient(180deg, #fff5f5 0%, #ffecec 100%);
|
||||
}
|
||||
.fail-reason-banner__badge {
|
||||
display: inline-block;
|
||||
margin-bottom: 8px;
|
||||
padding: 2px 10px;
|
||||
border-radius: 999px;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
color: #fff;
|
||||
background: #e11d48;
|
||||
}
|
||||
.fail-reason-text {
|
||||
white-space: pre-wrap;
|
||||
word-break: break-word;
|
||||
line-height: 1.65;
|
||||
font-size: 13px;
|
||||
color: #9f1239;
|
||||
}
|
||||
.fail-reason-desc {
|
||||
margin-top: 4px;
|
||||
}
|
||||
.fail-reason-desc :deep(.el-descriptions__label) {
|
||||
width: 110px;
|
||||
color: #64748b;
|
||||
background: #f8fafc;
|
||||
font-weight: 500;
|
||||
}
|
||||
.fail-reason-desc :deep(.fr-label-key) {
|
||||
color: #9a3412;
|
||||
background: #fff7ed;
|
||||
font-weight: 700;
|
||||
}
|
||||
.fail-reason-desc :deep(.fr-cell-key) {
|
||||
background: #fffbeb;
|
||||
}
|
||||
.fr-val {
|
||||
color: #0f172a;
|
||||
font-weight: 500;
|
||||
}
|
||||
.fr-val-key {
|
||||
color: #c2410c;
|
||||
font-weight: 700;
|
||||
}
|
||||
.fr-val-empty {
|
||||
color: #dc2626;
|
||||
font-weight: 700;
|
||||
background: #fee2e2;
|
||||
padding: 1px 8px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.fr-val-money {
|
||||
color: #b91c1c;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
.fr-val-raw {
|
||||
color: #7f1d1d;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user