Release/test 合并上生产 #2

Merged
admin merged 736 commits from release/test into main 2026-09-17 11:50:56 +08:00
8 changed files with 34 additions and 4 deletions
Showing only changes of commit 5b441626e9 - Show all commits
@@ -28,4 +28,9 @@ describe('CustomsCasesView', () => {
expect(source).not.toContain("label: '订单标识'")
expect(source).not.toContain("label: '入库单标识'")
})
it('keeps the detail dialog responsive with internal scrolling', () => {
expect(source).toContain('width:min(900px,calc(100vw - 28px))!important')
expect(source).toContain('max-height:90vh')
expect(source).toContain('overflow-y:auto')
})
})
@@ -29,4 +29,4 @@ watch([page, pageSize], () => void load()); onMounted(load)
</script>
<template><AdminPage as="div"><ListPageLayout aria-label="报关协同列表"><template #toolbar><QueryToolbar v-model="filters" class="surface-panel" ariaLabel="报关协同筛选" :fields="fields" @search="search" /></template><template v-if="error" #state><LoadErrorState title="报关协同加载失败" :message="error.message" :loading="loading" @retry="load"><template #details><ApiErrorMeta :details="error" /></template></LoadErrorState></template><section v-if="!error" class="surface-panel table-panel"><DataTable v-model:page="page" v-model:page-size="pageSize" :data="rows" :columns="columns" :loading="loading" :total="total" row-key="id" preference-key="customs-cases" empty-text="暂无报关协同记录"><template #actions="{ row }"><el-button v-if="access.can('platform:firstleg:customs:view')" link type="primary" @click="openDetail(row.id)"><Eye :size="15" />查看</el-button></template><template #status="{ row }"><StatusBadge :status="customsStatusLabel(row.status)" /></template></DataTable></section></ListPageLayout>
<AppDialog v-model="detailOpen" title="报关协同详情" width="900px" :show-confirm="false"><LoadErrorState v-if="actionError && !detail" title="详情加载失败" :message="actionError.message" :loading="detailLoading" :show-retry="false"><template #details><ApiErrorMeta :details="actionError" /></template></LoadErrorState><template v-if="detail"><el-alert v-if="actionError" type="error" :closable="false"><ApiErrorMeta :details="actionError" /></el-alert><el-descriptions :column="2" border><el-descriptions-item label="报关编号">{{ detail.customsCase.customsNo }}</el-descriptions-item><el-descriptions-item label="状态"><StatusBadge :status="customsStatusLabel(detail.customsCase.status)" /></el-descriptions-item><el-descriptions-item label="资料清单">{{ detail.customsCase.checklistCode }} / {{ detail.customsCase.checklistVersionNo }}</el-descriptions-item><el-descriptions-item label="资料/回执">{{ detail.documents.length }} / {{ detail.receipts.length }}</el-descriptions-item></el-descriptions><el-form v-if="canSubmit" label-position="top" class="submit-form"><el-row :gutter="12"><el-col :xs="24" :sm="12"><el-form-item label="报关代理"><el-input v-model="submitForm.customsAgentName" placeholder="请输入线下报关代理名称" /></el-form-item></el-col><el-col :xs="24" :sm="12"><el-form-item label="外部参考号"><el-input v-model="submitForm.externalReference" placeholder="请输入代理回传的参考号(可选)" /></el-form-item></el-col><el-col :xs="24"><el-form-item label="提交备注"><el-input v-model="submitForm.note" placeholder="请输入本次提交说明(可选)" /></el-form-item></el-col></el-row><el-button type="primary" @click="submit"><Send :size="16" />登记线下提交</el-button></el-form><h3>状态历史</h3><el-timeline><el-timeline-item v-for="entry in detail.history" :key="entry.id" :timestamp="entry.createdAt">{{ customsHistoryActionLabel(entry.action) }}:{{ customsStatusLabel(entry.fromStatus) }} → {{ customsStatusLabel(entry.toStatus) }}<small>{{ entry.note || '' }}</small></el-timeline-item></el-timeline></template></AppDialog></AdminPage></template>
<style scoped>.submit-form{margin-top:18px}.submit-form+.el-button{margin-bottom:12px}h3{margin:20px 0 12px}small{display:block;color:var(--cc-muted)}</style>
<style scoped>.submit-form{margin-top:18px}.submit-form+.el-button{margin-bottom:12px}h3{margin:20px 0 12px}small{display:block;color:var(--cc-muted)}:global(.el-dialog:has(.submit-form,.el-descriptions)){width:min(900px,calc(100vw - 28px))!important;max-height:90vh;overflow:hidden}:global(.el-dialog:has(.submit-form,.el-descriptions) .el-dialog__body){max-height:calc(90vh - 120px);overflow-y:auto}</style>
@@ -101,6 +101,9 @@ describe('FinanceCustomerArapView G2 operations', () => {
{ poolItemId: 'PAYABLE:43', feeSourceId: null, customerId: '10002', direction: 'PAYABLE', sourceType: 'MANUAL_COST', sourceId: '43', sourceVersion: 1, orderId: '11', inboundId: null, containerId: null, feeItemName: '拖车费', amount: 200, currency: 'CNY', status: 'POSTED', createdAt: '2026-08-10' },
])
const wrapper = await mountView(['platform:finance:bill:view'])
const vm = wrapper.vm as unknown as { load(model: Record<string, string>): Promise<void> }
await vm.load({ customerId: '', direction: '', status: '', keyword: '' })
await flushPromises()
expect(wrapper.text()).toContain('客户A')
expect(wrapper.text()).toContain('客户B')
})
@@ -145,4 +145,11 @@ describe('OutboundDispatchesView', () => {
expect(source).toContain("error.value?.code === 'A0409'")
expect(getOutboundDispatchDetail).not.toHaveBeenCalled()
})
it('keeps the gate detail dialog responsive with internal scrolling', () => {
const source = readFileSync(resolve(process.cwd(), 'src/modules/outbound-dispatch/pages/OutboundDispatchesView.vue'), 'utf8')
expect(source).toContain('width="min(900px, calc(100vw - 28px))"')
expect(source).toContain('max-height: 90vh')
expect(source).toContain('overflow-y: auto')
})
})
@@ -194,7 +194,7 @@ onBeforeUnmount(() => { disposed = true; listRequestId += 1; optionRequestId +=
</DataTable>
</section>
</ListPageLayout>
<AppDialog v-model="detailOpen" title="国内出仓详情" width="900px" :show-confirm="false">
<AppDialog v-model="detailOpen" class="outbound-detail-dialog" title="国内出仓详情" width="min(900px, calc(100vw - 28px))" :show-confirm="false">
<LoadErrorState v-if="actionError" title="出仓详情加载失败" :message="actionError.message" :loading="detailLoading" @retry="retryDetail"><template #details><ApiErrorMeta :details="actionError" /></template></LoadErrorState>
<div v-else-if="detailLoading" class="detail-loading" aria-busy="true"><el-skeleton :rows="5" animated /></div>
<template v-else-if="detail"><el-descriptions :column="2" border><el-descriptions-item label="订单号">{{ detail.aggregate.orderNo }}</el-descriptions-item><el-descriptions-item label="入库单号">{{ detail.aggregate.inboundCode }}</el-descriptions-item><el-descriptions-item label="柜号">{{ detail.aggregate.containerNo || '—' }}</el-descriptions-item><el-descriptions-item label="出仓状态"><StatusBadge :status="outboundDispatchStatusLabel(detail.aggregate.dispatchStatus)" /></el-descriptions-item></el-descriptions><h3>统一门禁</h3><el-table :data="detail.aggregate.gate?.items || []"><el-table-column label="结果" width="80"><template #default="scope"><el-tag :type="scope.row.passed ? 'success' : 'danger'">{{ scope.row.passed ? '通过' : '阻断' }}</el-tag></template></el-table-column><el-table-column prop="name" label="门禁" min-width="140" /><el-table-column prop="actual" label="当前情况" min-width="180" /><el-table-column prop="requirement" label="要求" min-width="180" /></el-table><h3>整体时间线</h3><el-timeline><el-timeline-item v-for="entry in detail.timeline" :key="`${entry.module}-${entry.occurredAt}-${entry.action}`" :timestamp="entry.occurredAt">{{ timelineModuleLabel(entry.module) }} · {{ timelineActionLabel(entry.action) }}:{{ timelineStatusLabel(entry.module, entry.fromStatus) }} → {{ timelineStatusLabel(entry.module, entry.toStatus) }}<small>{{ entry.operatorName || '系统' }} {{ entry.remark || '' }}</small></el-timeline-item></el-timeline></template>
@@ -206,4 +206,6 @@ onBeforeUnmount(() => { disposed = true; listRequestId += 1; optionRequestId +=
h3 { margin: 20px 0 12px; }
small { display: block; color: var(--cc-muted); }
.detail-loading { padding: 12px 0; }
:global(.outbound-detail-dialog.el-dialog) { max-height: 90vh; overflow: hidden; }
:global(.outbound-detail-dialog .el-dialog__body) { max-height: calc(90vh - 120px); overflow-y: auto; }
</style>
@@ -67,4 +67,10 @@ describe('PortSchedulesView', () => {
expect(source).toContain("prop: 'schedule.etaAt'")
expect(source).toContain("prop: 'schedule.updatedAt'")
})
it('formats observed and update times and keeps the editor within the viewport', () => {
expect(source).toContain('formatDateTime(row.schedule.observedAt)')
expect(source).toContain('formatDateTime(row.schedule.updatedAt)')
expect(source).toContain('width:min(820px,calc(100vw - 28px))!important')
expect(source).toContain('max-height:90vh')
})
})
@@ -112,4 +112,11 @@ describe('TruckAppointmentsView', () => {
await flushPromises()
expect(wrapper.find('button[aria-label="查看 TA-9001"]').exists()).toBe(false)
})
it('prioritizes outbound business fields and keeps finance amounts out of the list', () => {
expect(source).toContain("key: 'customerName'")
expect(source).toContain("key: 'updatedAt'")
expect(source).not.toContain("key: 'payableAmount'")
expect(source).not.toContain("key: 'receivableAmount'")
})
})
@@ -28,15 +28,15 @@ const columns: DataTableColumn[] = [
{ key: 'actions', label: '操作', width: 150, fixed: 'left', slotName: 'actions' },
{ key: 'appointmentNo', prop: 'appointmentNo', label: '派车单号', minWidth: 170 },
{ key: 'containerNo', prop: 'containerNo', label: '集装箱号', minWidth: 145, formatter: (row: TruckAppointmentResource) => row.containerNo || (row.containerBookingId ? '已关联柜次' : '—') },
{ key: 'customerName', prop: 'customerName', label: '客户', minWidth: 150, formatter: (row: TruckAppointmentResource) => row.customerName || '客户信息缺失' },
{ key: 'company', label: '运输公司', minWidth: 160, slotName: 'company' },
{ key: 'driver', label: '司机', minWidth: 130, slotName: 'driver' },
{ key: 'vehicleNo', prop: 'vehicleNo', label: '车头号', minWidth: 120, formatter: (row: TruckAppointmentResource) => row.vehicleNo || '—' },
{ key: 'store', label: '门店', minWidth: 140, slotName: 'store' },
{ key: 'payableAmount', label: '应付拖车费', minWidth: 130, formatter: (row: TruckAppointmentResource) => row.payableAmount ? `¥${row.payableAmount}` : '—' },
{ key: 'receivableAmount', label: '应收拖车费', minWidth: 130, formatter: (row: TruckAppointmentResource) => row.receivableAmount ? `¥${row.receivableAmount}` : '—' },
{ key: 'plannedLoadingAt', label: '预计装箱日期', minWidth: 170, formatter: (row: TruckAppointmentResource) => formatDateTime(row.plannedLoadingAt) },
{ key: 'status', label: '状态', width: 130, slotName: 'status' },
{ key: 'attachments', label: '附件', width: 90, formatter: (row: TruckAppointmentResource) => String(row.attachments?.length || 0) },
{ key: 'updatedAt', label: '更新时间', minWidth: 160, formatter: (row: TruckAppointmentResource) => formatDateTime(row.updatedAt) },
]
function formatDateTime(value?: string | null) { return value ? value.replace('T', ' ').slice(0, 16) : '—' }
function appointmentActionLabel(action: '查看' | '编辑', row: TruckAppointmentResource) {