fix(client): keep delivery filters and latest query result consistent
This commit is contained in:
@@ -0,0 +1,88 @@
|
||||
import assert from 'node:assert/strict'
|
||||
import test from 'node:test'
|
||||
import { readFileSync } from 'node:fs'
|
||||
import ts from 'typescript'
|
||||
import * as vue from 'vue'
|
||||
import dayjs from 'dayjs'
|
||||
|
||||
function harness(t) {
|
||||
const source = readFileSync(new URL('./views/DeliveryOrdersView.vue', import.meta.url), 'utf8')
|
||||
.match(/<script setup lang="ts">([\s\S]*?)<\/script>/)[1]
|
||||
const code = ts.transpileModule(source + '\nexport { filters, queryFields, tableColumns, page, pageSize, search, reset, loadOrders, records, total, loading, errorMessage }', {
|
||||
compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2022, esModuleInterop: true },
|
||||
}).outputText
|
||||
const calls = [], mounted = []
|
||||
const dependencies = {
|
||||
vue: { ...vue, onMounted: callback => mounted.push(callback) }, dayjs,
|
||||
'vue-router': { useRouter: () => ({ push() {} }) },
|
||||
'@/modules/auth/public': { useAuthStore: () => ({ hasPermission: () => true }) },
|
||||
'../api/deliveryOrders': { listDeliveryOrders: query => new Promise((resolve, reject) => calls.push({ query, resolve, reject })) },
|
||||
}
|
||||
const scope = vue.effectScope(), view = {}
|
||||
scope.run(() => new Function('require', 'exports', code)(id => {
|
||||
assert.ok(id in dependencies, id)
|
||||
return dependencies[id]
|
||||
}, view))
|
||||
t.after(() => scope.stop())
|
||||
return { view, calls, mount: () => mounted.forEach(fn => fn()) }
|
||||
}
|
||||
const result = name => ({ records: [{ deliveryNo: name }], total: 1 })
|
||||
|
||||
test('formal delivery filters submit exact values and format each known method in Chinese', async t => {
|
||||
const { view, calls } = harness(t)
|
||||
const expected = { FBA_PICKUP: 'FBA 自提', CUSTOMER_PICKUP: '客户自提', TRUCK_PALLET: '卡派打托', TRUCK_FLOOR: '卡派地板', EXPRESS: '快递', OUTSOURCED: '外配', DEDICATED_TRUCK: '专车直送', LTL: '零担派送', TRUCK: '卡车派送' }
|
||||
const options = view.queryFields.find(f => f.key === 'deliveryMethod').options
|
||||
assert.deepEqual(Object.fromEntries(options.map(o => [o.value, o.label])), expected)
|
||||
const column = view.tableColumns.find(c => c.prop === 'deliveryMethod')
|
||||
for (const [value, label] of Object.entries(expected)) {
|
||||
view.filters.deliveryMethod = value
|
||||
const pending = view.loadOrders()
|
||||
assert.equal(calls.at(-1).query.deliveryMethod, value)
|
||||
calls.at(-1).resolve(result(value)); await pending
|
||||
assert.equal(column.formatter({}, {}, value), label)
|
||||
}
|
||||
assert.equal(column.formatter({}, {}, null), '—')
|
||||
})
|
||||
|
||||
test('newest result survives older success and older failure', async t => {
|
||||
for (const oldFailure of [false, true]) {
|
||||
const { view, calls } = harness(t)
|
||||
view.filters.documentNo = 'old'; const old = view.loadOrders()
|
||||
view.filters.documentNo = 'new'; const current = view.loadOrders()
|
||||
calls[1].resolve(result('new')); await current
|
||||
if (oldFailure) calls[0].reject(new Error('old failed'))
|
||||
else calls[0].resolve(result('old'))
|
||||
await old
|
||||
assert.equal(view.records.value[0].deliveryNo, 'new')
|
||||
assert.equal(view.errorMessage.value, '')
|
||||
assert.equal(view.total.value, 1)
|
||||
assert.equal(calls.length, 2)
|
||||
}
|
||||
})
|
||||
|
||||
test('older completion cannot clear current loading; current failure can be retried', async t => {
|
||||
const { view, calls } = harness(t)
|
||||
const old = view.loadOrders(), current = view.loadOrders()
|
||||
calls[0].resolve(result('old')); await old
|
||||
assert.equal(view.loading.value, true)
|
||||
assert.equal(view.records.value.length, 0)
|
||||
calls[1].reject(new Error('current failed')); await current
|
||||
assert.equal(view.loading.value, false)
|
||||
assert.equal(view.errorMessage.value, 'current failed')
|
||||
const retry = view.loadOrders(); calls[2].resolve(result('retry')); await retry
|
||||
assert.equal(view.records.value[0].deliveryNo, 'retry')
|
||||
assert.equal(view.errorMessage.value, '')
|
||||
})
|
||||
|
||||
test('mount, search from later page, page-size change and reset each issue one request', async t => {
|
||||
const { view, calls, mount } = harness(t)
|
||||
mount(); await vue.nextTick(); assert.equal(calls.length, 1)
|
||||
view.page.value = 3; await vue.nextTick(); assert.equal(calls.length, 2)
|
||||
view.search(); await vue.nextTick(); assert.equal(calls.length, 3)
|
||||
assert.equal(calls[2].query.page, 1)
|
||||
view.page.value = 4; await vue.nextTick(); assert.equal(calls.length, 4)
|
||||
view.pageSize.value = 50; await vue.nextTick(); assert.equal(calls.length, 5)
|
||||
assert.equal(calls[4].query.page, 1); assert.equal(calls[4].query.size, 50)
|
||||
view.reset(); await vue.nextTick(); assert.equal(calls.length, 6)
|
||||
calls.forEach(call => call.resolve(result('resolved')))
|
||||
})
|
||||
@@ -19,6 +19,20 @@ const page = ref(1)
|
||||
const pageSize = ref(20)
|
||||
const filters = reactive({ documentNo: '', dispatchNo: '', waybillNo: '', status: '', deliveryMethod: '', plannedRange: [] as string[] })
|
||||
const canCreate = computed(() => auth.hasPermission('merchant:warehouse:delivery:create'))
|
||||
let requestSequence = 0
|
||||
|
||||
const deliveryMethodOptions = [
|
||||
{ value: 'FBA_PICKUP', label: 'FBA 自提' },
|
||||
{ value: 'CUSTOMER_PICKUP', label: '客户自提' },
|
||||
{ value: 'TRUCK_PALLET', label: '卡派打托' },
|
||||
{ value: 'TRUCK_FLOOR', label: '卡派地板' },
|
||||
{ value: 'EXPRESS', label: '快递' },
|
||||
{ value: 'OUTSOURCED', label: '外配' },
|
||||
{ value: 'DEDICATED_TRUCK', label: '专车直送' },
|
||||
{ value: 'LTL', label: '零担派送' },
|
||||
{ value: 'TRUCK', label: '卡车派送' },
|
||||
]
|
||||
const deliveryMethodLabels: Record<string, string> = Object.fromEntries(deliveryMethodOptions.map(item => [item.value, item.label]))
|
||||
|
||||
const statusOptions = [
|
||||
{ value: 'WAIT_DELIVERY', label: '待派送' },
|
||||
@@ -37,14 +51,14 @@ const queryFields: QueryField[] = [
|
||||
{ key: 'dispatchNo', label: '排车单号', type: 'input', placeholder: '请输入排车单号' },
|
||||
{ key: 'waybillNo', label: '运单号', type: 'input', placeholder: '请输入运单号' },
|
||||
{ key: 'status', label: '派送状态', type: 'select', options: statusOptions },
|
||||
{ key: 'deliveryMethod', label: '派送方式', type: 'select', options: [{ label: '卡车派送', value: 'TRUCK' }, { label: '快递派送', value: 'COURIER' }, { label: '客户自提', value: 'SELF_PICKUP' }] },
|
||||
{ key: 'deliveryMethod', label: '派送方式', type: 'select', options: deliveryMethodOptions },
|
||||
{ key: 'plannedRange', label: '计划时间', type: 'daterange' },
|
||||
]
|
||||
const tableColumns: DataTableColumn[] = [
|
||||
{ key: 'actions', label: '操作', slotName: 'actions', width: 90, fixed: 'left' }, { prop: 'deliveryNo', label: '派送单号', minWidth: 180 },
|
||||
{ prop: 'dispatchNo', label: '排车单号', minWidth: 170 }, { prop: 'outboundNo', label: '出库单号', minWidth: 170 },
|
||||
{ key: 'status', label: '状态', slotName: 'status', width: 110 }, { prop: 'overseasWarehouseName', label: '海外仓', minWidth: 160 },
|
||||
{ prop: 'deliveryMethod', label: '派送方式', width: 120 }, { prop: 'waybillNo', label: '运单号', minWidth: 150 },
|
||||
{ prop: 'deliveryMethod', label: '派送方式', width: 120, formatter: (_row: object, _column: unknown, value: string | null | undefined) => value ? deliveryMethodLabels[value] ?? value : '—' }, { prop: 'waybillNo', label: '运单号', minWidth: 150 },
|
||||
{ prop: 'carrierName', label: '承运商', minWidth: 140 }, { key: 'planned', label: '计划派送', slotName: 'planned', width: 160 },
|
||||
{ key: 'arrival', label: '预计到达', slotName: 'arrival', width: 160 }, { key: 'createdAt', label: '创建时间', slotName: 'createdAt', width: 160 },
|
||||
]
|
||||
@@ -84,18 +98,21 @@ function buildQuery(): DeliveryQuery {
|
||||
}
|
||||
|
||||
async function loadOrders() {
|
||||
const sequence = ++requestSequence
|
||||
loading.value = true
|
||||
errorMessage.value = ''
|
||||
try {
|
||||
const result = await listDeliveryOrders(buildQuery())
|
||||
if (sequence !== requestSequence) return
|
||||
records.value = result.records
|
||||
total.value = result.total
|
||||
} catch (error) {
|
||||
if (sequence !== requestSequence) return
|
||||
records.value = []
|
||||
total.value = 0
|
||||
errorMessage.value = error instanceof Error ? error.message : '派送单加载失败,请重试'
|
||||
} finally {
|
||||
loading.value = false
|
||||
if (sequence === requestSequence) loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user