This commit is contained in:
2026-09-30 14:01:58 +08:00
parent f14e1a2ea3
commit 8c51e9c1cf
109 changed files with 1525 additions and 651 deletions
@@ -906,8 +906,14 @@ onMounted(() => { void load() })
<template>
<AdminPage class="admin-density"><OperationErrorToast :details="actionError" /><ListPageLayout aria-label="尾程订单列表">
<template #toolbar>
<section class="surface-panel last-mile-toolbar" aria-label="尾程订单状态与筛选">
<DataTable appearance="integrated" :stripe="false" :results-visible="!loadError" :column-settings-mode="statusTab === '柜子' ? 'hide' : 'show'" v-model:page="page" v-model:page-size="pageSize" :data="rows" :columns="columns" :loading="loading" :total="statusTab === '柜子' ? undefined : total" preference-key="overseas-last-mile-orders" row-key="id" empty-text="暂无尾程订单" @selection-change="onSelectionChange" @update:page="load()" @update:page-size="page = 1; load()">
<template #toolbar-start><h1>尾程订单</h1></template>
<template #filters>
<section class="last-mile-toolbar integrated-query-group" aria-label="尾程订单状态与筛选">
<el-radio-group v-model="statusTab" aria-label="尾程订单状态" @change="changeStatusTab">
<el-radio-button value="全部">全部</el-radio-button>
<el-radio-button value="待分配">待分配</el-radio-button>
@@ -917,15 +923,14 @@ onMounted(() => { void load() })
<QueryToolbar v-model="filters" :fields="fields" :available-fields="allFields" :loading="loading" ariaLabel="尾程订单筛选" preference-key="overseas-last-mile-orders-query" :search-on-reset="false" @search="search" @reset="reset" />
</section>
</template>
<template v-if="loadError" #state>
<template #notices><template v-if="loadError">
<LoadErrorState title="尾程订单加载失败" :message="loadError.message" :loading="loading" @retry="load">
<template #details><ApiErrorMeta :details="loadError" /></template>
</LoadErrorState>
</template>
<LoadErrorState v-if="actionError" title="尾程订单操作失败" :message="actionError.message" compact>
</template><LoadErrorState v-if="actionError" title="尾程订单操作失败" :message="actionError.message" compact>
<template #details><ApiErrorMeta :details="actionError" /></template>
</LoadErrorState>
<section v-if="!loadError && statusTab === '柜子'" class="surface-panel container-workbench" :aria-busy="loading">
</LoadErrorState></template>
<template v-if="statusTab === '柜子'" #results><section class="container-workbench" :aria-busy="loading">
<header class="container-workbench__header">
<div><strong>货柜工作台</strong><span>按柜查看和修改海外接柜仓;整柜计划齐全后自动生成托盘码</span></div>
<el-tag type="warning" effect="plain">{{ containerTotal }} 个货柜</el-tag>
@@ -960,16 +965,16 @@ onMounted(() => { void load() })
<span>共 {{ containerTotal }} 个货柜</span>
<el-pagination :current-page="page" :page-size="pageSize" :page-sizes="[10, 20, 50]" :total="containerTotal" layout="sizes, prev, pager, next" background @current-change="changeContainerPage" @size-change="changeContainerPageSize" />
</div>
</section>
<section v-else-if="!loadError" class="surface-panel table-panel"><DataTable v-model:page="page" v-model:page-size="pageSize" :data="rows" :columns="columns" :loading="loading" :total="total" preference-key="overseas-last-mile-orders" row-key="id" empty-text="暂无尾程订单" @selection-change="onSelectionChange" @update:page="load()" @update:page-size="page = 1; load()">
<template #toolbar-actions><el-button v-if="access.can(permission.lastMileOrderExport)" plain :loading="exportLoading" @click="exportOrders"><Download :size="16" />导出订单</el-button><el-button v-if="access.can(permission.lastMileOrderTransfer)" plain :disabled="!selectedRows.length || hasSelectedRejectedReturn() || actionLoading" @click="openTransfer">转仓</el-button><el-button v-if="access.can(permission.inboundOrderCreate)" type="primary" plain :disabled="selectedRows.length !== 1 || hasSelectedRejectedReturn() || hasSelectedInboundPlan() || actionLoading" @click="openInboundPlan()">生成入库计划</el-button><el-button v-if="statusTab === '待分配' && access.can(permission.inboundOrderCreate)" type="primary" :disabled="actionLoading" @click="openContainerConversion">按柜批量生成</el-button></template>
</section></template>
<template v-if="statusTab !== '柜子'" #toolbar-actions><el-button v-if="access.can(permission.lastMileOrderExport)" plain :loading="exportLoading" @click="exportOrders"><Download :size="16" />导出订单</el-button><el-button v-if="access.can(permission.lastMileOrderTransfer)" plain :disabled="!selectedRows.length || hasSelectedRejectedReturn() || actionLoading" @click="openTransfer">转仓</el-button><el-button v-if="access.can(permission.inboundOrderCreate)" type="primary" plain :disabled="selectedRows.length !== 1 || hasSelectedRejectedReturn() || hasSelectedInboundPlan() || actionLoading" @click="openInboundPlan()">生成入库计划</el-button><el-button v-if="statusTab === '待分配' && access.can(permission.inboundOrderCreate)" type="primary" :disabled="actionLoading" @click="openContainerConversion">按柜批量生成</el-button></template>
<template #relabelOrderNo="{ row }"><div v-for="application in row.relabelApplications" :key="application.relabelNo" class="relabel-suborder">{{ application.relabelNo }}</div><span v-if="!row.relabelApplications?.length">{{ row.relabelOrderNo }}</span></template>
<template #relabelStatus="{ row }"><div v-for="application in row.relabelApplications" :key="application.relabelNo" class="relabel-suborder">{{ application.statusLabel }} · {{ application.quantity }} 件</div><span v-if="!row.relabelApplications?.length">{{ row.relabelStatus }}</span></template>
<template #actions="{ row }"><RowActions :actions="lastMileRowActions(row as LastMileOrderListItem)" :more-label="`更多尾程订单操作 · ${(row as LastMileOrderListItem).orderNo || '当前记录'}`" @action="handleLastMileRowAction($event, row as LastMileOrderListItem)" /></template>
<template #actions="{ row }"><RowActions text-actions :actions="lastMileRowActions(row as LastMileOrderListItem)" :more-label="`更多尾程订单操作 · ${(row as LastMileOrderListItem).orderNo || '当前记录'}`" @action="handleLastMileRowAction($event, row as LastMileOrderListItem)" /></template>
<template #orderNo="{ row }"><OrderReferenceText :value="String(row.orderNo || '')" copyable /></template>
<template #cartonDifference="{ row }"><span :class="{ 'package-difference--nonzero': typeof row.cartonDifference === 'number' && row.cartonDifference !== 0 }">{{ packageDifferenceText(row.cartonDifference) }}</span></template>
<template #pagination-left>共 {{ total }} 条尾程订单 · 已选 {{ selectedRows.length }} 条</template>
</DataTable></section>
</DataTable>
</ListPageLayout></AdminPage>
<WarehouseTransferReviewDialog v-if="transferReviewTarget" v-model="transferReviewOpen" :customer-id="transferReviewTarget.customerId" :customer-name="transferReviewTarget.owner" :transfer-no="transferReviewTarget.orderNo" @reviewed="load()" />
<AppDialog class="admin-density-dialog" v-model="transferOpen" title="转仓" confirm-text="确认转仓" :loading="transferWarehouseLoading" :confirm-loading="actionLoading" :confirm-disabled="!transferForm.warehouseId || Boolean(transferWarehouseError)" @confirm="submitTransfer">
@@ -1106,7 +1111,7 @@ onMounted(() => { void load() })
<style scoped>
.relabel-suborder { min-height: 30px; line-height: 30px; white-space: nowrap; }
.last-mile-toolbar { padding: var(--admin-space-5) var(--admin-space-5) 0; }
.last-mile-toolbar { min-width: 0; }
.row-actions { white-space: nowrap; }
.command-form { margin-top: var(--admin-space-5); }
.command-form__grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0 var(--admin-space-5); }
@@ -1117,7 +1122,7 @@ onMounted(() => { void load() })
.manual-node-table__head { position: sticky; top: 0; z-index: 1; background: var(--cc-surface); color: var(--cc-muted); font-size: var(--admin-font-body); font-weight: 600; }
.manual-node-table__row { border-top: 1px solid var(--cc-border); }
.manual-node-table__row :deep(.el-date-editor) { width: 100%; }
.last-mile-toolbar :deep(.query-toolbar) { margin-top: var(--admin-space-5); padding: var(--admin-space-5) 0 var(--admin-space-5); border: 0; border-top: 1px solid var(--cc-border); border-radius: 0; }
.container-convert-hint { margin: 0 0 var(--admin-space-5); color: var(--cc-muted); line-height: 1.6; }
.container-convert-alert { margin-top: var(--admin-space-5); }
.container-preview { margin-top: var(--admin-space-5); }