优化计费逻辑
This commit is contained in:
@@ -5,21 +5,21 @@
|
||||
:columns="columns"
|
||||
:search-columns="searchColumns"
|
||||
:query="queryList"
|
||||
search-action-align="center"
|
||||
:search-per-row="4"
|
||||
row-key="billId"
|
||||
selection
|
||||
:page-sizes="[100, 200, 300, 400]"
|
||||
:page-size="100"
|
||||
:height="tableHeight"
|
||||
virtual
|
||||
>
|
||||
<template #search-orderNos="{ model }">
|
||||
<multi-value-input v-model="model.orderNos" placeholder="订单号" width="100%" :search-method="searchOrderNoOptions" />
|
||||
</template>
|
||||
<template #search-trackingNos="{ model }">
|
||||
<multi-value-input v-model="model.trackingNos" placeholder="运单号" width="100%" :search-method="searchTrackingNoOptions" />
|
||||
</template>
|
||||
<template #search-transferNos="{ model }">
|
||||
<multi-value-input v-model="model.transferNos" placeholder="转单号" width="100%" :search-method="searchTransferNoOptions" />
|
||||
<template #search-orderIdentifiers="{ model }">
|
||||
<multi-value-input
|
||||
v-model="model.orderIdentifiers"
|
||||
placeholder="订单号/运单号/转单号"
|
||||
width="100%"
|
||||
:search-method="searchOrderIdentifiers"
|
||||
/>
|
||||
</template>
|
||||
<template #search-customerCodes="{ model }">
|
||||
<customer-select
|
||||
@@ -28,8 +28,9 @@
|
||||
value-key="customerCode"
|
||||
label-key="customerName"
|
||||
code-key="customerCode"
|
||||
placeholder="请输入"
|
||||
placeholder="客户名称/客户代码/对外代码"
|
||||
multiple
|
||||
remote-search
|
||||
/>
|
||||
</template>
|
||||
<template #search-productCodes="{ model }">
|
||||
@@ -232,19 +233,13 @@ import EditChargeWeightDialog from './components/EditChargeWeightDialog.vue'
|
||||
import ExcelImportDialog from '@/components/ExcelImportDialog'
|
||||
import ChargeWeightImportAttachDrawer from './components/ChargeWeightImportAttachDrawer.vue'
|
||||
|
||||
// 订单号下拉远程搜索:返回匹配的订单号列表(最多 20 条)
|
||||
function searchOrderNoOptions(keyword) {
|
||||
return listOrderNoOptions(keyword).then(res => res.data || [])
|
||||
}
|
||||
|
||||
// 运单号下拉远程搜索
|
||||
function searchTrackingNoOptions(keyword) {
|
||||
return listTrackingNoOptions(keyword).then(res => res.data || [])
|
||||
}
|
||||
|
||||
// 转单号下拉远程搜索
|
||||
function searchTransferNoOptions(keyword) {
|
||||
return listTransferNoOptions(keyword).then(res => res.data || [])
|
||||
// 单号下拉:订单号、运单号、转单号合并候选
|
||||
function searchOrderIdentifiers(keyword) {
|
||||
return Promise.all([
|
||||
listOrderNoOptions(keyword).then(res => res.data || []),
|
||||
listTrackingNoOptions(keyword).then(res => res.data || []),
|
||||
listTransferNoOptions(keyword).then(res => res.data || [])
|
||||
]).then(groups => [...new Set(groups.flat().map(v => String(v)))])
|
||||
}
|
||||
|
||||
// 产品下拉选项:复用 ProductSelect 的模块级产品缓存(全应用只拉一次)
|
||||
@@ -257,6 +252,37 @@ const productOptions = computed(() =>
|
||||
)
|
||||
|
||||
const tableRef = ref()
|
||||
const tableHeight = ref(480)
|
||||
let tableResizeObserver = null
|
||||
|
||||
function updateTableHeight() {
|
||||
const tableEl = document.querySelector('.billing-bill > .basic-table .el-table, .billing-bill > .basic-table .basic-table-virtual')
|
||||
if (!tableEl) return
|
||||
const top = tableEl.getBoundingClientRect().top
|
||||
const paginationReserve = 58
|
||||
const bottomGap = 12
|
||||
tableHeight.value = Math.max(240, Math.floor(window.innerHeight - top - paginationReserve - bottomGap))
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
nextTick(() => {
|
||||
updateTableHeight()
|
||||
const form = document.querySelector('.billing-bill > .basic-table .basic-form')
|
||||
if (form && typeof ResizeObserver !== 'undefined') {
|
||||
tableResizeObserver = new ResizeObserver(() => updateTableHeight())
|
||||
tableResizeObserver.observe(form)
|
||||
}
|
||||
})
|
||||
window.addEventListener('resize', updateTableHeight)
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
window.removeEventListener('resize', updateTableHeight)
|
||||
if (tableResizeObserver) {
|
||||
tableResizeObserver.disconnect()
|
||||
tableResizeObserver = null
|
||||
}
|
||||
})
|
||||
const open = ref(false)
|
||||
const title = ref('计费详情')
|
||||
const loading = ref(false)
|
||||
@@ -286,7 +312,7 @@ const recalcDialogOpen = ref(false)
|
||||
const recalcMode = ref('PRODUCT')
|
||||
|
||||
const columns = [
|
||||
{ label: '订单号', prop: 'orderNo', minWidth: 140, showOverflowTooltip: true },
|
||||
{ label: '订单号', prop: 'orderNo', width: 160, fixed: 'left', showOverflowTooltip: true },
|
||||
{ label: '运单号', prop: 'trackingNo', minWidth: 140, showOverflowTooltip: true },
|
||||
{ label: '转单号', prop: 'transferNo', minWidth: 140, showOverflowTooltip: true },
|
||||
{ label: '客户', prop: 'customerName', minWidth: 140, showOverflowTooltip: true },
|
||||
@@ -320,19 +346,16 @@ const refundStatusOptions = [
|
||||
{ label: '未退费', value: 'NONE' },
|
||||
{ label: '已退费', value: 'REFUNDED' }
|
||||
]
|
||||
/** billing_fail_status: 0 success / 1 product / 2 country / 3 weight */
|
||||
const billingFailStatusOptions = [
|
||||
{ label: '\u8ba1\u8d39\u6210\u529f', value: 0 },
|
||||
{ label: '\u672a\u5339\u914d\u4ea7\u54c1', value: 1 },
|
||||
{ label: '\u672a\u5339\u914d\u56fd\u5bb6', value: 2 },
|
||||
{ label: '\u672a\u5339\u914d\u91cd\u91cf\u6bb5', value: 3 }
|
||||
{ label: '计费成功', value: 0 },
|
||||
{ label: '未匹配产品', value: 1 },
|
||||
{ label: '未匹配国家', value: 2 },
|
||||
{ label: '未匹配重量段', value: 3 },
|
||||
]
|
||||
|
||||
const searchColumns = [
|
||||
{ label: '订单号', prop: 'orderNos', placeholder: '订单号' },
|
||||
{ label: '运单号', prop: 'trackingNos', placeholder: '运单号' },
|
||||
{ label: '转单号', prop: 'transferNos', placeholder: '转单号' },
|
||||
{ label: '客户', prop: 'customerCodes', placeholder: '客户代码' },
|
||||
{ label: '单号', prop: 'orderIdentifiers', placeholder: '订单号/运单号/转单号' },
|
||||
{ label: '客户', prop: 'customerCodes', placeholder: '客户名称/客户代码/对外代码' },
|
||||
{ label: '产品代码', prop: 'productCodes', placeholder: '产品代码' },
|
||||
{
|
||||
label: '额度欠费',
|
||||
@@ -443,7 +466,8 @@ function onDetailUpdated(data) {
|
||||
|
||||
function hasBatchFeeQuery(q) {
|
||||
const hasArr = (arr) => Array.isArray(arr) && arr.some(v => v != null && String(v).trim() !== '')
|
||||
return hasArr(q.orderNos)
|
||||
return hasArr(q.orderIdentifiers)
|
||||
|| hasArr(q.orderNos)
|
||||
|| hasArr(q.trackingNos)
|
||||
|| hasArr(q.transferNos)
|
||||
|| hasArr(q.customerCodes)
|
||||
@@ -488,6 +512,7 @@ function buildRecalcPayload() {
|
||||
orderNos: rest.orderNos || [],
|
||||
trackingNos: rest.trackingNos || [],
|
||||
transferNos: rest.transferNos || [],
|
||||
orderIdentifiers: rest.orderIdentifiers || [],
|
||||
customerCodes: rest.customerCodes || [],
|
||||
productCodes: rest.productCodes || [],
|
||||
creditArrearsStatus: rest.creditArrearsStatus || undefined
|
||||
@@ -578,6 +603,7 @@ function buildChargeWeightTemplateQuery() {
|
||||
orderNos: rest.orderNos || [],
|
||||
trackingNos: rest.trackingNos || [],
|
||||
transferNos: rest.transferNos || [],
|
||||
orderIdentifiers: rest.orderIdentifiers || [],
|
||||
customerCodes: rest.customerCodes || [],
|
||||
productCodes: rest.productCodes || [],
|
||||
creditArrearsStatus: rest.creditArrearsStatus || undefined
|
||||
@@ -691,15 +717,13 @@ function formatWeight(val) {
|
||||
|
||||
function billingFailLabel(status) {
|
||||
const code = status == null ? 0 : Number(status)
|
||||
if (code === 1) return '未匹配产品'
|
||||
if (code === 2) return '未匹配国家'
|
||||
if (code === 3) return '未匹配重量段'
|
||||
return '计费成功'
|
||||
const hit = billingFailStatusOptions.find(item => item.value === code)
|
||||
return hit ? hit.label : '未知失败'
|
||||
}
|
||||
|
||||
function billingFailTagType(status) {
|
||||
const code = status == null ? 0 : Number(status)
|
||||
if (code === 1 || code === 2 || code === 3) return 'danger'
|
||||
if (code > 0) return 'danger'
|
||||
return 'success'
|
||||
}
|
||||
|
||||
|
||||
@@ -193,6 +193,8 @@ function fmtList(arr, max = 5) {
|
||||
|
||||
function buildQueryTags(q) {
|
||||
const tags = []
|
||||
const identifiers = fmtList(q.orderIdentifiers)
|
||||
if (identifiers) tags.push("单号" + ':' + identifiers)
|
||||
const order = fmtList(q.orderNos)
|
||||
if (order) tags.push("订单号" + ':' + order)
|
||||
const tracking = fmtList(q.trackingNos)
|
||||
@@ -256,6 +258,7 @@ function buildPayload() {
|
||||
orderNos: q.orderNos || [],
|
||||
trackingNos: q.trackingNos || [],
|
||||
transferNos: q.transferNos || [],
|
||||
orderIdentifiers: q.orderIdentifiers || [],
|
||||
customerCodes: q.customerCodes || [],
|
||||
productCodes: q.productCodes || [],
|
||||
creditArrearsStatus: q.creditArrearsStatus || undefined,
|
||||
|
||||
@@ -96,7 +96,7 @@
|
||||
<div class="sec-title">费用明细</div>
|
||||
<el-tabs v-model="feeTab" class="fee-tabs">
|
||||
<el-tab-pane label="应收" name="income">
|
||||
<div class="fee-toolbar">
|
||||
<div v-if="!readonly" class="fee-toolbar">
|
||||
<el-button type="primary" plain size="small" icon="Plus" v-hasPermi="['price:billing:feeAdd']" @click="openAddFee">增加费用</el-button>
|
||||
<el-button type="primary" plain size="small" icon="Edit" v-hasPermi="['price:billing:chargeWeightEdit']" :disabled="detail.refundStatus === 'REFUNDED'" @click="openEditCw">修改计费重</el-button>
|
||||
</div>
|
||||
@@ -124,7 +124,7 @@
|
||||
<el-table-column label="说明" min-width="160" show-overflow-tooltip>
|
||||
<template #default="{ row }">{{ row.remark || '-' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="120" align="center" fixed="right">
|
||||
<el-table-column v-if="!readonly" label="操作" width="120" align="center" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<template v-if="canEditManualFee(row)">
|
||||
<el-button link type="primary" v-hasPermi="['price:billing:feeEdit']" @click="openEditFee(row)">编辑</el-button>
|
||||
@@ -198,7 +198,8 @@ const props = defineProps({
|
||||
modelValue: { type: Boolean, default: false },
|
||||
title: { type: String, default: '计费详情' },
|
||||
loading: { type: Boolean, default: false },
|
||||
detail: { type: Object, default: null }
|
||||
detail: { type: Object, default: null },
|
||||
readonly: { type: Boolean, default: false }
|
||||
})
|
||||
|
||||
const emit = defineEmits(['update:modelValue', 'updated'])
|
||||
@@ -335,17 +336,41 @@ function formatDateTime(val) {
|
||||
return d.getFullYear() + '-' + pad(d.getMonth() + 1) + '-' + pad(d.getDate()) + ' ' + pad(d.getHours()) + ':' + pad(d.getMinutes()) + ':' + pad(d.getSeconds())
|
||||
}
|
||||
|
||||
const billingFailStatusOptions = [
|
||||
{ label: '计费成功', value: 0 },
|
||||
{ label: '未匹配产品', value: 1 },
|
||||
{ label: '未匹配国家', value: 2 },
|
||||
{ label: '未匹配重量段', value: 3 },
|
||||
{ label: '未绑定套餐', value: 4 },
|
||||
{ label: '无生效套餐版本', value: 5 },
|
||||
{ label: '已生成应收账单', value: 6 },
|
||||
{ label: '产品档案缺失', value: 7 },
|
||||
{ label: '未命中价格', value: 8 },
|
||||
{ label: '分区无价格', value: 9 },
|
||||
{ label: '邮编必填', value: 10 },
|
||||
{ label: '邮编未匹配分区', value: 11 },
|
||||
{ label: '上游订单不存在', value: 12 },
|
||||
{ label: '上游订单号为空', value: 13 },
|
||||
{ label: '上游客户为空', value: 14 },
|
||||
{ label: '上游产品为空', value: 15 },
|
||||
{ label: '上游重量无效', value: 16 },
|
||||
{ label: '计费时间为空', value: 17 },
|
||||
{ label: '退费订单无法解析', value: 18 },
|
||||
{ label: '客户代码为空', value: 19 },
|
||||
{ label: '客户已停用', value: 20 },
|
||||
{ label: '运费为空', value: 21 },
|
||||
{ label: '未知失败', value: 22 }
|
||||
]
|
||||
|
||||
function billingFailLabel(status) {
|
||||
const code = status == null ? 0 : Number(status)
|
||||
if (code === 1) return '未匹配产品'
|
||||
if (code === 2) return '未匹配国家'
|
||||
if (code === 3) return '未匹配重量段'
|
||||
return '计费成功'
|
||||
const hit = billingFailStatusOptions.find(item => item.value === code)
|
||||
return hit ? hit.label : '未知失败'
|
||||
}
|
||||
|
||||
function billingFailTagType(status) {
|
||||
const code = status == null ? 0 : Number(status)
|
||||
if (code === 1 || code === 2 || code === 3) return 'danger'
|
||||
if (code > 0) return 'danger'
|
||||
return 'success'
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,376 @@
|
||||
<template>
|
||||
<div class="app-container order-bill-query">
|
||||
<basic-table
|
||||
ref="tableRef"
|
||||
:columns="columns"
|
||||
:search-columns="searchColumns"
|
||||
:query="queryList"
|
||||
:search-per-row="4"
|
||||
row-key="billId"
|
||||
:page-sizes="[100, 200, 300, 400]"
|
||||
:page-size="100"
|
||||
selection
|
||||
:height="tableHeight"
|
||||
virtual
|
||||
>
|
||||
<template #toolbar="{ selection }">
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
icon="Refresh"
|
||||
v-hasPermi="['price:orderBill:rebill']"
|
||||
@click="openFilterRebill()"
|
||||
>重新计费</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="info" plain icon="Document" @click="logDrawerRef.open()">操作记录</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="warning"
|
||||
plain
|
||||
icon="Refresh"
|
||||
:disabled="!selection.length"
|
||||
:loading="batchLoading"
|
||||
v-hasPermi="['price:orderBill:rebill']"
|
||||
@click="handleBatchRebill(selection)"
|
||||
>重新计费选中</el-button>
|
||||
</el-col>
|
||||
</template>
|
||||
<template #search-orderIdentifiers="{ model }">
|
||||
<multi-value-input
|
||||
v-model="model.orderIdentifiers"
|
||||
placeholder="订单号/运单号/转单号"
|
||||
width="100%"
|
||||
:search-method="searchOrderIdentifiers"
|
||||
/>
|
||||
</template>
|
||||
<template #search-customerCodes="{ model }">
|
||||
<customer-select
|
||||
v-model="model.customerCodes"
|
||||
type="customer"
|
||||
value-key="customerCode"
|
||||
label-key="customerName"
|
||||
code-key="customerCode"
|
||||
placeholder="客户名称/客户代码/对外代码"
|
||||
multiple
|
||||
remote-search
|
||||
/>
|
||||
</template>
|
||||
<template #col-product="{ row }">
|
||||
{{ row.productName || '-' }} ({{ row.productCode || '-' }})
|
||||
</template>
|
||||
<template #col-country="{ row }">
|
||||
{{ row.countryName || '-' }} ({{ row.countryCode || '-' }})
|
||||
</template>
|
||||
<template #col-weight="{ row }">
|
||||
{{ formatWeight(row.weight) }}
|
||||
</template>
|
||||
<template #col-volumeWeight="{ row }">
|
||||
{{ formatWeight(row.volumeWeight) }}
|
||||
</template>
|
||||
<template #col-receiveWeight="{ row }">
|
||||
{{ formatWeight(row.receiveWeight) }}
|
||||
</template>
|
||||
<template #col-orderStatus="{ row }">
|
||||
{{ orderStatusLabel(row.orderStatus) }}
|
||||
</template>
|
||||
<template #col-billingFailStatus="{ row }">
|
||||
<el-tag size="small" :type="billingFailTagType(row.billingFailStatus)">{{ billingFailLabel(row.billingFailStatus) }}</el-tag>
|
||||
</template>
|
||||
<template #col-action="{ row }">
|
||||
<el-button
|
||||
link
|
||||
type="warning"
|
||||
icon="Refresh"
|
||||
:loading="rebillingKey === row.billId"
|
||||
v-hasPermi="['price:orderBill:rebill']"
|
||||
@click="handleRebillOne(row)"
|
||||
>重新计费</el-button>
|
||||
</template>
|
||||
</basic-table>
|
||||
<order-source-sync-dialog ref="syncDialogRef" @success="onRebillSuccess" />
|
||||
<order-source-sync-log-drawer ref="logDrawerRef" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="OrderBillQuery">
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { listOrderBill, listOrderNoOptions, listTrackingNoOptions, listTransferNoOptions } from '@/api/price/billing'
|
||||
import { syncOrderSourceSelected } from '@/api/monitor/orderSource'
|
||||
import OrderSourceSyncDialog from '../../monitor/orderSource/components/OrderSourceSyncDialog.vue'
|
||||
import OrderSourceSyncLogDrawer from '../../monitor/orderSource/components/OrderSourceSyncLogDrawer.vue'
|
||||
|
||||
const tableRef = ref()
|
||||
const syncDialogRef = ref()
|
||||
const logDrawerRef = ref()
|
||||
const batchLoading = ref(false)
|
||||
const rebillingKey = ref(null)
|
||||
const tableHeight = ref(480)
|
||||
let tableResizeObserver = null
|
||||
|
||||
const columns = [
|
||||
{ label: '订单号', prop: 'orderNo', width: 160, fixed: 'left', showOverflowTooltip: true },
|
||||
{ label: '运单号', prop: 'trackingNo', minWidth: 140, showOverflowTooltip: true },
|
||||
{ label: '转单号', prop: 'transferNo', minWidth: 140, showOverflowTooltip: true },
|
||||
{ label: '客户', prop: 'customerName', minWidth: 140, showOverflowTooltip: true },
|
||||
{ label: '客户代码', prop: 'customerCode', width: 120 },
|
||||
{ label: '产品', prop: 'product', minWidth: 160, slot: 'product' },
|
||||
{ label: '国家', prop: 'country', minWidth: 130, slot: 'country' },
|
||||
{ label: '件数', prop: 'pieces', align: 'center', width: 70 },
|
||||
{ label: '计费重', prop: 'weight', align: 'center', width: 90, slot: 'weight' },
|
||||
{ label: '体积重', prop: 'volumeWeight', align: 'center', width: 90, slot: 'volumeWeight' },
|
||||
{ label: '实重', prop: 'receiveWeight', align: 'center', width: 90, slot: 'receiveWeight' },
|
||||
{ label: '收件人邮编', prop: 'destPostcode', minWidth: 120, showOverflowTooltip: true },
|
||||
{ label: '订单状态', prop: 'orderStatus', align: 'center', width: 110, slot: 'orderStatus' },
|
||||
{ label: '计费状态', prop: 'billingFailStatus', align: 'center', width: 140, slot: 'billingFailStatus' },
|
||||
{ label: '计费时间', prop: 'bizDate', align: 'center', width: 170, parseTime: true },
|
||||
{ label: '下单时间', prop: 'orderTime', align: 'center', width: 170, parseTime: true },
|
||||
{ label: '提货时间', prop: 'pickupDate', align: 'center', width: 170, parseTime: true },
|
||||
{ label: '销售员', prop: 'belongSaler', minWidth: 100, showOverflowTooltip: true },
|
||||
{ label: '结算员', prop: 'settlementMan', minWidth: 100, showOverflowTooltip: true },
|
||||
{ label: '操作', prop: 'action', align: 'center', width: 120, fixed: 'right', slot: 'action', showInToolbar: false }
|
||||
]
|
||||
|
||||
const orderStatusMap = {
|
||||
0: '预报订单',
|
||||
1: '确认订单',
|
||||
2: '已中转',
|
||||
3: '已收货',
|
||||
4: '已装箱',
|
||||
5: '已出库',
|
||||
6: '转运中',
|
||||
7: '已上网',
|
||||
8: '已签收',
|
||||
9: '已退件',
|
||||
10: '已删除'
|
||||
}
|
||||
|
||||
const billingFailStatusOptions = [
|
||||
{ label: '计费成功', value: 0 },
|
||||
{ label: '未匹配产品', value: 1 },
|
||||
{ label: '未匹配国家', value: 2 },
|
||||
{ label: '未匹配重量段', value: 3 },
|
||||
{ label: '未绑定套餐', value: 4 },
|
||||
{ label: '无生效套餐版本', value: 5 },
|
||||
{ label: '已生成应收账单', value: 6 },
|
||||
{ label: '产品档案缺失', value: 7 },
|
||||
{ label: '未命中价格', value: 8 },
|
||||
{ label: '分区无价格', value: 9 },
|
||||
{ label: '邮编必填', value: 10 },
|
||||
{ label: '邮编未匹配分区', value: 11 },
|
||||
{ label: '上游订单不存在', value: 12 },
|
||||
{ label: '上游订单号为空', value: 13 },
|
||||
{ label: '上游客户为空', value: 14 },
|
||||
{ label: '上游产品为空', value: 15 },
|
||||
{ label: '上游重量无效', value: 16 },
|
||||
{ label: '计费时间为空', value: 17 },
|
||||
{ label: '退费订单无法解析', value: 18 },
|
||||
{ label: '客户代码为空', value: 19 },
|
||||
{ label: '客户已停用', value: 20 },
|
||||
{ label: '运费为空', value: 21 },
|
||||
{ label: '未知失败', value: 22 }
|
||||
]
|
||||
|
||||
const searchColumns = [
|
||||
{ label: '单号', prop: 'orderIdentifiers', placeholder: '订单号/运单号/转单号' },
|
||||
{ label: '客户', prop: 'customerCodes', placeholder: '客户名称/客户代码/对外代码' },
|
||||
{
|
||||
label: '计费时间',
|
||||
prop: 'bizDateRange',
|
||||
component: 'datetimerange',
|
||||
colSpan: 2,
|
||||
props: {
|
||||
clearable: true,
|
||||
defaultTime: [new Date(2000, 0, 1, 0, 0, 0), new Date(2000, 0, 1, 23, 59, 59)]
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '计费状态',
|
||||
prop: 'billingFailStatuses',
|
||||
component: 'select',
|
||||
options: billingFailStatusOptions,
|
||||
placeholder: '请选择'
|
||||
}
|
||||
]
|
||||
|
||||
function queryList(params) {
|
||||
const { bizDateRange, ...rest } = params
|
||||
const query = { ...rest }
|
||||
if (Array.isArray(bizDateRange) && (bizDateRange[0] || bizDateRange[1])) {
|
||||
query.params = {
|
||||
...(query.params || {}),
|
||||
beginBizDate: bizDateRange[0] || null,
|
||||
endBizDate: bizDateRange[1] || null
|
||||
}
|
||||
}
|
||||
return listOrderBill(query)
|
||||
}
|
||||
|
||||
|
||||
const MAX_REBILL = 10000
|
||||
|
||||
function currentIdentifiers() {
|
||||
const q = tableRef.value?.getQueryParams?.() || {}
|
||||
return (Array.isArray(q.orderIdentifiers) ? q.orderIdentifiers : [])
|
||||
.map(v => String(v || '').trim())
|
||||
.filter(Boolean)
|
||||
}
|
||||
|
||||
function buildRebillFilter() {
|
||||
const q = tableRef.value?.getQueryParams?.() || {}
|
||||
const filter = { orderIdentifiers: currentIdentifiers() }
|
||||
if (Array.isArray(q.customerCodes) && q.customerCodes.length) {
|
||||
filter.customerCodes = q.customerCodes
|
||||
}
|
||||
return filter
|
||||
}
|
||||
|
||||
function openFilterRebill() {
|
||||
const identifiers = currentIdentifiers()
|
||||
if (!identifiers.length) {
|
||||
ElMessage.warning('请输入单号后再重新计费,或勾选订单后点重新计费选中')
|
||||
return
|
||||
}
|
||||
if (identifiers.length > MAX_REBILL) {
|
||||
ElMessage.warning('单次最多重新计费 ' + MAX_REBILL + ' 个单号')
|
||||
return
|
||||
}
|
||||
ElMessageBox.confirm(
|
||||
'将按 ' + identifiers.length + ' 个单号查上游订单并重新计费,确认继续?',
|
||||
'二次确认',
|
||||
{ type: 'warning' }
|
||||
).then(() => {
|
||||
syncDialogRef.value?.open(buildRebillFilter(), true)
|
||||
}).catch(() => {})
|
||||
}
|
||||
|
||||
function toSyncRow(row) {
|
||||
return {
|
||||
orderId: row.orderNo,
|
||||
orgOrderId: row.trackingNo,
|
||||
transferOrderId: row.transferNo,
|
||||
customerCode: row.customerCode,
|
||||
pickupDate: row.bizDate || row.pickupDate || row.orderTime || null
|
||||
}
|
||||
}
|
||||
|
||||
function handleRebillOne(row) {
|
||||
if (!toSyncRow(row).pickupDate) {
|
||||
ElMessage.warning('该订单没有计费时间,无法重新计费')
|
||||
return
|
||||
}
|
||||
ElMessageBox.confirm('确认重新计费该订单?', '二次确认', { type: 'warning' }).then(() => {
|
||||
rebillingKey.value = row.billId
|
||||
return syncOrderSourceSelected([toSyncRow(row)])
|
||||
}).then(res => {
|
||||
const data = (res && res.data) || {}
|
||||
if ((data.ok || 0) > 0 && !(data.fail || 0)) {
|
||||
ElMessage.success('重新计费成功')
|
||||
} else {
|
||||
const detail = data.details && data.details[0]
|
||||
ElMessage.error((detail && detail.errorMessage) || '重新计费失败')
|
||||
}
|
||||
tableRef.value?.refresh?.()
|
||||
}).catch(() => {}).finally(() => {
|
||||
rebillingKey.value = null
|
||||
})
|
||||
}
|
||||
|
||||
function handleBatchRebill(selection) {
|
||||
const rows = selection || []
|
||||
if (!rows.length) return
|
||||
const missingTime = rows.filter(row => !toSyncRow(row).pickupDate).length
|
||||
if (missingTime) {
|
||||
ElMessage.warning(missingTime + ' 条没有计费时间,无法重新计费')
|
||||
return
|
||||
}
|
||||
if (rows.length > 200) {
|
||||
ElMessage.warning('选中重新计费最多 200 条')
|
||||
return
|
||||
}
|
||||
ElMessageBox.confirm('确认重新计费选中的 ' + rows.length + ' 条订单?', '二次确认', { type: 'warning' }).then(() => {
|
||||
batchLoading.value = true
|
||||
return syncOrderSourceSelected(rows.map(toSyncRow))
|
||||
}).then(res => {
|
||||
const data = (res && res.data) || {}
|
||||
ElMessage.success('完成:成功 ' + (data.ok || 0) + ' / 失败 ' + (data.fail || 0))
|
||||
tableRef.value?.refresh?.()
|
||||
}).catch(() => {}).finally(() => {
|
||||
batchLoading.value = false
|
||||
})
|
||||
}
|
||||
|
||||
function onRebillSuccess() {
|
||||
tableRef.value?.refresh?.()
|
||||
}
|
||||
|
||||
function searchOrderIdentifiers(keyword) {
|
||||
return Promise.all([
|
||||
listOrderNoOptions(keyword).then(res => res.data || []),
|
||||
listTrackingNoOptions(keyword).then(res => res.data || []),
|
||||
listTransferNoOptions(keyword).then(res => res.data || [])
|
||||
]).then(groups => [...new Set(groups.flat().map(v => String(v)))])
|
||||
}
|
||||
|
||||
function formatWeight(val) {
|
||||
if (val == null || val === '') return '-'
|
||||
const num = Number(val)
|
||||
if (Number.isNaN(num)) return val
|
||||
return num.toFixed(3)
|
||||
}
|
||||
|
||||
function orderStatusLabel(status) {
|
||||
if (status === null || status === undefined || status === '') return '-'
|
||||
const key = Number(status)
|
||||
if (Object.prototype.hasOwnProperty.call(orderStatusMap, key)) return orderStatusMap[key]
|
||||
return String(status)
|
||||
}
|
||||
|
||||
function billingFailLabel(status) {
|
||||
const code = status == null ? 0 : Number(status)
|
||||
const hit = billingFailStatusOptions.find(item => item.value === code)
|
||||
return hit ? hit.label : '未知失败'
|
||||
}
|
||||
|
||||
function billingFailTagType(status) {
|
||||
const code = status == null ? 0 : Number(status)
|
||||
return code > 0 ? 'danger' : 'success'
|
||||
}
|
||||
|
||||
function updateTableHeight() {
|
||||
const tableEl = document.querySelector('.order-bill-query > .basic-table .el-table, .order-bill-query > .basic-table .basic-table-virtual')
|
||||
if (!tableEl) return
|
||||
const top = tableEl.getBoundingClientRect().top
|
||||
tableHeight.value = Math.max(240, Math.floor(window.innerHeight - top - 58 - 12))
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
nextTick(() => {
|
||||
updateTableHeight()
|
||||
const form = document.querySelector('.order-bill-query > .basic-table .basic-form')
|
||||
if (form && typeof ResizeObserver !== 'undefined') {
|
||||
tableResizeObserver = new ResizeObserver(() => updateTableHeight())
|
||||
tableResizeObserver.observe(form)
|
||||
}
|
||||
})
|
||||
window.addEventListener('resize', updateTableHeight)
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
window.removeEventListener('resize', updateTableHeight)
|
||||
if (tableResizeObserver) {
|
||||
tableResizeObserver.disconnect()
|
||||
tableResizeObserver = null
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.order-bill-query :deep(.basic-form .el-form-item .el-select),
|
||||
.order-bill-query :deep(.basic-form .el-form-item .el-input),
|
||||
.order-bill-query :deep(.basic-form .el-form-item .el-date-editor) {
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user