From 30afc1728cd2b42badd9d93ddb2acad64dbdfe96 Mon Sep 17 00:00:00 2001 From: zzh <1368735060@qq.com> Date: Sat, 11 Jul 2026 16:14:44 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BB=93=E5=82=A8=E4=B8=8E?= =?UTF-8?q?=E5=BA=97=E9=93=BA=E5=88=97=E8=A1=A8=E9=A1=B5=EF=BC=8C=E5=B9=B6?= =?UTF-8?q?=E8=A1=A5=E9=BD=90=20warehouse=20=E4=BB=A3=E7=90=86=E4=B8=8E?= =?UTF-8?q?=E5=88=97=E5=AE=BD=E8=87=AA=E9=80=82=E5=BA=94=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 管理端可正常查看列表;分页移至工具栏右侧,列定义对齐 WMS,Handsontable 开启 stretch-columns。 Co-authored-by: Cursor --- nginx-default.conf | 23 +- nginx-docker.conf | 9 +- src/locale/en-US.ts | 31 +++ src/locale/zh-CN.ts | 31 +++ src/styles/warehouse-list-page.less | 73 +++++++ src/utils/warehouseListMapper.ts | 73 +++++++ src/views/order/platformStore/index.vue | 148 +++++++++---- src/views/order/platformStoreOrder/index.vue | 208 +++++++++++++------ src/views/warehouse/inboundNotice/index.vue | 157 +++++++++----- src/views/warehouse/inventory/index.vue | 101 ++++++--- src/views/warehouse/parcel/index.vue | 144 +++++++++---- src/views/warehouse/sku/index.vue | 122 ++++++----- vite.config.ts | 39 ++++ 13 files changed, 891 insertions(+), 268 deletions(-) create mode 100644 src/styles/warehouse-list-page.less create mode 100644 src/utils/warehouseListMapper.ts diff --git a/nginx-default.conf b/nginx-default.conf index dd0e5ba..87b5653 100644 --- a/nginx-default.conf +++ b/nginx-default.conf @@ -119,6 +119,27 @@ server { location ^~ /order/templateMapping { try_files /index.html =200; } + location ^~ /order/platformStore { + try_files /index.html =200; + } + location ^~ /order/platformStoreOrder { + try_files /index.html =200; + } + + # ============ 仓储前端路由(^~ 须比下方 /warehouse/ API 正则代理更优先)============ + # 页面路径与 API 前缀相同者用精确匹配,避免 /warehouse/sku/list 等接口被误判为 SPA + location = /warehouse/parcel { + try_files /index.html =200; + } + location = /warehouse/inventory { + try_files /index.html =200; + } + location = /warehouse/sku { + try_files /index.html =200; + } + location ^~ /warehouse/inboundNotice { + try_files /index.html =200; + } # ============ 系统管理前端路由(^~ 须比下方 /system/ API 正则代理更优先)============ # 页面路径为 camelCase;API 为 kebab(如 /system/dict/list、/system/batch-email/…),勿用宽泛前缀匹配 dict @@ -198,7 +219,7 @@ server { } # ============ Admin API 同源反代(/rbac /order /xhs … → api.nanafa.com/admin)============ - location ~ ^/(rbac|order|monitor|customer|recharge|channel|system)/ { + location ~ ^/(rbac|order|monitor|customer|recharge|channel|system|warehouse)/ { rewrite ^/(.*)$ /admin/$1 break; proxy_pass http://api.nanafa.com; proxy_http_version 1.1; diff --git a/nginx-docker.conf b/nginx-docker.conf index ef23466..11b68e3 100644 --- a/nginx-docker.conf +++ b/nginx-docker.conf @@ -46,6 +46,13 @@ server { location ^~ /order/myOrders { try_files /index.html =200; } location ^~ /order/batchJobs { try_files /index.html =200; } location ^~ /order/templateMapping { try_files /index.html =200; } + location ^~ /order/platformStore { try_files /index.html =200; } + location ^~ /order/platformStoreOrder { try_files /index.html =200; } + + location = /warehouse/parcel { try_files /index.html =200; } + location = /warehouse/inventory { try_files /index.html =200; } + location = /warehouse/sku { try_files /index.html =200; } + location ^~ /warehouse/inboundNotice { try_files /index.html =200; } location ^~ /channel/salesProducts { try_files /index.html =200; } location ^~ /channel/serviceChannels { try_files /index.html =200; } @@ -101,7 +108,7 @@ server { proxy_set_header X-Forwarded-Proto $scheme; } - location ~ ^/(rbac|order|monitor|customer|recharge|channel|system)/ { + location ~ ^/(rbac|order|monitor|customer|recharge|channel|system|warehouse)/ { rewrite ^/(.*)$ /admin/$1 break; proxy_pass http://api.nanafa.com; proxy_http_version 1.1; diff --git a/src/locale/en-US.ts b/src/locale/en-US.ts index 6d2c3e6..059a3f0 100644 --- a/src/locale/en-US.ts +++ b/src/locale/en-US.ts @@ -99,6 +99,10 @@ export default { 'warehouse.common.confirm': 'Confirm', 'warehouse.common.detail': 'Detail', 'warehouse.common.warehouse': 'Warehouse', + 'warehouse.common.yes': 'Yes', + 'warehouse.common.no': 'No', + 'warehouse.sku.table.dimensions': 'Size(cm)', + 'warehouse.sku.table.createTime': 'Created At', 'warehouse.sku.table.skuCode': 'SKU Code', 'warehouse.sku.table.skuName': 'SKU Name', 'warehouse.sku.table.totalQty': 'Qty', @@ -119,6 +123,8 @@ export default { 'warehouse.sku.message.deleteFailed': 'Delete failed', 'warehouse.sku.message.deleteConfirm': 'Delete {code}?', 'warehouse.sku.message.required': 'SKU code and name are required', + 'warehouse.inventory.table.warehouse': 'Warehouse', + 'warehouse.inventory.table.binName': 'Bin Name', 'warehouse.inventory.table.skuCode': 'SKU Code', 'warehouse.inventory.table.skuName': 'SKU Name', 'warehouse.inventory.table.warehouseId': 'Warehouse', @@ -143,6 +149,10 @@ export default { 'warehouse.parcel.status.inTransit': 'In Transit', 'warehouse.parcel.status.arrived': 'Arrived', 'warehouse.parcel.status.stored': 'Stored', + 'warehouse.parcel.table.warehouse': 'Warehouse', + 'warehouse.parcel.table.items': 'Items', + 'warehouse.parcel.table.arrivalTime': 'Arrival Time', + 'warehouse.parcel.table.createTime': 'Created At', 'warehouse.parcel.table.trackingNo': 'Tracking No', 'warehouse.parcel.table.carrierName': 'Carrier', 'warehouse.parcel.table.lifecycleStatus': 'Status', @@ -163,6 +173,12 @@ export default { 'warehouse.inbound.status.all': 'All', 'warehouse.inbound.status.forecasted': 'Forecasted', 'warehouse.inbound.status.completed': 'Completed', + 'warehouse.inbound.status.received': 'Received', + 'warehouse.inbound.table.warehouse': 'Warehouse', + 'warehouse.inbound.table.inboundType': 'Inbound Type', + 'warehouse.inbound.table.expectedArrival': 'ETA', + 'warehouse.inbound.type.normal': 'Normal', + 'warehouse.inbound.type.return': 'Return', 'warehouse.inbound.table.noticeNo': 'Notice No', 'warehouse.inbound.table.markReference': 'Mark Ref', 'warehouse.inbound.table.receiptStatus': 'Status', @@ -188,6 +204,14 @@ export default { 'warehouse.inbound.message.required': 'Select warehouse and SKU', 'warehouse.inbound.message.detailFailed': 'Failed to load detail', 'order.platformStore.platform.manual': 'Manual', + 'order.platformStore.table.siteCountry': 'Site Country', + 'order.platformStore.table.autoFulfill': 'Auto Fulfill', + 'order.platformStore.table.authTime': 'Auth Time', + 'order.platformStore.table.createTime': 'Created At', + 'order.platformStore.form.siteCountry': 'Site Country', + 'order.platformStore.auth.active': 'Active', + 'order.platformStore.auth.expired': 'Expired', + 'order.platformStore.auth.revoked': 'Revoked', 'order.platformStore.table.platform': 'Platform', 'order.platformStore.table.nickname': 'Nickname', 'order.platformStore.table.account': 'Account', @@ -214,6 +238,13 @@ export default { 'order.platformStoreOrder.status.pending': 'Pending', 'order.platformStoreOrder.status.toShip': 'To Ship', 'order.platformStoreOrder.status.shipped': 'Shipped', + 'order.platformStoreOrder.status.suspended': 'Suspended', + 'order.platformStoreOrder.table.platform': 'Platform', + 'order.platformStoreOrder.table.country': 'Country', + 'order.platformStoreOrder.table.itemCount': 'Items', + 'order.platformStoreOrder.table.shippingLabel': 'Label No', + 'order.platformStoreOrder.table.warehouse': 'Warehouse', + 'order.platformStoreOrder.table.lastSync': 'Last Sync', 'order.platformStoreOrder.table.orderNo': 'Order No', 'order.platformStoreOrder.table.store': 'Store', 'order.platformStoreOrder.table.status': 'Status', diff --git a/src/locale/zh-CN.ts b/src/locale/zh-CN.ts index a9f3ea9..39ac50c 100644 --- a/src/locale/zh-CN.ts +++ b/src/locale/zh-CN.ts @@ -99,6 +99,10 @@ export default { 'warehouse.common.confirm': '确定', 'warehouse.common.detail': '详情', 'warehouse.common.warehouse': '仓库', + 'warehouse.common.yes': '是', + 'warehouse.common.no': '否', + 'warehouse.sku.table.dimensions': '尺寸(cm)', + 'warehouse.sku.table.createTime': '创建时间', 'warehouse.sku.table.skuCode': 'SKU 编码', 'warehouse.sku.table.skuName': 'SKU 名称', 'warehouse.sku.table.totalQty': '库存', @@ -119,6 +123,8 @@ export default { 'warehouse.sku.message.deleteFailed': '删除失败', 'warehouse.sku.message.deleteConfirm': '确定删除 {code}?', 'warehouse.sku.message.required': '请填写 SKU 编码和名称', + 'warehouse.inventory.table.warehouse': '仓库', + 'warehouse.inventory.table.binName': '库位名称', 'warehouse.inventory.table.skuCode': 'SKU 编码', 'warehouse.inventory.table.skuName': 'SKU 名称', 'warehouse.inventory.table.warehouseId': '仓库', @@ -143,6 +149,10 @@ export default { 'warehouse.parcel.status.inTransit': '在途', 'warehouse.parcel.status.arrived': '已到仓', 'warehouse.parcel.status.stored': '已上架', + 'warehouse.parcel.table.warehouse': '仓库', + 'warehouse.parcel.table.items': '商品信息', + 'warehouse.parcel.table.arrivalTime': '到仓时间', + 'warehouse.parcel.table.createTime': '创建时间', 'warehouse.parcel.table.trackingNo': '快递单号', 'warehouse.parcel.table.carrierName': '快递公司', 'warehouse.parcel.table.lifecycleStatus': '状态', @@ -163,6 +173,12 @@ export default { 'warehouse.inbound.status.all': '全部', 'warehouse.inbound.status.forecasted': '预报', 'warehouse.inbound.status.completed': '已完成', + 'warehouse.inbound.status.received': '待入库', + 'warehouse.inbound.table.warehouse': '仓库', + 'warehouse.inbound.table.inboundType': '入库类型', + 'warehouse.inbound.table.expectedArrival': '预计到货', + 'warehouse.inbound.type.normal': '普通入库', + 'warehouse.inbound.type.return': '退货入库', 'warehouse.inbound.table.noticeNo': '预报单号', 'warehouse.inbound.table.markReference': '唛头', 'warehouse.inbound.table.receiptStatus': '状态', @@ -188,6 +204,14 @@ export default { 'warehouse.inbound.message.required': '请选择仓库和 SKU', 'warehouse.inbound.message.detailFailed': '加载详情失败', 'order.platformStore.platform.manual': '手工店铺', + 'order.platformStore.table.siteCountry': '站点国家', + 'order.platformStore.table.autoFulfill': '自动发货', + 'order.platformStore.table.authTime': '授权时间', + 'order.platformStore.table.createTime': '创建时间', + 'order.platformStore.form.siteCountry': '站点国家', + 'order.platformStore.auth.active': '已授权', + 'order.platformStore.auth.expired': '已过期', + 'order.platformStore.auth.revoked': '已撤销', 'order.platformStore.table.platform': '平台', 'order.platformStore.table.nickname': '店铺昵称', 'order.platformStore.table.account': '账号', @@ -214,6 +238,13 @@ export default { 'order.platformStoreOrder.status.pending': '待处理', 'order.platformStoreOrder.status.toShip': '待发货', 'order.platformStoreOrder.status.shipped': '已发货', + 'order.platformStoreOrder.status.suspended': '已搁置', + 'order.platformStoreOrder.table.platform': '平台', + 'order.platformStoreOrder.table.country': '国家', + 'order.platformStoreOrder.table.itemCount': '商品数', + 'order.platformStoreOrder.table.shippingLabel': '面单号', + 'order.platformStoreOrder.table.warehouse': '发货仓库', + 'order.platformStoreOrder.table.lastSync': '同步时间', 'order.platformStoreOrder.table.orderNo': '平台订单号', 'order.platformStoreOrder.table.store': '店铺', 'order.platformStoreOrder.table.status': '状态', diff --git a/src/styles/warehouse-list-page.less b/src/styles/warehouse-list-page.less new file mode 100644 index 0000000..31eaba2 --- /dev/null +++ b/src/styles/warehouse-list-page.less @@ -0,0 +1,73 @@ +.warehouse-list-page { + width: 100%; + box-sizing: border-box; + padding: 16px 16px 24px; + display: flex; + flex-direction: column; + background: #f0f3f4; + flex: 1; + min-height: 0; + overflow: hidden; +} + +.warehouse-list-page-card { + flex: 1; + min-height: 0; + display: flex; + flex-direction: column; + background: #fff; + border-radius: 8px; + padding: 0; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08); + box-sizing: border-box; + overflow: hidden; +} + +.warehouse-list-page-card .status-tabs { + padding: 16px 16px 0; + flex-shrink: 0; +} + +.warehouse-list-page-card .table-toolbar { + display: flex; + align-items: flex-start; + justify-content: space-between; + gap: 12px; + padding: 16px 16px 0; + margin-bottom: 16px; + flex-shrink: 0; + flex-wrap: wrap; +} + +.warehouse-list-page-card .toolbar-left, +.warehouse-list-page-card .toolbar-right { + display: flex; + align-items: center; +} + +.warehouse-list-page-card .toolbar-left { + flex: 1; + min-width: 0; +} + +.warehouse-list-page-card .list-wrap { + flex: 1; + min-height: 0; + padding: 0 16px 16px; + box-sizing: border-box; + display: flex; + flex-direction: column; + overflow: hidden; +} + +.warehouse-list-page-card .list-wrap :deep(.handsontable-pro-container) { + flex: 1; + min-height: 0; + display: flex; + flex-direction: column; +} + +.warehouse-list-page-card .tab-count { + margin-left: 4px; + color: var(--app-primary-color); +} diff --git a/src/utils/warehouseListMapper.ts b/src/utils/warehouseListMapper.ts new file mode 100644 index 0000000..c3b1f7b --- /dev/null +++ b/src/utils/warehouseListMapper.ts @@ -0,0 +1,73 @@ +import dayjs from 'dayjs' + +type Row = Record + +export function pickField(row: Row, ...keys: string[]): T | undefined { + for (const key of keys) { + const value = row[key] + if (value !== undefined && value !== null && value !== '') { + return value as T + } + } + return undefined +} + +export function formatDateTime(value: unknown): string { + if (!value) return '-' + const formatted = dayjs(String(value)) + return formatted.isValid() ? formatted.format('YYYY-MM-DD HH:mm') : String(value) +} + +export function formatDimensions(row: Row): string { + const length = pickField(row, 'lengthCm', 'LengthCm') + const width = pickField(row, 'widthCm', 'WidthCm') + const height = pickField(row, 'heightCm', 'HeightCm') + if (length == null && width == null && height == null) return '-' + return `${length ?? '-'}×${width ?? '-'}×${height ?? '-'}` +} + +export function buildWarehouseNameMap( + options: Array<{ label: string; value: number }>, +): Map { + return new Map(options.map((item) => [item.value, item.label])) +} + +export function resolveWarehouseLabel( + warehouseId: number | undefined, + map: Map, +): string { + if (!warehouseId) return '-' + return map.get(warehouseId) || `#${warehouseId}` +} + +export function summarizeParcelItems(items: unknown): string { + const list = Array.isArray(items) ? items : [] + if (!list.length) return '-' + const first = list[0] as Row + const name = pickField(first, 'productName', 'ProductName') || '-' + const qty = Number(pickField(first, 'quantity', 'Quantity') ?? 0) + if (list.length === 1) return `${name} ×${qty}` + const totalQty = list.reduce( + (sum, item) => sum + Number(pickField(item as Row, 'quantity', 'Quantity') ?? 0), + 0, + ) + return `${name} 等${list.length}项 / ${totalQty}件` +} + +export function formatMoney(amount: unknown, currency?: string | null): string { + if (amount == null || amount === '') return '-' + const code = currency?.trim() + return code ? `${amount} ${code}` : String(amount) +} + +export function normalizeListRow(row: Row): Row { + const normalized: Row = { ...row } + Object.entries(row).forEach(([key, value]) => { + if (!key) return + const camel = key.charAt(0).toLowerCase() + key.slice(1) + if (normalized[camel] === undefined) normalized[camel] = value + const pascal = key.charAt(0).toUpperCase() + key.slice(1) + if (normalized[pascal] === undefined) normalized[pascal] = value + }) + return normalized +} diff --git a/src/views/order/platformStore/index.vue b/src/views/order/platformStore/index.vue index 623cf4c..be0ac4e 100644 --- a/src/views/order/platformStore/index.vue +++ b/src/views/order/platformStore/index.vue @@ -11,6 +11,7 @@ import { useTableContainerHeight } from '@/composables/useTableContainerHeight' import { deletePlatformStore, getPlatformStoreAuthUrl, getPlatformStoreList, savePlatformStore } from '@/api/order/platformStore' import { $baseMessage } from '@/utils' import { hasPermission } from '@/utils/rbac-permission' +import { formatDateTime, normalizeListRow, pickField } from '@/utils/warehouseListMapper' defineOptions({ name: 'PlatformStore' }) @@ -49,10 +50,33 @@ const hotColumns = computed(() => [ { key: 'platformName', label: t('order.platformStore.table.platform'), width: 100 }, { key: 'storeNickname', label: t('order.platformStore.table.nickname'), width: 160 }, { key: 'platformAccount', label: t('order.platformStore.table.account'), width: 140 }, + { key: 'siteCountry', label: t('order.platformStore.table.siteCountry'), width: 90 }, { key: 'authStatus', label: t('order.platformStore.table.authStatus'), width: 100 }, - { key: 'tokenExpireTime', label: t('order.platformStore.table.tokenExpire'), width: 170 }, + { key: 'isAutoFulfill', label: t('order.platformStore.table.autoFulfill'), width: 90 }, + { key: 'authTime', label: t('order.platformStore.table.authTime'), width: 160 }, + { key: 'tokenExpireTime', label: t('order.platformStore.table.tokenExpire'), width: 160 }, + { key: 'createTime', label: t('order.platformStore.table.createTime'), width: 160 }, ]) +function mapRow(raw: Record) { + const row = normalizeListRow(raw) + const authStatus = String(pickField(row, 'authStatus', 'AuthStatus') ?? '') + const autoFulfill = Boolean(pickField(row, 'isAutoFulfill', 'IsAutoFulfill')) + return { + id: pickField(row, 'id', 'Id'), + platformName: pickField(row, 'platformName', 'PlatformName') ?? pickField(row, 'platformCode', 'PlatformCode') ?? '-', + storeNickname: pickField(row, 'storeNickname', 'StoreNickname') ?? '-', + platformAccount: pickField(row, 'platformAccount', 'PlatformAccount') ?? '-', + siteCountry: pickField(row, 'siteCountry', 'SiteCountry') ?? '-', + authStatus: t(`order.platformStore.auth.${authStatus}`, authStatus), + isAutoFulfill: autoFulfill ? t('warehouse.common.yes') : t('warehouse.common.no'), + authTime: formatDateTime(pickField(row, 'authTime', 'AuthTime')), + tokenExpireTime: formatDateTime(pickField(row, 'tokenExpireTime', 'TokenExpireTime')), + createTime: formatDateTime(pickField(row, 'createTime', 'CreateTime')), + _raw: row, + } +} + async function loadList() { listLoading.value = true try { @@ -62,7 +86,7 @@ async function loadList() { platformCode: platformCode.value, storeNickname: keyword.value || undefined, }) - tableRows.value = items || [] + tableRows.value = (items || []).map((item: Record) => mapRow(item)) totalCount.value = Number(total || 0) } catch (e: any) { $baseMessage(e?.message || t('order.platformStore.message.loadFailed'), 'error') @@ -71,15 +95,48 @@ async function loadList() { } } +function handlePageChange(page: number) { + currentPage.value = page + loadList() +} + +function handlePageSizeChange(size: number) { + pageSize.value = size + currentPage.value = 1 + loadList() +} + +function handleSearch() { + currentPage.value = 1 + loadList() +} + function handleAdd() { if (!hasPermission('Order.PlatformStore.Save')) return - Object.assign(formModel, { id: 0, platformCode: 'manual', storeNickname: '', platformAccount: '', siteCountry: '', isAutoFulfill: false, remark: '' }) + Object.assign(formModel, { + id: 0, + platformCode: 'manual', + storeNickname: '', + platformAccount: '', + siteCountry: '', + isAutoFulfill: false, + remark: '', + }) openModal() } function handleEdit(row: any) { if (!hasPermission('Order.PlatformStore.Save')) return - Object.assign(formModel, row) + const raw = row._raw || row + Object.assign(formModel, { + id: Number(pickField(raw, 'id', 'Id') ?? 0), + platformCode: pickField(raw, 'platformCode', 'PlatformCode') ?? 'manual', + storeNickname: pickField(raw, 'storeNickname', 'StoreNickname') ?? '', + platformAccount: pickField(raw, 'platformAccount', 'PlatformAccount') ?? '', + siteCountry: pickField(raw, 'siteCountry', 'SiteCountry') ?? '', + isAutoFulfill: Boolean(pickField(raw, 'isAutoFulfill', 'IsAutoFulfill')), + remark: pickField(raw, 'remark', 'Remark') ?? '', + }) openModal() } @@ -103,8 +160,9 @@ async function handleSave() { async function handleAuth(row: any) { if (!hasPermission('Order.PlatformStore.Auth')) return + const raw = row._raw || row try { - const result = await getPlatformStoreAuthUrl({ platformCode: row.platformCode }) + const result = await getPlatformStoreAuthUrl({ platformCode: pickField(raw, 'platformCode', 'PlatformCode') }) $baseMessage(result?.message || result?.authUrl || t('order.platformStore.message.authPlaceholder'), 'info') } catch (e: any) { $baseMessage(e?.message || t('order.platformStore.message.authFailed'), 'error') @@ -148,25 +206,62 @@ onMounted(loadList) diff --git a/src/views/order/platformStoreOrder/index.vue b/src/views/order/platformStoreOrder/index.vue index 8b74436..be917d2 100644 --- a/src/views/order/platformStoreOrder/index.vue +++ b/src/views/order/platformStoreOrder/index.vue @@ -18,6 +18,14 @@ import { import { getWarehouseList } from '@/api/system/warehouse' import { $baseMessage } from '@/utils' import { hasPermission } from '@/utils/rbac-permission' +import { + buildWarehouseNameMap, + formatDateTime, + formatMoney, + normalizeListRow, + pickField, + resolveWarehouseLabel, +} from '@/utils/warehouseListMapper' defineOptions({ name: 'PlatformStoreOrder' }) @@ -28,6 +36,7 @@ const fulfillStatus = ref() const keyword = ref('') const summary = ref>({}) const warehouseOptions = ref>([]) +const warehouseNameMap = computed(() => buildWarehouseNameMap(warehouseOptions.value)) const productOptions = ref>([]) const listLoading = ref(false) const tableRows = ref([]) @@ -36,10 +45,10 @@ const pageSize = ref(30) const totalCount = ref(0) const statusTabs = [ - { value: undefined, labelKey: 'order.platformStoreOrder.status.all' }, - { value: 'pending', labelKey: 'order.platformStoreOrder.status.pending' }, - { value: 'toShip', labelKey: 'order.platformStoreOrder.status.toShip' }, - { value: 'shipped', labelKey: 'order.platformStoreOrder.status.shipped' }, + { value: undefined, labelKey: 'order.platformStoreOrder.status.all', countKey: null }, + { value: 'pending', labelKey: 'order.platformStoreOrder.status.pending', countKey: 'pending' as const }, + { value: 'toShip', labelKey: 'order.platformStoreOrder.status.toShip', countKey: 'toShip' as const }, + { value: 'shipped', labelKey: 'order.platformStoreOrder.status.shipped', countKey: 'shipped' as const }, ] const detailVisible = ref(false) @@ -49,13 +58,50 @@ const matchProductCode = ref() const hotColumns = computed(() => [ { key: 'platformOrderNo', label: t('order.platformStoreOrder.table.orderNo'), width: 160 }, + { key: 'platformName', label: t('order.platformStoreOrder.table.platform'), width: 90 }, { key: 'storeNickname', label: t('order.platformStoreOrder.table.store'), width: 120 }, + { key: 'orderCountry', label: t('order.platformStoreOrder.table.country'), width: 80 }, { key: 'fulfillStatus', label: t('order.platformStoreOrder.table.status'), width: 90 }, + { key: 'itemCount', label: t('order.platformStoreOrder.table.itemCount'), width: 70 }, + { key: 'totalAmountDisplay', label: t('order.platformStoreOrder.table.amount'), width: 110 }, { key: 'linkedServiceNo', label: t('order.platformStoreOrder.table.linkedServiceNo'), width: 150 }, - { key: 'totalAmount', label: t('order.platformStoreOrder.table.amount'), width: 90 }, - { key: 'platformOrderTime', label: t('order.platformStoreOrder.table.orderTime'), width: 170 }, + { key: 'shippingLabelNo', label: t('order.platformStoreOrder.table.shippingLabel'), width: 140 }, + { key: 'warehouseName', label: t('order.platformStoreOrder.table.warehouse'), width: 120 }, + { key: 'platformOrderTime', label: t('order.platformStoreOrder.table.orderTime'), width: 160 }, + { key: 'lastSyncTime', label: t('order.platformStoreOrder.table.lastSync'), width: 160 }, ]) +function mapRow(raw: Record) { + const row = normalizeListRow(raw) + const status = String(pickField(row, 'fulfillStatus', 'FulfillStatus') ?? '') + const platformCode = String(pickField(row, 'platformCode', 'PlatformCode') ?? '') + const whId = pickField(row, 'warehouseId', 'WarehouseId') + const currency = pickField(row, 'currency', 'Currency') + const amount = pickField(row, 'totalAmount', 'TotalAmount') + const suspended = Boolean(pickField(row, 'isSuspended', 'IsSuspended')) + let statusLabel = t(`order.platformStoreOrder.status.${status}`, status) + if (suspended) statusLabel = `${statusLabel}(${t('order.platformStoreOrder.status.suspended')})` + + return { + id: pickField(row, 'id', 'Id'), + platformOrderNo: pickField(row, 'platformOrderNo', 'PlatformOrderNo') ?? '-', + platformName: pickField(row, 'platformName', 'PlatformName') ?? platformCode ?? '-', + storeNickname: pickField(row, 'storeNickname', 'StoreNickname') ?? '-', + orderCountry: pickField(row, 'orderCountry', 'OrderCountry') ?? '-', + fulfillStatus: statusLabel, + itemCount: pickField(row, 'itemCount', 'ItemCount') ?? '-', + totalAmountDisplay: formatMoney(amount, currency), + linkedServiceNo: pickField(row, 'linkedServiceNo', 'LinkedServiceNo') || '-', + shippingLabelNo: pickField(row, 'shippingLabelNo', 'ShippingLabelNo') ?? '-', + warehouseName: resolveWarehouseLabel(whId, warehouseNameMap.value), + platformOrderTime: formatDateTime(pickField(row, 'platformOrderTime', 'PlatformOrderTime')), + lastSyncTime: formatDateTime(pickField(row, 'lastSyncTime', 'LastSyncTime')), + _raw: row, + _status: status, + _suspended: suspended, + } +} + async function loadProducts() { try { const { items } = await getSalesProductList({ page: 1, pageSize: 200, status: 1 }) @@ -77,7 +123,12 @@ async function loadProducts() { async function loadSummary() { try { const data = await getPlatformStoreOrderSummary() - summary.value = data || {} + summary.value = { + pending: data?.pending ?? data?.Pending ?? 0, + toShip: data?.toShip ?? data?.ToShip ?? 0, + shipped: data?.shipped ?? data?.Shipped ?? 0, + suspended: data?.suspended ?? data?.Suspended ?? 0, + } } catch { summary.value = {} } @@ -92,12 +143,7 @@ async function loadList() { fulfillStatus: fulfillStatus.value, platformOrderNo: keyword.value || undefined, }) - tableRows.value = (items || []).map((r: any) => ({ - ...r, - fulfillStatus: t(`order.platformStoreOrder.status.${r.fulfillStatus}`, r.fulfillStatus), - linkedServiceNo: r.linkedServiceNo || '-', - _raw: r, - })) + tableRows.value = (items || []).map((item: Record) => mapRow(item)) totalCount.value = Number(total || 0) } catch (e: any) { $baseMessage(e?.message || t('order.platformStoreOrder.message.loadFailed'), 'error') @@ -106,6 +152,22 @@ async function loadList() { } } +function handlePageChange(page: number) { + currentPage.value = page + loadList() +} + +function handlePageSizeChange(size: number) { + pageSize.value = size + currentPage.value = 1 + loadList() +} + +function handleSearch() { + currentPage.value = 1 + loadList() +} + async function showDetail(row: any) { try { detailData.value = await getPlatformStoreOrderDetail(row._raw?.id || row.id) @@ -147,9 +209,8 @@ async function handleBatchMatch() { } const pendingIds = tableRows.value - .map((row) => row._raw || row) - .filter((raw) => raw.fulfillStatus === 'pending' && !raw.linkedOrderId) - .map((raw) => raw.id) + .filter((row) => row._status === 'pending' && !pickField(row._raw, 'linkedOrderId', 'LinkedOrderId')) + .map((row) => Number(row.id)) if (!pendingIds.length) { $baseMessage(t('order.platformStoreOrder.message.batchEmpty'), 'warning') @@ -185,8 +246,12 @@ async function handleBatchMatch() { async function handleSuspend(row: any) { if (!hasPermission('Order.PlatformStoreOrder.ToggleSuspend')) return const raw = row._raw || row - await togglePlatformStoreOrderSuspend({ id: raw.id, isSuspended: !raw.isSuspended }) + await togglePlatformStoreOrderSuspend({ + id: Number(pickField(raw, 'id', 'Id')), + isSuspended: !Boolean(pickField(raw, 'isSuspended', 'IsSuspended')), + }) $baseMessage(t('order.platformStoreOrder.message.suspendSuccess'), 'success') + await loadSummary() await loadList() } @@ -194,10 +259,11 @@ function handleDelete(row: any) { if (!hasPermission('Order.PlatformStoreOrder.Delete')) return Modal.confirm({ title: t('order.platformStoreOrder.actions.delete'), - content: row._raw?.platformOrderNo || row.platformOrderNo, + content: row.platformOrderNo, onOk: async () => { - await deletePlatformStoreOrder([row._raw?.id || row.id]) + await deletePlatformStoreOrder([row.id]) $baseMessage(t('order.platformStoreOrder.message.deleteSuccess'), 'success') + await loadSummary() await loadList() }, }) @@ -206,11 +272,17 @@ function handleDelete(row: any) { function actionRenderer(row: any) { const parts = [``] const raw = row._raw || row - if (hasPermission('Order.PlatformStoreOrder.MatchInventory') && raw.fulfillStatus === 'pending' && !raw.linkedOrderId) { + if ( + hasPermission('Order.PlatformStoreOrder.MatchInventory') + && row._status === 'pending' + && !pickField(raw, 'linkedOrderId', 'LinkedOrderId') + ) { parts.push(``) } if (hasPermission('Order.PlatformStoreOrder.ToggleSuspend')) { - parts.push(``) + parts.push( + ``, + ) } if (hasPermission('Order.PlatformStoreOrder.Delete')) { parts.push(``) @@ -227,7 +299,10 @@ function handleActionClick(row: any, _i: number, action?: string) { onMounted(async () => { const { items } = await getWarehouseList({ page: 1, pageSize: 200 }).catch(() => ({ items: [] })) - warehouseOptions.value = (items || []).map((w: any) => ({ label: w.warehouseName || `#${w.id}`, value: Number(w.id) })) + warehouseOptions.value = (items || []).map((w: any) => ({ + label: w.warehouseName || `#${w.id}`, + value: Number(w.id), + })) if (warehouseOptions.value.length) matchWarehouseId.value = warehouseOptions.value[0].value await loadProducts() await loadSummary() @@ -236,34 +311,72 @@ onMounted(async () => {