diff --git a/src/modules/overseas-master-data/pages/DriverDetailView.vue b/src/modules/overseas-master-data/pages/DriverDetailView.vue
index 46635be5..fc702e6f 100644
--- a/src/modules/overseas-master-data/pages/DriverDetailView.vue
+++ b/src/modules/overseas-master-data/pages/DriverDetailView.vue
@@ -1,6 +1,6 @@
diff --git a/src/modules/overseas-master-data/pages/DriversBehavior.test.ts b/src/modules/overseas-master-data/pages/DriversBehavior.test.ts
index 7e769ab9..24106b5e 100644
--- a/src/modules/overseas-master-data/pages/DriversBehavior.test.ts
+++ b/src/modules/overseas-master-data/pages/DriversBehavior.test.ts
@@ -11,8 +11,13 @@ import DriverEditorDialog from '../components/DriverEditorDialog.vue'
import DriverLicensePreview from '../components/DriverLicensePreview.vue'
const security = vi.hoisted(() => ({ allowWrite: true }))
+const routing = vi.hoisted(() => ({ route: null as null | { name: string; params: { driverId?: string | string[] } } }))
vi.mock('@/core/access/accessPreview', () => ({ useAccessPreviewStore: () => ({ can: (code: string) => code.endsWith(':list') || security.allowWrite }) }))
-vi.mock('vue-router', () => ({ useRouter: () => ({ push: vi.fn() }), useRoute: () => ({ params: { driverId: '9223372036854775701' } }) }))
+vi.mock('vue-router', async () => {
+ const { reactive } = await import('vue')
+ routing.route = reactive({ name: 'overseas-master-data-driver-detail', params: { driverId: '9223372036854775701' } })
+ return { useRouter: () => ({ push: vi.fn() }), useRoute: () => routing.route }
+})
vi.mock('element-plus', () => ({ ElMessage: { success: vi.fn(), warning: vi.fn(), error: vi.fn() }, ElMessageBox: { confirm: vi.fn().mockResolvedValue(undefined), prompt: vi.fn().mockResolvedValue({ value: '暂停' }) } }))
vi.mock('../api/drivers', () => ({ listDrivers: vi.fn(), getDriver: vi.fn(), listDriverHistory: vi.fn(), listDriverWarehouses: vi.fn(), createDriver: vi.fn(), updateDriver: vi.fn(), changeDriverStatus: vi.fn(), reviewDriver: vi.fn(), submitDriver: vi.fn(), getDriverQualification: vi.fn(), uploadDriverLicense: vi.fn(), downloadDriverLicense: vi.fn() }))
@@ -33,9 +38,22 @@ function mount(component: object, props: Record = {}): VueWrapp
return wrapper
}
function findButton(wrapper: VueWrapper, text: string) { const found = wrapper.findAll('button').find(value => value.text().includes(text)); expect(found).toBeDefined(); return found! }
+function cachedDetails() {
+ const showing = ref(driver.id)
+ const host = defineComponent({ setup: () => () => h(KeepAlive, null, { default: () => showing.value ? h(DriverDetailView, { key: showing.value }) : h('div', '司机列表') }) })
+ const wrapper = shallowMount(host, { global: { stubs: { ...stubs, KeepAlive: false, DriverDetailView: false }, directives: { loading: () => undefined } } })
+ wrappers.push(wrapper)
+ return { wrapper, show(id: string | null) {
+ routing.route!.name = id ? 'overseas-master-data-driver-detail' : 'overseas-master-data-drivers'
+ routing.route!.params = id ? { driverId: id } : {}
+ showing.value = id
+ } }
+}
beforeEach(() => {
vi.clearAllMocks(); security.allowWrite = true
+ routing.route!.name = 'overseas-master-data-driver-detail'
+ routing.route!.params = { driverId: driver.id }
vi.mocked(api.listDrivers).mockResolvedValue({ records: [structuredClone(driver)], page: 1, size: 20, total: 24, pages: 2, summary: { total: 24, owned: 20, dispatched: 4, expiringSoon: 3, expired: 1 } })
vi.mocked(api.getDriver).mockResolvedValue(structuredClone(driver))
vi.mocked(api.listDriverWarehouses).mockResolvedValue([{ id: '92', code: 'WH-US', name: '洛杉矶仓', region: 'OVERSEAS' }])
@@ -45,6 +63,61 @@ beforeEach(() => {
afterEach(() => { wrappers.splice(0).forEach(wrapper => wrapper.unmount()); vi.unstubAllGlobals() })
describe('driver page workflows', () => {
+ it('does not fetch an absent driver on return to list and loads the valid driver on reentry', async () => {
+ const { wrapper, show } = cachedDetails(); await flushPromises()
+ expect(api.getDriver).toHaveBeenCalledExactlyOnceWith(driver.id)
+ show(null)
+ await flushPromises()
+ expect(api.getDriver).toHaveBeenCalledTimes(1)
+ const next = { ...structuredClone(driver), recordVersion: 3, details: { ...driver.details, name: '当前司机' } }
+ vi.mocked(api.getDriver).mockResolvedValueOnce(next)
+ show(next.id)
+ await flushPromises()
+ expect(api.getDriver).toHaveBeenCalledTimes(2)
+ expect(api.getDriver).toHaveBeenLastCalledWith(next.id)
+ expect(wrapper.text()).toContain('当前司机')
+ })
+
+ it('loads only the active driver instance when two parameter-keyed detail pages are cached', async () => {
+ const other = { ...structuredClone(driver), id: '9223372036854775702', driverNo: 'DR-OTHER', details: { ...driver.details, name: '另一司机' } }
+ vi.mocked(api.getDriver).mockImplementation(async id => structuredClone(id === driver.id ? driver : other))
+ const { wrapper, show } = cachedDetails(); await flushPromises()
+ const first = wrapper.findComponent(DriverDetailView)
+ expect(api.getDriver).toHaveBeenCalledExactlyOnceWith(driver.id)
+ show(other.id); await flushPromises()
+ expect(api.getDriver).toHaveBeenCalledTimes(2)
+ expect(api.getDriver).toHaveBeenLastCalledWith(other.id)
+ expect(first.text()).not.toContain('另一司机')
+ expect(wrapper.text()).toContain('另一司机')
+ show(driver.id); await flushPromises()
+ expect(api.getDriver).toHaveBeenCalledTimes(3)
+ expect(api.getDriver).toHaveBeenLastCalledWith(driver.id)
+ expect(wrapper.text()).toContain('测试司机')
+ show(null); await flushPromises()
+ expect(api.getDriver).toHaveBeenCalledTimes(3)
+ show(other.id); await flushPromises()
+ expect(api.getDriver).toHaveBeenCalledTimes(4)
+ expect(api.getDriver).toHaveBeenLastCalledWith(other.id)
+ })
+
+ it('invalidates a pending detail response when the route leaves even before params are cleared', async () => {
+ let finish!: (value: DriverDetail) => void
+ vi.mocked(api.getDriver).mockReturnValueOnce(new Promise(resolve => { finish = resolve }))
+ const wrapper = mount(DriverDetailView); await flushPromises()
+ routing.route!.name = 'overseas-master-data-drivers'
+ await flushPromises()
+ finish({ ...structuredClone(driver), details: { ...driver.details, name: '离页迟到司机' } })
+ await flushPromises()
+ expect(api.getDriver).toHaveBeenCalledTimes(1)
+ expect(wrapper.text()).not.toContain('离页迟到司机')
+ })
+
+ it.each([{ label: 'missing', id: undefined }, { label: 'empty', id: '' }, { label: 'array', id: [driver.id] }])('does not fabricate a detail ID from $label route params', async ({ id }) => {
+ routing.route!.params = { driverId: id }
+ mount(DriverDetailView); await flushPromises()
+ expect(api.getDriver).not.toHaveBeenCalled()
+ })
+
it('refreshes a cached list once on return and retains applied filters and pagination', async () => {
const showingList = ref(true)
const host = defineComponent({ setup: () => () => h(KeepAlive, null, { default: () => showingList.value ? h(DriversView) : h('div', '司机详情') }) })