fix(pl): 隔离合同范围弹窗重开后的加载与搜索请求以阻止旧数据保存。
This commit is contained in:
1 parent
726575aaf2
commit
bbe0144cdd
2 files changed
+158
-12
No files matched your search
@@ -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()
|
||||
})
|
||||
@@ -215,18 +215,33 @@
|
||||
return `车号:${c?.vehicleNumbersJson?.join('、') || '未填写'};引用:${c?.purchaseRefs?.map((r) => r.referenceNo).join('、') || '未填写'}`
|
||||
}
|
||||
let sessionVersion = 0
|
||||
onBeforeUnmount(() => {
|
||||
let loadVersion = 0
|
||||
let searchVersion = 0
|
||||
function invalidateSession() {
|
||||
sessionVersion++
|
||||
loadVersion++
|
||||
})
|
||||
searchVersion++
|
||||
loadedContractId.value = ''
|
||||
loading.value = false
|
||||
searching.value = false
|
||||
}
|
||||
watch(
|
||||
visible,
|
||||
(value) => {
|
||||
if (!value) invalidateSession()
|
||||
},
|
||||
{ flush: 'sync' }
|
||||
)
|
||||
onBeforeUnmount(invalidateSession)
|
||||
async function searchContracts(keyword = '') {
|
||||
const version = sessionVersion
|
||||
const session = sessionVersion
|
||||
const version = ++searchVersion
|
||||
searching.value = true
|
||||
try {
|
||||
const result = await quotaCertApi.contracts(keyword)
|
||||
if (version === sessionVersion) contracts.value = result
|
||||
if (session === sessionVersion && version === searchVersion) contracts.value = result
|
||||
} finally {
|
||||
searching.value = false
|
||||
if (session === sessionVersion && version === searchVersion) searching.value = false
|
||||
}
|
||||
}
|
||||
async function loadFixedContract(id: string) {
|
||||
@@ -239,7 +254,8 @@
|
||||
}
|
||||
async function open(id = '') {
|
||||
if (saving.value) return
|
||||
const session = ++sessionVersion
|
||||
invalidateSession()
|
||||
const session = sessionVersion
|
||||
visible.value = true
|
||||
id = props.fixedContractId || id
|
||||
contractId.value = id
|
||||
@@ -247,6 +263,8 @@
|
||||
bindings.value = []
|
||||
original.value = []
|
||||
products.value = []
|
||||
selectedProducts.value = []
|
||||
contracts.value = []
|
||||
loading.value = true
|
||||
try {
|
||||
const [purchase, transport] = await Promise.all([
|
||||
@@ -259,11 +277,11 @@
|
||||
certificates.transport = transport
|
||||
if (id) await loadContract()
|
||||
} finally {
|
||||
loading.value = false
|
||||
if (session === sessionVersion) loading.value = false
|
||||
}
|
||||
}
|
||||
let loadVersion = 0
|
||||
async function loadContract() {
|
||||
const session = sessionVersion
|
||||
const version = ++loadVersion
|
||||
const id = contractId.value
|
||||
loadedContractId.value = ''
|
||||
@@ -273,7 +291,7 @@
|
||||
quotaCertApi.products(id),
|
||||
quotaCertApi.bindings(id)
|
||||
])
|
||||
if (version !== loadVersion) return
|
||||
if (session !== sessionVersion || version !== loadVersion) return
|
||||
products.value = rows
|
||||
bindings.value = bindingPayload(scope)
|
||||
original.value = JSON.parse(JSON.stringify(bindings.value))
|
||||
@@ -282,7 +300,7 @@
|
||||
]
|
||||
loadedContractId.value = id
|
||||
} finally {
|
||||
if (version === loadVersion) loading.value = false
|
||||
if (session === sessionVersion && version === loadVersion) loading.value = false
|
||||
}
|
||||
}
|
||||
function setCertificates(kind: CertType, ids: string[]) {
|
||||
@@ -321,13 +339,26 @@
|
||||
)
|
||||
}
|
||||
async function save() {
|
||||
if (!props.canEdit) return
|
||||
if (
|
||||
!props.canEdit ||
|
||||
!visible.value ||
|
||||
loading.value ||
|
||||
!contractId.value ||
|
||||
loadedContractId.value !== contractId.value
|
||||
)
|
||||
return
|
||||
const session = sessionVersion
|
||||
const targetContract = contractId.value
|
||||
const version = loadVersion
|
||||
saving.value = true
|
||||
try {
|
||||
const result = await quotaCertApi.saveBindings(targetContract, bindingPayload(bindings.value))
|
||||
if (targetContract !== contractId.value || version !== loadVersion) return
|
||||
if (
|
||||
session !== sessionVersion ||
|
||||
targetContract !== contractId.value ||
|
||||
version !== loadVersion
|
||||
)
|
||||
return
|
||||
bindings.value = bindingPayload(result)
|
||||
original.value = JSON.parse(JSON.stringify(bindings.value))
|
||||
ElMessage.success('合同及产品关联已保存')
|
||||
|
||||
Reference in new issue
Block a user