fix(pl): 隔离合同范围弹窗重开后的加载与搜索请求以阻止旧数据保存。

This commit is contained in:
wuzilong committed 2026-11-01 22:13:28 +08:00
1 parent 726575aaf2
commit bbe0144cdd
2 files changed
+158 -12

No files matched your search

+115
View File
@@ -375,3 +375,118 @@ test('quick search and number header filter are sent as independent conditions',
await flush()
h.stop()
})
import { bindingPayload } from '../src/views/pl/quota-cert/model'
function scopeDialogHarness() {
const calls: any[] = [],
unmount: Function[] = []
const queue = (method: string, args: any[]) => {
const p = deferred()
calls.push({ method, args, ...p })
return p.promise
}
const api = Object.fromEntries(
['list', 'contracts', 'products', 'bindings', 'saveBindings'].map((method) => [
method,
(...args: any[]) => queue(method, args)
])
)
const source = ts.createSourceFile(
'scope.ts',
parse(
readFileSync(
new URL('../src/views/pl/quota-cert/ContractScopeDialog.vue', import.meta.url),
'utf8'
)
).descriptor.scriptSetup!.content,
ts.ScriptTarget.Latest,
true
)
const code = ts.transpile(
source.statements
.filter((s) => !ts.isImportDeclaration(s))
.map((s) => s.getText(source))
.join('\n'),
{ target: ts.ScriptTarget.ESNext }
)
const env = {
...Object.fromEntries(
Object.entries(vue).filter(([key]) => /^[a-zA-Z_$][\w$]*$/.test(key) && key !== 'default')
),
defineProps: () => ({ canEdit: true, fixedContractId: 'c1' }),
defineEmits: () => () => {},
defineExpose: () => {},
onBeforeUnmount: (fn: Function) => unmount.push(fn),
quotaCertApi: api,
quotaStatisticsApi: { contract: (...args: any[]) => queue('contract', args) },
bindingPayload,
ElMessage: { success: () => {} }
}
const scope = vue.effectScope()
const state = scope.run(() =>
new Function(
...Object.keys(env),
`${code};return {open, visible, loading, searching, loadedContractId, bindings, save, searchContracts, contracts}`
)(...Object.values(env))
)
return {
state,
calls,
stop() {
unmount.forEach((fn) => fn())
scope.stop()
}
}
}
test('closing and reopening same contract rejects old scope response and keeps save disabled', async () => {
const h = scopeDialogHarness(),
s = h.state
const first = s.open('c1')
h.calls[0].resolve([])
h.calls[1].resolve([])
h.calls[2].resolve({ id: 'c1', contractNo: 'HT-1', contractName: '合同' })
await flush()
assert.deepEqual(
h.calls.slice(3).map((c) => c.method),
['products', 'bindings']
)
s.visible.value = false
const second = s.open('c1')
h.calls[3].resolve([{ id: 'old-product' }])
h.calls[4].resolve([{ id: 'old-binding', certType: 'purchase', certId: 'p', products: [] }])
await first
assert.equal(s.loadedContractId.value, '')
assert.equal(s.loading.value, true)
assert.deepEqual(s.bindings.value, [])
const count = h.calls.length
await s.save()
assert.equal(h.calls.length, count)
h.calls[5].resolve([])
h.calls[6].resolve([])
h.calls[7].resolve({ id: 'c1', contractNo: 'HT-1', contractName: '合同' })
await flush()
h.calls[8].resolve([{ id: 'new-product' }])
h.calls[9].resolve([])
await second
assert.equal(s.loadedContractId.value, 'c1')
assert.equal(s.loading.value, false)
h.stop()
})
test('closing dialog invalidates pending searches including their finally state', async () => {
const h = scopeDialogHarness(),
s = h.state
s.visible.value = true
const old = s.searchContracts('old')
s.visible.value = false
s.visible.value = true
const fresh = s.searchContracts('new')
h.calls[0].resolve([{ id: 'old' }])
await old
assert.deepEqual(s.contracts.value, [])
assert.equal(s.searching.value, true)
h.calls[1].resolve([{ id: 'new' }])
await fresh
assert.equal(s.contracts.value[0].id, 'new')
assert.equal(s.searching.value, false)
h.stop()
})