From 57be5316d061d4aef9d51b8e568485f12967f9e6 Mon Sep 17 00:00:00 2001 From: ouyangxq <775153912@qq.com> Date: Thu, 29 Oct 2026 11:39:01 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=B5=B7=E5=A4=96=E9=87=87?= =?UTF-8?q?=E8=B4=AD=E6=96=B0=E5=A2=9E=E9=A1=B5=E5=B8=83=E5=B1=80=E5=92=8C?= =?UTF-8?q?=E9=A2=84=E7=AE=97=E6=91=98=E8=A6=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/modules/cross-border/procurement.test.mjs | 48 ++++++ .../cross-border/views/ProcurementView.vue | 154 +++++++++++++++--- 2 files changed, 175 insertions(+), 27 deletions(-) diff --git a/src/modules/cross-border/procurement.test.mjs b/src/modules/cross-border/procurement.test.mjs index c9c865a..5860eac 100644 --- a/src/modules/cross-border/procurement.test.mjs +++ b/src/modules/cross-border/procurement.test.mjs @@ -88,6 +88,54 @@ function viewHarness({ createPage = false, detailPage = false, api = {}, storage return { state, props, route, auth, pushes, storage, dispose: () => dispose.forEach(callback => callback()) } } +test('create summary follows the current form, preserves cents and clears with the draft', () => { + const harness = viewHarness({ createPage: true }) + try { + assert.equal(harness.state.summaryQuantity.value, '1') + assert.equal(harness.state.summaryUnitPrice.value, '—') + assert.equal(harness.state.budgetTotal.value, null) + assert.equal(harness.state.summaryCurrencyName.value, '美元') + Object.assign(harness.state.form, draft()) + assert.equal(harness.state.summaryQuantity.value, '3') + assert.equal(harness.state.summaryUnitPrice.value, '0.29') + assert.equal(harness.state.budgetTotal.value, '0.87') + harness.state.form.currency = 'CNY' + assert.equal(harness.state.summaryCurrencyName.value, '人民币') + assert.equal(presentation.money(harness.state.budgetTotal.value, harness.state.form.currency), 'CNY 0.87') + harness.state.form.budgetUnitPrice = '0' + assert.equal(harness.state.summaryUnitPrice.value, '0.00') + assert.equal(harness.state.budgetTotal.value, '0.00') + harness.state.resetForm() + assert.equal(harness.state.summaryQuantity.value, '1') + assert.equal(harness.state.summaryUnitPrice.value, '—') + assert.equal(harness.state.budgetTotal.value, null) + assert.equal(harness.state.summaryCurrencyName.value, '美元') + } finally { harness.dispose() } +}) + +test('create summary never retains a stale budget when quantity or price becomes invalid', () => { + const harness = viewHarness({ createPage: true }) + try { + Object.assign(harness.state.form, draft()) + for (const quantity of ['', '0', '-1', '1.5', '9007199254740992']) { + harness.state.form.purchaseQuantity = quantity + assert.equal(harness.state.summaryQuantity.value, '—') + assert.equal(harness.state.budgetTotal.value, null) + assert.equal(harness.state.form.purchaseQuantity, quantity) + } + harness.state.form.purchaseQuantity = '3' + assert.equal(harness.state.budgetTotal.value, '0.87') + for (const price of ['', '-1', '1.001', 'invalid']) { + harness.state.form.budgetUnitPrice = price + assert.equal(harness.state.summaryUnitPrice.value, '—') + assert.equal(harness.state.budgetTotal.value, null) + assert.equal(harness.state.form.budgetUnitPrice, price) + } + harness.state.form.budgetUnitPrice = '0.29' + assert.equal(harness.state.budgetTotal.value, '0.87') + } finally { harness.dispose() } +}) + test('detail has one in-flight request and an older response cannot overwrite another order', async () => { const calls = [], pending = new Map() const harness = viewHarness({ detailPage: true, api: { getProcurement: orderNo => { calls.push(orderNo); return new Promise(resolve => pending.set(orderNo, resolve)) } } }) diff --git a/src/modules/cross-border/views/ProcurementView.vue b/src/modules/cross-border/views/ProcurementView.vue index d9e4e5b..6259465 100644 --- a/src/modules/cross-border/views/ProcurementView.vue +++ b/src/modules/cross-border/views/ProcurementView.vue @@ -10,7 +10,7 @@ import QueryToolbar, { type QueryField, type QueryModel } from '@/components/com import type { TableAction } from '@/components/common/tableActions' import ProcurementDetailPanel from '../components/ProcurementDetailPanel.vue' import { confirmProcurement, createProcurement, getProcurement, getProcurementWorkflow, listProcurements, type ProcurementDetail, type ProcurementSummary, type ProcurementWorkflow } from '../api/procurement' -import { procurementDirections, directionLabel, regionText, buildProcurementCreate, dateTime, emptyProcurementDraft, money, procurementStatuses, statusLabel, statusTone } from '../procurementPresentation' +import { procurementDirections, directionLabel, regionText, buildProcurementCreate, dateTime, draftBudgetTotal, emptyProcurementDraft, money, procurementStatuses, statusLabel, statusTone } from '../procurementPresentation' import { procurementRequestKey } from '../procurementSubmission' const props = withDefaults(defineProps<{ createPage?: boolean; detailPage?: boolean }>(), { createPage: false, detailPage: false }) @@ -22,6 +22,10 @@ const canList = computed(() => auth.hasPermission('merchant:cross-border:procure const identity = computed(() => auth.token ? accessTokenIdentity(auth.token) : null) const companyName = computed(() => auth.profile.tenantName || (auth.user?.tenantName && !/^企业租户 \d+$/.test(auth.user.tenantName) ? auth.user.tenantName : '')) const form = reactive(emptyProcurementDraft()) +const summaryQuantity = computed(() => /^\d+$/.test(form.purchaseQuantity) && Number.isSafeInteger(Number(form.purchaseQuantity)) && Number(form.purchaseQuantity) > 0 ? String(Number(form.purchaseQuantity)) : '—') +const summaryUnitPrice = computed(() => draftBudgetTotal({ ...form, purchaseQuantity: '1' }) ?? '—') +const summaryCurrencyName = computed(() => form.currency === 'USD' ? '美元' : '人民币') +const budgetTotal = computed(() => summaryQuantity.value === '—' ? null : draftBudgetTotal(form)) const submitError = ref(''), submittedOrder = ref('') const submitting = ref(false), loading = ref(false), error = ref('') const detail = shallowRef(null) @@ -126,7 +130,7 @@ onBeforeUnmount(() => { active = false; ++requestSequence })