fix(customers): wait for billing defaults before loading
This commit is contained in:
@@ -93,6 +93,24 @@ describe('CustomerBillingRulesPanel', () => {
|
||||
expect(getCustomerDimensionConfig).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('does not request billing rules before customer defaults are ready', async () => {
|
||||
mount(CustomerBillingRulesPanel, {
|
||||
attachTo: document.body,
|
||||
props: {
|
||||
customerId: '8',
|
||||
customerName: '柏悦物流',
|
||||
dimensionInitializationStatus: 'PENDING',
|
||||
},
|
||||
global: { plugins: [ElementPlus] },
|
||||
})
|
||||
await flushPromises()
|
||||
|
||||
expect(document.body.textContent).toContain('客户计费规则正在初始化')
|
||||
expect(document.body.textContent).not.toContain('箱规尺寸规则加载失败')
|
||||
expect(getCustomerDimensionConfig).not.toHaveBeenCalled()
|
||||
expect(listCustomerChargeRules).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('submits all four rows in one update request', async () => {
|
||||
const wrapper = mount(CustomerBillingRulesPanel, { attachTo: document.body, props: { customerId: '8', customerName: '柏悦物流' }, global: { plugins: [ElementPlus] } })
|
||||
await flushPromises()
|
||||
|
||||
@@ -19,12 +19,21 @@ import AppDialog from '@/shared/components/AppDialog.vue'
|
||||
import DataTable, { type DataTableColumn } from '@/shared/components/DataTable.vue'
|
||||
import { createCustomerChargeRule, listCustomerChargeRules, setCustomerChargeRuleDefault, type CustomerChargeRule, type CreateCustomerChargeRuleRequest } from '@/modules/fba-pricing'
|
||||
|
||||
const props = defineProps<{ customerId: string; customerName: string; customerCode?: string }>()
|
||||
const props = defineProps<{
|
||||
customerId: string
|
||||
customerName: string
|
||||
customerCode?: string
|
||||
dimensionInitializationStatus?: string
|
||||
}>()
|
||||
const access = useAccessPreviewStore()
|
||||
const canView = computed(() => access.can('firstleg:dimension-config:view'))
|
||||
const canEdit = computed(() => canView.value && access.can('firstleg:dimension-config:edit'))
|
||||
const canChargeView = computed(() => access.can('firstleg:fba-pricing:rule:view'))
|
||||
const canChargeEdit = computed(() => canChargeView.value && access.can('firstleg:fba-pricing:rule:edit'))
|
||||
const initializationStatus = computed(() => props.dimensionInitializationStatus?.trim().toUpperCase() || 'READY')
|
||||
const initializationPending = computed(() => ['PENDING', 'SENDING'].includes(initializationStatus.value))
|
||||
const initializationFailed = computed(() => initializationStatus.value === 'FAILED')
|
||||
const initializationReady = computed(() => !initializationPending.value && !initializationFailed.value)
|
||||
const config = ref<CustomerDimensionConfig | null>(null)
|
||||
const loading = ref(false)
|
||||
const loadError = ref<ApiErrorDetails | null>(null)
|
||||
@@ -92,7 +101,17 @@ watch(form, () => {
|
||||
watch(() => props.customerId, () => {
|
||||
editorVisible.value = false
|
||||
defaultCalculationMode.value = 'ACTUAL'
|
||||
void loadConfig()
|
||||
if (initializationReady.value) {
|
||||
void loadConfig()
|
||||
void loadChargeRules()
|
||||
}
|
||||
})
|
||||
|
||||
watch(initializationReady, (ready, previous) => {
|
||||
if (ready && !previous) {
|
||||
void loadConfig()
|
||||
void loadChargeRules()
|
||||
}
|
||||
})
|
||||
|
||||
function sortRules(rules: CustomerDimensionRule[]) {
|
||||
@@ -101,7 +120,7 @@ function sortRules(rules: CustomerDimensionRule[]) {
|
||||
}
|
||||
|
||||
async function loadConfig() {
|
||||
if (!canView.value) return
|
||||
if (!canView.value || !initializationReady.value) return
|
||||
loading.value = true
|
||||
loadError.value = null
|
||||
try {
|
||||
@@ -266,7 +285,7 @@ function formatDate(value: string | null) {
|
||||
}
|
||||
|
||||
async function loadChargeRules() {
|
||||
if (!canChargeView.value) return
|
||||
if (!canChargeView.value || !initializationReady.value) return
|
||||
chargeLoading.value = true
|
||||
try {
|
||||
chargeRules.value = await listCustomerChargeRules(props.customerId)
|
||||
@@ -287,17 +306,29 @@ async function loadChargeRules() {
|
||||
finally { chargeLoading.value = false }
|
||||
}
|
||||
|
||||
onMounted(() => { void loadConfig(); void loadChargeRules() })
|
||||
onMounted(() => {
|
||||
if (!initializationReady.value) return
|
||||
void loadConfig()
|
||||
void loadChargeRules()
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="dimension-rule-panel">
|
||||
<div v-if="!canView || loadError" class="panel-title rule-heading">
|
||||
<div v-if="initializationPending" class="rule-state initialization-state" role="status">
|
||||
<Ruler :size="30" />
|
||||
<strong>客户计费规则正在初始化</strong>
|
||||
<p>默认箱规和权重系数正在后台准备,请稍后重新加载客户详情。</p>
|
||||
</div>
|
||||
<div v-else-if="initializationFailed" class="rule-error initialization-state" role="alert">
|
||||
<div><strong>客户计费规则初始化失败</strong><p>请在客户管理中重试初始化,成功后再查看计费规则。</p></div>
|
||||
</div>
|
||||
<div v-if="initializationReady && (!canView || loadError)" class="panel-title rule-heading">
|
||||
<div><h2>按箱规尺寸计算</h2><p>{{ customerName }}{{ customerCode ? `(${customerCode})` : '' }}的尺寸计算规则</p></div>
|
||||
</div>
|
||||
<div v-if="!canView" class="rule-state"><Ruler :size="30" /><strong>暂无尺寸计算规则查看权限</strong><p>请联系管理员申请尺寸计算规则查看权限。</p></div>
|
||||
<div v-else-if="loadError" class="rule-error" role="alert"><div><strong>箱规尺寸规则加载失败</strong><p>{{ loadError.message }}</p><ApiErrorMeta :details="loadError" /></div><el-button type="primary" plain :loading="loading" @click="loadConfig">重新加载</el-button></div>
|
||||
<section v-else-if="config" class="rule-card">
|
||||
<div v-if="initializationReady && !canView" class="rule-state"><Ruler :size="30" /><strong>暂无尺寸计算规则查看权限</strong><p>请联系管理员申请尺寸计算规则查看权限。</p></div>
|
||||
<div v-else-if="initializationReady && loadError" class="rule-error" role="alert"><div><strong>箱规尺寸规则加载失败</strong><p>{{ loadError.message }}</p><ApiErrorMeta :details="loadError" /></div><el-button type="primary" plain :loading="loading" @click="loadConfig">重新加载</el-button></div>
|
||||
<section v-else-if="initializationReady && config" class="rule-card">
|
||||
<header class="rule-card-heading">
|
||||
<div class="rule-title-row">
|
||||
<h2>按箱规尺寸计算</h2>
|
||||
@@ -315,9 +346,9 @@ onMounted(() => { void loadConfig(); void loadChargeRules() })
|
||||
<template #empty><div class="rule-state"><Ruler :size="28" /><strong>当前客户暂无箱规尺寸规则</strong></div></template>
|
||||
</DataTable>
|
||||
</section>
|
||||
<div v-else v-loading="loading" class="loading-state" />
|
||||
<div v-else-if="initializationReady" v-loading="loading" class="loading-state" />
|
||||
|
||||
<section v-if="canChargeView" class="rule-card charge-rule-card">
|
||||
<section v-if="initializationReady && canChargeView" class="rule-card charge-rule-card">
|
||||
<header class="rule-card-heading"><div><div class="rule-title-row"><h2>箱规按权重系数计算</h2><el-checkbox :model-value="defaultCalculationMode === 'WEIGHT'" :disabled="!canEdit || defaultModeSaving" @change="selectDefaultCalculationMode('WEIGHT', Boolean($event))">设为默认规则</el-checkbox></div><p class="section-hint">勾选后客户下单数据才会按该规则调整;未勾选时默认使用实测原尺寸(不调整)。</p></div></header>
|
||||
<el-table v-loading="chargeLoading" :data="chargeDisplayRows" border stripe empty-text="暂无系数配置">
|
||||
<el-table-column prop="boxTypeName" label="箱规类型" min-width="110" />
|
||||
|
||||
Reference in New Issue
Block a user