订单数据源增加订单对比功能

This commit is contained in:
wuweihua
2026-10-20 13:55:55 +08:00
parent 8093cb52e7
commit 45969a4d29
20 changed files with 1865 additions and 96 deletions
@@ -0,0 +1,146 @@
<template>
<el-drawer v-model="visible" size="920px" title="产品库比对(套餐销售价)" append-to-body destroy-on-close>
<div v-loading="loading">
<el-empty v-if="!loading && !result" description="暂无结果" />
<template v-else-if="result">
<el-descriptions :column="3" border size="small" class="mb12">
<el-descriptions-item label="筛选订单数">{{ result.orderTotal ?? 0 }}</el-descriptions-item>
<el-descriptions-item label="套餐截止时间">{{ result.asOfTime || '-' }}</el-descriptions-item>
<el-descriptions-item label="套餐产品数">{{ result.packageProductCount ?? 0 }}</el-descriptions-item>
<el-descriptions-item label="库内/缺失">
<el-tag type="success" size="small">{{ result.matchedCount ?? 0 }}</el-tag>
<span class="sep">/</span>
<el-tag type="danger" size="small">{{ result.missingCount ?? 0 }}</el-tag>
</el-descriptions-item>
</el-descriptions>
<el-tabs v-model="activeTab">
<el-tab-pane :label="missingTabLabel" name="missing">
<el-table :data="result.missingProducts || []" border size="small" max-height="520">
<el-table-column prop="customerCode" label="客户代码" min-width="110" show-overflow-tooltip />
<el-table-column prop="productCode" label="套餐产品代码" min-width="130" show-overflow-tooltip />
<el-table-column prop="productName" label="产品名称" min-width="140" show-overflow-tooltip />
<el-table-column prop="orderCount" label="订单数" width="100" align="center">
<template #default="{ row }">
<el-button link type="primary" :title="'点击查订单'" @click="openOrders(row)">{{ row.orderCount ?? 0 }}</el-button>
</template>
</el-table-column>
</el-table>
<el-empty v-if="!(result.missingProducts || []).length" description="未发现产品库缺失" />
</el-tab-pane>
<el-tab-pane :label="packageTabLabel" name="package">
<el-table :data="result.packageProducts || []" border size="small" max-height="520">
<el-table-column prop="customerCode" label="客户代码" min-width="110" show-overflow-tooltip />
<el-table-column prop="productCode" label="套餐产品代码" min-width="130" show-overflow-tooltip />
<el-table-column prop="productName" label="产品名称" min-width="140" show-overflow-tooltip />
<el-table-column label="产品库" width="110" align="center">
<template #default="{ row }">
<el-tag v-if="row.inLibrary" size="small" type="success">在库</el-tag>
<el-tag v-else size="small" type="danger">不在库</el-tag>
</template>
</el-table-column>
<el-table-column prop="orderCount" label="订单数" width="100" align="center">
<template #default="{ row }">
<el-button link type="primary" @click="openOrders(row)">{{ row.orderCount ?? 0 }}</el-button>
</template>
</el-table-column>
</el-table>
<el-empty v-if="!(result.packageProducts || []).length" description="客户未绑定套餐产品" />
</el-tab-pane>
</el-tabs>
</template>
</div>
<el-dialog v-model="orderDialogVisible" :title="orderDialogTitle" width="860px" append-to-body destroy-on-close>
<el-table v-loading="orderLoading" :data="orderRows" border size="small" max-height="480">
<el-table-column prop="orderNo" label="订单号" min-width="160" show-overflow-tooltip />
<el-table-column prop="trackingNo" label="运单号" min-width="150" show-overflow-tooltip />
<el-table-column prop="transferNo" label="转单号" min-width="160" show-overflow-tooltip />
<el-table-column prop="bizDate" label="计费时间" width="170" align="center" />
</el-table>
<el-empty v-if="!orderLoading && !orderRows.length" description="未查到订单" />
</el-dialog>
</el-drawer>
</template>
<script setup>
import { ElMessage } from 'element-plus'
import { checkOrderBillProducts, listOrderBillsByProduct } from '@/api/price/billing'
const visible = ref(false)
const loading = ref(false)
const result = ref(null)
const activeTab = ref('missing')
const lastQuery = ref(null)
const orderDialogVisible = ref(false)
const orderLoading = ref(false)
const orderRows = ref([])
const orderDialogTitle = ref('产品订单')
const missingTabLabel = computed(() => {
const n = result.value?.missingCount ?? 0
return '缺失产品 (' + n + ')'
})
const packageTabLabel = computed(() => {
const n = result.value?.packageProductCount ?? 0
return '套餐产品 (' + n + ')'
})
function openOrders(row) {
if (!row || !row.productCode || !lastQuery.value) {
ElMessage.warning('请先选择客户并选择计费时间')
return
}
const q = {
...lastQuery.value,
customerCodes: row.customerCode ? [row.customerCode] : lastQuery.value.customerCodes,
productCode: row.productCode
}
orderDialogTitle.value = '产品订单: ' + row.productCode
orderDialogVisible.value = true
orderRows.value = []
orderLoading.value = true
listOrderBillsByProduct(q).then(res => {
orderRows.value = Array.isArray(res.data) ? res.data : []
}).catch(() => {
orderRows.value = []
}).finally(() => {
orderLoading.value = false
})
}
function open(query) {
const hasCustomer = !!(query && Array.isArray(query.customerCodes) && query.customerCodes.length)
const begin = query?.params?.beginBizDate
const end = query?.params?.endBizDate
if (!query || !hasCustomer || !begin || !end) {
ElMessage.warning('请先选择客户并选择计费时间')
return
}
lastQuery.value = { ...query }
visible.value = true
activeTab.value = 'missing'
result.value = null
loading.value = true
checkOrderBillProducts(query).then(res => {
result.value = res.data || null
if ((result.value?.missingCount || 0) > 0) {
activeTab.value = 'missing'
} else {
activeTab.value = 'package'
}
}).catch(() => {
result.value = null
}).finally(() => {
loading.value = false
})
}
defineExpose({ open })
</script>
<style scoped>
.mb12 { margin-bottom: 12px; }
.sep { margin: 0 6px; color: var(--el-text-color-secondary); }
</style>
+64 -8
View File
@@ -24,6 +24,16 @@
@click="openFilterRebill()"
>重新计费</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="success"
plain
icon="Search"
:loading="checkLoading"
v-hasPermi="['price:orderBill:list']"
@click="handleCheckProducts()"
>检测产品</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="info" plain icon="Document" @click="logDrawerRef.open()">操作记录</el-button>
</el-col>
@@ -83,6 +93,7 @@
</basic-table>
<order-source-sync-dialog ref="syncDialogRef" @success="onRebillSuccess" />
<order-source-sync-log-drawer ref="logDrawerRef" />
<order-bill-product-check-drawer ref="productCheckDrawerRef" />
</div>
</template>
@@ -92,10 +103,13 @@ import { listOrderBill, listOrderNoOptions, listTrackingNoOptions, listTransferN
import { syncOrderSourceSelected } from '@/api/monitor/orderSource'
import OrderSourceSyncDialog from '../../monitor/orderSource/components/OrderSourceSyncDialog.vue'
import OrderSourceSyncLogDrawer from '../../monitor/orderSource/components/OrderSourceSyncLogDrawer.vue'
import OrderBillProductCheckDrawer from './components/OrderBillProductCheckDrawer.vue'
const tableRef = ref()
const syncDialogRef = ref()
const logDrawerRef = ref()
const productCheckDrawerRef = ref()
const checkLoading = ref(false)
const batchLoading = ref(false)
const tableHeight = ref(480)
let tableResizeObserver = null
@@ -183,6 +197,16 @@ const searchColumns = [
defaultTime: [new Date(2000, 0, 1, 0, 0, 0), new Date(2000, 0, 1, 23, 59, 59)]
}
},
{
label: '提货时间',
prop: 'pickupDateRange',
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: 'orderStatuses',
@@ -205,17 +229,26 @@ const searchColumns = [
}
]
function queryList(params) {
const { bizDateRange, ...rest } = params
function applySearchRanges(params) {
const { bizDateRange, pickupDateRange, ...rest } = params
const query = { ...rest }
const rangeParams = {}
if (Array.isArray(bizDateRange) && (bizDateRange[0] || bizDateRange[1])) {
query.params = {
...(query.params || {}),
beginBizDate: bizDateRange[0] || null,
endBizDate: bizDateRange[1] || null
}
rangeParams.beginBizDate = bizDateRange[0] || null
rangeParams.endBizDate = bizDateRange[1] || null
}
return listOrderBill(query)
if (Array.isArray(pickupDateRange) && (pickupDateRange[0] || pickupDateRange[1])) {
rangeParams.beginPickupDate = pickupDateRange[0] || null
rangeParams.endPickupDate = pickupDateRange[1] || null
}
if (Object.keys(rangeParams).length) {
query.params = { ...(query.params || {}), ...rangeParams }
}
return query
}
function queryList(params) {
return listOrderBill(applySearchRanges(params))
}
@@ -237,6 +270,29 @@ function buildRebillFilter() {
return filter
}
function buildCheckQuery() {
const q = tableRef.value?.getQueryParams?.() || {}
return applySearchRanges(q)
}
function handleCheckProducts() {
const q = buildCheckQuery()
const hasCustomer = Array.isArray(q.customerCodes) && q.customerCodes.length > 0
const begin = q.params?.beginBizDate
const end = q.params?.endBizDate
if (!hasCustomer || !begin || !end) {
ElMessage.warning('检测产品需先选择客户并选择计费时间')
return
}
checkLoading.value = true
try {
productCheckDrawerRef.value?.open(q)
} finally {
checkLoading.value = false
}
}
function openFilterRebill() {
const identifiers = currentIdentifiers()
if (!identifiers.length) {