Compare commits

...
Author SHA1 Message Date
wuzilongandCursor ba62ec7b36 fix(pl): 证书齐备待完善列表将多选列置于首列。
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-11-15 15:13:14 +08:00
wuzilong 579285a7bf merge: issue #34 归档运费运输量回填修复。
将任务分支合入本地 V3.0,供本机验收测试;未推送远端集成分支。
2026-11-15 15:03:44 +08:00
wuzilongandCursor f41e47601d fix(pl): 归档再次打开时按实际数量回填运费运输量。
resetFreightCalc 在产品已加载后会清空运输量且不再触发 watch,导致再次归档误报运输量须大于0。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-11-15 15:03:44 +08:00
4 changed files with 116 additions and 7 deletions

No files matched your search

@@ -25,6 +25,20 @@ test('incomplete certificate filter is sent as quotaCertWaivedIn=0', () => {
assert.deepEqual(params, { quotaCertWaivedIn: '0' })
})
test('fulfill tab selection column is first among table columns', () => {
const web = readFileSync(new URL('../src/views/pl/cert-ready/index.vue', import.meta.url), 'utf8')
const selectionIdx = web.indexOf('type="selection"')
const columnsIdx = web.indexOf('v-for="column in columns"')
const actionsIdx = web.indexOf('label="操作"')
assert.ok(selectionIdx > 0, 'selection column missing')
assert.ok(columnsIdx > selectionIdx, 'selection must precede business columns')
assert.ok(actionsIdx > columnsIdx, 'actions must follow business columns')
assert.match(
web,
/v-if="listMode === 'fulfill'"[\s\S]*?type="selection"[\s\S]*?v-for="column in columns"/
)
})
test('web funnel and miniprogram status dropdown default to incomplete', () => {
const web = readFileSync(new URL('../src/views/pl/cert-ready/index.vue', import.meta.url), 'utf8')
const mobile = readFileSync(
@@ -0,0 +1,91 @@
import assert from 'node:assert/strict'
import { existsSync, readFileSync } from 'node:fs'
import test from 'node:test'
import ts from 'typescript'
import * as vue from 'vue'
function loadFreightCalc() {
const path = new URL('../src/composables/usePlFreightCalc.ts', import.meta.url)
assert.ok(existsSync(path), 'usePlFreightCalc must exist')
const source = readFileSync(path, 'utf8')
const ast = ts.createSourceFile('usePlFreightCalc.ts', source, ts.ScriptTarget.Latest, true)
const code = ast.statements
.filter((node) => !ts.isImportDeclaration(node))
.map((node) => node.getText(ast))
.join('\n')
const compiled = ts.transpileModule(code, {
compilerOptions: { target: ts.ScriptTarget.ES2022, module: ts.ModuleKind.CommonJS }
}).outputText
const module = { exports: {} as Record<string, unknown> }
const requireStub = () => {
throw new Error('unexpected require in freight calc test harness')
}
// eslint-disable-next-line no-new-func
const factory = new Function(
'exports',
'require',
'module',
'__filename',
'__dirname',
'computed',
'ref',
'watch',
compiled + '\nmodule.exports = { usePlFreightCalc, sumProductActualQuantities };'
)
factory(
module.exports,
requireStub,
module,
'usePlFreightCalc.ts',
'.',
vue.computed,
vue.ref,
vue.watch
)
return module.exports as {
usePlFreightCalc: typeof import('../src/composables/usePlFreightCalc').usePlFreightCalc
}
}
test('resetFreightCalc re-sums transport qty from existing product actuals', async () => {
const { usePlFreightCalc } = loadFreightCalc()
const products = vue.ref([{ actualQty: 0.04, unit: '吨' }, { actualQty: 0.04, unit: '吨' }])
const hasFreight = vue.ref(1)
const freightType = vue.ref('by_weight')
const scope = vue.effectScope()
const calc = scope.run(() => usePlFreightCalc({ products, hasFreight, freightType }))
assert.ok(calc)
await vue.nextTick()
assert.equal(calc!.transportQty.value, 0.08)
calc!.resetFreightCalc()
await vue.nextTick()
assert.equal(calc!.transportQtyTouched.value, false)
assert.equal(
calc!.transportQty.value,
0.08,
'reset after products already loaded must not leave transport qty empty'
)
scope.stop()
})
test('resetFreightCalc restores product sum after manual transport qty edit', async () => {
const { usePlFreightCalc } = loadFreightCalc()
const products = vue.ref([{ actualQty: 0.04, unit: '吨' }])
const hasFreight = vue.ref(1)
const freightType = vue.ref('by_weight')
const scope = vue.effectScope()
const calc = scope.run(() => usePlFreightCalc({ products, hasFreight, freightType }))
assert.ok(calc)
await vue.nextTick()
calc!.transportQty.value = 9.9
calc!.markTransportQtyTouched()
calc!.resetFreightCalc()
await vue.nextTick()
assert.equal(calc!.transportQtyTouched.value, false)
assert.equal(calc!.transportQty.value, 0.04)
scope.stop()
})
@@ -86,11 +86,13 @@ export function usePlFreightCalc(options: {
)
function resetFreightCalc() {
transportQty.value = null
transportQtyTouched.value = false
freightAmount.value = null
freightAmountTouched.value = false
freightPrice.value = null
// 产品已加载时 watch 不会因 reset 重跑;须立即按当前实际数量回填,
// 否则归档弹窗再次打开会误报「运输量须大于0」。
transportQty.value = defaultTransportQty.value || null
}
function markTransportQtyTouched() {
+8 -6
View File
@@ -50,6 +50,14 @@
class="list-table"
@selection-change="onSelectionChange"
>
<el-table-column
v-if="listMode === 'fulfill'"
type="selection"
width="48"
align="center"
header-align="center"
:reserve-selection="false"
/>
<el-table-column
v-for="column in columns"
:key="column.prop"
@@ -93,12 +101,6 @@
<span v-else>{{ row[column.prop] || '—' }}</span>
</template>
</el-table-column>
<el-table-column
v-if="listMode === 'fulfill'"
type="selection"
width="48"
:reserve-selection="false"
/>
<el-table-column label="操作" min-width="260" fixed="right">
<template #default="{ row }">
<template v-if="listMode === 'fulfill'">