fix(admin): 完善弹窗滚动与表单交互布局

This commit is contained in:
2026-10-21 07:40:08 +08:00
parent 2f9a3330e5
commit 0a1968a134
41 changed files with 614 additions and 131 deletions
@@ -1,5 +1,5 @@
import { beforeEach, describe, expect, it, vi } from 'vitest'
import { flushPromises, mount } from '@vue/test-utils'
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import { enableAutoUnmount, flushPromises, mount } from '@vue/test-utils'
import ElementPlus from 'element-plus'
import { createPinia, setActivePinia } from 'pinia'
import CarrierScheduleQueryPanel from './CarrierScheduleQueryPanel.vue'
@@ -8,6 +8,10 @@ import { appendContainerShippingNode, getContainerShippingNodes } from '../api/p
import { getPortSchedule } from '../api/platformPortSchedules'
import { useAuthStore } from '@/core/auth/auth'
import { ApiResponseError } from '@/shared/api/http'
import { h } from 'vue'
import AppDialog from '@/shared/components/AppDialog.vue'
enableAutoUnmount(afterEach)
vi.mock('../api/platformCarrierScheduleQueries', () => ({
listCarrierScheduleQueries: vi.fn(),
@@ -91,6 +95,49 @@ describe('CarrierScheduleQueryPanel loading states', () => {
wrapper.unmount()
})
it.each([false, true])('isolates nested manual entry from its parent and reopens in ordinary mode (dense=%s)', async (dense) => {
vi.mocked(listCarrierScheduleQueries).mockResolvedValue({ records: [], page: 1, size: 200, total: 0, pages: 0 })
const host = mount(AppDialog, {
props: { modelValue: true, title: '船期详情', showConfirm: false },
attrs: { class: 'parent-schedule-dialog admin-density-dialog admin-workbench is-dialog' },
slots: { default: () => h(CarrierScheduleQueryPanel, { oceanBookingId: '2', containerBookingId: '3', dense }) },
global: { plugins: [ElementPlus, pinia] },
attachTo: document.body,
})
try {
await flushPromises()
const panel = host.findComponent(CarrierScheduleQueryPanel)
const open = async () => {
await panel.findAll('button').find(button => button.text().includes('人工补录节点'))!.trigger('click')
await flushPromises()
}
await open()
const child = panel.findComponent(AppDialog)
const dialog = document.body.querySelector<HTMLElement>('.el-dialog:not(.parent-schedule-dialog)')!
expect(dialog).not.toBeNull()
expect(dialog.parentElement?.closest('.parent-schedule-dialog')).toBeNull()
expect(document.body.contains(dialog)).toBe(true)
expect(dialog.querySelector(':scope > .el-dialog__body .app-dialog__body')).not.toBeNull()
expect(dialog.querySelector(':scope > .el-dialog__footer')).not.toBeNull()
expect(child.props('width')).toContain(dense ? '780px' : '620px')
if (dense) {
dialog.querySelector<HTMLButtonElement>('[aria-label="展开全屏"]')!.click()
await flushPromises()
expect(dialog.classList.contains('is-fullscreen')).toBe(true)
}
Array.from(dialog.querySelectorAll<HTMLButtonElement>('.el-dialog__footer button')).find(button => button.textContent?.trim() === '取消')!.click()
await flushPromises()
expect(child.props('modelValue')).toBe(false)
expect(host.props('modelValue')).toBe(true)
await open()
expect(child.props('modelValue')).toBe(true)
expect(dialog.classList.contains('is-fullscreen')).toBe(false)
expect(appendContainerShippingNode).not.toHaveBeenCalled()
} finally {
host.unmount()
}
})
it('shows manual entry without creating a tracking task', async () => {
vi.mocked(listCarrierScheduleQueries).mockResolvedValue({ records: [], page: 1, size: 200, total: 0, pages: 0 })
const wrapper = mount(CarrierScheduleQueryPanel, { props: { oceanBookingId: '2', containerBookingId: '3' }, global: { plugins: [ElementPlus, pinia] } })
@@ -291,14 +338,15 @@ describe('CarrierScheduleQueryPanel loading states', () => {
inputs.find((input) => String(input.props('maxlength')) === '128')!.vm.$emit('update:modelValue', 'Pacific Ocean')
inputs.find((input) => input.props('type') === 'textarea')!.vm.$emit('update:modelValue', '船司官网人工核验')
await wrapper.findComponent({ name: 'AppDialog' }).vm.$emit('confirm'); await flushPromises()
expect(wrapper.text()).toContain('补录未成功,已保留当前填写内容')
expect(wrapper.text()).toContain('req-409'); expect(wrapper.text()).toContain('trace-409')
expect(document.body.querySelector('.el-dialog')?.textContent).toContain('补录未成功,已保留当前填写内容')
expect(document.body.querySelector('.el-dialog')?.textContent).toContain('req-409'); expect(document.body.querySelector('.el-dialog')?.textContent).toContain('trace-409')
const dialog = wrapper.findComponent({ name: 'AppDialog' })
expect(dialog.props('title')).toBe('人工补录船节点 · CAB-3')
if (dense) {
await dialog.get('[aria-label="展开全屏"]').trigger('click')
expect(dialog.find('.el-dialog.is-fullscreen').exists()).toBe(true)
expect(dialog.find('textarea').element.value).toBe('船司官网人工核验')
document.body.querySelector<HTMLButtonElement>('[aria-label="展开全屏"]')!.click()
await flushPromises()
expect(document.body.querySelector('.el-dialog.is-fullscreen')).not.toBeNull()
expect(document.body.querySelector<HTMLTextAreaElement>('.el-dialog textarea')!.value).toBe('船司官网人工核验')
}
await wrapper.findComponent({ name: 'AppDialog' }).vm.$emit('confirm'); await flushPromises()
@@ -323,7 +371,7 @@ describe('CarrierScheduleQueryPanel loading states', () => {
wrapper.findAllComponents({ name: 'ElInput' }).find((input) => input.props('type') === 'textarea')!.vm.$emit('update:modelValue', '人工核验可提柜')
await wrapper.findComponent({ name: 'AppDialog' }).vm.$emit('confirm'); await flushPromises()
expect(wrapper.text()).toContain('预计时间和实际时间至少填写一项')
expect(document.body.querySelector('.el-dialog')?.textContent).toContain('预计时间和实际时间至少填写一项')
expect(appendContainerShippingNode).not.toHaveBeenCalled()
})
})