增加查看计费历史记录
This commit is contained in:
@@ -187,6 +187,15 @@ export function getBill(billId) {
|
||||
})
|
||||
}
|
||||
|
||||
/** ES billing history for one bill/order */
|
||||
export function listBillHistory(billId, query) {
|
||||
return request({
|
||||
url: '/price/billing/bill/' + encodeURIComponent(billId) + '/history',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
/** Refund receivable bill (billType=REFUND) */
|
||||
export function refundBill(billId) {
|
||||
return request({
|
||||
|
||||
@@ -0,0 +1,265 @@
|
||||
<template>
|
||||
<el-drawer
|
||||
:model-value="modelValue"
|
||||
:title="title"
|
||||
size="860px"
|
||||
append-to-body
|
||||
destroy-on-close
|
||||
@update:model-value="$emit('update:modelValue', $event)"
|
||||
>
|
||||
<div class="hist-wrap" v-loading="loading">
|
||||
<el-table :data="rows" border height="calc(100vh - 220px)" :empty-text="emptyText">
|
||||
<el-table-column :label="colTime" width="170" align="center" prop="updateTime" />
|
||||
<el-table-column :label="colAction" width="100" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag size="small" :type="actionTag(row)">{{ actionLabel(row) }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="colStatus" width="90" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag size="small" :type="statusTag(row.status)">{{ statusLabel(row.status) }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="colSet" min-width="150" show-overflow-tooltip>
|
||||
<template #default="{ row }">{{ priceSetOf(row) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="colVer" width="90" align="center">
|
||||
<template #default="{ row }">{{ versionOf(row) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="colPsd" width="100" align="center">
|
||||
<template #default="{ row }">{{ psdOf(row) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="colAmount" width="110" align="right">
|
||||
<template #default="{ row }">{{ amountOf(row) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="colOps" width="80" align="center" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<el-button link type="primary" @click="openDetail(row)">查看</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div class="pager">
|
||||
<el-pagination
|
||||
v-model:current-page="query.pageNum"
|
||||
v-model:page-size="query.pageSize"
|
||||
:total="total"
|
||||
:page-sizes="[10, 20, 50]"
|
||||
layout="total, sizes, prev, pager, next"
|
||||
@size-change="reload"
|
||||
@current-change="reload"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-drawer v-model="detailOpen" size="720px" :title="detailTitle" append-to-body destroy-on-close>
|
||||
<div v-if="!active">
|
||||
<el-empty :description="emptyDetail" />
|
||||
</div>
|
||||
<div v-else>
|
||||
<el-descriptions :column="2" border size="small" class="mb12">
|
||||
<el-descriptions-item :label="colTime">{{ active.updateTime || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item :label="colAction">{{ actionLabel(active) }}</el-descriptions-item>
|
||||
<el-descriptions-item :label="colStatus">{{ statusLabel(active.status) }}</el-descriptions-item>
|
||||
<el-descriptions-item :label="colSet">{{ priceSetOf(active) }}</el-descriptions-item>
|
||||
<el-descriptions-item :label="colVer">{{ versionOf(active) }}</el-descriptions-item>
|
||||
<el-descriptions-item :label="colPsd">{{ psdOf(active) }}</el-descriptions-item>
|
||||
<el-descriptions-item :label="colAmount">{{ amountOf(active) }}</el-descriptions-item>
|
||||
<el-descriptions-item :label="lbMatch">{{ matchMsgOf(active) }}</el-descriptions-item>
|
||||
<el-descriptions-item v-if="active.errorMessage" :label="lbError" :span="2">
|
||||
<span class="err">{{ active.errorMessage }}</span>
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
<div class="json-panel">
|
||||
<div class="json-panel__head">请求报文</div>
|
||||
<pre class="code-pre">{{ formatJson(active.requestJson) }}</pre>
|
||||
</div>
|
||||
<div class="json-panel">
|
||||
<div class="json-panel__head">返回报文</div>
|
||||
<pre class="code-pre">{{ formatJson(active.responseJson) }}</pre>
|
||||
</div>
|
||||
</div>
|
||||
</el-drawer>
|
||||
</el-drawer>
|
||||
</template>
|
||||
|
||||
<script setup name="BillBillingHistoryDrawer">
|
||||
import { listBillHistory } from '@/api/price/billing'
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: { type: Boolean, default: false },
|
||||
billId: { type: String, default: '' },
|
||||
orderNo: { type: String, default: '' }
|
||||
})
|
||||
defineEmits(['update:modelValue'])
|
||||
|
||||
const loading = ref(false)
|
||||
const rows = ref([])
|
||||
const total = ref(0)
|
||||
const query = reactive({ pageNum: 1, pageSize: 10 })
|
||||
const detailOpen = ref(false)
|
||||
const active = ref(null)
|
||||
|
||||
const emptyText = '暂无历史计费记录'
|
||||
const emptyDetail = '暂无详情'
|
||||
const colTime = '写入时间'
|
||||
const colAction = '类型'
|
||||
const colStatus = '状态'
|
||||
const colSet = '套餐'
|
||||
const colVer = '版本'
|
||||
const colPsd = '明细ID'
|
||||
const colAmount = '应收总额'
|
||||
const colOps = '操作'
|
||||
const lbMatch = '命中说明'
|
||||
const lbError = '错误信息'
|
||||
|
||||
const title = computed(() => {
|
||||
const no = props.orderNo || ''
|
||||
return no ? '历史计费记录 · ' + no : '历史计费记录'
|
||||
})
|
||||
const detailTitle = computed(() => '计费记录详情')
|
||||
|
||||
function parseJson(val) {
|
||||
if (val == null || val === '') return {}
|
||||
if (typeof val === 'object') return val
|
||||
try { return JSON.parse(val) } catch (e) { return {} }
|
||||
}
|
||||
|
||||
function respOf(row) {
|
||||
return parseJson(row && row.responseJson)
|
||||
}
|
||||
|
||||
function reqOf(row) {
|
||||
return parseJson(row && row.requestJson)
|
||||
}
|
||||
|
||||
function actionLabel(row) {
|
||||
const req = reqOf(row)
|
||||
if (req.action === 'UPSTREAM_SYNC') return '上游同步'
|
||||
if (row && row.status === 'FAIL') return '计费失败'
|
||||
const resp = respOf(row)
|
||||
if (resp.matchSource === 'BILLING_FAIL' || resp.billingFailStatus) return '计费失败'
|
||||
return '计费'
|
||||
}
|
||||
|
||||
function actionTag(row) {
|
||||
const label = actionLabel(row)
|
||||
if (label === '上游同步') return 'info'
|
||||
if (label === '计费失败') return 'danger'
|
||||
return 'success'
|
||||
}
|
||||
|
||||
function statusLabel(status) {
|
||||
return status === 'FAIL' ? '失败' : '成功'
|
||||
}
|
||||
|
||||
function statusTag(status) {
|
||||
return status === 'FAIL' ? 'danger' : 'success'
|
||||
}
|
||||
|
||||
function priceSetOf(row) {
|
||||
const r = respOf(row)
|
||||
if (r.priceSetName && r.priceSetCode) return r.priceSetName + ' (' + r.priceSetCode + ')'
|
||||
return r.priceSetName || r.priceSetCode || (r.priceSetId != null ? String(r.priceSetId) : '-')
|
||||
}
|
||||
|
||||
function versionOf(row) {
|
||||
const r = respOf(row)
|
||||
if (r.versionNo) return r.versionNo
|
||||
return r.psvId != null ? ('psv=' + r.psvId) : '-'
|
||||
}
|
||||
|
||||
function psdOf(row) {
|
||||
const r = respOf(row)
|
||||
return r.psdId != null ? String(r.psdId) : '-'
|
||||
}
|
||||
|
||||
function amountOf(row) {
|
||||
const r = respOf(row)
|
||||
const v = r.incomeTotal != null ? r.incomeTotal : r.payableTotal
|
||||
if (v == null || v === '') return '-'
|
||||
const n = Number(v)
|
||||
return Number.isNaN(n) ? String(v) : n.toFixed(2)
|
||||
}
|
||||
|
||||
function matchMsgOf(row) {
|
||||
return respOf(row).matchMessage || '-'
|
||||
}
|
||||
|
||||
function formatJson(val) {
|
||||
if (val == null || val === '') return '无数据'
|
||||
if (typeof val === 'object') {
|
||||
try { return JSON.stringify(val, null, 2) } catch (e) { return String(val) }
|
||||
}
|
||||
try { return JSON.stringify(JSON.parse(val), null, 2) } catch (e) { return String(val) }
|
||||
}
|
||||
|
||||
function openDetail(row) {
|
||||
active.value = row
|
||||
detailOpen.value = true
|
||||
}
|
||||
|
||||
async function reload() {
|
||||
if (!props.billId) {
|
||||
rows.value = []
|
||||
total.value = 0
|
||||
return
|
||||
}
|
||||
loading.value = true
|
||||
try {
|
||||
const res = await listBillHistory(props.billId, {
|
||||
pageNum: query.pageNum,
|
||||
pageSize: query.pageSize
|
||||
})
|
||||
rows.value = res.rows || []
|
||||
total.value = Number(res.total) || 0
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
watch(
|
||||
() => [props.modelValue, props.billId],
|
||||
([open]) => {
|
||||
if (open) {
|
||||
query.pageNum = 1
|
||||
detailOpen.value = false
|
||||
active.value = null
|
||||
reload()
|
||||
}
|
||||
}
|
||||
)
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.hist-wrap { min-height: 200px; }
|
||||
.pager {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
margin-top: 12px;
|
||||
}
|
||||
.mb12 { margin-bottom: 12px; }
|
||||
.err { color: #f56c6c; word-break: break-all; }
|
||||
.json-panel {
|
||||
margin-bottom: 12px;
|
||||
background: #f7f9fb;
|
||||
border: 1px solid #e8ecf0;
|
||||
border-radius: 6px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.json-panel__head {
|
||||
padding: 8px 12px;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: #606266;
|
||||
background: #fafbfc;
|
||||
border-bottom: 1px solid #ebeef5;
|
||||
}
|
||||
.code-pre {
|
||||
margin: 0;
|
||||
padding: 12px 14px;
|
||||
max-height: 260px;
|
||||
overflow: auto;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-all;
|
||||
}
|
||||
</style>
|
||||
@@ -41,7 +41,17 @@
|
||||
</section>
|
||||
|
||||
<section class="detail-block">
|
||||
<div class="sec-title">计费信息</div>
|
||||
<div class="sec-title">
|
||||
计费信息
|
||||
<el-button
|
||||
v-if="detail && detail.billId"
|
||||
type="primary"
|
||||
link
|
||||
size="small"
|
||||
class="hist-btn"
|
||||
@click="historyOpen = true"
|
||||
>历史计费</el-button>
|
||||
</div>
|
||||
<div class="amount-summary">
|
||||
<div class="amount-card">
|
||||
<div class="amount-label">运费金额</div>
|
||||
@@ -184,12 +194,18 @@
|
||||
:current-charge-weight="detail?.chargeWeight"
|
||||
@success="onChargeWeightUpdated"
|
||||
/>
|
||||
<bill-billing-history-drawer
|
||||
v-model="historyOpen"
|
||||
:bill-id="detail?.billId || ''"
|
||||
:order-no="detail?.orderNo || ''"
|
||||
/>
|
||||
</el-drawer>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import AddIncomeFeeDialog from './AddIncomeFeeDialog.vue'
|
||||
import EditChargeWeightDialog from './EditChargeWeightDialog.vue'
|
||||
import BillBillingHistoryDrawer from './BillBillingHistoryDrawer.vue'
|
||||
import { deleteIncomeBillFee } from '@/api/price/billing'
|
||||
|
||||
const { proxy } = getCurrentInstance()
|
||||
@@ -205,6 +221,7 @@ const props = defineProps({
|
||||
const emit = defineEmits(['update:modelValue', 'updated'])
|
||||
|
||||
const feeTab = ref('income')
|
||||
const historyOpen = ref(false)
|
||||
const addFeeOpen = ref(false)
|
||||
const editFeeRow = ref(null)
|
||||
const editCwOpen = ref(false)
|
||||
@@ -224,6 +241,7 @@ const payFees = computed(() => {
|
||||
|
||||
watch(() => props.detail, () => {
|
||||
feeTab.value = 'income'
|
||||
historyOpen.value = false
|
||||
})
|
||||
|
||||
function canEditManualFee(row) {
|
||||
@@ -465,6 +483,9 @@ function refundLabel(status) {
|
||||
border-radius: 2px;
|
||||
background: var(--el-color-primary);
|
||||
}
|
||||
.hist-btn {
|
||||
margin-left: auto;
|
||||
}
|
||||
.amount-summary {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(5, 1fr);
|
||||
|
||||
Reference in New Issue
Block a user