fix(fleet): preserve drafts before dialog close and respect dashboard permissions

This commit is contained in:
2026-10-07 18:21:03 +08:00
parent 2477667c27
commit 2bc095ecac
11 changed files with 150 additions and 34 deletions
@@ -0,0 +1,58 @@
import ElementPlus, { ElMessageBox } from 'element-plus'
import { createPinia } from 'pinia'
import { mount, flushPromises, type VueWrapper } from '@vue/test-utils'
import { afterEach, beforeEach, expect, it, vi } from 'vitest'
import CapacityInquiryDialog from './CapacityInquiryDialog.vue'
import CapacityOfferSettingsDialog from './CapacityOfferSettingsDialog.vue'
import type { CapacityOffer } from '../capacityOfferTypes'
import type { FleetTrip } from '../tripTypes'
import { useUnsavedWorkStore } from '@/core/unsaved-work/unsavedWork'
import { listCapacityOffers } from '../api/capacityOffers'
vi.mock('@/core/access/accessPreview', () => ({ useAccessPreviewStore: () => ({ can: () => true }) }))
vi.mock('../api/capacityOffers', () => ({ listCapacityOffers: vi.fn(), listOfferCustomers: vi.fn(), listOfferQuotes: vi.fn(), saveCapacityInquiry: vi.fn(), saveCapacityOffer: vi.fn(), commandCapacityOffer: vi.fn() }))
const offer: CapacityOffer = { offerNo: 'CO-CLOSE', tripNo: 'TR-CLOSE', status: 'OPEN', recordVersion: 1, tripRecordVersion: 2, warehouseId: '9', warehouseName: '测试仓', plannedDepartureAt: '2026-10-20T12:00:00Z', cutoffAt: '2026-10-20T10:00:00Z', contactName: null, contactPhone: null, remark: null, destinations: [{ destinationKey: 'FBA:ONT8', name: 'ONT8', deliveryIndex: 1, remaining: null }], segments: [], capacityKnown: false, canCreateInquiry: true, canConfirm: false, blockReason: '待核对', actions: { canEdit: true, canPublish: false, canPause: true, canResume: false, canClose: true } }
const wrappers: VueWrapper[] = []
beforeEach(() => { vi.clearAllMocks(); vi.mocked(listCapacityOffers).mockResolvedValue({ records: [offer], total: 1, page: 1, size: 1, pages: 1 }) })
afterEach(async () => { ElMessageBox.close(); wrappers.splice(0).forEach(wrapper => wrapper.unmount()); await flushPromises(); document.body.innerHTML = '' })
function mountDialog(kind: 'inquiry' | 'settings') {
const pinia = createPinia()
const wrapper = kind === 'inquiry'
? mount(CapacityInquiryDialog, { attachTo: document.body, props: { modelValue: true, offer, inquiry: null, readonly: false }, global: { plugins: [pinia, ElementPlus] } })
: mount(CapacityOfferSettingsDialog, { attachTo: document.body, props: { modelValue: false, trip: { tripNo: offer.tripNo, recordVersion: 2 } as FleetTrip }, global: { plugins: [pinia, ElementPlus] } })
wrappers.push(wrapper)
return { wrapper, pinia }
}
async function continueEditing() {
const message = [...document.querySelectorAll('.el-message-box')].at(-1)!
expect(message).not.toBeNull()
const button = [...message.querySelectorAll('button')].find(node => node.textContent?.trim() === '继续填写')!
expect(button).toBeTruthy(); button.click(); await flushPromises()
await new Promise(resolve => setTimeout(resolve, 50)); await flushPromises()
}
it.each(['inquiry', 'settings'] as const)('keeps the real %s dialog and edited input visible through cancel, Escape and X vetoes', async kind => {
const { wrapper, pinia } = mountDialog(kind)
if (kind === 'settings') await wrapper.setProps({ modelValue: true })
await flushPromises()
const input = wrapper.find<HTMLInputElement>(kind === 'inquiry' ? 'input[placeholder="选填"]' : 'input[placeholder="填写公开给销售的联系人(选填)"]')
expect(input.exists()).toBe(true)
await input.setValue('UI-未保存检查'); await flushPromises()
const assertPreserved = () => {
expect(wrapper.find('.app-dialog').isVisible()).toBe(true)
expect(input.element.isConnected).toBe(true)
expect(input.element.value).toBe('UI-未保存检查')
expect(wrapper.emitted('update:modelValue')).toBeUndefined()
expect(useUnsavedWorkStore(pinia).findBlocking(new Set(), true)).toHaveLength(1)
}
await wrapper.findAll('.app-dialog__footer button').find(button => button.text() === '取消')!.trigger('click'); await flushPromises()
assertPreserved(); await continueEditing(); assertPreserved()
input.element.focus(); await input.trigger('keydown', { key: 'Escape', code: 'Escape' }); await flushPromises()
assertPreserved(); await continueEditing(); assertPreserved()
await wrapper.get('.el-dialog__headerbtn').trigger('click'); await flushPromises()
assertPreserved(); await continueEditing(); assertPreserved()
await wrapper.findAll('.app-dialog__footer button').find(button => button.text() === '取消')!.trigger('click'); await flushPromises()
const message = [...document.querySelectorAll('.el-message-box')].at(-1)!
const accept = [...message.querySelectorAll('button')].find(button => button.textContent?.trim() === '确认关闭') as HTMLButtonElement
accept.click(); await flushPromises()
expect(wrapper.emitted('update:modelValue')).toEqual([[false]])
})