优化
This commit is contained in:
@@ -0,0 +1,85 @@
|
||||
import { flushPromises, mount, type VueWrapper } from '@vue/test-utils'
|
||||
import ElementPlus from 'element-plus'
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { listSailingCalendar } from '../api/platformSailingCalendar'
|
||||
import type { SailingCalendarResponse } from '../types'
|
||||
import SailingCalendarView from './SailingCalendarView.vue'
|
||||
|
||||
const permissions = vi.hoisted(() => ({ write: true }))
|
||||
vi.mock('@/core/access/accessPreview', () => ({
|
||||
useAccessPreviewStore: () => ({ can: () => permissions.write }),
|
||||
}))
|
||||
vi.mock('@/modules/warehouses', () => ({ listAllPlatformWarehouses: vi.fn().mockResolvedValue([]) }))
|
||||
vi.mock('../api/platformSailingCalendar', () => ({
|
||||
listSailingCalendar: vi.fn(), cancelSailing: vi.fn(), copySailing: vi.fn(),
|
||||
createSailingTemplate: vi.fn(), createSailing: vi.fn(), generateSailingTemplate: vi.fn(),
|
||||
getSailing: vi.fn(), getSailingBookingAdvice: vi.fn(), getSailingVolumeDetail: vi.fn(),
|
||||
listSailingChannels: vi.fn().mockResolvedValue([]), listSailingTemplates: vi.fn().mockResolvedValue([]),
|
||||
previewSailingTemplateGeneration: vi.fn(), updateSailingTemplate: vi.fn(), updateSailing: vi.fn(),
|
||||
}))
|
||||
const volume = { actualReceivedCbm: 12, catchableForecastCbm: 0, riskUnmatchedCbm: 0, bookedContainerCount: 0, recommendedContainerCount: 1, pendingContainerCount: 1, lateBatchCount: 0, overCapacity: false }
|
||||
const calendar: SailingCalendarResponse = {
|
||||
month: '2026-09', timezone: 'Asia/Shanghai', summary: { ...volume, sailingCount: 1 },
|
||||
filterOptions: { warehouses: [], channels: [], departurePorts: [], destinationPorts: [] },
|
||||
records: [{ id: 'sailing-1', sailingNo: 'SAIL-TEST', warehouseId: 'warehouse-1', warehouseName: '测试仓', channelCode: 'CH-TEST', channelName: '测试渠道', vesselName: 'TEST VESSEL', voyageNo: 'TEST-01', departurePortCode: 'CNSHA', destinationPortCode: 'USLAX', etdAt: '2026-09-30T10:00:00', cutoffAt: '2026-09-29T18:00:00', status: 'OPEN', volumeSummary: volume, version: 1 }],
|
||||
}
|
||||
let wrapper: VueWrapper | undefined
|
||||
async function mountView() {
|
||||
wrapper = mount(SailingCalendarView, { attachTo: document.body, global: { plugins: [ElementPlus] } })
|
||||
await flushPromises()
|
||||
return wrapper
|
||||
}
|
||||
beforeEach(() => {
|
||||
vi.useFakeTimers({ toFake: ['Date'] })
|
||||
vi.setSystemTime(new Date(2026, 8, 30, 12))
|
||||
vi.clearAllMocks()
|
||||
permissions.write = true
|
||||
vi.mocked(listSailingCalendar).mockResolvedValue(calendar)
|
||||
})
|
||||
afterEach(() => { wrapper?.unmount(); wrapper = undefined; document.body.innerHTML = ''; vi.useRealTimers() })
|
||||
|
||||
describe('SailingCalendarView integrated workspace', () => {
|
||||
it('preserves keyword search, date selection and month navigation with the shared header', async () => {
|
||||
const view = await mountView()
|
||||
expect(view.get('h1').text()).toBe('船期日历')
|
||||
expect(view.get('.data-table__business-actions').text()).toContain('新增单个船次')
|
||||
expect(view.find('button[aria-label="列设置"]').exists()).toBe(false)
|
||||
expect(view.find('.data-table__pagination').exists()).toBe(false)
|
||||
expect(view.get('.detail-panel').text()).toContain('SAIL-TEST')
|
||||
await view.get('.sailing-card').trigger('click')
|
||||
expect(view.get('.detail-actions').text()).toContain('货量明细')
|
||||
const otherDate = view.findAll('.calendar-cell').find(cell => cell.get('.day-number').text() === '29')!
|
||||
await otherDate.trigger('click')
|
||||
expect(view.get('.detail-header h2').text()).toBe('2026-09-29')
|
||||
expect(view.get('.detail-panel').text()).toContain('当天暂无船期')
|
||||
const keyword = view.get('input[placeholder="渠道 / 船名 / 航次 / 船期编号"]')
|
||||
await keyword.setValue('TEST VESSEL')
|
||||
await keyword.trigger('keyup', { key: 'Enter' })
|
||||
await flushPromises()
|
||||
expect(listSailingCalendar).toHaveBeenLastCalledWith(expect.objectContaining({ month: '2026-09', keyword: 'TEST VESSEL' }))
|
||||
await view.get('.toolbar-filters button').trigger('click')
|
||||
await flushPromises()
|
||||
expect(listSailingCalendar).toHaveBeenLastCalledWith(expect.objectContaining({ keyword: undefined }))
|
||||
await view.get('button[aria-label="下个月"]').trigger('click')
|
||||
await flushPromises()
|
||||
expect(listSailingCalendar).toHaveBeenLastCalledWith(expect.objectContaining({ month: '2026-10' }))
|
||||
})
|
||||
|
||||
it('keeps filters and retry available on failure and preserves read-only permissions', async () => {
|
||||
permissions.write = false
|
||||
vi.mocked(listSailingCalendar).mockRejectedValueOnce(new Error('日历加载失败'))
|
||||
const view = await mountView()
|
||||
expect(view.get('h1').text()).toBe('船期日历')
|
||||
expect(view.find('.toolbar-filters').exists()).toBe(true)
|
||||
expect(view.find('.calendar-layout').exists()).toBe(false)
|
||||
expect(view.find('.data-table__business-actions button').exists()).toBe(false)
|
||||
await view.get('.load-error-state__actions button').trigger('click')
|
||||
await flushPromises()
|
||||
expect(view.get('.calendar-layout').attributes('aria-busy')).toBe('false')
|
||||
expect(view.get('.detail-panel').text()).toContain('SAIL-TEST')
|
||||
await view.get('.sailing-card').trigger('click')
|
||||
const operations = view.get('.detail-actions').text()
|
||||
expect(operations).toContain('货量明细')
|
||||
for (const action of ['编辑', '复制下一班', '取消']) expect(operations).not.toContain(action)
|
||||
})
|
||||
})
|
||||
File diff suppressed because one or more lines are too long
@@ -4,5 +4,5 @@ export const sailingCalendarRoutes = [{
|
||||
path: 'sailing-calendar',
|
||||
name: 'sailing-calendar',
|
||||
component: () => import('./pages/SailingCalendarView.vue'),
|
||||
meta: { title: '船期日历', description: '维护计划船期、查看收货体积和订柜建议', permission: 'platform:firstleg:sailing-calendar:list' },
|
||||
meta: { title: '船期日历', description: '维护计划船期、查看收货体积和订柜建议', showPageHeading: false, permission: 'platform:firstleg:sailing-calendar:list' },
|
||||
}] satisfies RouteRecordRaw[]
|
||||
|
||||
Reference in New Issue
Block a user