fix(admin): restore loading list detail page navigation
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
import { flushPromises, shallowMount } from '@vue/test-utils'
|
||||
import { beforeEach, afterEach, describe, expect, it, vi } from 'vitest'
|
||||
import { reactive } from 'vue'
|
||||
import ConfirmedLoadingListDetailView from './ConfirmedLoadingListDetailView.vue'
|
||||
|
||||
const mocks = vi.hoisted(() => ({ get: vi.fn(), push: vi.fn(), replace: vi.fn(), back: vi.fn(), route: { params: { planId: '1' }, query: { warehouseId: '11' } as Record<string, string> } }))
|
||||
vi.mock('vue-router', () => ({ useRoute: () => mocks.route, useRouter: () => mocks }))
|
||||
vi.mock('../api/platformLoadingLists', () => ({ getConfirmedLoadingList: mocks.get }))
|
||||
vi.mock('@/shared/api/http', () => ({ getApiErrorDetails: (cause: Error) => ({ message: cause.message }) }))
|
||||
|
||||
function render() {
|
||||
return shallowMount(ConfirmedLoadingListDetailView, { global: { stubs: {
|
||||
AdminPage: { template: '<main><slot /></main>' },
|
||||
LoadingListDetailDrawer: { name: 'LoadingListDetailDrawer', props: ['detail', 'error', 'loading'], template: '<section />' },
|
||||
} } })
|
||||
}
|
||||
const completed = (planId = '1') => ({ summary: { status: 'COMPLETED', planId, loadingListNo: `LIST-${planId}` }, items: [], histories: [] })
|
||||
|
||||
describe('confirmed loading list detail page', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
mocks.route = reactive({ params: { planId: '1' }, query: { warehouseId: '11' } })
|
||||
mocks.get.mockResolvedValue(completed())
|
||||
vi.spyOn(window, 'print').mockImplementation(() => {})
|
||||
})
|
||||
afterEach(() => vi.restoreAllMocks())
|
||||
it('loads read-only detail on the dedicated page and returns to the list', async () => {
|
||||
const wrapper = render(); await flushPromises()
|
||||
expect(mocks.get).toHaveBeenCalledWith('1', '11')
|
||||
const detail = wrapper.findComponent({ name: 'LoadingListDetailDrawer' })
|
||||
expect(detail.props('detail')).toEqual(completed())
|
||||
expect(window.print).not.toHaveBeenCalled()
|
||||
vi.spyOn(window.history, 'length', 'get').mockReturnValue(1)
|
||||
detail.vm.$emit('back')
|
||||
expect(mocks.push).toHaveBeenCalledWith({ name: 'loading-lists' })
|
||||
wrapper.unmount()
|
||||
})
|
||||
it('prints only after detail has loaded and removes the print request', async () => {
|
||||
mocks.route.query.print = '1'
|
||||
const wrapper = render(); await flushPromises()
|
||||
expect(window.print).toHaveBeenCalledTimes(1)
|
||||
expect(wrapper.findComponent({ name: 'LoadingListDetailDrawer' }).props('loading')).toBe(false)
|
||||
expect(mocks.replace).toHaveBeenCalledWith({ name: 'loading-list-detail', params: { planId: '1' }, query: { warehouseId: '11' } })
|
||||
wrapper.unmount()
|
||||
})
|
||||
it('keeps failure visible and does not print when loading fails', async () => {
|
||||
mocks.route.query.print = '1'
|
||||
mocks.get.mockRejectedValueOnce(new Error('清单不可用'))
|
||||
const wrapper = render(); await flushPromises()
|
||||
expect(wrapper.findComponent({ name: 'LoadingListDetailDrawer' }).props('error').message).toBe('清单不可用')
|
||||
expect(window.print).not.toHaveBeenCalled()
|
||||
wrapper.unmount()
|
||||
})
|
||||
it('ignores a stale response after switching manifests', async () => {
|
||||
let resolveFirst!: (value: unknown) => void
|
||||
mocks.get.mockImplementationOnce(() => new Promise(resolve => { resolveFirst = resolve }))
|
||||
.mockResolvedValueOnce(completed('2'))
|
||||
const wrapper = render()
|
||||
mocks.route.params.planId = '2'
|
||||
await flushPromises()
|
||||
resolveFirst(completed()); await flushPromises()
|
||||
expect(wrapper.findComponent({ name: 'LoadingListDetailDrawer' }).props('detail')).toEqual(completed('2'))
|
||||
wrapper.unmount()
|
||||
})
|
||||
})
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, ref, watch } from 'vue'
|
||||
import { computed, nextTick, onMounted, ref, watch } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { getApiErrorDetails, type ApiErrorDetails } from '@/shared/api/http'
|
||||
import AdminPage from '@/shared/components/AdminPage.vue'
|
||||
@@ -37,6 +37,14 @@ async function loadDetail() {
|
||||
return
|
||||
}
|
||||
detail.value = result
|
||||
loading.value = false
|
||||
if (route.query.print === '1') {
|
||||
await nextTick()
|
||||
if (sequence !== loadSequence) return
|
||||
window.print()
|
||||
const { print: _print, ...query } = route.query
|
||||
await router.replace({ name: 'loading-list-detail', params: { planId: planId.value }, query })
|
||||
}
|
||||
} catch (cause) {
|
||||
if (sequence !== loadSequence) return
|
||||
detail.value = null
|
||||
@@ -51,6 +59,10 @@ function goBack() {
|
||||
else void router.push({ name: 'loading-lists' })
|
||||
}
|
||||
|
||||
function printDetail() {
|
||||
window.print()
|
||||
}
|
||||
|
||||
onMounted(() => void loadDetail())
|
||||
watch([planId, warehouseId], () => void loadDetail())
|
||||
</script>
|
||||
@@ -58,6 +70,7 @@ watch([planId, warehouseId], () => void loadDetail())
|
||||
<template>
|
||||
<AdminPage>
|
||||
<div class="confirmed-loading-detail-page">
|
||||
<el-button v-if="detail" class="print-action" :disabled="loading" @click="printDetail">打印</el-button>
|
||||
<LoadingListDetailDrawer
|
||||
mode="confirmed"
|
||||
:loading="loading"
|
||||
@@ -73,5 +86,6 @@ watch([planId, warehouseId], () => void loadDetail())
|
||||
|
||||
<style scoped>
|
||||
.confirmed-loading-detail-page { display: grid; min-width: 0; max-width: 1280px; gap: 16px; }
|
||||
@media print { .print-action { display: none; } }
|
||||
@media (min-width: 1920px) { .confirmed-loading-detail-page { width: 100%; max-width: none; gap: 12px; } }
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user