feat(admin): streamline container identity workflows
This commit is contained in:
@@ -17,6 +17,8 @@ const props = defineProps<{
|
||||
detail: LoadingDetail | null
|
||||
error: ApiErrorDetails | null
|
||||
canEdit: boolean
|
||||
canEditTransport?: boolean
|
||||
transportNote?: string
|
||||
confirmSaving?: boolean
|
||||
}>()
|
||||
|
||||
@@ -125,7 +127,7 @@ function formatNumber(value?: number | null, digits = 3) {
|
||||
</div>
|
||||
<div v-if="detail && summary" class="detail-page-header__actions">
|
||||
<template v-if="canEditItemRemark">
|
||||
<el-button @click="emit('edit')"><Pencil :size="16" />{{ preloadSummary?.containerNo ? '修改集装箱号 / 航次' : '录入集装箱号' }}</el-button>
|
||||
<el-button v-if="canEditTransport" @click="emit('edit')"><Pencil :size="16" />纠正集装箱号 / 航次</el-button>
|
||||
<el-button type="primary" :loading="confirmSaving" :disabled="!preloadSummary?.readiness.canConfirm" @click="emit('confirmPreload')"><Check :size="16" />确认预装</el-button>
|
||||
</template>
|
||||
</div>
|
||||
@@ -156,6 +158,7 @@ function formatNumber(value?: number | null, digits = 3) {
|
||||
<strong>暂不能确认预装:</strong>
|
||||
<span>{{ blockingReasonText || '请刷新详情获取最新确认条件' }}</span>
|
||||
</div>
|
||||
<div v-if="preloadSummary" class="readiness-note transport-note" role="status"><strong>真实集装箱号:</strong><span>{{ transportNote }}</span></div>
|
||||
|
||||
<section class="detail-section">
|
||||
<DataTable
|
||||
|
||||
@@ -96,8 +96,9 @@ describe('LoadingListsView', () => {
|
||||
it('keeps transport editing as a detail-page action', () => {
|
||||
expect(source).not.toContain('openEditFromRow(row)')
|
||||
expect(source).not.toContain('编辑运输信息')
|
||||
expect(detailSource).toContain("{{ preloadSummary?.containerNo ? '修改集装箱号 / 航次' : '录入集装箱号' }}")
|
||||
expect(preloadDetailSource).toContain("const editDialogTitle = computed(() => detail.value?.summary.containerNo ? '修改集装箱号 / 航次' : '录入集装箱号')")
|
||||
expect(detailSource).toContain('纠正集装箱号 / 航次')
|
||||
expect(detailSource).toContain('v-if="canEditTransport"')
|
||||
expect(preloadDetailSource).toContain("const editDialogTitle = computed(() => '纠正集装箱号 / 航次')")
|
||||
})
|
||||
|
||||
it('refreshes the detail version on A0409 while preserving transport input', () => {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
export * from './api/platformLoadingLists'
|
||||
export { loadingListRoutes } from './routes'
|
||||
export { loadingListStatusLabel } from './status'
|
||||
export { default as ContainerFeesPanel } from './components/ContainerFeesPanel.vue'
|
||||
|
||||
@@ -49,11 +49,20 @@ describe('PreloadPlanDetailView prototype contract', () => {
|
||||
expect(source).toContain(':confirm-loading="remarkSaving"')
|
||||
})
|
||||
|
||||
it('is the formal container-number entry and does not imply that preload directly opens PDA', () => {
|
||||
it('keeps preload as a correction-only entry before PDA claim and does not imply that preload directly opens PDA', () => {
|
||||
expect(source).toContain("{ required: true, message: '请输入集装箱号'")
|
||||
expect(source).toContain("detail.value?.summary.containerNo ? '修改集装箱号 / 航次' : '录入集装箱号'")
|
||||
expect(source).toContain('成功后由服务端按真实条件创建唯一 PDA 任务')
|
||||
expect(source).toContain('当前页面不会伪造 PDA 成功')
|
||||
expect(source).toContain("summary.status === 'EDITING'")
|
||||
expect(source).toContain("(!pdaStatus || pdaStatus === 'PENDING')")
|
||||
expect(source).toContain('PDA 已领取或作业已开始,真实集装箱号只读。')
|
||||
expect(source).toContain("const editDialogTitle = computed(() => '纠正集装箱号 / 航次')")
|
||||
expect(source).toContain("{ required: true, message: '请输入操作原因'")
|
||||
expect(source).toContain("String(value || '').trim().toUpperCase()")
|
||||
expect(source).toContain('operationReason: editForm.operationReason.trim()')
|
||||
expect(detailSource).toContain('v-if="canEditTransport"')
|
||||
expect(detailSource).not.toContain("preloadSummary?.containerNo ? '修改集装箱号 / 航次' : '录入集装箱号'")
|
||||
expect(source).toContain('由服务端按真实条件创建唯一 PDA 任务')
|
||||
expect(source).toContain('真实集装箱号未录入时不能确认')
|
||||
expect(source).toContain('PDA 领取或作业开始后集装箱号只读')
|
||||
})
|
||||
|
||||
it('uses a flat blocking note instead of repeated colored page alerts', () => {
|
||||
|
||||
@@ -37,16 +37,34 @@ let loadSequence = 0
|
||||
const planId = computed(() => String(route.params.planId || ''))
|
||||
const warehouseId = computed(() => typeof route.query.warehouseId === 'string' ? route.query.warehouseId : '')
|
||||
const canEdit = computed(() => access.can('platform:firstleg:preload:edit'))
|
||||
const editDialogTitle = computed(() => detail.value?.summary.containerNo ? '修改集装箱号 / 航次' : '录入集装箱号')
|
||||
const canCorrectTransport = computed(() => {
|
||||
const summary = detail.value?.summary
|
||||
const pdaStatus = summary?.readiness.pdaTaskStatus
|
||||
return Boolean(canEdit.value && summary?.containerNo && summary.status === 'EDITING'
|
||||
&& summary.readiness.preloadPushed && (!pdaStatus || pdaStatus === 'PENDING'))
|
||||
})
|
||||
const transportCorrectionNote = computed(() => {
|
||||
const summary = detail.value?.summary
|
||||
if (!summary?.containerNo) return '真实集装箱号尚未录入;请返回货柜管理,通过“关联已放舱订舱”完成初次录入。'
|
||||
if (summary.readiness.pdaTaskStatus && summary.readiness.pdaTaskStatus !== 'PENDING') return 'PDA 已领取或作业已开始,真实集装箱号只读。'
|
||||
if (!canCorrectTransport.value) return '当前服务端阶段不允许纠正真实集装箱号。'
|
||||
return '真实集装箱号仅可在 PDA 领取前纠错,保存时必须填写操作原因。'
|
||||
})
|
||||
const editDialogTitle = computed(() => '纠正集装箱号 / 航次')
|
||||
const editRules: FormRules = {
|
||||
containerNo: [
|
||||
{ required: true, message: '请输入集装箱号', trigger: 'blur' },
|
||||
{ max: 64, message: '集装箱号不能超过 64 个字符', trigger: 'blur' },
|
||||
],
|
||||
voyageNo: [{ max: 64, message: '航次不能超过 64 个字符', trigger: 'blur' }],
|
||||
operationReason: [{ max: 500, message: '修改原因不能超过 500 个字符', trigger: 'blur' }],
|
||||
operationReason: [
|
||||
{ required: true, message: '请输入操作原因', trigger: 'blur' },
|
||||
{ max: 500, message: '操作原因不能超过 500 个字符', trigger: 'blur' },
|
||||
],
|
||||
}
|
||||
|
||||
function normalizedContainerNo(value: string | null | undefined) { return String(value || '').trim().toUpperCase() }
|
||||
|
||||
async function loadDetail() {
|
||||
const sequence = ++loadSequence
|
||||
detail.value = null
|
||||
@@ -80,7 +98,7 @@ function goBack() {
|
||||
}
|
||||
|
||||
function openEdit() {
|
||||
if (!detail.value || !['EDITING', 'LOADING'].includes(detail.value.summary.status)) return
|
||||
if (!detail.value || !canCorrectTransport.value) return
|
||||
editForm.containerNo = detail.value.summary.containerNo || ''
|
||||
editForm.voyageNo = detail.value.summary.voyageNo || ''
|
||||
editForm.operationReason = ''
|
||||
@@ -89,14 +107,15 @@ function openEdit() {
|
||||
}
|
||||
|
||||
async function saveTransport() {
|
||||
if (!detail.value || !(await editFormRef.value?.validate().catch(() => false))) return
|
||||
if (!detail.value || !canCorrectTransport.value || !(await editFormRef.value?.validate().catch(() => false))) return
|
||||
editForm.containerNo = normalizedContainerNo(editForm.containerNo)
|
||||
editSaving.value = true
|
||||
editError.value = null
|
||||
try {
|
||||
detail.value = await updatePreloadTransport(planId.value, warehouseId.value, {
|
||||
containerNo: editForm.containerNo.trim(),
|
||||
containerNo: editForm.containerNo,
|
||||
voyageNo: editForm.voyageNo.trim() || null,
|
||||
operationReason: editForm.operationReason.trim() || null,
|
||||
operationReason: editForm.operationReason.trim(),
|
||||
expectedVersion: detail.value.summary.version,
|
||||
})
|
||||
editOpen.value = false
|
||||
@@ -196,14 +215,14 @@ watch([planId, warehouseId], () => void loadDetail())
|
||||
<AdminPage>
|
||||
<div class="preload-detail-page">
|
||||
<OperationErrorToast :details="remarkError || editError || confirmError" />
|
||||
<LoadingListDetailDrawer :mode="'preload'" :loading="loading" :detail="detail" :error="error" :can-edit="canEdit" :confirm-saving="confirmSaving" @back="goBack" @retry="loadDetail" @edit="openEdit" @edit-warehouse-remark="openWarehouseRemark" @confirm-preload="openConfirmPreload" />
|
||||
<LoadingListDetailDrawer :mode="'preload'" :loading="loading" :detail="detail" :error="error" :can-edit="canEdit" :can-edit-transport="canCorrectTransport" :transport-note="transportCorrectionNote" :confirm-saving="confirmSaving" @back="goBack" @retry="loadDetail" @edit="openEdit" @edit-warehouse-remark="openWarehouseRemark" @confirm-preload="openConfirmPreload" />
|
||||
</div>
|
||||
|
||||
<AppDialog v-model="editOpen" class="loading-transport-dialog" :title="editDialogTitle" width="min(620px, calc(100vw - 28px))" confirm-text="保存" :confirm-loading="editSaving" :confirm-disabled="!canEdit" @confirm="saveTransport">
|
||||
<el-form ref="editFormRef" class="transport-form" :model="editForm" :rules="editRules" label-position="top">
|
||||
<el-form-item label="集装箱号" prop="containerNo"><el-input v-model="editForm.containerNo" maxlength="64" placeholder="请输入集装箱号" /></el-form-item>
|
||||
<el-form-item label="真实集装箱号" prop="containerNo"><el-input v-model="editForm.containerNo" maxlength="64" placeholder="请输入港口取得的真实集装箱号" @blur="editForm.containerNo = normalizedContainerNo(editForm.containerNo)" /></el-form-item>
|
||||
<el-form-item label="航次" prop="voyageNo"><el-input v-model="editForm.voyageNo" maxlength="64" placeholder="请输入航次" /></el-form-item>
|
||||
<el-form-item label="修改原因" prop="operationReason"><el-input v-model="editForm.operationReason" type="textarea" :rows="3" maxlength="500" placeholder="选填,填写后将记录在操作历史中" /></el-form-item>
|
||||
<el-form-item label="操作原因" prop="operationReason"><el-input v-model="editForm.operationReason" type="textarea" :rows="3" maxlength="500" placeholder="必填,将记录在操作历史中" /></el-form-item>
|
||||
</el-form>
|
||||
</AppDialog>
|
||||
|
||||
@@ -219,7 +238,7 @@ watch([planId, warehouseId], () => void loadDetail())
|
||||
|
||||
<AppDialog v-model="confirmOpen" class="preload-confirm-dialog" title="确认整份预装计划" width="min(520px, calc(100vw - 28px))" confirm-text="确认预装" :confirm-loading="confirmSaving" :confirm-disabled="!detail?.summary.readiness.canConfirm" @confirm="submitConfirmPreload">
|
||||
<div class="confirm-content">
|
||||
<el-alert type="warning" :closable="false" show-icon title="本操作将确认整份预装计划" description="系统会原子确认本计划包含的全部入库预装票据;成功后由服务端按真实条件创建唯一 PDA 任务,当前页面不会伪造 PDA 成功。" />
|
||||
<el-alert type="warning" :closable="false" show-icon title="本操作将确认整份预装计划" description="真实集装箱号未录入时不能确认;PDA 领取或作业开始后集装箱号只读。系统会原子确认本计划包含的全部入库预装票据,并由服务端按真实条件创建唯一 PDA 任务;当前页面不会伪造 PDA 成功。" />
|
||||
<el-descriptions v-if="detail" :column="1" border>
|
||||
<el-descriptions-item label="预装单号">{{ detail.summary.preloadNo }}</el-descriptions-item>
|
||||
<el-descriptions-item label="柜次号">{{ detail.summary.cabinetNo || '—' }}</el-descriptions-item>
|
||||
|
||||
Reference in New Issue
Block a user