优化
This commit is contained in:
@@ -12,6 +12,7 @@
|
|||||||
|
|
||||||
| 关键词 | 页面/路由入口 | API 与联查 |
|
| 关键词 | 页面/路由入口 | API 与联查 |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
|
| 仓储转运审核、通过与驳回 | `pages/LastMileOrdersView.vue`、`components/WarehouseTransferReviewDialog.vue` | `api/warehouseTransfers.ts` 的列表、详情及 `/warehouse-transfers/{transferNo}/review`;审核按钮要求 `permissions.ts` 的 `warehouseTransferApprove` 且行类型为 `WAREHOUSE_TRANSFER`、状态为 `PENDING_REVIEW`。请求携带该行客户范围和转运单号,审核结论为 `APPROVE/REJECT`,驳回原因必填;这是尾程订单内的审核弹框,不是独立审核路由或排车转派送入口 |
|
||||||
| 尾程订单控制与柜级入仓计划转换 | `pages/LastMileOrdersView.vue`、`pages/LastMileOrderDetailView.vue` | 柜子工作台按 `sourceContainerBookingId` 消费 `/orders/container-summaries`;服务端分页返回全部稳定来源柜,计划已经齐全的柜仍保留用于接柜仓查询和修改,并按冻结 `plannedArrivalAt` 升序、空值最后排序。详情返回全柜订单且不受列表分页截断;单票手工计划保存后,由服务端在整柜最后一票计划齐全时自动生成并绑定全柜托盘码,一票柜立即生成;`resourcePendingPlanCount` 只表示自动生成异常的重试入口,不是正常人工步骤。柜级确认弹框先调用 `/inbound-plans/container-preview`,按服务端 FBA/一件代发/私人地址分组展示头程已有、海外仓补充和最终托数以及 `readiness/blockers`;最终托数不得少于头程已有托盘码,差额连续补码且不阻断流程。前端不计算托数、不逐票建计划、不预占托盘,再把同一 `snapshotVersion/snapshotHash` 原样提交 `/inbound-plans/convert-container`,快照冲突后重新获取预览。服务端只为剩余订单逐票生成独立计划,并为异常遗留的人工计划补齐真实托盘,整批原子且重放不得产生第二批资源。单票完整入库计划、暂扣/解除和八行人工节点均从 `api/lastMileOrders.ts` 提交订单版本与幂等键;入仓码只读展示并提交订单冻结值,不允许人工改写,暂扣资格、接柜仓锁定原因和操作能力只消费服务端结果,人工节点一经保存不得回退柜级节点。转仓候选只取 `../iam/api/platformIam.ts` 的平台公共 `ACTIVE + FBA` 仓主档;Backend `lastleg-service/` |
|
| 尾程订单控制与柜级入仓计划转换 | `pages/LastMileOrdersView.vue`、`pages/LastMileOrderDetailView.vue` | 柜子工作台按 `sourceContainerBookingId` 消费 `/orders/container-summaries`;服务端分页返回全部稳定来源柜,计划已经齐全的柜仍保留用于接柜仓查询和修改,并按冻结 `plannedArrivalAt` 升序、空值最后排序。详情返回全柜订单且不受列表分页截断;单票手工计划保存后,由服务端在整柜最后一票计划齐全时自动生成并绑定全柜托盘码,一票柜立即生成;`resourcePendingPlanCount` 只表示自动生成异常的重试入口,不是正常人工步骤。柜级确认弹框先调用 `/inbound-plans/container-preview`,按服务端 FBA/一件代发/私人地址分组展示头程已有、海外仓补充和最终托数以及 `readiness/blockers`;最终托数不得少于头程已有托盘码,差额连续补码且不阻断流程。前端不计算托数、不逐票建计划、不预占托盘,再把同一 `snapshotVersion/snapshotHash` 原样提交 `/inbound-plans/convert-container`,快照冲突后重新获取预览。服务端只为剩余订单逐票生成独立计划,并为异常遗留的人工计划补齐真实托盘,整批原子且重放不得产生第二批资源。单票完整入库计划、暂扣/解除和八行人工节点均从 `api/lastMileOrders.ts` 提交订单版本与幂等键;入仓码只读展示并提交订单冻结值,不允许人工改写,暂扣资格、接柜仓锁定原因和操作能力只消费服务端结果,人工节点一经保存不得回退柜级节点。转仓候选只取 `../iam/api/platformIam.ts` 的平台公共 `ACTIVE + FBA` 仓主档;Backend `lastleg-service/` |
|
||||||
| 入仓单、入仓计划、FBA 预约 | `pages/InboundOrdersView.vue`、`pages/InboundOrderDetailView.vue` | 列表/预约 `api/inboundOrders.ts`,计划列表/详情 `api/inboundPlans.ts`;Backend `lastleg-service/inboundplan/` |
|
| 入仓单、入仓计划、FBA 预约 | `pages/InboundOrdersView.vue`、`pages/InboundOrderDetailView.vue` | 列表/预约 `api/inboundOrders.ts`,计划列表/详情 `api/inboundPlans.ts`;Backend `lastleg-service/inboundplan/` |
|
||||||
| 排车查询、智能预排与转派送 | `pages/DispatchesView.vue`、`pages/DispatchDetailView.vue`、`components/SmartDispatchDialog.vue` | `api/dispatches.ts`;Backend `lastleg-service/dispatch/`、`delivery/`;覆盖详情、候选、草稿柜、托盘锁、`pre-arrange` 和 `transfer-delivery`。页面能力必须由正式路由、API 调用和测试共同证明 |
|
| 排车查询、智能预排与转派送 | `pages/DispatchesView.vue`、`pages/DispatchDetailView.vue`、`components/SmartDispatchDialog.vue` | `api/dispatches.ts`;Backend `lastleg-service/dispatch/`、`delivery/`;覆盖详情、候选、草稿柜、托盘锁、`pre-arrange` 和 `transfer-delivery`。页面能力必须由正式路由、API 调用和测试共同证明 |
|
||||||
@@ -27,5 +28,7 @@
|
|||||||
|
|
||||||
## 验证
|
## 验证
|
||||||
|
|
||||||
|
- 转运审核定向测试:从应用根运行 `pnpm test src/modules/overseas-warehouse/api/warehouseTransfers.test.ts src/modules/overseas-warehouse/pages/prototypeContract.test.ts`,分别核对客户作用域/API 与审核入口权限、状态。
|
||||||
|
- 排车列表编辑、托盘移除、BOL PDF 和转派送从 `pages/DispatchesView.vue` → `api/dispatches.ts` 进入;智能预排的候选、草稿柜与托盘锁从 `components/SmartDispatchDialog.vue` 进入。回归为 `pages/DispatchesView.test.ts`、`pages/prototypeContract.test.ts`,不要把列表操作与预排草稿当作同一入口。
|
||||||
- 从应用根按目标页面选择相邻测试:`pnpm test src/modules/overseas-warehouse/<目标测试路径>`;API 或类型变化同时运行 `pnpm typecheck`。应用完整门禁继续遵循上层指引。
|
- 从应用根按目标页面选择相邻测试:`pnpm test src/modules/overseas-warehouse/<目标测试路径>`;API 或类型变化同时运行 `pnpm typecheck`。应用完整门禁继续遵循上层指引。
|
||||||
- 索引变更从后端根运行 `./scripts/agents-index-contract.ps1`。离线检查不代替真实 HTTP 和人工截图验收。
|
- 索引变更从后端根运行 `./scripts/agents-index-contract.ps1`。离线检查不代替真实 HTTP 和人工截图验收。
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, nextTick, onBeforeUnmount, onMounted, reactive, ref, watch, type Component } from 'vue'
|
import { computed, nextTick, onBeforeUnmount, onMounted, reactive, ref, watch, type Component } from 'vue'
|
||||||
import { ElMessage, ElMessageBox, type UploadFile } from 'element-plus'
|
import { ElMessage, ElMessageBox, type UploadFile } from 'element-plus'
|
||||||
import { ArrowLeft, Box, ChevronDown, ChevronUp, CircleCheck, Download, Eye, FileUp, GitBranch, History, Link2, ListChecks, MoreHorizontal, PackageOpen, Printer, Ruler, Send, ShieldAlert, SlidersHorizontal, StickyNote, Undo2, WalletCards } from '@lucide/vue'
|
import { ArrowLeft, ChevronDown, ChevronUp, CircleCheck, Download, Eye, FileUp, GitBranch, Link2, MoreHorizontal, PackageOpen, Printer, Ruler, Send, ShieldAlert, SlidersHorizontal, StickyNote, Undo2 } from '@lucide/vue'
|
||||||
import { useRoute, useRouter } from 'vue-router'
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
import {
|
import {
|
||||||
getPlatformUnifiedOrder,
|
getPlatformUnifiedOrder,
|
||||||
@@ -622,7 +622,7 @@ const queryFields = computed(() => showAdvancedFilters.value
|
|||||||
|
|
||||||
const columns = computed<DataTableColumn[]>(() => [
|
const columns = computed<DataTableColumn[]>(() => [
|
||||||
// 业务表格保持操作列第一;批量选择列仅在有对应权限时紧随其后。
|
// 业务表格保持操作列第一;批量选择列仅在有对应权限时紧随其后。
|
||||||
{ key: 'actions', label: '操作', width: 290, fixed: 'left', slotName: 'actions' },
|
{ key: 'actions', label: '操作', width: 260, fixed: 'left', slotName: 'actions' },
|
||||||
...(can('platform:order:approve') || can('platform:firstleg:smart-loading:assign') || can('platform:order:export') || can('platform:firstleg:inbound:view') ? [{
|
...(can('platform:order:approve') || can('platform:firstleg:smart-loading:assign') || can('platform:order:export') || can('platform:firstleg:inbound:view') ? [{
|
||||||
key: 'selection', type: 'selection', width: 48, fixed: 'left',
|
key: 'selection', type: 'selection', width: 48, fixed: 'left',
|
||||||
selectable: () => !batchApprovalSaving.value && !coLoadSaving.value && !exportSubmitting.value,
|
selectable: () => !batchApprovalSaving.value && !coLoadSaving.value && !exportSubmitting.value,
|
||||||
@@ -2199,7 +2199,7 @@ onBeforeUnmount(() => {
|
|||||||
<template>
|
<template>
|
||||||
<AdminPage>
|
<AdminPage>
|
||||||
<section class="business-page unified-orders-page">
|
<section class="business-page unified-orders-page">
|
||||||
<AppDialog v-model="feeConfirmOpen" title="编辑计费数据" confirm-text="确认并发送" :confirm-loading="confirmFeeSending || feeConfirmReferenceLoading" width="min(920px, 94vw)" :teleported="false" @confirm="executeConfirmAndSendFee">
|
<AppDialog class="order-density-dialog" v-model="feeConfirmOpen" title="编辑计费数据" confirm-text="确认并发送" :confirm-loading="confirmFeeSending || feeConfirmReferenceLoading" width="min(920px, 94vw)" :teleported="false" @confirm="executeConfirmAndSendFee">
|
||||||
<el-alert class="fee-confirm-price-alert" title="价格表单价仅作为参考;本次对客单价可人工修改,客户将一次确认计费量、单价和费用合计。" type="info" :closable="false" show-icon />
|
<el-alert class="fee-confirm-price-alert" title="价格表单价仅作为参考;本次对客单价可人工修改,客户将一次确认计费量、单价和费用合计。" type="info" :closable="false" show-icon />
|
||||||
<el-form label-position="top" class="fee-confirm-form">
|
<el-form label-position="top" class="fee-confirm-form">
|
||||||
<el-form-item label="入仓码"><el-input :model-value="detail?.summary.inboundCode || '—'" disabled /></el-form-item>
|
<el-form-item label="入仓码"><el-input :model-value="detail?.summary.inboundCode || '—'" disabled /></el-form-item>
|
||||||
@@ -2219,7 +2219,7 @@ onBeforeUnmount(() => {
|
|||||||
<el-table :data="feeConfirmRows" border size="small">
|
<el-table :data="feeConfirmRows" border size="small">
|
||||||
<el-table-column prop="source" label="数据来源" />
|
<el-table-column prop="source" label="数据来源" />
|
||||||
<el-table-column prop="count" label="件数" />
|
<el-table-column prop="count" label="件数" />
|
||||||
<el-table-column label="总毛重(T.K.GW)"><template #default="{ row }">{{ row.weight == null ? '—' : formatNumber(row.weight, 2) }}</template></el-table-column>
|
<el-table-column label="总毛重(T.K.GW)" align="right"><template #default="{ row }">{{ row.weight == null ? '—' : formatNumber(row.weight, 2) }}</template></el-table-column>
|
||||||
<el-table-column label="总体积(m³)"><template #default="{ row }">{{ row.volume == null ? '—' : formatNumber(row.volume, 4) }}</template></el-table-column>
|
<el-table-column label="总体积(m³)"><template #default="{ row }">{{ row.volume == null ? '—' : formatNumber(row.volume, 4) }}</template></el-table-column>
|
||||||
<el-table-column label="总材积重"><template #default="{ row }">{{ row.volume == null ? '—' : formatNumber(Number(row.volume) * 167, 2) }}</template></el-table-column>
|
<el-table-column label="总材积重"><template #default="{ row }">{{ row.volume == null ? '—' : formatNumber(Number(row.volume) * 167, 2) }}</template></el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
@@ -2229,7 +2229,6 @@ onBeforeUnmount(() => {
|
|||||||
<header class="order-detail-header">
|
<header class="order-detail-header">
|
||||||
<div class="order-detail-heading">
|
<div class="order-detail-heading">
|
||||||
<el-button link class="back-button" @click="fromMeasurementReview ? returnToMeasurementReviews() : closeDetail()"><ArrowLeft :size="16" />{{ fromMeasurementReview ? '返回测量审核列表' : '返回列表' }}</el-button>
|
<el-button link class="back-button" @click="fromMeasurementReview ? returnToMeasurementReviews() : closeDetail()"><ArrowLeft :size="16" />{{ fromMeasurementReview ? '返回测量审核列表' : '返回列表' }}</el-button>
|
||||||
<span class="order-mark" aria-hidden="true"><Box :size="19" /></span>
|
|
||||||
<div class="order-title">
|
<div class="order-title">
|
||||||
<h1>{{ detail?.summary.inboundCode || '入仓码暂缺' }}</h1>
|
<h1>{{ detail?.summary.inboundCode || '入仓码暂缺' }}</h1>
|
||||||
</div>
|
</div>
|
||||||
@@ -2247,12 +2246,15 @@ onBeforeUnmount(() => {
|
|||||||
|
|
||||||
<div v-else v-loading="detailLoading" class="order-detail-content">
|
<div v-else v-loading="detailLoading" class="order-detail-content">
|
||||||
<template v-if="detail">
|
<template v-if="detail">
|
||||||
<section class="detail-overview" aria-label="订单概览">
|
<dl class="detail-overview" aria-label="订单概览">
|
||||||
<article><span>客户</span><strong>{{ customerName(detail.summary) }}</strong><small>{{ detail.summary.channelName || '未配置渠道' }}</small></article>
|
<div><dt>客户</dt><dd>{{ customerName(detail.summary) }}</dd></div>
|
||||||
<article><span>总箱数</span><strong>{{ detail.summary.totalPackageCount ?? '—' }}<small>箱</small></strong><small>{{ detail.order.cargoes.length }} 条货品明细</small></article>
|
<div><dt>渠道</dt><dd>{{ detail.summary.channelName || '未配置渠道' }}</dd></div>
|
||||||
<article><span>实测毛重</span><strong>{{ formatNumber(detail.summary.measurement?.actualGrossWeightKg) }}<small>kg</small></strong><small>{{ measurementStatusLabel(detail.summary.measurement?.reviewStatus) }}</small></article>
|
<div><dt>总箱数</dt><dd>{{ detail.summary.totalPackageCount ?? '—' }} 箱 <small>({{ detail.order.cargoes.length }} 条货品)</small></dd></div>
|
||||||
<article><span>实测体积</span><strong>{{ formatNumber(detail.summary.measurement?.actualVolumeCbm, 3) }}<small>m³</small></strong><small>最近更新 {{ formatDate(detail.summary.updatedAt) }}</small></article>
|
<div><dt>实测毛重</dt><dd>{{ formatNumber(detail.summary.measurement?.actualGrossWeightKg) }} kg</dd></div>
|
||||||
</section>
|
<div><dt>实测体积</dt><dd>{{ formatNumber(detail.summary.measurement?.actualVolumeCbm, 3) }} m³</dd></div>
|
||||||
|
<div><dt>测量审核</dt><dd>{{ measurementStatusLabel(detail.summary.measurement?.reviewStatus) }}</dd></div>
|
||||||
|
<div><dt>最近更新</dt><dd>{{ formatDate(detail.summary.updatedAt) }}</dd></div>
|
||||||
|
</dl>
|
||||||
|
|
||||||
<div class="detail-tabs" role="tablist" aria-label="订单详情模块">
|
<div class="detail-tabs" role="tablist" aria-label="订单详情模块">
|
||||||
<button v-for="tab in detailTabs" :id="`order-detail-tab-${tab.key}`" :key="tab.key" type="button" role="tab" :class="{ active: activeDetailTab === tab.key }" :aria-selected="activeDetailTab === tab.key" :aria-controls="`order-detail-panel-${tab.key}`" :tabindex="activeDetailTab === tab.key ? 0 : -1" @click="selectDetailTab(tab.key)"><component :is="tab.icon" :size="17" /><span>{{ tab.label }}</span></button>
|
<button v-for="tab in detailTabs" :id="`order-detail-tab-${tab.key}`" :key="tab.key" type="button" role="tab" :class="{ active: activeDetailTab === tab.key }" :aria-selected="activeDetailTab === tab.key" :aria-controls="`order-detail-panel-${tab.key}`" :tabindex="activeDetailTab === tab.key ? 0 : -1" @click="selectDetailTab(tab.key)"><component :is="tab.icon" :size="17" /><span>{{ tab.label }}</span></button>
|
||||||
@@ -2260,17 +2262,16 @@ onBeforeUnmount(() => {
|
|||||||
|
|
||||||
<section :id="`order-detail-panel-${activeDetailTab}`" class="detail-panel" role="tabpanel" :aria-labelledby="`order-detail-tab-${activeDetailTab}`" tabindex="0">
|
<section :id="`order-detail-panel-${activeDetailTab}`" class="detail-panel" role="tabpanel" :aria-labelledby="`order-detail-tab-${activeDetailTab}`" tabindex="0">
|
||||||
<template v-if="activeDetailTab === 'basic'">
|
<template v-if="activeDetailTab === 'basic'">
|
||||||
<div class="panel-title"><div><h2>订单基本信息</h2><p>{{ detail.summary.inboundCode || '入仓码暂缺' }}</p></div><Box :size="19" /></div>
|
|
||||||
<dl class="order-info-grid">
|
<dl class="order-info-grid">
|
||||||
<div><dt>公司</dt><dd>{{ customerName(detail.summary) }}</dd></div>
|
<div><dt>公司</dt><dd>{{ customerName(detail.summary) }}</dd></div>
|
||||||
<div><dt>运输方式</dt><dd>{{ firstLegTransportModeLabel(detail.order.order?.transportMode) }}</dd></div>
|
<div><dt>运输方式</dt><dd>{{ firstLegTransportModeLabel(detail.order.order?.transportMode) }}</dd></div>
|
||||||
<div><dt>当前状态</dt><dd><StatusBadge :status="statusLabel(detail.summary.mainStatus)" /></dd></div>
|
<div><dt>当前状态</dt><dd><StatusBadge :status="statusLabel(detail.summary.mainStatus)" /></dd></div>
|
||||||
<div><dt>内部订单状态</dt><dd>{{ orderStatusLabel(detail.summary.internalOrderStatus || detail.summary.mainStatus) }}</dd></div>
|
<div><dt>内部订单状态</dt><dd>{{ orderStatusLabel(detail.summary.internalOrderStatus || detail.summary.mainStatus) }}</dd></div>
|
||||||
<div><dt>平台收货仓</dt><dd>{{ detail.summary.receivingWarehouseName || '—' }}</dd></div>
|
<div><dt>平台收货仓</dt><dd>{{ detail.summary.receivingWarehouseName || '—' }}</dd></div>
|
||||||
<div><dt>收货仓地址</dt><dd>{{ detail.order.order?.receivingWarehouseAddress || '—' }}</dd></div>
|
<div class="wide"><dt>收货仓地址</dt><dd>{{ detail.order.order?.receivingWarehouseAddress || '—' }}</dd></div>
|
||||||
<div><dt>目的仓</dt><dd>{{ detail.summary.destinationWarehouseName || '—' }}</dd></div>
|
<div><dt>目的仓</dt><dd>{{ detail.summary.destinationWarehouseName || '—' }}</dd></div>
|
||||||
<div><dt>目的国家</dt><dd>{{ detail.order.order?.destinationCountryCode || '—' }}</dd></div>
|
<div><dt>目的国家</dt><dd>{{ detail.order.order?.destinationCountryCode || '—' }}</dd></div>
|
||||||
<div><dt>目的仓地址</dt><dd>{{ detail.summary.destinationAddress || '—' }}</dd></div>
|
<div class="wide"><dt>目的仓地址</dt><dd>{{ detail.summary.destinationAddress || '—' }}</dd></div>
|
||||||
<div><dt>目的仓邮编</dt><dd>{{ detail.summary.destinationPostalCode || '—' }}</dd></div>
|
<div><dt>目的仓邮编</dt><dd>{{ detail.summary.destinationPostalCode || '—' }}</dd></div>
|
||||||
<div><dt>目的仓联系人</dt><dd>{{ detail.summary.destinationContact || '—' }}</dd></div>
|
<div><dt>目的仓联系人</dt><dd>{{ detail.summary.destinationContact || '—' }}</dd></div>
|
||||||
<div><dt>目的仓联系人电话</dt><dd>{{ detail.summary.destinationContactPhone || '—' }}</dd></div>
|
<div><dt>目的仓联系人电话</dt><dd>{{ detail.summary.destinationContactPhone || '—' }}</dd></div>
|
||||||
@@ -2324,30 +2325,29 @@ onBeforeUnmount(() => {
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template v-else-if="activeDetailTab === 'cargo'">
|
<template v-else-if="activeDetailTab === 'cargo'">
|
||||||
<div class="panel-title"><div><h2>货品信息</h2><p>共 {{ detail.order.cargoes.length }} 条</p></div><el-button v-if="canEditDeclaration" type="primary" size="small" @click="declarationEditorOpen = true">编辑客户申报资料</el-button></div>
|
<div class="panel-title"><p>共 {{ detail.order.cargoes.length }} 条货品</p><el-button v-if="canEditDeclaration" type="primary" size="small" @click="declarationEditorOpen = true">编辑客户申报资料</el-button></div>
|
||||||
<div class="detail-table-wrap">
|
<div class="detail-table-wrap">
|
||||||
<el-table :data="detail.order.cargoes" border stripe empty-text="暂无货品信息">
|
<el-table :data="detail.order.cargoes" border stripe empty-text="暂无货品信息">
|
||||||
<el-table-column prop="itemNo" label="#" width="64" />
|
<el-table-column prop="itemNo" label="#" width="64" />
|
||||||
<el-table-column label="报关HS_CODE" min-width="130"><template #default="{ row }">{{ cargoValue(row as Record<string, unknown>, 'hsCode') }}</template></el-table-column>
|
<el-table-column label="报关HS_CODE" min-width="130"><template #default="{ row }">{{ cargoValue(row as Record<string, unknown>, 'hsCode') }}</template></el-table-column>
|
||||||
<el-table-column label="货品图片" width="110"><template #default="{ row }"><el-image v-if="cargoImage(row as Record<string, unknown>)" :src="cargoImage(row as Record<string, unknown>)" fit="contain" class="cargo-image" /><span v-else>—</span></template></el-table-column>
|
<el-table-column label="货品图片" width="110"><template #default="{ row }"><el-image v-if="cargoImage(row as Record<string, unknown>)" :src="cargoImage(row as Record<string, unknown>)" :preview-src-list="[cargoImage(row as Record<string, unknown>)]" preview-teleported fit="contain" class="cargo-image" /><span v-else>—</span></template></el-table-column>
|
||||||
<el-table-column label="SKU NO." min-width="150"><template #default="{ row }">{{ cargoValue(row as Record<string, unknown>, 'skuNo') }}</template></el-table-column>
|
<el-table-column label="SKU NO." min-width="150"><template #default="{ row }">{{ cargoValue(row as Record<string, unknown>, 'skuNo') }}</template></el-table-column>
|
||||||
<el-table-column label="中文品名" min-width="170"><template #default="{ row }">{{ cargoValue(row as Record<string, unknown>, 'cargoName') }}</template></el-table-column>
|
<el-table-column label="中文品名" min-width="170"><template #default="{ row }">{{ cargoValue(row as Record<string, unknown>, 'cargoName') }}</template></el-table-column>
|
||||||
<el-table-column label="英文品名" min-width="170"><template #default="{ row }">{{ cargoValue(row as Record<string, unknown>, 'cargoNameEn') }}</template></el-table-column>
|
<el-table-column label="英文品名" min-width="170"><template #default="{ row }">{{ cargoValue(row as Record<string, unknown>, 'cargoNameEn') }}</template></el-table-column>
|
||||||
<el-table-column label="清关HS_CODE" min-width="130"><template #default="{ row }">{{ cargoValue(row as Record<string, unknown>, 'customsHsCode') }}</template></el-table-column>
|
<el-table-column label="清关HS_CODE" min-width="130"><template #default="{ row }">{{ cargoValue(row as Record<string, unknown>, 'customsHsCode') }}</template></el-table-column>
|
||||||
<el-table-column label="箱规长(cm)" min-width="110"><template #default="{ row }">{{ cargoValue(row as Record<string, unknown>, 'lengthCm') }}</template></el-table-column>
|
<el-table-column label="箱规长(cm)" align="right" min-width="110"><template #default="{ row }">{{ cargoValue(row as Record<string, unknown>, 'lengthCm') }}</template></el-table-column>
|
||||||
<el-table-column label="箱规宽(cm)" min-width="110"><template #default="{ row }">{{ cargoValue(row as Record<string, unknown>, 'widthCm') }}</template></el-table-column>
|
<el-table-column label="箱规宽(cm)" align="right" min-width="110"><template #default="{ row }">{{ cargoValue(row as Record<string, unknown>, 'widthCm') }}</template></el-table-column>
|
||||||
<el-table-column label="箱规高(cm)" min-width="110"><template #default="{ row }">{{ cargoValue(row as Record<string, unknown>, 'heightCm') }}</template></el-table-column>
|
<el-table-column label="箱规高(cm)" align="right" min-width="110"><template #default="{ row }">{{ cargoValue(row as Record<string, unknown>, 'heightCm') }}</template></el-table-column>
|
||||||
<el-table-column label="箱规周长(cm)" min-width="120"><template #default="{ row }">{{ cargoValue(row as Record<string, unknown>, 'circumferenceCm') }}</template></el-table-column>
|
<el-table-column label="箱规周长(cm)" align="right" min-width="120"><template #default="{ row }">{{ cargoValue(row as Record<string, unknown>, 'circumferenceCm') }}</template></el-table-column>
|
||||||
<el-table-column label="每箱产品数量" min-width="130"><template #default="{ row }">{{ cargoValue(row as Record<string, unknown>, 'unitsPerPackage') }}</template></el-table-column>
|
<el-table-column label="每箱产品数量" align="right" min-width="130"><template #default="{ row }">{{ cargoValue(row as Record<string, unknown>, 'unitsPerPackage') }}</template></el-table-column>
|
||||||
<el-table-column label="箱数" width="90"><template #default="{ row }">{{ cargoValue(row as Record<string, unknown>, 'packageCount') }}</template></el-table-column>
|
<el-table-column label="箱数" align="right" width="90"><template #default="{ row }">{{ cargoValue(row as Record<string, unknown>, 'packageCount') }}</template></el-table-column>
|
||||||
<el-table-column label="总毛重(T.KGW)" min-width="140"><template #default="{ row }">{{ cargoValue(row as Record<string, unknown>, 'grossWeightKg') }}</template></el-table-column>
|
<el-table-column label="总毛重(T.KGW)" align="right" min-width="140"><template #default="{ row }">{{ cargoValue(row as Record<string, unknown>, 'grossWeightKg') }}</template></el-table-column>
|
||||||
<el-table-column label="总净重(T.KNW)" min-width="140"><template #default="{ row }">{{ cargoValue(row as Record<string, unknown>, 'netWeightKg') }}</template></el-table-column>
|
<el-table-column label="总净重(T.KNW)" align="right" min-width="140"><template #default="{ row }">{{ cargoValue(row as Record<string, unknown>, 'netWeightKg') }}</template></el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template v-else-if="activeDetailTab === 'fees'">
|
<template v-else-if="activeDetailTab === 'fees'">
|
||||||
<div class="panel-title"><div><h2>费用信息</h2><p>头程自动初算与其他人工费用在同一页维护</p></div><WalletCards :size="19" /></div>
|
|
||||||
<el-empty v-if="!normalizedInboundId" description="订单尚未生成入库单,暂无入仓费用" :image-size="64" />
|
<el-empty v-if="!normalizedInboundId" description="订单尚未生成入库单,暂无入仓费用" :image-size="64" />
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<dl class="fee-overview" aria-label="费用汇总">
|
<dl class="fee-overview" aria-label="费用汇总">
|
||||||
@@ -2375,13 +2375,18 @@ onBeforeUnmount(() => {
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template v-else-if="activeDetailTab === 'history'">
|
<template v-else-if="activeDetailTab === 'history'">
|
||||||
<div class="panel-title"><div><h2>状态历史</h2><p>共 {{ detail.timeline.length }} 条</p></div><History :size="19" /></div>
|
<div class="panel-title"><p>共 {{ detail.timeline.length }} 条记录</p></div>
|
||||||
<el-empty v-if="detail.timeline.length === 0" description="暂无状态历史" :image-size="64" />
|
<el-table :data="detail.timeline" class="history-table" border stripe empty-text="暂无状态历史" aria-label="状态历史">
|
||||||
<ol v-else class="history-list" aria-label="状态历史"><li v-for="(item, index) in detail.timeline" :key="`${item.source || 'event'}-${item.occurredAt || index}`"><span class="history-marker" aria-hidden="true" /><div><div class="history-line"><strong>{{ historyActionLabel(item.action || item.eventType) }}</strong><StatusBadge :status="statusLabel(item.mainStatus)" /></div><p>{{ item.operatorName || '系统' }} · {{ formatDate(item.occurredAt) }}</p><p v-if="item.remark" class="history-remark">{{ item.remark }}</p></div></li></ol>
|
<el-table-column label="时间" width="172"><template #default="{ row }">{{ formatDate(row.occurredAt) }}</template></el-table-column>
|
||||||
|
<el-table-column label="事件" min-width="180"><template #default="{ row }">{{ historyActionLabel(row.action || row.eventType) }}</template></el-table-column>
|
||||||
|
<el-table-column label="状态" width="166"><template #default="{ row }"><StatusBadge :status="statusLabel(row.mainStatus)" /></template></el-table-column>
|
||||||
|
<el-table-column label="操作人" min-width="160"><template #default="{ row }">{{ row.operatorName || '系统' }}</template></el-table-column>
|
||||||
|
<el-table-column label="备注" min-width="300"><template #default="{ row }">{{ row.remark || '—' }}</template></el-table-column>
|
||||||
|
</el-table>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template v-else-if="activeDetailTab === 'fulfillment'">
|
<template v-else-if="activeDetailTab === 'fulfillment'">
|
||||||
<div class="panel-title"><div><h2>拆分履约</h2><p>根单、已完成分片与当前余量</p></div><ListChecks :size="19" /></div>
|
<div class="panel-title"><p>根单、已完成分片与当前余量</p></div>
|
||||||
<OrderFulfillmentTreePanel :tenant-id="String(detail.summary.tenantId)" :order-id="String(detail.summary.orderId)" :inbound-code="detail.summary.inboundCode" />
|
<OrderFulfillmentTreePanel :tenant-id="String(detail.summary.tenantId)" :order-id="String(detail.summary.orderId)" :inbound-code="detail.summary.inboundCode" />
|
||||||
<section v-loading="subsequentFulfillmentLoading" class="subsequent-fulfillment" aria-label="后续履约数据">
|
<section v-loading="subsequentFulfillmentLoading" class="subsequent-fulfillment" aria-label="后续履约数据">
|
||||||
<h3>后续履约数据</h3>
|
<h3>后续履约数据</h3>
|
||||||
@@ -2407,7 +2412,7 @@ onBeforeUnmount(() => {
|
|||||||
|
|
||||||
<template v-else-if="activeDetailTab === 'measurement'">
|
<template v-else-if="activeDetailTab === 'measurement'">
|
||||||
<div class="panel-title">
|
<div class="panel-title">
|
||||||
<div><h2>测量数据</h2><p>{{ detail.summary.measurement?.completedAt ? `完成于 ${formatDate(detail.summary.measurement.completedAt)}` : '尚未完成测量' }}</p></div>
|
<p>{{ detail.summary.measurement?.completedAt ? `测量完成于 ${formatDate(detail.summary.measurement.completedAt)}` : '尚未完成测量' }}</p>
|
||||||
<div class="measurement-title-actions">
|
<div class="measurement-title-actions">
|
||||||
<el-button
|
<el-button
|
||||||
v-if="can('firstleg:inbound:confirm-send-fee')"
|
v-if="can('firstleg:inbound:confirm-send-fee')"
|
||||||
@@ -2426,17 +2431,17 @@ onBeforeUnmount(() => {
|
|||||||
</LoadErrorState>
|
</LoadErrorState>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<section class="measurement-section">
|
<section class="measurement-section">
|
||||||
<div class="measurement-section-title"><button type="button" class="measurement-section-toggle" :aria-expanded="!measurementCollapsed.customer" @click="toggleMeasurementSection('customer')"><h3>一、客户下单数据</h3><span class="measurement-section-summary"><span>总件数:{{ measurementTotals(customerMeasurementRows).count ?? '—' }}</span><span>总毛重:{{ formatNumber(measurementTotals(customerMeasurementRows).gross) }}</span><span>总体积:{{ formatNumber(measurementTotals(customerMeasurementRows).volume, 6) }}</span><span>总材积重:{{ formatNumber(measurementTotals(customerMeasurementRows).volumetric) }}</span><span>重量体积:{{ formatNumber(measurementTotals(customerMeasurementRows).weightVolume, 6) }}</span></span><ChevronUp v-if="!measurementCollapsed.customer" :size="18" /><ChevronDown v-else :size="18" /></button><el-select class="measurement-section-mode" :model-value="measurementSectionModes.customer" aria-label="客户下单数据计算方式" @change="updateMeasurementSectionMode('customer', $event)"><el-option label="实测原尺寸(不调整)" value="ACTUAL" /><el-option label="箱规尺寸" value="ADD" /><el-option label="箱规系数" value="MULTIPLY" :disabled="multiplyModeDisabled" /></el-select></div>
|
<div class="measurement-section-title"><button type="button" class="measurement-section-toggle" :aria-expanded="!measurementCollapsed.customer" @click="toggleMeasurementSection('customer')"><h3>一、客户下单数据</h3><span v-if="measurementCollapsed.customer" class="measurement-section-summary"><span>总件数:{{ measurementTotals(customerMeasurementRows).count ?? '—' }}</span><span>总毛重:{{ formatNumber(measurementTotals(customerMeasurementRows).gross) }}</span><span>总体积:{{ formatNumber(measurementTotals(customerMeasurementRows).volume, 6) }}</span><span>总材积重:{{ formatNumber(measurementTotals(customerMeasurementRows).volumetric) }}</span><span>重量体积:{{ formatNumber(measurementTotals(customerMeasurementRows).weightVolume, 6) }}</span></span><ChevronUp v-if="!measurementCollapsed.customer" :size="18" /><ChevronDown v-else :size="18" /></button><el-select class="measurement-section-mode" :model-value="measurementSectionModes.customer" aria-label="客户下单数据计算方式" @change="updateMeasurementSectionMode('customer', $event)"><el-option label="实测原尺寸(不调整)" value="ACTUAL" /><el-option label="箱规尺寸" value="ADD" /><el-option label="箱规系数" value="MULTIPLY" :disabled="multiplyModeDisabled" /></el-select></div>
|
||||||
<div v-if="!measurementCollapsed.customer">
|
<div v-if="!measurementCollapsed.customer">
|
||||||
<div class="detail-table-wrap"><el-table :data="customerMeasurementRows" border stripe empty-text="暂无客户下单数据"><el-table-column v-for="column in customerMeasurementColumns" :key="column.key" :label="column.label" :prop="column.key" min-width="120"><template #default="{ row }"><LineClampText v-if="column.key === 'remark'" :content="measurementValue(row as Record<string, unknown>, column.key)" /><template v-else>{{ measurementValue(row as Record<string, unknown>, column.key) }}</template></template></el-table-column></el-table></div>
|
<div class="detail-table-wrap"><el-table :data="customerMeasurementRows" border stripe empty-text="暂无客户下单数据"><el-table-column v-for="column in customerMeasurementColumns" :key="column.key" :label="column.label" :prop="column.key" min-width="120"><template #default="{ row }"><LineClampText v-if="column.key === 'remark'" :content="measurementValue(row as Record<string, unknown>, column.key)" /><template v-else>{{ measurementValue(row as Record<string, unknown>, column.key) }}</template></template></el-table-column></el-table></div>
|
||||||
<div class="measurement-total"><strong>合计</strong><span>箱数:{{ measurementTotals(customerMeasurementRows).count ?? '—' }}</span><span>总毛重:{{ formatNumber(measurementTotals(customerMeasurementRows).gross) }}</span><span>总体积:{{ formatNumber(measurementTotals(customerMeasurementRows).volume, 6) }}</span><span>总材积:{{ formatNumber(measurementTotals(customerMeasurementRows).volumetric) }}</span></div>
|
<div class="measurement-total"><strong>合计</strong><span>箱数:{{ measurementTotals(customerMeasurementRows).count ?? '—' }}</span><span>总毛重:{{ formatNumber(measurementTotals(customerMeasurementRows).gross) }}</span><span>总体积:{{ formatNumber(measurementTotals(customerMeasurementRows).volume, 6) }}</span><span>总材积重:{{ formatNumber(measurementTotals(customerMeasurementRows).volumetric) }}</span><span>重量体积:{{ formatNumber(measurementTotals(customerMeasurementRows).weightVolume, 6) }}</span></div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<section class="measurement-section">
|
<section class="measurement-section">
|
||||||
<div class="measurement-section-title"><button type="button" class="measurement-section-toggle" :aria-expanded="!measurementCollapsed.outer" @click="toggleMeasurementSection('outer')"><h3>二、外箱规数据</h3><span class="measurement-section-summary"><span>总件数:{{ measurementTotals(outerMeasurementRows).count ?? '—' }}</span><span>总毛重:{{ formatNumber(measurementTotals(outerMeasurementRows).gross) }}</span><span>总体积:{{ formatNumber(measurementTotals(outerMeasurementRows).volume, 6) }}</span><span>总材积重:{{ formatNumber(measurementTotals(outerMeasurementRows).volumetric) }}</span><span>重量体积:{{ formatNumber(measurementTotals(outerMeasurementRows).weightVolume, 6) }}</span></span><ChevronUp v-if="!measurementCollapsed.outer" :size="18" /><ChevronDown v-else :size="18" /></button><el-tooltip :content="measurementStrategyLockedHint" :disabled="measurementStrategyEditable"><span><el-select class="measurement-section-mode" :model-value="measurementSectionModes.outer" aria-label="外箱规数据计算方式" :loading="Boolean(measurementStrategySavingId)" :disabled="Boolean(measurementStrategySavingId) || !measurementStrategyEditable" @change="selectAndSaveOuterMeasurementMode($event)"><el-option label="实测原尺寸(不调整)" value="ACTUAL" /><el-option label="箱规尺寸" value="ADD" /><el-option label="箱规系数" value="MULTIPLY" :disabled="multiplyModeDisabled" /></el-select></span></el-tooltip></div>
|
<div class="measurement-section-title"><button type="button" class="measurement-section-toggle" :aria-expanded="!measurementCollapsed.outer" @click="toggleMeasurementSection('outer')"><h3>二、外箱规数据</h3><span v-if="measurementCollapsed.outer" class="measurement-section-summary"><span>总件数:{{ measurementTotals(outerMeasurementRows).count ?? '—' }}</span><span>总毛重:{{ formatNumber(measurementTotals(outerMeasurementRows).gross) }}</span><span>总体积:{{ formatNumber(measurementTotals(outerMeasurementRows).volume, 6) }}</span><span>总材积重:{{ formatNumber(measurementTotals(outerMeasurementRows).volumetric) }}</span><span>重量体积:{{ formatNumber(measurementTotals(outerMeasurementRows).weightVolume, 6) }}</span></span><ChevronUp v-if="!measurementCollapsed.outer" :size="18" /><ChevronDown v-else :size="18" /></button></div>
|
||||||
<div v-if="!measurementCollapsed.outer">
|
<div v-if="!measurementCollapsed.outer">
|
||||||
<div v-loading="measurementLoading" class="detail-table-wrap"><el-table :data="outerMeasurementRows" border stripe empty-text="暂无外箱规数据"><el-table-column label="#" prop="itemNo" min-width="70" /><el-table-column v-for="column in outerColumns.filter((item) => item.key !== 'itemNo')" :key="column.key" :label="column.label" :prop="column.key" min-width="150"><template #default="{ row }"><LineClampText v-if="column.key === 'remark'" :content="measurementValue(row as Record<string, unknown>, column.key)" /><template v-else>{{ measurementValue(row as Record<string, unknown>, column.key) }}</template></template></el-table-column></el-table></div>
|
<div v-loading="measurementLoading" class="detail-table-wrap"><el-table :data="outerMeasurementRows" border stripe empty-text="暂无外箱规数据"><el-table-column label="#" prop="itemNo" min-width="70" /><el-table-column v-for="column in outerColumns.filter((item) => item.key !== 'itemNo')" :key="column.key" :label="column.label" :prop="column.key" min-width="150"><template #default="{ row }"><LineClampText v-if="column.key === 'remark'" :content="measurementValue(row as Record<string, unknown>, column.key)" /><template v-else>{{ measurementValue(row as Record<string, unknown>, column.key) }}</template></template></el-table-column></el-table></div>
|
||||||
<div class="measurement-total"><strong>合计</strong><span>箱数:{{ measurementTotals(outerMeasurementRows).count ?? '—' }}</span><span>总毛重:{{ formatNumber(measurementTotals(outerMeasurementRows).gross) }}</span><span>总体积:{{ formatNumber(measurementTotals(outerMeasurementRows).volume, 6) }}</span><span>总材积:{{ formatNumber(measurementTotals(outerMeasurementRows).volumetric) }}</span></div>
|
<div class="measurement-total"><strong>合计</strong><span>箱数:{{ measurementTotals(outerMeasurementRows).count ?? '—' }}</span><span>总毛重:{{ formatNumber(measurementTotals(outerMeasurementRows).gross) }}</span><span>总体积:{{ formatNumber(measurementTotals(outerMeasurementRows).volume, 6) }}</span><span>总材积重:{{ formatNumber(measurementTotals(outerMeasurementRows).volumetric) }}</span><span>重量体积:{{ formatNumber(measurementTotals(outerMeasurementRows).weightVolume, 6) }}</span></div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<div class="measurement-rule-selector">
|
<div class="measurement-rule-selector">
|
||||||
@@ -2449,10 +2454,13 @@ onBeforeUnmount(() => {
|
|||||||
<span class="measurement-rule-selector__hint">{{ measurementStrategyEditable ? '规则参数来源:客户管理' : measurementStrategyLockedHint }}</span>
|
<span class="measurement-rule-selector__hint">{{ measurementStrategyEditable ? '规则参数来源:客户管理' : measurementStrategyLockedHint }}</span>
|
||||||
</div>
|
</div>
|
||||||
<section class="measurement-section">
|
<section class="measurement-section">
|
||||||
<div class="measurement-section-title"><button type="button" class="measurement-section-toggle" :aria-expanded="!measurementCollapsed.actual" @click="toggleMeasurementSection('actual')"><h3>三、实际测量数据</h3><span class="measurement-section-summary"><span>总件数:{{ measurementTotals(actualMeasurementRows).count ?? '—' }}</span><span>总毛重:{{ formatNumber(measurementTotals(actualMeasurementRows).gross) }}</span><span>总体积:{{ formatNumber(measurementTotals(actualMeasurementRows).volume, 6) }}</span><span>总材积重:{{ formatNumber(measurementTotals(actualMeasurementRows).volumetric) }}</span><span>重量体积:{{ formatNumber(measurementTotals(actualMeasurementRows).weightVolume, 6) }}</span></span><ChevronUp v-if="!measurementCollapsed.actual" :size="18" /><ChevronDown v-else :size="18" /></button><el-select class="measurement-section-mode" :model-value="measurementSectionModes.actual" aria-label="实际测量数据计算方式" @change="updateMeasurementSectionMode('actual', $event)"><el-option label="实测原尺寸(不调整)" value="ACTUAL" /><el-option label="箱规尺寸" value="ADD" /><el-option label="箱规系数" value="MULTIPLY" :disabled="multiplyModeDisabled" /></el-select></div>
|
<div class="measurement-section-title"><button type="button" class="measurement-section-toggle" :aria-expanded="!measurementCollapsed.actual" @click="toggleMeasurementSection('actual')"><h3>三、实际测量数据</h3><span v-if="measurementCollapsed.actual" class="measurement-section-summary"><span>总件数:{{ measurementTotals(actualMeasurementRows).count ?? '—' }}</span><span>总毛重:{{ formatNumber(measurementTotals(actualMeasurementRows).gross) }}</span><span>总体积:{{ formatNumber(measurementTotals(actualMeasurementRows).volume, 6) }}</span><span>总材积重:{{ formatNumber(measurementTotals(actualMeasurementRows).volumetric) }}</span><span>重量体积:{{ formatNumber(measurementTotals(actualMeasurementRows).weightVolume, 6) }}</span></span><ChevronUp v-if="!measurementCollapsed.actual" :size="18" /><ChevronDown v-else :size="18" /></button><el-select class="measurement-section-mode" :model-value="measurementSectionModes.actual" aria-label="实际测量数据计算方式" @change="updateMeasurementSectionMode('actual', $event)"><el-option label="实测原尺寸(不调整)" value="ACTUAL" /><el-option label="箱规尺寸" value="ADD" /><el-option label="箱规系数" value="MULTIPLY" :disabled="multiplyModeDisabled" /></el-select></div>
|
||||||
<div v-if="!measurementCollapsed.actual">
|
<div v-if="!measurementCollapsed.actual">
|
||||||
<div v-loading="measurementLoading" class="detail-table-wrap">
|
<div v-loading="measurementLoading" class="detail-table-wrap">
|
||||||
<el-table :data="actualMeasurementRows" row-key="id" border stripe empty-text="暂无实际测量数据">
|
<el-table :data="actualMeasurementRows" row-key="id" border stripe empty-text="暂无实际测量数据">
|
||||||
|
<el-table-column v-if="measurementStrategyEditable" label="操作" width="110" fixed="left">
|
||||||
|
<template #default="{ row }"><el-button type="primary" plain size="small" :loading="measurementOuterSavingId === String(row.id)" :disabled="Boolean(measurementOuterSavingId)" @click="saveWarehouseOuterDimensions(row as Record<string, unknown>)">保存外箱</el-button></template>
|
||||||
|
</el-table-column>
|
||||||
<el-table-column label="箱号" prop="boxCode" min-width="190" fixed="left" show-overflow-tooltip>
|
<el-table-column label="箱号" prop="boxCode" min-width="190" fixed="left" show-overflow-tooltip>
|
||||||
<template #default="{ row }"><strong class="measurement-box-code">{{ row.boxCode || '—' }}</strong></template>
|
<template #default="{ row }"><strong class="measurement-box-code">{{ row.boxCode || '—' }}</strong></template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
@@ -2466,9 +2474,7 @@ onBeforeUnmount(() => {
|
|||||||
<el-table-column label="仓库修改外箱(高)" width="190" align="center">
|
<el-table-column label="仓库修改外箱(高)" width="190" align="center">
|
||||||
<template #default="{ row }"><el-input-number v-if="measurementStrategyEditable" v-model="row.outerHeightCm" class="measurement-outer-dimension-input" aria-label="仓库修改外箱高" :min="0.001" :precision="3" :controls="false" placeholder="不修改" /><template v-else>{{ formatNumber(row.outerHeightCm) }}</template></template>
|
<template #default="{ row }"><el-input-number v-if="measurementStrategyEditable" v-model="row.outerHeightCm" class="measurement-outer-dimension-input" aria-label="仓库修改外箱高" :min="0.001" :precision="3" :controls="false" placeholder="不修改" /><template v-else>{{ formatNumber(row.outerHeightCm) }}</template></template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column v-if="measurementStrategyEditable" label="仓库外箱操作" min-width="120">
|
|
||||||
<template #default="{ row }"><el-button type="primary" plain size="small" :loading="measurementOuterSavingId === String(row.id)" :disabled="Boolean(measurementOuterSavingId)" @click="saveWarehouseOuterDimensions(row as Record<string, unknown>)">保存外箱</el-button></template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column v-for="column in actualMeasuredColumns" :key="column.key" :label="column.label" :prop="column.key" min-width="120">
|
<el-table-column v-for="column in actualMeasuredColumns" :key="column.key" :label="column.label" :prop="column.key" min-width="120">
|
||||||
<template #default="{ row }"><LineClampText v-if="column.key === 'remark'" :content="measurementValue(row as Record<string, unknown>, column.key)" /><template v-else>{{ measurementValue(row as Record<string, unknown>, column.key) }}</template></template>
|
<template #default="{ row }"><LineClampText v-if="column.key === 'remark'" :content="measurementValue(row as Record<string, unknown>, column.key)" /><template v-else>{{ measurementValue(row as Record<string, unknown>, column.key) }}</template></template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
@@ -2486,7 +2492,7 @@ onBeforeUnmount(() => {
|
|||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
</div>
|
</div>
|
||||||
<div class="measurement-total"><strong>合计</strong><span>箱数:{{ measurementTotals(actualMeasurementRows).count ?? '—' }}</span><span>总毛重:{{ formatNumber(measurementTotals(actualMeasurementRows).gross) }}</span><span>总体积:{{ formatNumber(measurementTotals(actualMeasurementRows).volume, 6) }}</span><span>总材积:{{ formatNumber(measurementTotals(actualMeasurementRows).volumetric) }}</span></div>
|
<div class="measurement-total"><strong>合计</strong><span>箱数:{{ measurementTotals(actualMeasurementRows).count ?? '—' }}</span><span>总毛重:{{ formatNumber(measurementTotals(actualMeasurementRows).gross) }}</span><span>总体积:{{ formatNumber(measurementTotals(actualMeasurementRows).volume, 6) }}</span><span>总材积重:{{ formatNumber(measurementTotals(actualMeasurementRows).volumetric) }}</span><span>重量体积:{{ formatNumber(measurementTotals(actualMeasurementRows).weightVolume, 6) }}</span></div>
|
||||||
<section class="weight-evidence" aria-label="称重图片与服务端确认重量">
|
<section class="weight-evidence" aria-label="称重图片与服务端确认重量">
|
||||||
<div class="weight-evidence__heading">
|
<div class="weight-evidence__heading">
|
||||||
<div><h4>称重图片与确认重量</h4><p>称重图片独立展示;重量值直接采用服务端确认结果。</p></div>
|
<div><h4>称重图片与确认重量</h4><p>称重图片独立展示;重量值直接采用服务端确认结果。</p></div>
|
||||||
@@ -2510,7 +2516,7 @@ onBeforeUnmount(() => {
|
|||||||
</dl>
|
</dl>
|
||||||
</article>
|
</article>
|
||||||
</div>
|
</div>
|
||||||
<el-empty v-else description="暂无称重图片与确认结果" :image-size="48" />
|
<p v-else class="compact-empty">暂无称重图片与确认结果</p>
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
@@ -2537,7 +2543,7 @@ onBeforeUnmount(() => {
|
|||||||
</section>
|
</section>
|
||||||
<section class="other-notes-section" aria-label="内部备注">
|
<section class="other-notes-section" aria-label="内部备注">
|
||||||
<div class="panel-title"><div><h2>内部备注</h2><p>仅平台内部可见,独立保存</p></div><el-button v-if="can('platform:order:note')" type="primary" size="small" @click="openNoteDialog()"><StickyNote :size="15" />添加备注</el-button></div>
|
<div class="panel-title"><div><h2>内部备注</h2><p>仅平台内部可见,独立保存</p></div><el-button v-if="can('platform:order:note')" type="primary" size="small" @click="openNoteDialog()"><StickyNote :size="15" />添加备注</el-button></div>
|
||||||
<el-empty v-if="detail.order.internalNotes.length === 0" description="暂无内部备注" :image-size="64" />
|
<p v-if="detail.order.internalNotes.length === 0" class="compact-empty">暂无内部备注</p>
|
||||||
<ul v-else class="notes-list" aria-label="内部备注"><li v-for="item in detail.order.internalNotes" :key="String(item.id)"><p>{{ item.content }}</p><span>操作人 {{ item.createdBy || '系统' }} · {{ formatDate(item.createdAt) }}</span></li></ul>
|
<ul v-else class="notes-list" aria-label="内部备注"><li v-for="item in detail.order.internalNotes" :key="String(item.id)"><p>{{ item.content }}</p><span>操作人 {{ item.createdBy || '系统' }} · {{ formatDate(item.createdAt) }}</span></li></ul>
|
||||||
</section>
|
</section>
|
||||||
</template>
|
</template>
|
||||||
@@ -2680,7 +2686,7 @@ onBeforeUnmount(() => {
|
|||||||
</template>
|
</template>
|
||||||
</ListPageLayout>
|
</ListPageLayout>
|
||||||
<SmartLoadingDialog v-model="smartLoadingOpen" />
|
<SmartLoadingDialog v-model="smartLoadingOpen" />
|
||||||
<AppDialog v-model="batchApprovalOpen" title="批量审核订单" confirm-text="确认批量审核" confirm-type="success" width="min(1100px, calc(100vw - 28px))" :loading="batchApprovalLoading" :confirm-loading="batchApprovalSaving" :confirm-disabled="batchApprovalLoading || !batchApprovalSelectedRows.length" destroy-on-close @confirm="batchApproveOrders">
|
<AppDialog class="order-density-dialog" v-model="batchApprovalOpen" title="批量审核订单" confirm-text="确认批量审核" confirm-type="success" width="min(1100px, calc(100vw - 28px))" :loading="batchApprovalLoading" :confirm-loading="batchApprovalSaving" :confirm-disabled="batchApprovalLoading || !batchApprovalSelectedRows.length" destroy-on-close @confirm="batchApproveOrders">
|
||||||
<el-alert title="请逐票核对入仓码、客户、中文品名、渠道和目的仓;审核通过后订单进入待测量。" type="info" :closable="false" show-icon />
|
<el-alert title="请逐票核对入仓码、客户、中文品名、渠道和目的仓;审核通过后订单进入待测量。" type="info" :closable="false" show-icon />
|
||||||
<LoadErrorState v-if="batchApprovalError" compact title="批量审核预览加载失败" :message="batchApprovalError.message" :loading="batchApprovalLoading" @retry="openBatchApproval"><template #details><ApiErrorMeta :details="batchApprovalError" /></template></LoadErrorState>
|
<LoadErrorState v-if="batchApprovalError" compact title="批量审核预览加载失败" :message="batchApprovalError.message" :loading="batchApprovalLoading" @retry="openBatchApproval"><template #details><ApiErrorMeta :details="batchApprovalError" /></template></LoadErrorState>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
@@ -2696,12 +2702,12 @@ onBeforeUnmount(() => {
|
|||||||
</el-table>
|
</el-table>
|
||||||
</template>
|
</template>
|
||||||
</AppDialog>
|
</AppDialog>
|
||||||
<AppDialog v-model="coLoadCreateOpen" title="合柜报关" confirm-text="确认建组" :confirm-loading="coLoadSaving" :confirm-disabled="!canCreateCoLoadGroup" @confirm="createCoLoadGroup">
|
<AppDialog class="order-density-dialog" v-model="coLoadCreateOpen" title="合柜报关" confirm-text="确认建组" :confirm-loading="coLoadSaving" :confirm-disabled="!canCreateCoLoadGroup" @confirm="createCoLoadGroup">
|
||||||
<el-alert type="warning" :closable="false" show-icon :title="`所选 ${selectedOrders.length} 票须属于同一国内仓、同一渠道,并全量进入同一柜;目的仓或目的港可以不同。资格由服务端按完整入仓片段校验。`" />
|
<el-alert type="warning" :closable="false" show-icon :title="`所选 ${selectedOrders.length} 票须属于同一国内仓、同一渠道,并全量进入同一柜;目的仓或目的港可以不同。资格由服务端按完整入仓片段校验。`" />
|
||||||
<div class="coload-selection"><span v-for="row in selectedOrders" :key="String(row.orderId)">{{ row.inboundCode || '待生成入仓码' }}</span></div>
|
<div class="coload-selection"><span v-for="row in selectedOrders" :key="String(row.orderId)">{{ row.inboundCode || '待生成入仓码' }}</span></div>
|
||||||
<div v-if="coLoadError" class="coload-error" role="alert"><span>{{ coLoadError.message }}</span><ApiErrorMeta :details="coLoadError" /></div>
|
<div v-if="coLoadError" class="coload-error" role="alert"><span>{{ coLoadError.message }}</span><ApiErrorMeta :details="coLoadError" /></div>
|
||||||
</AppDialog>
|
</AppDialog>
|
||||||
<AppDialog v-model="coLoadDetailOpen" title="同柜组成员" :show-confirm="false" cancel-text="关闭" width="min(720px, 94vw)" :loading="coLoadDetailLoading" :confirm-loading="coLoadCancelSaving">
|
<AppDialog class="order-density-dialog" v-model="coLoadDetailOpen" title="同柜组成员" :show-confirm="false" cancel-text="关闭" width="min(720px, 94vw)" :loading="coLoadDetailLoading" :confirm-loading="coLoadCancelSaving">
|
||||||
<LoadErrorState v-if="coLoadDetailError" title="同柜组成员加载失败" :message="coLoadDetailError.message" :loading="coLoadDetailLoading" @retry="loadCoLoadDetail"><template #details><ApiErrorMeta :details="coLoadDetailError" /></template></LoadErrorState>
|
<LoadErrorState v-if="coLoadDetailError" title="同柜组成员加载失败" :message="coLoadDetailError.message" :loading="coLoadDetailLoading" @retry="loadCoLoadDetail"><template #details><ApiErrorMeta :details="coLoadDetailError" /></template></LoadErrorState>
|
||||||
<template v-else-if="coLoadDetailSummary">
|
<template v-else-if="coLoadDetailSummary">
|
||||||
<div class="coload-detail-summary"><el-tag size="small" :type="coLoadDetailSummary.status === 'ACTIVE' ? 'warning' : 'info'">{{ coLoadGroupSummary(coLoadDetailSummary) }}</el-tag><span>{{ coLoadDetailSummary.memberCount }} 个成员</span></div>
|
<div class="coload-detail-summary"><el-tag size="small" :type="coLoadDetailSummary.status === 'ACTIVE' ? 'warning' : 'info'">{{ coLoadGroupSummary(coLoadDetailSummary) }}</el-tag><span>{{ coLoadDetailSummary.memberCount }} 个成员</span></div>
|
||||||
@@ -2728,17 +2734,24 @@ onBeforeUnmount(() => {
|
|||||||
/>
|
/>
|
||||||
</AppDrawer>
|
</AppDrawer>
|
||||||
|
|
||||||
<AppDialog v-model="noteOpen" title="添加内部备注" confirm-text="保存备注" :confirm-loading="noteSaving" @confirm="saveNote">
|
<AppDialog class="order-density-dialog" v-model="noteOpen" title="添加内部备注" confirm-text="保存备注" :confirm-loading="noteSaving" @confirm="saveNote">
|
||||||
<el-input v-model="noteContent" type="textarea" :rows="5" maxlength="2000" show-word-limit placeholder="请输入仅平台内部可见的备注" aria-label="内部备注内容" />
|
<el-input v-model="noteContent" type="textarea" :rows="5" maxlength="2000" show-word-limit placeholder="请输入仅平台内部可见的备注" aria-label="内部备注内容" />
|
||||||
</AppDialog>
|
</AppDialog>
|
||||||
|
|
||||||
<AppDialog v-model="holdDialogOpen" :title="holdAction === 'HOLD' ? '确认扣货' : '确认解除扣货'" :confirm-text="holdAction === 'HOLD' ? '确认扣货' : '确认解除'" :confirm-type="holdAction === 'HOLD' ? 'danger' : 'primary'" :confirm-loading="holdSaving" :confirm-disabled="!holdReason.trim() || holdReason.trim().length > 1000" width="min(600px, calc(100vw - 28px))" destroy-on-close @confirm="submitHoldAction">
|
<AppDialog class="order-density-dialog" v-model="holdDialogOpen" :title="holdAction === 'HOLD' ? '确认扣货' : '确认解除扣货'" :confirm-text="holdAction === 'HOLD' ? '确认扣货' : '确认解除'" :confirm-type="holdAction === 'HOLD' ? 'danger' : 'primary'" :confirm-loading="holdSaving" :confirm-disabled="!holdReason.trim() || holdReason.trim().length > 1000" width="min(600px, calc(100vw - 28px))" destroy-on-close @confirm="submitHoldAction">
|
||||||
<el-alert :title="holdAction === 'HOLD' ? '扣货后服务端将阻止后续实物履约操作。' : '解除后是否可继续履约仍以服务端状态为准。'" :type="holdAction === 'HOLD' ? 'warning' : 'info'" :closable="false" show-icon />
|
<el-alert :title="holdAction === 'HOLD' ? '扣货后服务端将阻止后续实物履约操作。' : '解除后是否可继续履约仍以服务端状态为准。'" :type="holdAction === 'HOLD' ? 'warning' : 'info'" :closable="false" show-icon />
|
||||||
<el-form label-position="top" class="hold-command-form"><el-form-item :label="holdAction === 'HOLD' ? '扣货原因' : '解除原因'" required><el-input v-model="holdReason" type="textarea" :rows="4" maxlength="1000" show-word-limit :placeholder="holdAction === 'HOLD' ? '请输入扣货原因' : '请输入解除扣货原因'" aria-label="扣货操作原因" /></el-form-item><el-form-item label="关联文件(选填)"><el-upload :key="holdEvidenceUploadKey" :auto-upload="false" :limit="1" accept=".txt,.pdf,.jpg,.jpeg,.png,text/plain,application/pdf,image/jpeg,image/png" :disabled="holdSaving" :on-change="selectHoldEvidenceFile" :on-remove="removeHoldEvidenceFile"><el-button :disabled="holdSaving"><FileUp :size="15" />选择文件</el-button><template #tip><div class="el-upload__tip">仅支持 TXT、PDF、JPG、PNG,单文件不超过 10MB;服务端将校验文件真实内容。</div></template></el-upload></el-form-item><LoadErrorState v-if="holdEvidenceError" compact title="关联文件处理失败" :message="holdEvidenceError.message" :show-retry="false"><template #details><ApiErrorMeta :details="holdEvidenceError" /></template></LoadErrorState></el-form>
|
<el-form label-position="top" class="hold-command-form"><el-form-item :label="holdAction === 'HOLD' ? '扣货原因' : '解除原因'" required><el-input v-model="holdReason" type="textarea" :rows="4" maxlength="1000" show-word-limit :placeholder="holdAction === 'HOLD' ? '请输入扣货原因' : '请输入解除扣货原因'" aria-label="扣货操作原因" /></el-form-item><el-form-item label="关联文件(选填)"><el-upload :key="holdEvidenceUploadKey" :auto-upload="false" :limit="1" accept=".txt,.pdf,.jpg,.jpeg,.png,text/plain,application/pdf,image/jpeg,image/png" :disabled="holdSaving" :on-change="selectHoldEvidenceFile" :on-remove="removeHoldEvidenceFile"><el-button :disabled="holdSaving"><FileUp :size="15" />选择文件</el-button><template #tip><div class="el-upload__tip">仅支持 TXT、PDF、JPG、PNG,单文件不超过 10MB;服务端将校验文件真实内容。</div></template></el-upload></el-form-item><LoadErrorState v-if="holdEvidenceError" compact title="关联文件处理失败" :message="holdEvidenceError.message" :show-retry="false"><template #details><ApiErrorMeta :details="holdEvidenceError" /></template></LoadErrorState></el-form>
|
||||||
</AppDialog>
|
</AppDialog>
|
||||||
|
|
||||||
<AppDialog v-model="approvalOpen" title="审核订单" confirm-text="审核通过" confirm-type="success" :confirm-loading="approvalSaving" destroy-on-close @confirm="approveOrder" @cancel="closeApproval">
|
<AppDialog class="order-density-dialog" v-model="approvalOpen" title="审核订单" confirm-text="审核通过" confirm-type="success" :confirm-loading="approvalSaving" destroy-on-close @confirm="approveOrder" @cancel="closeApproval">
|
||||||
<el-alert title="确认订单资料无误后审核通过,订单将进入待测量阶段。" type="info" :closable="false" show-icon />
|
<dl v-if="approvalOrder" class="approval-facts">
|
||||||
|
<div><dt>入仓码</dt><dd>{{ approvalOrder.inboundCode || '—' }}</dd></div>
|
||||||
|
<div><dt>客户</dt><dd>{{ customerName(approvalOrder) }}</dd></div>
|
||||||
|
<div><dt>渠道</dt><dd>{{ approvalOrder.channelName || '—' }}</dd></div>
|
||||||
|
<div><dt>目的仓</dt><dd>{{ approvalOrder.destinationWarehouseName || '—' }}</dd></div>
|
||||||
|
<div><dt>总箱数</dt><dd>{{ approvalOrder.totalPackageCount ?? '—' }} 箱</dd></div>
|
||||||
|
</dl>
|
||||||
|
<p class="approval-hint">确认订单资料无误后审核通过,订单将进入待测量阶段。</p>
|
||||||
</AppDialog>
|
</AppDialog>
|
||||||
|
|
||||||
<OrderDeclarationEditorDialog
|
<OrderDeclarationEditorDialog
|
||||||
@@ -2749,6 +2762,7 @@ onBeforeUnmount(() => {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<AppDialog
|
<AppDialog
|
||||||
|
class="order-density-dialog"
|
||||||
v-model="measurementPreviewOpen"
|
v-model="measurementPreviewOpen"
|
||||||
:title="measurementPreviewTitle"
|
:title="measurementPreviewTitle"
|
||||||
width="min(920px, calc(100vw - 28px))"
|
width="min(920px, calc(100vw - 28px))"
|
||||||
@@ -2764,6 +2778,7 @@ onBeforeUnmount(() => {
|
|||||||
</AppDialog>
|
</AppDialog>
|
||||||
|
|
||||||
<InboundMeasurementDialog
|
<InboundMeasurementDialog
|
||||||
|
class="order-density-dialog order-measurement-dialog"
|
||||||
v-if="measurementOrder?.inboundId"
|
v-if="measurementOrder?.inboundId"
|
||||||
v-model="measurementDialogOpen"
|
v-model="measurementDialogOpen"
|
||||||
:customer-id="String(measurementOrder.tenantId)"
|
:customer-id="String(measurementOrder.tenantId)"
|
||||||
@@ -2780,21 +2795,46 @@ onBeforeUnmount(() => {
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.unified-orders-page { min-width: 0; gap: 16px; }
|
.detail-overview > div { display: flex; min-width: 0; align-items: baseline; gap: 6px; }
|
||||||
|
.detail-overview dt { flex: 0 0 auto; color: var(--cc-muted); font-size: 12px; }
|
||||||
|
.detail-overview dd { min-width: 0; margin: 0; color: var(--cc-text); font-size: 13px; overflow-wrap: anywhere; font-variant-numeric: tabular-nums; }
|
||||||
|
.detail-overview small { color: var(--cc-muted); font-size: 12px; }
|
||||||
|
.detail-tabs button:focus-visible { outline: 2px solid var(--cc-primary); outline-offset: -2px; }
|
||||||
|
.panel-title > div:first-child { display: flex; align-items: baseline; flex-wrap: wrap; gap: 4px 12px; }
|
||||||
|
.compact-empty { margin: 0; padding: 14px 10px; color: var(--cc-muted); text-align: center; font-size: 13px; }
|
||||||
|
.approval-facts { margin: 0; border-top: 1px solid var(--cc-border); }
|
||||||
|
.approval-facts > div { display: grid; grid-template-columns: 88px minmax(0, 1fr); gap: 12px; padding: 8px 0; border-bottom: 1px solid var(--cc-border); }
|
||||||
|
.approval-facts dt { color: var(--cc-muted); font-size: 12px; }
|
||||||
|
.approval-facts dd { min-width: 0; margin: 0; overflow-wrap: anywhere; font-size: 13px; }
|
||||||
|
.approval-hint { margin: 12px 0 0; color: #475467; font-size: 13px; }
|
||||||
|
.detail-panel :deep(.el-table) { font-size: 13px; --el-table-header-bg-color: #f8fafc; }
|
||||||
|
.detail-panel :deep(.el-table__cell) { height: 36px; padding: 4px 0; }
|
||||||
|
.detail-panel :deep(.el-table .cell) { line-height: 22px; overflow-wrap: anywhere; }
|
||||||
|
.detail-panel :deep(.el-table__empty-block) { min-height: 44px; }
|
||||||
|
.detail-panel :deep(.el-table__empty-text) { line-height: 44px; }
|
||||||
|
.history-table :deep(.cell) { white-space: normal; }
|
||||||
|
.measurement-section-title:has([aria-expanded="false"]) { border-bottom: 1px solid var(--cc-border); }
|
||||||
|
.fulfillment-facts + .fulfillment-facts { border-top: 0; }
|
||||||
|
@media (min-width: 961px) {
|
||||||
|
.table-card :deep(.data-table__toolbar.is-compact) { min-height: 44px; padding: 6px 12px; }
|
||||||
|
.table-card :deep(.data-table__pagination) { min-height: 44px; padding: 6px 12px; }
|
||||||
|
}
|
||||||
|
|
||||||
|
.unified-orders-page { min-width: 0; gap: 12px; --cc-list-table-font-size: 13px; --cc-list-query-font-size: 13px; --cc-list-action-font-size: 13px; }
|
||||||
.page-intro { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; margin-bottom: 16px; }
|
.page-intro { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; margin-bottom: 16px; }
|
||||||
.page-intro h1 { margin: 0; color: var(--cc-text); font-size: 22px; line-height: 1.35; }
|
.page-intro h1 { margin: 0; color: var(--cc-text); font-size: 22px; line-height: 1.35; }
|
||||||
.page-intro p { margin: 5px 0 0; color: var(--cc-muted); font-size: 13px; line-height: 1.6; }
|
.page-intro p { margin: 5px 0 0; color: var(--cc-muted); font-size: 13px; line-height: 1.6; }
|
||||||
.page-intro :deep(.el-button) { flex: 0 0 auto; }
|
.page-intro :deep(.el-button) { flex: 0 0 auto; }
|
||||||
.orders-query-panel { overflow: hidden; border: 1px solid var(--cc-border); border-radius: 8px; background: #fff; }
|
.orders-query-panel { overflow: hidden; border: 1px solid var(--cc-border); border-radius: 4px; background: #fff; }
|
||||||
.status-tabs { display: flex; max-width: 100%; overflow-x: auto; margin-bottom: 0; border-bottom: 1px solid #e5e7eb; background: #f9fafb; scrollbar-width: thin; }
|
.status-tabs { display: flex; max-width: 100%; overflow-x: auto; margin-bottom: 0; border-bottom: 1px solid #e5e7eb; background: #f9fafb; scrollbar-width: thin; }
|
||||||
.status-tab { position: relative; flex: 0 0 auto; min-height: 44px; padding: 0 18px; border: 0; background: transparent; color: var(--cc-muted); font: inherit; font-size: 14px; cursor: pointer; }
|
.status-tab { position: relative; flex: 0 0 auto; min-height: 38px; padding: 0 16px; border: 0; background: transparent; color: var(--cc-muted); font: inherit; font-size: 13px; cursor: pointer; }
|
||||||
.status-tab::after { position: absolute; right: 12px; bottom: 0; left: 12px; height: 3px; background: transparent; content: ''; }
|
.status-tab::after { position: absolute; right: 12px; bottom: 0; left: 12px; height: 3px; background: transparent; content: ''; }
|
||||||
.status-tab:hover { color: var(--cc-primary); background: #f6f9fd; }
|
.status-tab:hover { color: var(--cc-primary); background: #f6f9fd; }
|
||||||
.status-tab:focus-visible { outline: 2px solid var(--cc-primary); outline-offset: -2px; }
|
.status-tab:focus-visible { outline: 2px solid var(--cc-primary); outline-offset: -2px; }
|
||||||
.status-tab.is-active { color: var(--cc-primary); font-weight: 600; }
|
.status-tab.is-active { color: var(--cc-primary); font-weight: 600; }
|
||||||
.status-tab.is-active::after { background: var(--cc-primary); }
|
.status-tab.is-active::after { background: var(--cc-primary); }
|
||||||
.orders-query-toolbar { border: 0; }
|
.orders-query-toolbar { border: 0; padding: 10px 12px; }
|
||||||
.table-card { overflow: hidden; }
|
.table-card { overflow: hidden; border-radius: 4px; box-shadow: none; }
|
||||||
.coload-error { margin: 8px 12px; padding: 8px 10px; border: 1px solid var(--el-color-danger-light-5); border-radius: 4px; background: var(--el-color-danger-light-9); color: var(--el-color-danger); font-size: 12px; }
|
.coload-error { margin: 8px 12px; padding: 8px 10px; border: 1px solid var(--el-color-danger-light-5); border-radius: 4px; background: var(--el-color-danger-light-9); color: var(--el-color-danger); font-size: 12px; }
|
||||||
.coload-error :deep(.api-error-meta) { margin-top: 5px; }
|
.coload-error :deep(.api-error-meta) { margin-top: 5px; }
|
||||||
.order-code-cell { display: grid; justify-items: start; gap: 4px; }
|
.order-code-cell { display: grid; justify-items: start; gap: 4px; }
|
||||||
@@ -2814,20 +2854,20 @@ onBeforeUnmount(() => {
|
|||||||
.coload-detail-table td span { color: var(--cc-muted); }
|
.coload-detail-table td span { color: var(--cc-muted); }
|
||||||
.coload-detail-table :deep(.el-button) { min-height: 24px; padding: 2px 0; }
|
.coload-detail-table :deep(.el-button) { min-height: 24px; padding: 2px 0; }
|
||||||
.coload-toolbar-action { display: flex; min-width: 0; align-items: center; gap: 8px; }
|
.coload-toolbar-action { display: flex; min-width: 0; align-items: center; gap: 8px; }
|
||||||
.subsequent-fulfillment { min-height: 72px; margin-top: 16px; padding-top: 14px; border-top: 1px solid var(--cc-border); }
|
.subsequent-fulfillment { margin-top: 12px; padding-top: 10px; border-top: 1px solid var(--cc-border); }
|
||||||
.subsequent-fulfillment h3 { margin: 0 0 10px; font-size: 15px; }
|
.subsequent-fulfillment h3 { margin: 0 0 8px; font-size: 14px; }
|
||||||
.fulfillment-facts { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); margin: 0 0 10px; border: 1px solid var(--cc-border); border-radius: 6px; overflow: hidden; }
|
.fulfillment-facts { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); margin: 0; border-top: 1px solid var(--cc-border); border-left: 1px solid var(--cc-border); overflow: hidden; }
|
||||||
.batch-approval-summary { display: flex; align-items: center; justify-content: space-between; gap: 16px; margin: 12px 0 8px; color: var(--cc-muted); font-size: 13px; white-space: nowrap; }
|
.batch-approval-summary { display: flex; align-items: center; justify-content: space-between; gap: 16px; margin: 12px 0 8px; color: var(--cc-muted); font-size: 13px; white-space: nowrap; }
|
||||||
.batch-approval-summary strong { color: var(--cc-text); }
|
.batch-approval-summary strong { color: var(--cc-text); }
|
||||||
.batch-approval-table :deep(.cell) { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
.batch-approval-table :deep(.cell) { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||||
.fulfillment-facts > div { min-width: 0; padding: 9px 10px; border-right: 1px solid var(--cc-border); background: #fff; }
|
.fulfillment-facts > div { display: flex; align-items: baseline; gap: 10px; min-width: 0; padding: 8px 10px; border-right: 1px solid var(--cc-border); border-bottom: 1px solid var(--cc-border); background: #fff; }
|
||||||
.fulfillment-facts > div.wide { grid-column: span 2; }
|
.fulfillment-facts > div.wide { grid-column: span 2; }
|
||||||
.fulfillment-facts dt { color: var(--cc-muted); font-size: 12px; }
|
.fulfillment-facts dt { flex: 0 0 auto; color: var(--cc-muted); font-size: 12px; }
|
||||||
.fulfillment-facts dd { min-width: 0; margin: 4px 0 0; overflow-wrap: anywhere; font-size: 13px; }
|
.fulfillment-facts dd { min-width: 0; margin: 0; overflow-wrap: anywhere; font-size: 13px; }
|
||||||
.fulfillment-empty { margin: 0; padding: 12px; border: 1px dashed var(--cc-border); border-radius: 6px; color: var(--cc-muted); text-align: center; }
|
.fulfillment-empty { margin: 0; padding: 12px; border: 1px dashed var(--cc-border); border-radius: 6px; color: var(--cc-muted); text-align: center; }
|
||||||
.table-card :deep(.el-table) { --el-table-border-color: #e5e7eb; --el-table-header-bg-color: #f9fafb; --el-table-row-hover-bg-color: #f3f7fb; --el-table-tr-bg-color: #fff; color: #374151; font-size: 12px; }
|
.table-card :deep(.el-table) { --el-table-border-color: #e5e7eb; --el-table-header-bg-color: #f9fafb; --el-table-row-hover-bg-color: #f3f7fb; --el-table-tr-bg-color: #fff; color: #374151; font-size: 13px; }
|
||||||
.table-card :deep(.el-table th.el-table__cell) { height: 42px; padding: 0; background: #f9fafb; color: #374151; font-size: 12px; font-weight: 600; }
|
.table-card :deep(.el-table th.el-table__cell) { height: 36px; padding: 4px 0; background: #f9fafb; color: #374151; font-size: 13px; font-weight: 600; }
|
||||||
.table-card :deep(.el-table td.el-table__cell) { min-height: 42px; padding: 9px 0; }
|
.table-card :deep(.el-table td.el-table__cell) { height: 36px; padding: 4px 0; }
|
||||||
.table-card :deep(.el-table--striped .el-table__body tr.el-table__row--striped td.el-table__cell) { background: #fcfcfd; }
|
.table-card :deep(.el-table--striped .el-table__body tr.el-table__row--striped td.el-table__cell) { background: #fcfcfd; }
|
||||||
.table-card :deep(.el-table__body tr:hover > td.el-table__cell) { background: #f3f7fb; }
|
.table-card :deep(.el-table__body tr:hover > td.el-table__cell) { background: #f3f7fb; }
|
||||||
.row-actions { display: flex; align-items: center; gap: 4px; white-space: nowrap; }
|
.row-actions { display: flex; align-items: center; gap: 4px; white-space: nowrap; }
|
||||||
@@ -2844,34 +2884,27 @@ onBeforeUnmount(() => {
|
|||||||
.primary-cell { display: grid; min-width: 0; gap: 4px; }
|
.primary-cell { display: grid; min-width: 0; gap: 4px; }
|
||||||
.primary-cell strong { overflow-wrap: anywhere; }
|
.primary-cell strong { overflow-wrap: anywhere; }
|
||||||
.primary-cell span { color: var(--cc-muted); font-size: 12px; overflow-wrap: anywhere; }
|
.primary-cell span { color: var(--cc-muted); font-size: 12px; overflow-wrap: anywhere; }
|
||||||
.order-detail-header { display: flex; min-width: 0; min-height: 42px; align-items: center; justify-content: space-between; gap: 16px; margin-bottom: 12px; }
|
.order-detail-header { display: flex; min-width: 0; min-height: 40px; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 0; padding: 8px 12px; border: 1px solid var(--cc-border); border-bottom: 0; border-radius: 4px 4px 0 0; background: #fff; }
|
||||||
.order-detail-heading { display: flex; min-width: 0; align-items: center; gap: 10px; }
|
.order-detail-heading { display: flex; min-width: 0; align-items: center; gap: 10px; }
|
||||||
.back-button { flex: 0 0 auto; padding: 0; }
|
.back-button { flex: 0 0 auto; padding: 0; }
|
||||||
.order-mark { display: grid; width: 36px; height: 36px; flex: 0 0 36px; place-items: center; border-radius: 7px; background: #1d2939; color: #fff; }
|
|
||||||
.order-title { min-width: 0; }
|
.order-title { min-width: 0; }
|
||||||
.order-title h1 { margin: 0; overflow: hidden; color: #172033; font-size: 18px; line-height: 1.2; text-overflow: ellipsis; white-space: nowrap; }
|
.order-title h1 { margin: 0; color: #172033; font-size: 16px; line-height: 1.5; overflow-wrap: anywhere; }
|
||||||
.order-title p { margin: 2px 0 0; color: var(--cc-muted); font-size: 11px; }
|
|
||||||
.detail-header-actions { display: flex; flex: 0 0 auto; gap: 8px; }
|
.detail-header-actions { display: flex; flex: 0 0 auto; gap: 8px; }
|
||||||
.detail-header-actions :deep(.el-button + .el-button) { margin-left: 0; }
|
.detail-header-actions :deep(.el-button + .el-button) { margin-left: 0; }
|
||||||
.detail-load-error { min-height: 280px; }
|
.detail-load-error { min-height: 280px; }
|
||||||
.order-detail-content { min-height: 520px; }
|
.order-detail-content { min-width: 0; }
|
||||||
.detail-overview { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); overflow: hidden; margin-bottom: 16px; border: 1px solid var(--cc-border); border-radius: 8px; background: #fff; }
|
.detail-overview { display: flex; flex-wrap: wrap; gap: 6px 24px; margin: 0; padding: 8px 12px; border: 1px solid var(--cc-border); border-top: 0; background: #fff; }
|
||||||
.detail-overview article { min-width: 0; padding: 18px 20px; border-right: 1px solid var(--cc-border); }
|
.detail-tabs { display: flex; overflow-x: auto; overflow-y: hidden; border: 1px solid var(--cc-border); border-block: 0; background: #fff; scrollbar-width: thin; }
|
||||||
.detail-overview article:last-child { border-right: 0; }
|
.detail-tabs button { position: relative; display: inline-flex; min-width: max-content; min-height: 38px; flex: 0 0 auto; align-items: center; justify-content: center; gap: 6px; padding: 0 16px; border: 0; background: transparent; color: #475467; font-size: 13px; font-weight: 500; cursor: pointer; }
|
||||||
.detail-overview article > span, .detail-overview article > small { display: block; overflow: hidden; color: var(--cc-muted); font-size: 11px; text-overflow: ellipsis; white-space: nowrap; }
|
|
||||||
.detail-overview strong { display: block; margin: 7px 0 5px; overflow: hidden; color: #172033; font-size: 20px; line-height: 1.2; text-overflow: ellipsis; white-space: nowrap; }
|
|
||||||
.detail-overview strong small { margin-left: 5px; color: #475467; font-size: 11px; }
|
|
||||||
.detail-tabs { display: flex; overflow-x: auto; overflow-y: hidden; border: 1px solid var(--cc-border); border-bottom: 0; border-radius: 8px 8px 0 0; background: #fff; scrollbar-width: thin; }
|
|
||||||
.detail-tabs button { position: relative; display: inline-flex; min-width: max-content; min-height: 56px; flex: 0 0 auto; align-items: center; justify-content: center; gap: 7px; padding: 0 22px; border: 0; border-right: 1px solid #edf0f4; background: transparent; color: #667085; font-weight: 650; cursor: pointer; }
|
|
||||||
.detail-tabs button:last-child { border-right: 0; }
|
.detail-tabs button:last-child { border-right: 0; }
|
||||||
.detail-tabs button::after { position: absolute; right: 14px; bottom: 0; left: 14px; height: 3px; background: transparent; content: ''; }
|
.detail-tabs button::after { position: absolute; right: 12px; bottom: 0; left: 12px; height: 2px; background: transparent; content: ''; }
|
||||||
.detail-tabs button:hover { background: #fafbff; color: #344054; }
|
.detail-tabs button:hover { background: #fafbff; color: #344054; }
|
||||||
.detail-tabs button.active { background: #f7f8ff; color: var(--cc-primary); }
|
.detail-tabs button.active { background: #f7f8ff; color: var(--cc-primary); font-weight: 600; }
|
||||||
.detail-tabs button.active::after { background: var(--cc-primary); }
|
.detail-tabs button.active::after { background: var(--cc-primary); }
|
||||||
.detail-panel { min-width: 0; min-height: 390px; padding: 16px; border: 1px solid var(--cc-border); border-radius: 0 0 8px 8px; background: #fff; }
|
.detail-panel { min-width: 0; padding: 12px; border: 1px solid var(--cc-border); border-radius: 0 0 4px 4px; background: #fff; }
|
||||||
.panel-title { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; margin-bottom: 10px; padding-bottom: 8px; border-bottom: 1px solid var(--cc-border); }
|
.panel-title { display: flex; align-items: center; justify-content: space-between; gap: 12px; min-height: 28px; margin-bottom: 8px; }
|
||||||
.panel-title h2 { margin: 0; color: #344054; font-size: 16px; }
|
.panel-title h2 { margin: 0; color: #344054; font-size: 14px; font-weight: 600; }
|
||||||
.panel-title p { margin: 5px 0 0; color: var(--cc-muted); font-size: 11px; }
|
.panel-title p { margin: 0; color: var(--cc-muted); font-size: 12px; line-height: 1.5; }
|
||||||
.panel-title > svg { color: var(--cc-primary); }
|
.panel-title > svg { color: var(--cc-primary); }
|
||||||
.measurement-title-actions { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: 8px; }
|
.measurement-title-actions { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: 8px; }
|
||||||
.measurement-title-actions :deep(.el-button) { margin-left: 0; }
|
.measurement-title-actions :deep(.el-button) { margin-left: 0; }
|
||||||
@@ -2889,86 +2922,82 @@ onBeforeUnmount(() => {
|
|||||||
.fee-confirm-price-summary dt { color: var(--cc-muted); font-size: 11px; }
|
.fee-confirm-price-summary dt { color: var(--cc-muted); font-size: 11px; }
|
||||||
.fee-confirm-price-summary dd { margin: 4px 0 0; color: var(--cc-text); font-size: 13px; font-weight: 650; }
|
.fee-confirm-price-summary dd { margin: 4px 0 0; color: var(--cc-text); font-size: 13px; font-weight: 650; }
|
||||||
@media (max-width: 680px) { .fee-confirm-form { grid-template-columns: 1fr; } .fee-confirm-unit { right: 12px; } }
|
@media (max-width: 680px) { .fee-confirm-form { grid-template-columns: 1fr; } .fee-confirm-unit { right: 12px; } }
|
||||||
.fee-section-title { margin: 14px 0 8px; color: #344054; font-size: 13px; line-height: 1.4; }
|
.fee-section-title { margin: 12px 0 8px; color: #344054; font-size: 14px; line-height: 1.4; }
|
||||||
.fee-overview { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); margin: 8px 0 0; border: 1px solid var(--cc-border); }
|
.fee-overview { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); margin: 0; border: 1px solid var(--cc-border); }
|
||||||
.fee-overview > div { display: grid; grid-template-columns: auto 1fr; align-items: baseline; gap: 4px 12px; padding: 10px 12px; }
|
.fee-overview > div { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 12px; padding: 8px 10px; }
|
||||||
.fee-overview > div + div { border-left: 1px solid var(--cc-border); }
|
.fee-overview > div + div { border-left: 1px solid var(--cc-border); }
|
||||||
.fee-overview dt, .fee-overview small { color: var(--cc-muted); font-size: 11px; }
|
.fee-overview dt, .fee-overview small { color: var(--cc-muted); font-size: 12px; }
|
||||||
.fee-overview dd { margin: 0; font-size: 18px; font-weight: 700; text-align: right; }
|
.fee-overview dd { margin: 0; font-size: 16px; font-weight: 600; font-variant-numeric: tabular-nums; }
|
||||||
.fee-overview small { grid-column: 1 / -1; }
|
.fee-overview small { margin-left: auto; }
|
||||||
.fee-section-title--secondary { margin-top: 20px; padding-top: 14px; border-top: 1px solid var(--cc-border); }
|
.fee-section-title--secondary { margin-top: 12px; padding-top: 10px; border-top: 1px solid var(--cc-border); }
|
||||||
.order-info-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 0; margin: 0; overflow: hidden; border: 1px solid #edf0f4; background: #fff; }
|
.order-info-grid { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 0; margin: 0; overflow: hidden; border-top: 1px solid var(--cc-border); border-left: 1px solid var(--cc-border); background: #fff; }
|
||||||
.order-info-grid > div, .measurement-grid > div { display: flex; min-width: 0; min-height: 38px; align-items: baseline; gap: 8px; padding: 8px 12px; border-right: 1px solid #edf0f4; border-bottom: 1px solid #edf0f4; background: #fff; }
|
.order-info-grid > div, .measurement-grid > div { display: grid; grid-template-columns: 112px minmax(0, 1fr); min-width: 0; min-height: 36px; align-items: stretch; gap: 0; border-right: 1px solid var(--cc-border); border-bottom: 1px solid var(--cc-border); background: #fff; }
|
||||||
.order-info-grid > div.wide { grid-column: span 2; }
|
.order-info-grid > div.wide { grid-column: span 2; }
|
||||||
.order-info-grid dt, .measurement-grid dt { flex: 0 0 auto; margin: 0; color: var(--cc-muted); font-size: 11px; white-space: nowrap; }
|
.order-info-grid dt, .measurement-grid dt { margin: 0; padding: 8px; color: #667085; background: #f8fafc; font-size: 12px; line-height: 20px; }
|
||||||
.order-info-grid dd, .measurement-grid dd { min-width: 0; margin: 0; color: #25324a; font-size: 12px; font-weight: 650; line-height: 1.35; overflow-wrap: anywhere; }
|
.order-info-grid dd, .measurement-grid dd { min-width: 0; margin: 0; padding: 8px; color: #25324a; font-size: 13px; font-weight: 400; line-height: 20px; overflow-wrap: anywhere; }
|
||||||
.detail-table-wrap { overflow-x: auto; }
|
.detail-table-wrap { overflow-x: auto; }
|
||||||
.detail-table-wrap :deep(.el-table) { min-width: 720px; --el-table-header-bg-color: #f8fafc; }
|
.detail-table-wrap :deep(.el-table) { min-width: 720px; --el-table-header-bg-color: #f8fafc; }
|
||||||
.cargo-image { width: 72px; height: 56px; border: 1px solid var(--cc-border); border-radius: 6px; background: #f8fafc; }
|
.cargo-image { width: 40px; height: 32px; border: 1px solid var(--cc-border); border-radius: 2px; background: #f8fafc; cursor: zoom-in; }
|
||||||
.measurement-grid { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 1px; margin: 0; border: 1px solid var(--cc-border); background: var(--cc-border); }
|
.measurement-grid { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 1px; margin: 0; border: 1px solid var(--cc-border); background: var(--cc-border); }
|
||||||
.measurement-section { margin-top: 18px; }
|
.measurement-section { margin-top: 12px; }
|
||||||
.measurement-section-title { display: flex; width: 100%; min-height: 48px; align-items: center; gap: 12px; padding: 7px 12px; border: 1px solid var(--cc-border); border-radius: 7px 7px 0 0; background: #f8fafc; color: var(--cc-text); }
|
.measurement-section-title { display: flex; width: 100%; min-height: 36px; align-items: center; gap: 12px; padding: 4px 10px; border: 1px solid var(--cc-border); border-bottom: 0; background: #f8fafc; color: var(--cc-text); }
|
||||||
.measurement-section-title:hover { background: #f2f6fb; }
|
.measurement-section-title:hover { background: #f2f6fb; }
|
||||||
.measurement-section-title h3 { flex: 0 0 auto; margin: 0; color: var(--cc-text); font-size: 14px; }
|
.measurement-section-title h3 { flex: 0 0 auto; margin: 0; color: var(--cc-text); font-size: 13px; font-weight: 600; }
|
||||||
.measurement-section-summary { display: flex; min-width: 0; flex: 1; align-items: center; justify-content: flex-end; gap: 18px; color: var(--cc-muted); font-size: 12px; white-space: nowrap; }
|
.measurement-section-summary { display: flex; min-width: 0; flex: 1; flex-wrap: wrap; align-items: center; justify-content: flex-end; gap: 4px 16px; color: var(--cc-muted); font-size: 12px; }
|
||||||
.measurement-section-toggle { display: flex; min-width: 0; flex: 1; align-items: center; gap: 16px; padding: 2px; border: 0; background: transparent; color: inherit; font: inherit; text-align: left; cursor: pointer; }
|
.measurement-section-toggle { display: flex; min-width: 0; min-height: 28px; flex: 1; align-items: center; gap: 16px; padding: 0; border: 0; background: transparent; color: inherit; font: inherit; text-align: left; cursor: pointer; }
|
||||||
.measurement-section-toggle:focus-visible { outline: 2px solid var(--cc-primary); outline-offset: 2px; }
|
.measurement-section-toggle:focus-visible { outline: 2px solid var(--cc-primary); outline-offset: 2px; }
|
||||||
.measurement-section-toggle > svg { flex: 0 0 auto; color: var(--cc-muted); }
|
.measurement-section-toggle > svg { flex: 0 0 auto; margin-left: auto; color: var(--cc-muted); }
|
||||||
.measurement-section-mode { flex: 0 0 190px; }
|
.measurement-section-mode { flex: 0 0 190px; }
|
||||||
.measurement-section-title > .measurement-section-mode { display: none; }
|
.measurement-section-title > .measurement-section-mode { display: none; }
|
||||||
.measurement-rule-selector { display: flex; align-items: center; gap: 14px; margin: 8px 0 12px; padding: 12px 16px; border: 1px solid var(--cc-border); border-radius: 7px; background: #f8fafc; }
|
.measurement-rule-selector { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; margin: 0; padding: 8px 10px; border: 1px solid var(--cc-border); border-top: 0; background: #fff; }
|
||||||
.measurement-rule-selector__label { color: var(--cc-text); font-size: 12px; font-weight: 650; white-space: nowrap; }
|
.measurement-rule-selector__label { color: var(--cc-text); font-size: 12px; font-weight: 650; white-space: nowrap; }
|
||||||
.measurement-rule-selector__control { width: 270px; }
|
.measurement-rule-selector__control { width: 270px; }
|
||||||
.measurement-rule-selector__hint { color: var(--cc-muted); font-size: 11px; }
|
.measurement-rule-selector__hint { color: var(--cc-muted); font-size: 12px; }
|
||||||
.measurement-outer-dimension-input { width: 128px; max-width: 100%; }
|
.measurement-outer-dimension-input { width: 128px; max-width: 100%; }
|
||||||
.measurement-total { display: flex; min-height: 42px; align-items: center; justify-content: flex-end; gap: 24px; padding: 9px 14px; border: 1px solid var(--cc-border); background: #f8fafc; color: var(--cc-muted); font-size: 12px; }
|
.measurement-total { display: flex; min-height: 32px; flex-wrap: wrap; align-items: center; justify-content: flex-end; gap: 4px 20px; padding: 6px 10px; border: 1px solid var(--cc-border); border-top: 0; background: #f8fafc; color: #475467; font-size: 12px; font-variant-numeric: tabular-nums; }
|
||||||
.measurement-total { border-top: 0; border-radius: 0 0 7px 7px; }
|
|
||||||
.measurement-total strong { margin-right: auto; color: var(--cc-text); font-size: 13px; }
|
.measurement-total strong { margin-right: auto; color: var(--cc-text); font-size: 13px; }
|
||||||
.weight-evidence { margin-top: 20px; padding: 16px; border: 1px solid var(--cc-border); border-radius: 8px; background: #fbfcfe; }
|
.weight-evidence { margin-top: 12px; padding: 10px 0 0; border-top: 1px solid var(--cc-border); background: #fff; }
|
||||||
.weight-evidence__heading { display: flex; align-items: flex-start; justify-content: space-between; gap: 20px; margin-bottom: 14px; }
|
.weight-evidence__heading { display: flex; align-items: center; justify-content: space-between; gap: 16px; margin-bottom: 8px; }
|
||||||
.weight-evidence__heading h4 { margin: 0; color: var(--cc-text); font-size: 14px; }
|
.weight-evidence__heading h4 { margin: 0; color: var(--cc-text); font-size: 14px; }
|
||||||
.weight-evidence__heading p { margin: 5px 0 0; color: var(--cc-muted); font-size: 12px; }
|
.weight-evidence__heading p { margin: 5px 0 0; color: var(--cc-muted); font-size: 12px; }
|
||||||
.weight-evidence__heading dl { margin: 0; text-align: right; }
|
.weight-evidence__heading dl { margin: 0; text-align: right; }
|
||||||
.weight-evidence__heading dt { color: var(--cc-muted); font-size: 11px; }
|
.weight-evidence__heading dt { color: var(--cc-muted); font-size: 11px; }
|
||||||
.weight-evidence__heading dd { margin: 4px 0 0; color: var(--cc-text); font-size: 16px; font-weight: 700; }
|
.weight-evidence__heading dd { margin: 0; color: var(--cc-text); font-size: 13px; font-weight: 600; }
|
||||||
.weight-evidence-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 14px; }
|
.weight-evidence-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: 8px; }
|
||||||
.weight-evidence-card { min-width: 0; overflow: hidden; border: 1px solid var(--cc-border); border-radius: 8px; background: #fff; }
|
.weight-evidence-card { display: grid; grid-template-columns: 96px minmax(0, 1fr); align-items: center; min-width: 0; padding: 8px; border: 1px solid var(--cc-border); background: #fff; }
|
||||||
.weight-evidence-card > dl { display: grid; gap: 7px; margin: 0; padding: 12px; }
|
.weight-evidence-card > dl { display: grid; gap: 4px; margin: 0; padding: 0 0 0 8px; }
|
||||||
.weight-evidence-card > dl > div { display: flex; min-width: 0; justify-content: space-between; gap: 12px; }
|
.weight-evidence-card > dl > div { display: flex; min-width: 0; justify-content: space-between; gap: 12px; }
|
||||||
.weight-evidence-card dt { color: var(--cc-muted); font-size: 11px; }
|
.weight-evidence-card dt { color: var(--cc-muted); font-size: 11px; }
|
||||||
.weight-evidence-card dd { min-width: 0; margin: 0; color: var(--cc-text); font-size: 12px; font-weight: 650; text-align: right; overflow-wrap: anywhere; }
|
.weight-evidence-card dd { min-width: 0; margin: 0; color: var(--cc-text); font-size: 12px; font-weight: 650; text-align: right; overflow-wrap: anywhere; }
|
||||||
.measurement-original-preview { display: block; width: 100%; max-height: min(72vh, 760px); object-fit: contain; border-radius: 8px; background: #f2f4f7; }
|
.measurement-original-preview { display: block; width: 100%; max-height: min(72vh, 760px); object-fit: contain; border-radius: 8px; background: #f2f4f7; }
|
||||||
.hold-section { padding-bottom: 18px; border-bottom: 1px solid var(--cc-border); }
|
.hold-section { padding-bottom: 12px; border-bottom: 1px solid var(--cc-border); }
|
||||||
.other-notes-section { padding-top: 18px; }
|
.other-notes-section { padding-top: 10px; }
|
||||||
.hold-summary { display: flex; align-items: flex-start; justify-content: space-between; gap: 20px; padding: 12px 14px; border: 1px solid var(--cc-border); border-radius: 6px; background: #f8fafc; }
|
.hold-summary { display: flex; align-items: center; justify-content: space-between; gap: 16px; padding: 8px 10px; border: 1px solid var(--cc-border); background: #fff; }
|
||||||
.hold-summary dl { display: grid; min-width: 0; flex: 1; grid-template-columns: 150px minmax(280px, 1fr); gap: 18px; margin: 0; }
|
.hold-summary dl { display: grid; min-width: 0; flex: 1; grid-template-columns: 210px minmax(280px, 1fr); gap: 12px; margin: 0; }
|
||||||
.hold-summary dl > div { min-width: 0; }
|
.hold-summary dl > div { display: flex; align-items: baseline; gap: 10px; min-width: 0; }
|
||||||
.hold-summary dt { margin-bottom: 5px; color: var(--cc-muted); font-size: 12px; }
|
.hold-summary dt { flex: 0 0 auto; margin: 0; color: var(--cc-muted); font-size: 12px; }
|
||||||
.hold-summary dd { margin: 0; color: var(--cc-text); font-size: 13px; overflow-wrap: anywhere; }
|
.hold-summary dd { margin: 0; color: var(--cc-text); font-size: 13px; overflow-wrap: anywhere; }
|
||||||
.hold-actions { display: flex; flex: 0 0 auto; gap: 8px; }
|
.hold-actions { display: flex; flex: 0 0 auto; gap: 8px; }
|
||||||
.hold-actions :deep(.el-button) { margin-left: 0; }
|
.hold-actions :deep(.el-button) { margin-left: 0; }
|
||||||
.hold-block-reason { margin: 12px 0 0; padding: 9px 12px; border: 1px solid var(--el-color-warning-light-5); border-radius: 4px; background: var(--el-color-warning-light-9); color: var(--el-color-warning-dark-2); font-size: 12px; }
|
.hold-block-reason { margin: 12px 0 0; padding: 9px 12px; border: 1px solid var(--el-color-warning-light-5); border-radius: 4px; background: var(--el-color-warning-light-9); color: var(--el-color-warning-dark-2); font-size: 12px; }
|
||||||
.hold-history-title { margin: 16px 0 8px; color: #344054; font-size: 14px; }
|
.hold-history-title { margin: 16px 0 8px; color: #344054; font-size: 14px; }
|
||||||
.hold-history-table { min-height: 90px; overflow-x: auto; }
|
.hold-history-table { min-width: 0; overflow-x: auto; }
|
||||||
.hold-command-form { margin-top: 16px; }
|
.hold-command-form { margin-top: 16px; }
|
||||||
.hold-evidence-hint { margin: -4px 0 0; color: var(--cc-muted); font-size: 12px; }
|
.hold-evidence-hint { margin: -4px 0 0; color: var(--cc-muted); font-size: 12px; }
|
||||||
.history-list, .notes-list { display: grid; gap: 0; margin: 0; padding: 0; list-style: none; }
|
.notes-list { display: grid; gap: 0; margin: 0; padding: 0; list-style: none; }
|
||||||
.history-list li { position: relative; display: grid; grid-template-columns: 16px minmax(0, 1fr); gap: 12px; padding-bottom: 20px; }
|
.notes-list p { margin: 4px 0 0; color: var(--cc-muted); font-size: 12px; line-height: 1.55; overflow-wrap: anywhere; }
|
||||||
.history-list li:not(:last-child)::after { position: absolute; top: 15px; bottom: 0; left: 7px; width: 1px; background: var(--cc-border); content: ''; }
|
|
||||||
.history-marker { z-index: 1; width: 14px; height: 14px; margin-top: 3px; border: 3px solid var(--cc-primary); border-radius: 50%; background: #fff; }
|
|
||||||
.history-line { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
|
|
||||||
.history-list strong { color: #344054; font-size: 13px; }
|
|
||||||
.history-list p, .notes-list p { margin: 4px 0 0; color: var(--cc-muted); font-size: 12px; line-height: 1.55; overflow-wrap: anywhere; }
|
|
||||||
.history-list .history-remark { margin-top: 8px; padding: 10px 12px; background: #f8fafc; color: #475467; }
|
|
||||||
.notes-list li { padding: 14px 0; border-bottom: 1px solid #edf0f4; }
|
.notes-list li { padding: 14px 0; border-bottom: 1px solid #edf0f4; }
|
||||||
.notes-list li:first-child { padding-top: 0; }
|
.notes-list li:first-child { padding-top: 0; }
|
||||||
.notes-list li:last-child { padding-bottom: 0; border-bottom: 0; }
|
.notes-list li:last-child { padding-bottom: 0; border-bottom: 0; }
|
||||||
.notes-list li > p { margin: 0; color: var(--cc-text); font-size: 13px; }
|
.notes-list li > p { margin: 0; color: var(--cc-text); font-size: 13px; }
|
||||||
.notes-list span { display: block; margin-top: 5px; color: var(--cc-muted); font-size: 12px; }
|
.notes-list span { display: block; margin-top: 5px; color: var(--cc-muted); font-size: 12px; }
|
||||||
@media (max-width: 960px) { .detail-overview { grid-template-columns: repeat(2, minmax(0, 1fr)); } .detail-overview article:nth-child(2) { border-right: 0; } .detail-overview article:nth-child(-n+2) { border-bottom: 1px solid var(--cc-border); } .order-info-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } .measurement-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
|
@media (max-width: 960px) { .detail-overview { grid-template-columns: repeat(2, minmax(0, 1fr)); } .order-info-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } .measurement-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
|
||||||
@media (max-width: 720px) { .page-intro { align-items: stretch; flex-direction: column; } .page-intro :deep(.el-button) { width: 100%; min-height: 44px; } .status-tab { min-height: 46px; padding: 0 14px; } .table-actions :deep(.el-button) { width: 100%; min-height: 40px; } .row-actions { flex-wrap: wrap; } .order-detail-header { align-items: stretch; flex-direction: column; } .order-detail-heading { display: grid; grid-template-columns: auto 36px minmax(0, 1fr) auto; } .detail-header-actions { display: grid; grid-template-columns: 1fr; } .detail-header-actions :deep(.el-button) { min-height: 40px; } .detail-overview, .order-info-grid, .measurement-grid { grid-template-columns: 1fr; } .detail-overview article { border-right: 0; border-bottom: 1px solid var(--cc-border); } .detail-overview article:last-child { border-bottom: 0; } .order-info-grid > div.wide { grid-column: auto; } .detail-tabs button { padding: 0 18px; } .detail-panel { padding: 16px; } .measurement-section-title { align-items: flex-start; flex-wrap: wrap; padding: 12px; } .measurement-section-summary { width: 100%; flex-basis: calc(100% - 34px); flex-wrap: wrap; justify-content: flex-start; gap: 6px 16px; white-space: normal; } .measurement-section-title > svg { margin-left: auto; } .measurement-total { flex-wrap: wrap; justify-content: flex-start; gap: 6px 16px; } .measurement-total strong { width: 100%; } }
|
@media (max-width: 720px) { .page-intro { align-items: stretch; flex-direction: column; } .page-intro :deep(.el-button) { width: 100%; min-height: 44px; } .status-tab { min-height: 46px; padding: 0 14px; } .table-actions :deep(.el-button) { width: 100%; min-height: 40px; } .row-actions { flex-wrap: wrap; } .order-detail-header { align-items: stretch; flex-direction: column; } .order-detail-heading { flex-wrap: wrap; } .detail-header-actions { display: grid; grid-template-columns: 1fr; } .detail-header-actions :deep(.el-button) { min-height: 40px; } .order-info-grid, .measurement-grid { grid-template-columns: 1fr; } .order-info-grid > div.wide { grid-column: auto; } .detail-tabs button { padding: 0 18px; } .detail-panel { padding: 16px; } .measurement-section-title { align-items: flex-start; flex-wrap: wrap; padding: 12px; } .measurement-section-summary { width: 100%; flex-basis: calc(100% - 34px); flex-wrap: wrap; justify-content: flex-start; gap: 6px 16px; white-space: normal; } .measurement-section-title > svg { margin-left: auto; } .measurement-total { flex-wrap: wrap; justify-content: flex-start; gap: 6px 16px; } .measurement-total strong { width: 100%; } }
|
||||||
@media (max-width: 720px) { .weight-evidence__heading { flex-direction: column; } .weight-evidence__heading dl { text-align: left; } }
|
@media (max-width: 720px) { .weight-evidence__heading { flex-direction: column; } .weight-evidence__heading dl { text-align: left; } }
|
||||||
@media (max-width: 720px) { .fee-overview { grid-template-columns: 1fr; } .fee-overview > div + div { border-top: 1px solid var(--cc-border); border-left: 0; } }
|
@media (max-width: 720px) { .fee-overview { grid-template-columns: 1fr; } .fee-overview > div + div { border-top: 1px solid var(--cc-border); border-left: 0; } }
|
||||||
@media (max-width: 720px) { .measurement-section-title { align-items: stretch; flex-direction: column; } .measurement-section-toggle { align-items: flex-start; flex-wrap: wrap; } .measurement-section-toggle > svg { margin-left: auto; } .measurement-section-mode { width: 100%; flex-basis: auto; } }
|
@media (max-width: 720px) { .measurement-section-title { align-items: stretch; flex-direction: column; } .measurement-section-toggle { align-items: flex-start; flex-wrap: wrap; } .measurement-section-toggle > svg { margin-left: auto; } .measurement-section-mode { width: 100%; flex-basis: auto; } }
|
||||||
@media (max-width: 420px) { .order-detail-heading { grid-template-columns: auto minmax(0, 1fr) auto; } .order-mark { display: none; } .weight-evidence { padding: 10px; } .weight-evidence-grid { grid-template-columns: 1fr; } }
|
@media (max-width: 420px) { .weight-evidence { padding: 10px; } .weight-evidence-grid { grid-template-columns: 1fr; } }
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
<style src="../styles/orderDensity.css"></style>
|
||||||
|
|||||||
@@ -0,0 +1,97 @@
|
|||||||
|
/* Order workspace pilot. Keep these rules local until the visual baseline is approved. */
|
||||||
|
.unified-orders-page .inbound-fee-panel .fee-summary {
|
||||||
|
grid-template-columns: minmax(230px, 1.3fr) repeat(4, minmax(150px, 1fr));
|
||||||
|
border-top: 0;
|
||||||
|
}
|
||||||
|
.unified-orders-page .inbound-fee-panel .fee-total,
|
||||||
|
.unified-orders-page .inbound-fee-panel .fee-summary-item { padding: 8px 10px; gap: 4px; }
|
||||||
|
.unified-orders-page .inbound-fee-panel .fee-summary span,
|
||||||
|
.unified-orders-page .inbound-fee-panel .fee-summary small { font-size: 12px; }
|
||||||
|
.unified-orders-page .inbound-fee-panel .fee-total strong { font-size: 17px; }
|
||||||
|
.unified-orders-page .inbound-fee-panel .fee-summary-item strong { font-size: 14px; }
|
||||||
|
.unified-orders-page .inbound-fee-panel .calculation-flow > div { min-height: 36px; padding: 6px 10px; }
|
||||||
|
.unified-orders-page .inbound-fee-panel .calculation-flow span { font-size: 12px; }
|
||||||
|
.unified-orders-page .inbound-fee-panel .fee-detail-grid > section { padding: 10px 12px 10px 0; }
|
||||||
|
.unified-orders-page .inbound-fee-panel .fee-detail-grid > section + section { padding-right: 0; padding-left: 12px; }
|
||||||
|
.unified-orders-page .inbound-fee-panel .fee-detail-grid dl { margin-top: 8px; }
|
||||||
|
.unified-orders-page .inbound-fee-panel .fee-detail-grid dl > div { padding: 6px 0; }
|
||||||
|
.unified-orders-page .inbound-fee-panel .fee-timeline ol { margin-top: 8px; }
|
||||||
|
.unified-orders-page .inbound-fee-panel .fee-advanced > summary { padding: 8px 2px; }
|
||||||
|
.unified-orders-page .fee-detail-manager { gap: 8px; }
|
||||||
|
.unified-orders-page .fulfillment-tree-panel { gap: 8px; }
|
||||||
|
.unified-orders-page .fulfillment-tree-panel .tree-summary {
|
||||||
|
display: flex; flex-wrap: wrap; gap: 6px 28px; padding: 8px 10px;
|
||||||
|
border-radius: 0; background: #fff;
|
||||||
|
}
|
||||||
|
.unified-orders-page .fulfillment-tree-panel .tree-summary > div { display: flex; align-items: baseline; gap: 8px; }
|
||||||
|
.unified-orders-page .fulfillment-tree-panel .tree-summary strong { overflow: visible; white-space: normal; overflow-wrap: anywhere; font-size: 13px; font-weight: 600; }
|
||||||
|
.unified-orders-page .detail-panel .el-empty { padding: 12px 0; }
|
||||||
|
.unified-orders-page .detail-panel .el-empty__image { display: none; }
|
||||||
|
.unified-orders-page .detail-panel .el-empty__description { margin-top: 0; }
|
||||||
|
|
||||||
|
/* Dialogs are teleported; opt in explicitly instead of styling all AppDialog instances. */
|
||||||
|
.order-density-dialog.el-dialog {
|
||||||
|
--el-font-size-base: 13px;
|
||||||
|
--el-component-size: 32px;
|
||||||
|
padding: 0;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
.order-density-dialog.el-dialog:not(.is-fullscreen) { margin-top: 6vh; max-height: 88vh; }
|
||||||
|
.order-density-dialog.el-dialog .el-dialog__header { margin: 0; padding: 10px 48px 10px 16px; border-bottom: 1px solid var(--cc-border); }
|
||||||
|
.order-density-dialog.el-dialog .el-dialog__title { font-size: 16px; line-height: 24px; font-weight: 600; }
|
||||||
|
.order-density-dialog.el-dialog .el-dialog__body { padding: 12px 16px; }
|
||||||
|
.order-density-dialog.el-dialog .el-dialog__footer { padding: 8px 16px; border-top: 1px solid var(--cc-border); }
|
||||||
|
.order-density-dialog.el-dialog .el-table { font-size: 13px; }
|
||||||
|
.order-density-dialog.el-dialog .el-table__cell { height: 36px; padding: 4px 0; }
|
||||||
|
.order-density-dialog.el-dialog .el-table .cell { line-height: 22px; }
|
||||||
|
.order-density-dialog.el-dialog .el-form-item { margin-bottom: 14px; }
|
||||||
|
.order-density-dialog.el-dialog .el-form-item__label { font-size: 13px; }
|
||||||
|
.order-density-dialog.el-dialog .el-alert { padding: 8px 10px; border-radius: 3px; }
|
||||||
|
.order-density-dialog.el-dialog .el-empty { padding: 12px 0; }
|
||||||
|
.order-density-dialog.el-dialog .el-empty__image { display: none; }
|
||||||
|
.order-density-dialog.el-dialog .el-empty__description { margin-top: 0; }
|
||||||
|
.order-density-dialog.el-dialog .batch-approval-table .cell { white-space: normal; overflow-wrap: anywhere; }
|
||||||
|
.order-density-dialog.el-dialog .batch-approval-table .el-tag { height: auto; min-height: 24px; white-space: normal; line-height: 20px; }
|
||||||
|
|
||||||
|
@media (min-width: 961px) {
|
||||||
|
.order-measurement-dialog.el-dialog:not(.is-fullscreen) {
|
||||||
|
/* The inherited dialog width is inline; only this order entry opts into the wider workspace. */
|
||||||
|
width: min(1560px, calc(100vw - 48px)) !important;
|
||||||
|
height: 92vh; max-height: 92vh; margin-top: 4vh;
|
||||||
|
}
|
||||||
|
.order-measurement-dialog .measurement-dialog-weight { padding-bottom: 10px; }
|
||||||
|
.order-measurement-dialog .measurement-section { padding: 10px 0; }
|
||||||
|
.order-measurement-dialog .measurement-summary-section { gap: 8px; }
|
||||||
|
.order-measurement-dialog .measurement-section__heading { align-items: center; margin-bottom: 8px; }
|
||||||
|
.order-measurement-dialog .measurement-metrics { grid-template-columns: repeat(5, minmax(0, 1fr)); }
|
||||||
|
.order-measurement-dialog .measurement-metrics > div { min-height: 36px; padding: 6px 10px; display: flex; align-items: baseline; gap: 8px; }
|
||||||
|
.order-measurement-dialog .measurement-metrics dd { font-size: 14px; }
|
||||||
|
.order-measurement-dialog .measurement-metrics small { font-size: 12px; }
|
||||||
|
.order-measurement-dialog .measurement-data-grid { gap: 8px 12px; }
|
||||||
|
.order-measurement-dialog .measurement-declaration { padding-top: 8px; }
|
||||||
|
.order-measurement-dialog .measurement-declaration h4 { margin-bottom: 6px; font-size: 13px; }
|
||||||
|
.order-measurement-dialog .measurement-panel > .el-alert { margin: 8px 0; }
|
||||||
|
.order-measurement-dialog .measurement-summary-header span { font-size: 12px; }
|
||||||
|
.order-measurement-dialog .measurement-table .el-table__header th { padding: 4px 0; font-size: 13px; }
|
||||||
|
.order-measurement-dialog .weight-heading { flex-wrap: wrap; gap: 8px; padding-bottom: 8px; }
|
||||||
|
.order-measurement-dialog .weight-heading__actions { flex-wrap: wrap; gap: 12px; }
|
||||||
|
.order-measurement-dialog .pallet-picker { width: 340px; }
|
||||||
|
.order-measurement-dialog .weight-list { margin-top: 8px; }
|
||||||
|
.order-measurement-dialog .weight-grid { grid-template-columns: repeat(auto-fill, minmax(420px, 1fr)); gap: 8px; }
|
||||||
|
.order-measurement-dialog .weight-card { display: grid; grid-template-columns: 120px minmax(0, 1fr); gap: 10px; align-items: start; padding: 8px; border-radius: 2px; }
|
||||||
|
.order-measurement-dialog .weight-card .weight-thumb { width: 120px; border-radius: 2px; }
|
||||||
|
.order-measurement-dialog .weight-card__body { min-width: 0; padding: 0; text-align: left; }
|
||||||
|
.order-measurement-dialog .weight-card__value { min-height: 28px; padding-right: 32px; text-align: left; }
|
||||||
|
.order-measurement-dialog .weight-card__value strong { font-size: 14px; }
|
||||||
|
.order-measurement-dialog .weight-card__facts { gap: 3px; margin-top: 4px; padding-top: 4px; }
|
||||||
|
.order-measurement-dialog .weight-card__facts div { font-size: 12px; }
|
||||||
|
.order-measurement-dialog .weight-card__correct { width: auto; margin-top: 6px; }
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 960px) {
|
||||||
|
.unified-orders-page .inbound-fee-panel .fee-summary { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
||||||
|
}
|
||||||
|
@media (max-width: 720px) {
|
||||||
|
.unified-orders-page .inbound-fee-panel .fee-summary { grid-template-columns: 1fr; }
|
||||||
|
.unified-orders-page .inbound-fee-panel .fee-detail-grid > section { padding: 10px 0; }
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user