fix: align audit and dispatch workflows and release failed requests

This commit is contained in:
2026-10-22 23:10:09 +08:00
parent 9cdcb9c077
commit 4a3456756f
124 changed files with 1296 additions and 2120 deletions
@@ -18,7 +18,7 @@ function setup(value = container, realDialog = false) {
}
describe('original container schedule', () => {
beforeEach(() => { vi.clearAllMocks(); can.mockReturnValue(true); vi.mocked(getPortSchedule).mockResolvedValue(result()); vi.mocked(updatePortSchedule).mockResolvedValue(result()); vi.mocked(createPortSchedule).mockResolvedValue(result()) })
it('loads only the server selected ID, preserves original fields, and keeps key/body after failure', async () => {
it('loads only the server selected ID, preserves original fields, and unlocks editing and uses a fresh key after failure', async () => {
const { wrapper, vm } = setup(container, true); await vm.begin()
expect(getPortSchedule).toHaveBeenCalledExactlyOnceWith('S1')
vm.form.vesselName = 'NEW'; vm.form.voyageNo = 'V2'; vm.form.reason = '原船满载改配'
@@ -27,12 +27,12 @@ describe('original container schedule', () => {
await flushPromises()
const dialog = wrapper.getComponent(AppDialog)
expect(dialog.props('title')).toBe('原柜船期与修改历史 · D1')
expect(dialog.getComponent({ name: 'ElForm' }).props('disabled')).toBe(true)
expect(dialog.getComponent({ name: 'ElForm' }).props('disabled')).toBe(false)
await dialog.get('[aria-label="展开全屏"]').trigger('click')
expect(dialog.find('.el-dialog.is-fullscreen').exists()).toBe(true)
expect(dialog.get<HTMLInputElement>('input[placeholder="请输入船名"]').element.value).toBe('NEW')
await vm.save()
expect(vi.mocked(updatePortSchedule).mock.calls[1]).toEqual(vi.mocked(updatePortSchedule).mock.calls[0])
expect(vi.mocked(updatePortSchedule).mock.calls[1]![1]).toEqual(vi.mocked(updatePortSchedule).mock.calls[0]![1]); expect(vi.mocked(updatePortSchedule).mock.calls[1]![2]).not.toBe(vi.mocked(updatePortSchedule).mock.calls[0]![2])
expect(updatePortSchedule).toHaveBeenLastCalledWith('S1', { expectedVersion: 5, reason: '原船满载改配', data: expect.objectContaining({ vesselName: 'NEW', voyageNo: 'V2', sourceType: 'CARRIER', sourceName: '官网', sourceRef: 'REF', observedAt: original.observedAt, expiresAt: original.expiresAt, portCutoffAt: original.portCutoffAt, remark: '原备注', containerBookingId: 'C1' }) }, expect.any(String))
expect(createPortSchedule).not.toHaveBeenCalled(); expect(wrapper.emitted('saved')).toHaveLength(1); wrapper.unmount()
})
@@ -96,7 +96,7 @@ async function save() {
error.value = getApiErrorDetails(cause, cause instanceof Error ? cause.message : '船期保存失败').message
if (cause instanceof ApiResponseError && cause.code === 'A0409') error.value += ';输入已保留,请关闭后重新打开核对最新版本。'
}
} finally { if (current === generation) busy.value = false }
} finally { pending.value = null; if (current === generation) busy.value = false }
}
watch(open, value => { if (!value) { generation++; loading.value = false; busy.value = false } })
watch(scope, () => { open.value = false; generation++; schedule.value = null; pending.value = null })
@@ -108,7 +108,7 @@ onBeforeUnmount(() => { generation++ })
<span v-if="prerequisite">{{ prerequisite }}</span>
<span v-else-if="!canRead">当前账号无船期读取权限。</span>
</div>
<AppDialog v-model="open" class="admin-density-dialog admin-workbench is-dialog" :title="dialogTitle" fullscreenable :width="container.currentPortScheduleId ? '1000px' : '1120px'" :show-confirm="canWrite" :confirm-disabled="loading || !!prerequisite || (!!container.currentPortScheduleId && !schedule)" :confirm-loading="busy" :confirm-text="pending ? '重试原请求' : '保存船期'" @confirm="save">
<AppDialog v-model="open" class="admin-density-dialog admin-workbench is-dialog" :title="dialogTitle" fullscreenable :width="container.currentPortScheduleId ? '1000px' : '1120px'" :show-confirm="canWrite" :confirm-disabled="loading || !!prerequisite || (!!container.currentPortScheduleId && !schedule)" :confirm-loading="busy" confirm-text="保存船期" @confirm="save">
<div v-loading="loading" class="density-dialog-sections">
<el-alert v-if="error" :title="error" type="error" :closable="false" />
<section class="density-dialog-section" data-tone="source">