优化仓储与店铺列表页,并补齐 warehouse 代理与列宽自适应。
管理端可正常查看列表;分页移至工具栏右侧,列定义对齐 WMS,Handsontable 开启 stretch-columns。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
5191d1981f
commit
30afc1728c
@ -119,6 +119,27 @@ server {
|
|||||||
location ^~ /order/templateMapping {
|
location ^~ /order/templateMapping {
|
||||||
try_files /index.html =200;
|
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 正则代理更优先)============
|
# ============ 系统管理前端路由(^~ 须比下方 /system/ API 正则代理更优先)============
|
||||||
# 页面路径为 camelCase;API 为 kebab(如 /system/dict/list、/system/batch-email/…),勿用宽泛前缀匹配 dict
|
# 页面路径为 camelCase;API 为 kebab(如 /system/dict/list、/system/batch-email/…),勿用宽泛前缀匹配 dict
|
||||||
@ -198,7 +219,7 @@ server {
|
|||||||
}
|
}
|
||||||
|
|
||||||
# ============ Admin API 同源反代(/rbac /order /xhs … → api.nanafa.com/admin)============
|
# ============ 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;
|
rewrite ^/(.*)$ /admin/$1 break;
|
||||||
proxy_pass http://api.nanafa.com;
|
proxy_pass http://api.nanafa.com;
|
||||||
proxy_http_version 1.1;
|
proxy_http_version 1.1;
|
||||||
|
|||||||
@ -46,6 +46,13 @@ server {
|
|||||||
location ^~ /order/myOrders { try_files /index.html =200; }
|
location ^~ /order/myOrders { try_files /index.html =200; }
|
||||||
location ^~ /order/batchJobs { try_files /index.html =200; }
|
location ^~ /order/batchJobs { try_files /index.html =200; }
|
||||||
location ^~ /order/templateMapping { 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/salesProducts { try_files /index.html =200; }
|
||||||
location ^~ /channel/serviceChannels { 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;
|
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;
|
rewrite ^/(.*)$ /admin/$1 break;
|
||||||
proxy_pass http://api.nanafa.com;
|
proxy_pass http://api.nanafa.com;
|
||||||
proxy_http_version 1.1;
|
proxy_http_version 1.1;
|
||||||
|
|||||||
@ -99,6 +99,10 @@ export default {
|
|||||||
'warehouse.common.confirm': 'Confirm',
|
'warehouse.common.confirm': 'Confirm',
|
||||||
'warehouse.common.detail': 'Detail',
|
'warehouse.common.detail': 'Detail',
|
||||||
'warehouse.common.warehouse': 'Warehouse',
|
'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.skuCode': 'SKU Code',
|
||||||
'warehouse.sku.table.skuName': 'SKU Name',
|
'warehouse.sku.table.skuName': 'SKU Name',
|
||||||
'warehouse.sku.table.totalQty': 'Qty',
|
'warehouse.sku.table.totalQty': 'Qty',
|
||||||
@ -119,6 +123,8 @@ export default {
|
|||||||
'warehouse.sku.message.deleteFailed': 'Delete failed',
|
'warehouse.sku.message.deleteFailed': 'Delete failed',
|
||||||
'warehouse.sku.message.deleteConfirm': 'Delete {code}?',
|
'warehouse.sku.message.deleteConfirm': 'Delete {code}?',
|
||||||
'warehouse.sku.message.required': 'SKU code and name are required',
|
'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.skuCode': 'SKU Code',
|
||||||
'warehouse.inventory.table.skuName': 'SKU Name',
|
'warehouse.inventory.table.skuName': 'SKU Name',
|
||||||
'warehouse.inventory.table.warehouseId': 'Warehouse',
|
'warehouse.inventory.table.warehouseId': 'Warehouse',
|
||||||
@ -143,6 +149,10 @@ export default {
|
|||||||
'warehouse.parcel.status.inTransit': 'In Transit',
|
'warehouse.parcel.status.inTransit': 'In Transit',
|
||||||
'warehouse.parcel.status.arrived': 'Arrived',
|
'warehouse.parcel.status.arrived': 'Arrived',
|
||||||
'warehouse.parcel.status.stored': 'Stored',
|
'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.trackingNo': 'Tracking No',
|
||||||
'warehouse.parcel.table.carrierName': 'Carrier',
|
'warehouse.parcel.table.carrierName': 'Carrier',
|
||||||
'warehouse.parcel.table.lifecycleStatus': 'Status',
|
'warehouse.parcel.table.lifecycleStatus': 'Status',
|
||||||
@ -163,6 +173,12 @@ export default {
|
|||||||
'warehouse.inbound.status.all': 'All',
|
'warehouse.inbound.status.all': 'All',
|
||||||
'warehouse.inbound.status.forecasted': 'Forecasted',
|
'warehouse.inbound.status.forecasted': 'Forecasted',
|
||||||
'warehouse.inbound.status.completed': 'Completed',
|
'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.noticeNo': 'Notice No',
|
||||||
'warehouse.inbound.table.markReference': 'Mark Ref',
|
'warehouse.inbound.table.markReference': 'Mark Ref',
|
||||||
'warehouse.inbound.table.receiptStatus': 'Status',
|
'warehouse.inbound.table.receiptStatus': 'Status',
|
||||||
@ -188,6 +204,14 @@ export default {
|
|||||||
'warehouse.inbound.message.required': 'Select warehouse and SKU',
|
'warehouse.inbound.message.required': 'Select warehouse and SKU',
|
||||||
'warehouse.inbound.message.detailFailed': 'Failed to load detail',
|
'warehouse.inbound.message.detailFailed': 'Failed to load detail',
|
||||||
'order.platformStore.platform.manual': 'Manual',
|
'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.platform': 'Platform',
|
||||||
'order.platformStore.table.nickname': 'Nickname',
|
'order.platformStore.table.nickname': 'Nickname',
|
||||||
'order.platformStore.table.account': 'Account',
|
'order.platformStore.table.account': 'Account',
|
||||||
@ -214,6 +238,13 @@ export default {
|
|||||||
'order.platformStoreOrder.status.pending': 'Pending',
|
'order.platformStoreOrder.status.pending': 'Pending',
|
||||||
'order.platformStoreOrder.status.toShip': 'To Ship',
|
'order.platformStoreOrder.status.toShip': 'To Ship',
|
||||||
'order.platformStoreOrder.status.shipped': 'Shipped',
|
'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.orderNo': 'Order No',
|
||||||
'order.platformStoreOrder.table.store': 'Store',
|
'order.platformStoreOrder.table.store': 'Store',
|
||||||
'order.platformStoreOrder.table.status': 'Status',
|
'order.platformStoreOrder.table.status': 'Status',
|
||||||
|
|||||||
@ -99,6 +99,10 @@ export default {
|
|||||||
'warehouse.common.confirm': '确定',
|
'warehouse.common.confirm': '确定',
|
||||||
'warehouse.common.detail': '详情',
|
'warehouse.common.detail': '详情',
|
||||||
'warehouse.common.warehouse': '仓库',
|
'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.skuCode': 'SKU 编码',
|
||||||
'warehouse.sku.table.skuName': 'SKU 名称',
|
'warehouse.sku.table.skuName': 'SKU 名称',
|
||||||
'warehouse.sku.table.totalQty': '库存',
|
'warehouse.sku.table.totalQty': '库存',
|
||||||
@ -119,6 +123,8 @@ export default {
|
|||||||
'warehouse.sku.message.deleteFailed': '删除失败',
|
'warehouse.sku.message.deleteFailed': '删除失败',
|
||||||
'warehouse.sku.message.deleteConfirm': '确定删除 {code}?',
|
'warehouse.sku.message.deleteConfirm': '确定删除 {code}?',
|
||||||
'warehouse.sku.message.required': '请填写 SKU 编码和名称',
|
'warehouse.sku.message.required': '请填写 SKU 编码和名称',
|
||||||
|
'warehouse.inventory.table.warehouse': '仓库',
|
||||||
|
'warehouse.inventory.table.binName': '库位名称',
|
||||||
'warehouse.inventory.table.skuCode': 'SKU 编码',
|
'warehouse.inventory.table.skuCode': 'SKU 编码',
|
||||||
'warehouse.inventory.table.skuName': 'SKU 名称',
|
'warehouse.inventory.table.skuName': 'SKU 名称',
|
||||||
'warehouse.inventory.table.warehouseId': '仓库',
|
'warehouse.inventory.table.warehouseId': '仓库',
|
||||||
@ -143,6 +149,10 @@ export default {
|
|||||||
'warehouse.parcel.status.inTransit': '在途',
|
'warehouse.parcel.status.inTransit': '在途',
|
||||||
'warehouse.parcel.status.arrived': '已到仓',
|
'warehouse.parcel.status.arrived': '已到仓',
|
||||||
'warehouse.parcel.status.stored': '已上架',
|
'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.trackingNo': '快递单号',
|
||||||
'warehouse.parcel.table.carrierName': '快递公司',
|
'warehouse.parcel.table.carrierName': '快递公司',
|
||||||
'warehouse.parcel.table.lifecycleStatus': '状态',
|
'warehouse.parcel.table.lifecycleStatus': '状态',
|
||||||
@ -163,6 +173,12 @@ export default {
|
|||||||
'warehouse.inbound.status.all': '全部',
|
'warehouse.inbound.status.all': '全部',
|
||||||
'warehouse.inbound.status.forecasted': '预报',
|
'warehouse.inbound.status.forecasted': '预报',
|
||||||
'warehouse.inbound.status.completed': '已完成',
|
'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.noticeNo': '预报单号',
|
||||||
'warehouse.inbound.table.markReference': '唛头',
|
'warehouse.inbound.table.markReference': '唛头',
|
||||||
'warehouse.inbound.table.receiptStatus': '状态',
|
'warehouse.inbound.table.receiptStatus': '状态',
|
||||||
@ -188,6 +204,14 @@ export default {
|
|||||||
'warehouse.inbound.message.required': '请选择仓库和 SKU',
|
'warehouse.inbound.message.required': '请选择仓库和 SKU',
|
||||||
'warehouse.inbound.message.detailFailed': '加载详情失败',
|
'warehouse.inbound.message.detailFailed': '加载详情失败',
|
||||||
'order.platformStore.platform.manual': '手工店铺',
|
'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.platform': '平台',
|
||||||
'order.platformStore.table.nickname': '店铺昵称',
|
'order.platformStore.table.nickname': '店铺昵称',
|
||||||
'order.platformStore.table.account': '账号',
|
'order.platformStore.table.account': '账号',
|
||||||
@ -214,6 +238,13 @@ export default {
|
|||||||
'order.platformStoreOrder.status.pending': '待处理',
|
'order.platformStoreOrder.status.pending': '待处理',
|
||||||
'order.platformStoreOrder.status.toShip': '待发货',
|
'order.platformStoreOrder.status.toShip': '待发货',
|
||||||
'order.platformStoreOrder.status.shipped': '已发货',
|
'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.orderNo': '平台订单号',
|
||||||
'order.platformStoreOrder.table.store': '店铺',
|
'order.platformStoreOrder.table.store': '店铺',
|
||||||
'order.platformStoreOrder.table.status': '状态',
|
'order.platformStoreOrder.table.status': '状态',
|
||||||
|
|||||||
73
src/styles/warehouse-list-page.less
Normal file
73
src/styles/warehouse-list-page.less
Normal file
@ -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);
|
||||||
|
}
|
||||||
73
src/utils/warehouseListMapper.ts
Normal file
73
src/utils/warehouseListMapper.ts
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
import dayjs from 'dayjs'
|
||||||
|
|
||||||
|
type Row = Record<string, unknown>
|
||||||
|
|
||||||
|
export function pickField<T = unknown>(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<number | string>(row, 'lengthCm', 'LengthCm')
|
||||||
|
const width = pickField<number | string>(row, 'widthCm', 'WidthCm')
|
||||||
|
const height = pickField<number | string>(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<number, string> {
|
||||||
|
return new Map(options.map((item) => [item.value, item.label]))
|
||||||
|
}
|
||||||
|
|
||||||
|
export function resolveWarehouseLabel(
|
||||||
|
warehouseId: number | undefined,
|
||||||
|
map: Map<number, string>,
|
||||||
|
): 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<string>(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
|
||||||
|
}
|
||||||
@ -11,6 +11,7 @@ import { useTableContainerHeight } from '@/composables/useTableContainerHeight'
|
|||||||
import { deletePlatformStore, getPlatformStoreAuthUrl, getPlatformStoreList, savePlatformStore } from '@/api/order/platformStore'
|
import { deletePlatformStore, getPlatformStoreAuthUrl, getPlatformStoreList, savePlatformStore } from '@/api/order/platformStore'
|
||||||
import { $baseMessage } from '@/utils'
|
import { $baseMessage } from '@/utils'
|
||||||
import { hasPermission } from '@/utils/rbac-permission'
|
import { hasPermission } from '@/utils/rbac-permission'
|
||||||
|
import { formatDateTime, normalizeListRow, pickField } from '@/utils/warehouseListMapper'
|
||||||
|
|
||||||
defineOptions({ name: 'PlatformStore' })
|
defineOptions({ name: 'PlatformStore' })
|
||||||
|
|
||||||
@ -49,10 +50,33 @@ const hotColumns = computed(() => [
|
|||||||
{ key: 'platformName', label: t('order.platformStore.table.platform'), width: 100 },
|
{ key: 'platformName', label: t('order.platformStore.table.platform'), width: 100 },
|
||||||
{ key: 'storeNickname', label: t('order.platformStore.table.nickname'), width: 160 },
|
{ key: 'storeNickname', label: t('order.platformStore.table.nickname'), width: 160 },
|
||||||
{ key: 'platformAccount', label: t('order.platformStore.table.account'), width: 140 },
|
{ 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: '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<string, unknown>) {
|
||||||
|
const row = normalizeListRow(raw)
|
||||||
|
const authStatus = String(pickField(row, 'authStatus', 'AuthStatus') ?? '')
|
||||||
|
const autoFulfill = Boolean(pickField(row, 'isAutoFulfill', 'IsAutoFulfill'))
|
||||||
|
return {
|
||||||
|
id: pickField<number>(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() {
|
async function loadList() {
|
||||||
listLoading.value = true
|
listLoading.value = true
|
||||||
try {
|
try {
|
||||||
@ -62,7 +86,7 @@ async function loadList() {
|
|||||||
platformCode: platformCode.value,
|
platformCode: platformCode.value,
|
||||||
storeNickname: keyword.value || undefined,
|
storeNickname: keyword.value || undefined,
|
||||||
})
|
})
|
||||||
tableRows.value = items || []
|
tableRows.value = (items || []).map((item: Record<string, unknown>) => mapRow(item))
|
||||||
totalCount.value = Number(total || 0)
|
totalCount.value = Number(total || 0)
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
$baseMessage(e?.message || t('order.platformStore.message.loadFailed'), 'error')
|
$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() {
|
function handleAdd() {
|
||||||
if (!hasPermission('Order.PlatformStore.Save')) return
|
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()
|
openModal()
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleEdit(row: any) {
|
function handleEdit(row: any) {
|
||||||
if (!hasPermission('Order.PlatformStore.Save')) return
|
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()
|
openModal()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -103,8 +160,9 @@ async function handleSave() {
|
|||||||
|
|
||||||
async function handleAuth(row: any) {
|
async function handleAuth(row: any) {
|
||||||
if (!hasPermission('Order.PlatformStore.Auth')) return
|
if (!hasPermission('Order.PlatformStore.Auth')) return
|
||||||
|
const raw = row._raw || row
|
||||||
try {
|
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')
|
$baseMessage(result?.message || result?.authUrl || t('order.platformStore.message.authPlaceholder'), 'info')
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
$baseMessage(e?.message || t('order.platformStore.message.authFailed'), 'error')
|
$baseMessage(e?.message || t('order.platformStore.message.authFailed'), 'error')
|
||||||
@ -148,25 +206,62 @@ onMounted(loadList)
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="platform-store-container">
|
<div class="platform-store-container warehouse-list-page">
|
||||||
<div class="page-card">
|
<div class="warehouse-list-page-card">
|
||||||
<div class="table-toolbar">
|
<div class="table-toolbar">
|
||||||
<div class="toolbar-left">
|
<div class="toolbar-left">
|
||||||
<a-space wrap :size="8">
|
<a-space wrap :size="8">
|
||||||
<a-select v-model="platformCode" allow-clear :placeholder="t('order.platformStore.search.platform')" :options="platformOptions" style="width: 160px" />
|
<a-select
|
||||||
<a-input v-model="keyword" allow-clear :placeholder="t('order.platformStore.search.nickname')" style="width: 200px" />
|
v-model="platformCode"
|
||||||
<a-button type="primary" @click="() => { currentPage = 1; loadList() }">{{ t('warehouse.common.search') }}</a-button>
|
allow-clear
|
||||||
<a-button v-if="hasPermission('Order.PlatformStore.Save')" type="primary" @click="handleAdd">{{ t('warehouse.common.add') }}</a-button>
|
:placeholder="t('order.platformStore.search.platform')"
|
||||||
|
:options="platformOptions"
|
||||||
|
style="width: 160px"
|
||||||
|
/>
|
||||||
|
<a-input
|
||||||
|
v-model="keyword"
|
||||||
|
allow-clear
|
||||||
|
:placeholder="t('order.platformStore.search.nickname')"
|
||||||
|
style="width: 200px"
|
||||||
|
@press-enter="handleSearch"
|
||||||
|
/>
|
||||||
|
<a-button type="primary" @click="handleSearch">{{ t('warehouse.common.search') }}</a-button>
|
||||||
|
<a-button v-if="hasPermission('Order.PlatformStore.Save')" type="primary" @click="handleAdd">
|
||||||
|
{{ t('warehouse.common.add') }}
|
||||||
|
</a-button>
|
||||||
</a-space>
|
</a-space>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="toolbar-right">
|
||||||
|
<PaginationPro
|
||||||
|
:total-count="totalCount"
|
||||||
|
v-model:current-page="currentPage"
|
||||||
|
v-model:page-size="pageSize"
|
||||||
|
:page-sizes="[10, 30, 50, 100]"
|
||||||
|
@page-change="handlePageChange"
|
||||||
|
@page-size-change="handlePageSizeChange"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div ref="tableContainerRef" class="list-wrap">
|
<div ref="tableContainerRef" class="list-wrap">
|
||||||
<HandsontablePro :columns="hotColumns" :data="tableRows" :height="tableHeight" :loading="listLoading" show-action-column :action-renderer="actionRenderer" @action-click="handleActionClick" />
|
<HandsontablePro
|
||||||
|
:columns="hotColumns"
|
||||||
|
:data="tableRows"
|
||||||
|
:height="tableHeight"
|
||||||
|
:loading="listLoading"
|
||||||
|
:stretch-columns="true"
|
||||||
|
show-action-column
|
||||||
|
:action-renderer="actionRenderer"
|
||||||
|
@action-click="handleActionClick"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<PaginationPro v-model:current="currentPage" v-model:page-size="pageSize" :total="totalCount" @change="loadList" @page-size-change="() => { currentPage = 1; loadList() }" />
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ModalPro v-model:visible="modalVisible" :title="formModel.id ? t('order.platformStore.modal.edit') : t('order.platformStore.modal.add')" :width="640" @cancel="closeModal">
|
<ModalPro
|
||||||
|
v-model:visible="modalVisible"
|
||||||
|
:title="formModel.id ? t('order.platformStore.modal.edit') : t('order.platformStore.modal.add')"
|
||||||
|
:width="640"
|
||||||
|
@cancel="closeModal"
|
||||||
|
>
|
||||||
<FormPro :model="formModel" layout="horizontal" :row-gutter="20" :default-span="12">
|
<FormPro :model="formModel" layout="horizontal" :row-gutter="20" :default-span="12">
|
||||||
<a-form-item :label="t('order.platformStore.form.platform')" required>
|
<a-form-item :label="t('order.platformStore.form.platform')" required>
|
||||||
<a-select v-model="formModel.platformCode" :options="platformOptions" :disabled="formModel.id > 0" />
|
<a-select v-model="formModel.platformCode" :options="platformOptions" :disabled="formModel.id > 0" />
|
||||||
@ -177,6 +272,9 @@ onMounted(loadList)
|
|||||||
<a-form-item :label="t('order.platformStore.form.account')">
|
<a-form-item :label="t('order.platformStore.form.account')">
|
||||||
<a-input v-model="formModel.platformAccount" />
|
<a-input v-model="formModel.platformAccount" />
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
<a-form-item :label="t('order.platformStore.form.siteCountry')">
|
||||||
|
<a-input v-model="formModel.siteCountry" />
|
||||||
|
</a-form-item>
|
||||||
<a-form-item :label="t('order.platformStore.form.autoFulfill')">
|
<a-form-item :label="t('order.platformStore.form.autoFulfill')">
|
||||||
<a-switch v-model="formModel.isAutoFulfill" />
|
<a-switch v-model="formModel.isAutoFulfill" />
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
@ -190,25 +288,5 @@ onMounted(loadList)
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped lang="less">
|
<style scoped lang="less">
|
||||||
.platform-store-container {
|
@import '@/styles/warehouse-list-page.less';
|
||||||
padding: 16px;
|
|
||||||
background: #f0f3f4;
|
|
||||||
min-height: 100%;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
.page-card {
|
|
||||||
background: #fff;
|
|
||||||
padding: 16px;
|
|
||||||
flex: 1;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
min-height: 0;
|
|
||||||
border-radius: 4px;
|
|
||||||
}
|
|
||||||
.list-wrap {
|
|
||||||
flex: 1;
|
|
||||||
min-height: 0;
|
|
||||||
margin-top: 12px;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -18,6 +18,14 @@ import {
|
|||||||
import { getWarehouseList } from '@/api/system/warehouse'
|
import { getWarehouseList } from '@/api/system/warehouse'
|
||||||
import { $baseMessage } from '@/utils'
|
import { $baseMessage } from '@/utils'
|
||||||
import { hasPermission } from '@/utils/rbac-permission'
|
import { hasPermission } from '@/utils/rbac-permission'
|
||||||
|
import {
|
||||||
|
buildWarehouseNameMap,
|
||||||
|
formatDateTime,
|
||||||
|
formatMoney,
|
||||||
|
normalizeListRow,
|
||||||
|
pickField,
|
||||||
|
resolveWarehouseLabel,
|
||||||
|
} from '@/utils/warehouseListMapper'
|
||||||
|
|
||||||
defineOptions({ name: 'PlatformStoreOrder' })
|
defineOptions({ name: 'PlatformStoreOrder' })
|
||||||
|
|
||||||
@ -28,6 +36,7 @@ const fulfillStatus = ref<string>()
|
|||||||
const keyword = ref('')
|
const keyword = ref('')
|
||||||
const summary = ref<Record<string, number>>({})
|
const summary = ref<Record<string, number>>({})
|
||||||
const warehouseOptions = ref<Array<{ label: string; value: number }>>([])
|
const warehouseOptions = ref<Array<{ label: string; value: number }>>([])
|
||||||
|
const warehouseNameMap = computed(() => buildWarehouseNameMap(warehouseOptions.value))
|
||||||
const productOptions = ref<Array<{ label: string; value: string }>>([])
|
const productOptions = ref<Array<{ label: string; value: string }>>([])
|
||||||
const listLoading = ref(false)
|
const listLoading = ref(false)
|
||||||
const tableRows = ref<any[]>([])
|
const tableRows = ref<any[]>([])
|
||||||
@ -36,10 +45,10 @@ const pageSize = ref(30)
|
|||||||
const totalCount = ref(0)
|
const totalCount = ref(0)
|
||||||
|
|
||||||
const statusTabs = [
|
const statusTabs = [
|
||||||
{ value: undefined, labelKey: 'order.platformStoreOrder.status.all' },
|
{ value: undefined, labelKey: 'order.platformStoreOrder.status.all', countKey: null },
|
||||||
{ value: 'pending', labelKey: 'order.platformStoreOrder.status.pending' },
|
{ value: 'pending', labelKey: 'order.platformStoreOrder.status.pending', countKey: 'pending' as const },
|
||||||
{ value: 'toShip', labelKey: 'order.platformStoreOrder.status.toShip' },
|
{ value: 'toShip', labelKey: 'order.platformStoreOrder.status.toShip', countKey: 'toShip' as const },
|
||||||
{ value: 'shipped', labelKey: 'order.platformStoreOrder.status.shipped' },
|
{ value: 'shipped', labelKey: 'order.platformStoreOrder.status.shipped', countKey: 'shipped' as const },
|
||||||
]
|
]
|
||||||
|
|
||||||
const detailVisible = ref(false)
|
const detailVisible = ref(false)
|
||||||
@ -49,13 +58,50 @@ const matchProductCode = ref<string>()
|
|||||||
|
|
||||||
const hotColumns = computed(() => [
|
const hotColumns = computed(() => [
|
||||||
{ key: 'platformOrderNo', label: t('order.platformStoreOrder.table.orderNo'), width: 160 },
|
{ 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: '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: '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: 'linkedServiceNo', label: t('order.platformStoreOrder.table.linkedServiceNo'), width: 150 },
|
||||||
{ key: 'totalAmount', label: t('order.platformStoreOrder.table.amount'), width: 90 },
|
{ key: 'shippingLabelNo', label: t('order.platformStoreOrder.table.shippingLabel'), width: 140 },
|
||||||
{ key: 'platformOrderTime', label: t('order.platformStoreOrder.table.orderTime'), width: 170 },
|
{ 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<string, unknown>) {
|
||||||
|
const row = normalizeListRow(raw)
|
||||||
|
const status = String(pickField(row, 'fulfillStatus', 'FulfillStatus') ?? '')
|
||||||
|
const platformCode = String(pickField(row, 'platformCode', 'PlatformCode') ?? '')
|
||||||
|
const whId = pickField<number>(row, 'warehouseId', 'WarehouseId')
|
||||||
|
const currency = pickField<string>(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<number>(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<number>(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() {
|
async function loadProducts() {
|
||||||
try {
|
try {
|
||||||
const { items } = await getSalesProductList({ page: 1, pageSize: 200, status: 1 })
|
const { items } = await getSalesProductList({ page: 1, pageSize: 200, status: 1 })
|
||||||
@ -77,7 +123,12 @@ async function loadProducts() {
|
|||||||
async function loadSummary() {
|
async function loadSummary() {
|
||||||
try {
|
try {
|
||||||
const data = await getPlatformStoreOrderSummary()
|
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 {
|
} catch {
|
||||||
summary.value = {}
|
summary.value = {}
|
||||||
}
|
}
|
||||||
@ -92,12 +143,7 @@ async function loadList() {
|
|||||||
fulfillStatus: fulfillStatus.value,
|
fulfillStatus: fulfillStatus.value,
|
||||||
platformOrderNo: keyword.value || undefined,
|
platformOrderNo: keyword.value || undefined,
|
||||||
})
|
})
|
||||||
tableRows.value = (items || []).map((r: any) => ({
|
tableRows.value = (items || []).map((item: Record<string, unknown>) => mapRow(item))
|
||||||
...r,
|
|
||||||
fulfillStatus: t(`order.platformStoreOrder.status.${r.fulfillStatus}`, r.fulfillStatus),
|
|
||||||
linkedServiceNo: r.linkedServiceNo || '-',
|
|
||||||
_raw: r,
|
|
||||||
}))
|
|
||||||
totalCount.value = Number(total || 0)
|
totalCount.value = Number(total || 0)
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
$baseMessage(e?.message || t('order.platformStoreOrder.message.loadFailed'), 'error')
|
$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) {
|
async function showDetail(row: any) {
|
||||||
try {
|
try {
|
||||||
detailData.value = await getPlatformStoreOrderDetail(row._raw?.id || row.id)
|
detailData.value = await getPlatformStoreOrderDetail(row._raw?.id || row.id)
|
||||||
@ -147,9 +209,8 @@ async function handleBatchMatch() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const pendingIds = tableRows.value
|
const pendingIds = tableRows.value
|
||||||
.map((row) => row._raw || row)
|
.filter((row) => row._status === 'pending' && !pickField(row._raw, 'linkedOrderId', 'LinkedOrderId'))
|
||||||
.filter((raw) => raw.fulfillStatus === 'pending' && !raw.linkedOrderId)
|
.map((row) => Number(row.id))
|
||||||
.map((raw) => raw.id)
|
|
||||||
|
|
||||||
if (!pendingIds.length) {
|
if (!pendingIds.length) {
|
||||||
$baseMessage(t('order.platformStoreOrder.message.batchEmpty'), 'warning')
|
$baseMessage(t('order.platformStoreOrder.message.batchEmpty'), 'warning')
|
||||||
@ -185,8 +246,12 @@ async function handleBatchMatch() {
|
|||||||
async function handleSuspend(row: any) {
|
async function handleSuspend(row: any) {
|
||||||
if (!hasPermission('Order.PlatformStoreOrder.ToggleSuspend')) return
|
if (!hasPermission('Order.PlatformStoreOrder.ToggleSuspend')) return
|
||||||
const raw = row._raw || row
|
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')
|
$baseMessage(t('order.platformStoreOrder.message.suspendSuccess'), 'success')
|
||||||
|
await loadSummary()
|
||||||
await loadList()
|
await loadList()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -194,10 +259,11 @@ function handleDelete(row: any) {
|
|||||||
if (!hasPermission('Order.PlatformStoreOrder.Delete')) return
|
if (!hasPermission('Order.PlatformStoreOrder.Delete')) return
|
||||||
Modal.confirm({
|
Modal.confirm({
|
||||||
title: t('order.platformStoreOrder.actions.delete'),
|
title: t('order.platformStoreOrder.actions.delete'),
|
||||||
content: row._raw?.platformOrderNo || row.platformOrderNo,
|
content: row.platformOrderNo,
|
||||||
onOk: async () => {
|
onOk: async () => {
|
||||||
await deletePlatformStoreOrder([row._raw?.id || row.id])
|
await deletePlatformStoreOrder([row.id])
|
||||||
$baseMessage(t('order.platformStoreOrder.message.deleteSuccess'), 'success')
|
$baseMessage(t('order.platformStoreOrder.message.deleteSuccess'), 'success')
|
||||||
|
await loadSummary()
|
||||||
await loadList()
|
await loadList()
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@ -206,11 +272,17 @@ function handleDelete(row: any) {
|
|||||||
function actionRenderer(row: any) {
|
function actionRenderer(row: any) {
|
||||||
const parts = [`<button type="button" class="hot-action-btn" data-action="detail">${t('warehouse.common.detail')}</button>`]
|
const parts = [`<button type="button" class="hot-action-btn" data-action="detail">${t('warehouse.common.detail')}</button>`]
|
||||||
const raw = row._raw || row
|
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(`<button type="button" class="hot-action-btn" data-action="match">${t('order.platformStoreOrder.actions.match')}</button>`)
|
parts.push(`<button type="button" class="hot-action-btn" data-action="match">${t('order.platformStoreOrder.actions.match')}</button>`)
|
||||||
}
|
}
|
||||||
if (hasPermission('Order.PlatformStoreOrder.ToggleSuspend')) {
|
if (hasPermission('Order.PlatformStoreOrder.ToggleSuspend')) {
|
||||||
parts.push(`<button type="button" class="hot-action-btn" data-action="suspend">${raw.isSuspended ? t('order.platformStoreOrder.actions.resume') : t('order.platformStoreOrder.actions.suspend')}</button>`)
|
parts.push(
|
||||||
|
`<button type="button" class="hot-action-btn" data-action="suspend">${row._suspended ? t('order.platformStoreOrder.actions.resume') : t('order.platformStoreOrder.actions.suspend')}</button>`,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
if (hasPermission('Order.PlatformStoreOrder.Delete')) {
|
if (hasPermission('Order.PlatformStoreOrder.Delete')) {
|
||||||
parts.push(`<button type="button" class="hot-action-btn" data-action="delete">${t('warehouse.common.delete')}</button>`)
|
parts.push(`<button type="button" class="hot-action-btn" data-action="delete">${t('warehouse.common.delete')}</button>`)
|
||||||
@ -227,7 +299,10 @@ function handleActionClick(row: any, _i: number, action?: string) {
|
|||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
const { items } = await getWarehouseList({ page: 1, pageSize: 200 }).catch(() => ({ items: [] }))
|
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
|
if (warehouseOptions.value.length) matchWarehouseId.value = warehouseOptions.value[0].value
|
||||||
await loadProducts()
|
await loadProducts()
|
||||||
await loadSummary()
|
await loadSummary()
|
||||||
@ -236,34 +311,72 @@ onMounted(async () => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="platform-store-order-container">
|
<div class="platform-store-order-container warehouse-list-page">
|
||||||
<div class="page-card">
|
<div class="warehouse-list-page-card">
|
||||||
<a-tabs v-model:active-key="fulfillStatus" @change="() => { currentPage = 1; loadList() }">
|
<a-tabs
|
||||||
<a-tab-pane v-for="tab in statusTabs" :key="tab.value ?? 'all'" :title="t(tab.labelKey)" />
|
v-model:active-key="fulfillStatus"
|
||||||
|
class="status-tabs"
|
||||||
|
@change="() => { currentPage = 1; loadList() }"
|
||||||
|
>
|
||||||
|
<a-tab-pane v-for="tab in statusTabs" :key="tab.value ?? 'all'">
|
||||||
|
<template #title>
|
||||||
|
{{ t(tab.labelKey) }}
|
||||||
|
<span v-if="tab.countKey" class="tab-count">({{ summary[tab.countKey] ?? 0 }})</span>
|
||||||
|
</template>
|
||||||
|
</a-tab-pane>
|
||||||
</a-tabs>
|
</a-tabs>
|
||||||
<div class="table-toolbar">
|
<div class="table-toolbar">
|
||||||
<div class="toolbar-left">
|
<div class="toolbar-left">
|
||||||
<a-space wrap :size="8">
|
<a-space wrap :size="8">
|
||||||
<a-select v-model="matchWarehouseId" :placeholder="t('warehouse.common.warehouse')" :options="warehouseOptions" style="width: 180px" />
|
<a-select
|
||||||
<a-select v-model="matchProductCode" allow-clear :placeholder="t('order.platformStoreOrder.form.product')" :options="productOptions" style="width: 220px" />
|
v-model="matchWarehouseId"
|
||||||
<a-input v-model="keyword" allow-clear :placeholder="t('order.platformStoreOrder.search.orderNo')" style="width: 180px" />
|
:placeholder="t('warehouse.common.warehouse')"
|
||||||
<a-button type="primary" @click="() => { currentPage = 1; loadList() }">{{ t('warehouse.common.search') }}</a-button>
|
:options="warehouseOptions"
|
||||||
|
style="width: 180px"
|
||||||
|
/>
|
||||||
|
<a-select
|
||||||
|
v-model="matchProductCode"
|
||||||
|
allow-clear
|
||||||
|
:placeholder="t('order.platformStoreOrder.form.product')"
|
||||||
|
:options="productOptions"
|
||||||
|
style="width: 220px"
|
||||||
|
/>
|
||||||
|
<a-input
|
||||||
|
v-model="keyword"
|
||||||
|
allow-clear
|
||||||
|
:placeholder="t('order.platformStoreOrder.search.orderNo')"
|
||||||
|
style="width: 180px"
|
||||||
|
@press-enter="handleSearch"
|
||||||
|
/>
|
||||||
|
<a-button type="primary" @click="handleSearch">{{ t('warehouse.common.search') }}</a-button>
|
||||||
<a-button v-if="hasPermission('Order.PlatformStoreOrder.MatchInventory')" @click="handleBatchMatch">
|
<a-button v-if="hasPermission('Order.PlatformStoreOrder.MatchInventory')" @click="handleBatchMatch">
|
||||||
{{ t('order.platformStoreOrder.actions.batchMatch') }}
|
{{ t('order.platformStoreOrder.actions.batchMatch') }}
|
||||||
</a-button>
|
</a-button>
|
||||||
</a-space>
|
</a-space>
|
||||||
</div>
|
</div>
|
||||||
<div class="toolbar-right">
|
<div class="toolbar-right">
|
||||||
<a-space :size="12">
|
<PaginationPro
|
||||||
<span>{{ t('order.platformStoreOrder.summary.pending') }}: {{ summary.pending ?? 0 }}</span>
|
:total-count="totalCount"
|
||||||
<span>{{ t('order.platformStoreOrder.summary.toShip') }}: {{ summary.toShip ?? 0 }}</span>
|
v-model:current-page="currentPage"
|
||||||
</a-space>
|
v-model:page-size="pageSize"
|
||||||
|
:page-sizes="[10, 30, 50, 100]"
|
||||||
|
@page-change="handlePageChange"
|
||||||
|
@page-size-change="handlePageSizeChange"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div ref="tableContainerRef" class="list-wrap">
|
<div ref="tableContainerRef" class="list-wrap">
|
||||||
<HandsontablePro :columns="hotColumns" :data="tableRows" :height="tableHeight" :loading="listLoading" show-action-column :action-renderer="actionRenderer" @action-click="handleActionClick" />
|
<HandsontablePro
|
||||||
|
:columns="hotColumns"
|
||||||
|
:data="tableRows"
|
||||||
|
:height="tableHeight"
|
||||||
|
:loading="listLoading"
|
||||||
|
:stretch-columns="true"
|
||||||
|
show-action-column
|
||||||
|
:action-renderer="actionRenderer"
|
||||||
|
@action-click="handleActionClick"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<PaginationPro v-model:current="currentPage" v-model:page-size="pageSize" :total="totalCount" @change="loadList" @page-size-change="() => { currentPage = 1; loadList() }" />
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<a-drawer v-model:visible="detailVisible" :title="t('order.platformStoreOrder.drawer.detail')" :width="720">
|
<a-drawer v-model:visible="detailVisible" :title="t('order.platformStoreOrder.drawer.detail')" :width="720">
|
||||||
@ -271,7 +384,6 @@ onMounted(async () => {
|
|||||||
<p>{{ detailData.platformOrderNo }} · {{ detailData.storeNickname }}</p>
|
<p>{{ detailData.platformOrderNo }} · {{ detailData.storeNickname }}</p>
|
||||||
<p v-if="detailData.linkedServiceNo">
|
<p v-if="detailData.linkedServiceNo">
|
||||||
{{ t('order.platformStoreOrder.table.linkedServiceNo') }}: {{ detailData.linkedServiceNo }}
|
{{ t('order.platformStoreOrder.table.linkedServiceNo') }}: {{ detailData.linkedServiceNo }}
|
||||||
<span v-if="detailData.linkedOrderId">(#{{ detailData.linkedOrderId }})</span>
|
|
||||||
</p>
|
</p>
|
||||||
<a-table :data="detailData.lines || []" :pagination="false" size="small">
|
<a-table :data="detailData.lines || []" :pagination="false" size="small">
|
||||||
<template #columns>
|
<template #columns>
|
||||||
@ -287,25 +399,5 @@ onMounted(async () => {
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped lang="less">
|
<style scoped lang="less">
|
||||||
.platform-store-order-container {
|
@import '@/styles/warehouse-list-page.less';
|
||||||
padding: 16px;
|
|
||||||
background: #f0f3f4;
|
|
||||||
min-height: 100%;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
.page-card {
|
|
||||||
background: #fff;
|
|
||||||
padding: 16px;
|
|
||||||
flex: 1;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
min-height: 0;
|
|
||||||
border-radius: 4px;
|
|
||||||
}
|
|
||||||
.list-wrap {
|
|
||||||
flex: 1;
|
|
||||||
min-height: 0;
|
|
||||||
margin-top: 12px;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -19,6 +19,13 @@ import { getWarehouseList } from '@/api/system/warehouse'
|
|||||||
import { getWarehouseSkuList } from '@/api/warehouse'
|
import { getWarehouseSkuList } from '@/api/warehouse'
|
||||||
import { $baseMessage } from '@/utils'
|
import { $baseMessage } from '@/utils'
|
||||||
import { hasPermission } from '@/utils/rbac-permission'
|
import { hasPermission } from '@/utils/rbac-permission'
|
||||||
|
import {
|
||||||
|
buildWarehouseNameMap,
|
||||||
|
formatDateTime,
|
||||||
|
normalizeListRow,
|
||||||
|
pickField,
|
||||||
|
resolveWarehouseLabel,
|
||||||
|
} from '@/utils/warehouseListMapper'
|
||||||
|
|
||||||
defineOptions({ name: 'WarehouseInboundNotice' })
|
defineOptions({ name: 'WarehouseInboundNotice' })
|
||||||
|
|
||||||
@ -30,6 +37,7 @@ const summary = ref({ forecasted: 0, received: 0, completed: 0 })
|
|||||||
const keyword = ref('')
|
const keyword = ref('')
|
||||||
const warehouseId = ref<number>()
|
const warehouseId = ref<number>()
|
||||||
const warehouseOptions = ref<Array<{ label: string; value: number }>>([])
|
const warehouseOptions = ref<Array<{ label: string; value: number }>>([])
|
||||||
|
const warehouseNameMap = computed(() => buildWarehouseNameMap(warehouseOptions.value))
|
||||||
const skuOptions = ref<Array<{ label: string; value: number; code: string }>>([])
|
const skuOptions = ref<Array<{ label: string; value: number; code: string }>>([])
|
||||||
const listLoading = ref(false)
|
const listLoading = ref(false)
|
||||||
const tableRows = ref<any[]>([])
|
const tableRows = ref<any[]>([])
|
||||||
@ -39,8 +47,9 @@ const totalCount = ref(0)
|
|||||||
|
|
||||||
const statusTabs = [
|
const statusTabs = [
|
||||||
{ value: undefined, labelKey: 'warehouse.inbound.status.all', countKey: null },
|
{ value: undefined, labelKey: 'warehouse.inbound.status.all', countKey: null },
|
||||||
{ value: 'forecasted', labelKey: 'warehouse.inbound.status.forecasted', countKey: 'forecasted' },
|
{ value: 'forecasted', labelKey: 'warehouse.inbound.status.forecasted', countKey: 'forecasted' as const },
|
||||||
{ value: 'completed', labelKey: 'warehouse.inbound.status.completed', countKey: 'completed' },
|
{ value: 'received', labelKey: 'warehouse.inbound.status.received', countKey: 'received' as const },
|
||||||
|
{ value: 'completed', labelKey: 'warehouse.inbound.status.completed', countKey: 'completed' as const },
|
||||||
]
|
]
|
||||||
|
|
||||||
const detailVisible = ref(false)
|
const detailVisible = ref(false)
|
||||||
@ -62,12 +71,36 @@ const formModel = reactive({
|
|||||||
const hotColumns = computed(() => [
|
const hotColumns = computed(() => [
|
||||||
{ key: 'noticeNo', label: t('warehouse.inbound.table.noticeNo'), width: 180 },
|
{ key: 'noticeNo', label: t('warehouse.inbound.table.noticeNo'), width: 180 },
|
||||||
{ key: 'markReference', label: t('warehouse.inbound.table.markReference'), width: 120 },
|
{ key: 'markReference', label: t('warehouse.inbound.table.markReference'), width: 120 },
|
||||||
|
{ key: 'warehouseName', label: t('warehouse.inbound.table.warehouse'), width: 140 },
|
||||||
|
{ key: 'inboundType', label: t('warehouse.inbound.table.inboundType'), width: 100 },
|
||||||
{ key: 'receiptStatus', label: t('warehouse.inbound.table.receiptStatus'), width: 100 },
|
{ key: 'receiptStatus', label: t('warehouse.inbound.table.receiptStatus'), width: 100 },
|
||||||
{ key: 'boxCount', label: t('warehouse.inbound.table.boxCount'), width: 80 },
|
{ key: 'boxCount', label: t('warehouse.inbound.table.boxCount'), width: 80 },
|
||||||
{ key: 'plannedQty', label: t('warehouse.inbound.table.plannedQty'), width: 90 },
|
{ key: 'plannedQty', label: t('warehouse.inbound.table.plannedQty'), width: 90 },
|
||||||
{ key: 'createTime', label: t('warehouse.inbound.table.createTime'), width: 170 },
|
{ key: 'expectedArrivalTime', label: t('warehouse.inbound.table.expectedArrival'), width: 160 },
|
||||||
|
{ key: 'createTime', label: t('warehouse.inbound.table.createTime'), width: 160 },
|
||||||
])
|
])
|
||||||
|
|
||||||
|
function mapRow(raw: Record<string, unknown>) {
|
||||||
|
const row = normalizeListRow(raw)
|
||||||
|
const status = String(pickField(row, 'receiptStatus', 'ReceiptStatus') ?? '')
|
||||||
|
const inboundType = String(pickField(row, 'inboundType', 'InboundType') ?? 'normal')
|
||||||
|
const whId = pickField<number>(row, 'warehouseId', 'WarehouseId')
|
||||||
|
return {
|
||||||
|
id: pickField<number>(row, 'id', 'Id'),
|
||||||
|
noticeNo: pickField(row, 'noticeNo', 'NoticeNo') ?? '-',
|
||||||
|
markReference: pickField(row, 'markReference', 'MarkReference') ?? '-',
|
||||||
|
warehouseName: resolveWarehouseLabel(whId, warehouseNameMap.value),
|
||||||
|
inboundType: t(`warehouse.inbound.type.${inboundType}`, inboundType),
|
||||||
|
receiptStatus: t(`warehouse.inbound.status.${status}`, status),
|
||||||
|
boxCount: pickField<number>(row, 'boxCount', 'BoxCount') ?? 0,
|
||||||
|
plannedQty: pickField<number>(row, 'plannedQty', 'PlannedQty') ?? 0,
|
||||||
|
expectedArrivalTime: formatDateTime(pickField(row, 'expectedArrivalTime', 'ExpectedArrivalTime')),
|
||||||
|
createTime: formatDateTime(pickField(row, 'createTime', 'CreateTime')),
|
||||||
|
_raw: row,
|
||||||
|
_status: status,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function loadWarehouses() {
|
async function loadWarehouses() {
|
||||||
const { items } = await getWarehouseList({ page: 1, pageSize: 200 }).catch(() => ({ items: [] }))
|
const { items } = await getWarehouseList({ page: 1, pageSize: 200 }).catch(() => ({ items: [] }))
|
||||||
warehouseOptions.value = (items || []).map((w: any) => ({
|
warehouseOptions.value = (items || []).map((w: any) => ({
|
||||||
@ -79,9 +112,9 @@ async function loadWarehouses() {
|
|||||||
async function loadSkus() {
|
async function loadSkus() {
|
||||||
const { items } = await getWarehouseSkuList({ page: 1, pageSize: 500 }).catch(() => ({ items: [] }))
|
const { items } = await getWarehouseSkuList({ page: 1, pageSize: 500 }).catch(() => ({ items: [] }))
|
||||||
skuOptions.value = (items || []).map((s: any) => ({
|
skuOptions.value = (items || []).map((s: any) => ({
|
||||||
label: `${s.skuCode} - ${s.skuName}`,
|
label: `${s.skuCode || s.SkuCode} - ${s.skuName || s.SkuName}`,
|
||||||
value: Number(s.id),
|
value: Number(s.id || s.Id),
|
||||||
code: s.skuCode,
|
code: s.skuCode || s.SkuCode,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -108,11 +141,7 @@ async function loadList() {
|
|||||||
keyword: keyword.value || undefined,
|
keyword: keyword.value || undefined,
|
||||||
warehouseId: warehouseId.value,
|
warehouseId: warehouseId.value,
|
||||||
})
|
})
|
||||||
tableRows.value = (items || []).map((r: any) => ({
|
tableRows.value = (items || []).map((item: Record<string, unknown>) => mapRow(item))
|
||||||
...r,
|
|
||||||
receiptStatus: t(`warehouse.inbound.status.${r.receiptStatus}`, r.receiptStatus),
|
|
||||||
_raw: r,
|
|
||||||
}))
|
|
||||||
totalCount.value = Number(total || 0)
|
totalCount.value = Number(total || 0)
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
$baseMessage(e?.message || t('warehouse.inbound.message.loadFailed'), 'error')
|
$baseMessage(e?.message || t('warehouse.inbound.message.loadFailed'), 'error')
|
||||||
@ -121,6 +150,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) {
|
async function showDetail(row: any) {
|
||||||
try {
|
try {
|
||||||
detailData.value = await getWarehouseInboundDetail(row._raw?.id || row.id)
|
detailData.value = await getWarehouseInboundDetail(row._raw?.id || row.id)
|
||||||
@ -179,7 +224,7 @@ function handleDelete(row: any) {
|
|||||||
if (!hasPermission('Warehouse.InboundNotice.Delete')) return
|
if (!hasPermission('Warehouse.InboundNotice.Delete')) return
|
||||||
Modal.confirm({
|
Modal.confirm({
|
||||||
title: t('warehouse.inbound.actions.delete'),
|
title: t('warehouse.inbound.actions.delete'),
|
||||||
content: row._raw?.noticeNo || row.noticeNo,
|
content: row.noticeNo,
|
||||||
onOk: async () => {
|
onOk: async () => {
|
||||||
await deleteWarehouseInbound(row._raw?.id || row.id)
|
await deleteWarehouseInbound(row._raw?.id || row.id)
|
||||||
$baseMessage(t('warehouse.inbound.message.deleteSuccess'), 'success')
|
$baseMessage(t('warehouse.inbound.message.deleteSuccess'), 'success')
|
||||||
@ -191,8 +236,7 @@ function handleDelete(row: any) {
|
|||||||
|
|
||||||
function actionRenderer(row: any) {
|
function actionRenderer(row: any) {
|
||||||
const parts = [`<button type="button" class="hot-action-btn" data-action="detail">${t('warehouse.common.detail')}</button>`]
|
const parts = [`<button type="button" class="hot-action-btn" data-action="detail">${t('warehouse.common.detail')}</button>`]
|
||||||
const rawStatus = row._raw?.receiptStatus
|
if (hasPermission('Warehouse.InboundNotice.Delete') && row._status === 'forecasted') {
|
||||||
if (hasPermission('Warehouse.InboundNotice.Delete') && rawStatus === 'forecasted') {
|
|
||||||
parts.push(`<button type="button" class="hot-action-btn" data-action="delete">${t('warehouse.common.delete')}</button>`)
|
parts.push(`<button type="button" class="hot-action-btn" data-action="delete">${t('warehouse.common.delete')}</button>`)
|
||||||
}
|
}
|
||||||
return parts.join('')
|
return parts.join('')
|
||||||
@ -212,35 +256,73 @@ onMounted(async () => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="warehouse-inbound-container">
|
<div class="warehouse-inbound-container warehouse-list-page">
|
||||||
<div class="page-card">
|
<div class="warehouse-list-page-card">
|
||||||
<a-tabs v-model:active-key="receiptStatus" @change="() => { currentPage = 1; loadList() }">
|
<a-tabs
|
||||||
|
v-model:active-key="receiptStatus"
|
||||||
|
class="status-tabs"
|
||||||
|
@change="() => { currentPage = 1; loadList() }"
|
||||||
|
>
|
||||||
<a-tab-pane v-for="tab in statusTabs" :key="tab.value ?? 'all'">
|
<a-tab-pane v-for="tab in statusTabs" :key="tab.value ?? 'all'">
|
||||||
<template #title>
|
<template #title>
|
||||||
{{ t(tab.labelKey) }}
|
{{ t(tab.labelKey) }}
|
||||||
<span v-if="tab.countKey" class="tab-count">({{ summary[tab.countKey as 'forecasted' | 'completed'] }})</span>
|
<span v-if="tab.countKey" class="tab-count">({{ summary[tab.countKey] }})</span>
|
||||||
</template>
|
</template>
|
||||||
</a-tab-pane>
|
</a-tab-pane>
|
||||||
</a-tabs>
|
</a-tabs>
|
||||||
<div class="table-toolbar">
|
<div class="table-toolbar">
|
||||||
<div class="toolbar-left">
|
<div class="toolbar-left">
|
||||||
<a-space wrap :size="8">
|
<a-space wrap :size="8">
|
||||||
<a-select v-model="warehouseId" allow-clear :placeholder="t('warehouse.common.warehouse')" :options="warehouseOptions" style="width: 200px" @change="() => { loadSummary(); currentPage = 1; loadList() }" />
|
<a-select
|
||||||
<a-input v-model="keyword" allow-clear :placeholder="t('warehouse.inbound.search.keyword')" style="width: 200px" />
|
v-model="warehouseId"
|
||||||
<a-button type="primary" @click="() => { currentPage = 1; loadList() }">{{ t('warehouse.common.search') }}</a-button>
|
allow-clear
|
||||||
<a-button v-if="hasPermission('Warehouse.InboundNotice.Save')" type="primary" @click="handleAdd">{{ t('warehouse.common.add') }}</a-button>
|
:placeholder="t('warehouse.common.warehouse')"
|
||||||
|
:options="warehouseOptions"
|
||||||
|
style="width: 200px"
|
||||||
|
@change="() => { loadSummary(); handleSearch() }"
|
||||||
|
/>
|
||||||
|
<a-input
|
||||||
|
v-model="keyword"
|
||||||
|
allow-clear
|
||||||
|
:placeholder="t('warehouse.inbound.search.keyword')"
|
||||||
|
style="width: 200px"
|
||||||
|
@press-enter="handleSearch"
|
||||||
|
/>
|
||||||
|
<a-button type="primary" @click="handleSearch">{{ t('warehouse.common.search') }}</a-button>
|
||||||
|
<a-button v-if="hasPermission('Warehouse.InboundNotice.Save')" type="primary" @click="handleAdd">
|
||||||
|
{{ t('warehouse.common.add') }}
|
||||||
|
</a-button>
|
||||||
</a-space>
|
</a-space>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="toolbar-right">
|
||||||
|
<PaginationPro
|
||||||
|
:total-count="totalCount"
|
||||||
|
v-model:current-page="currentPage"
|
||||||
|
v-model:page-size="pageSize"
|
||||||
|
:page-sizes="[10, 30, 50, 100]"
|
||||||
|
@page-change="handlePageChange"
|
||||||
|
@page-size-change="handlePageSizeChange"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div ref="tableContainerRef" class="list-wrap">
|
<div ref="tableContainerRef" class="list-wrap">
|
||||||
<HandsontablePro :columns="hotColumns" :data="tableRows" :height="tableHeight" :loading="listLoading" show-action-column :action-renderer="actionRenderer" @action-click="handleActionClick" />
|
<HandsontablePro
|
||||||
|
:columns="hotColumns"
|
||||||
|
:data="tableRows"
|
||||||
|
:height="tableHeight"
|
||||||
|
:loading="listLoading"
|
||||||
|
:stretch-columns="true"
|
||||||
|
show-action-column
|
||||||
|
:action-renderer="actionRenderer"
|
||||||
|
@action-click="handleActionClick"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<PaginationPro v-model:current="currentPage" v-model:page-size="pageSize" :total="totalCount" @change="loadList" @page-size-change="() => { currentPage = 1; loadList() }" />
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<a-drawer v-model:visible="detailVisible" :title="t('warehouse.inbound.drawer.detail')" :width="640">
|
<a-drawer v-model:visible="detailVisible" :title="t('warehouse.inbound.drawer.detail')" :width="640">
|
||||||
<template v-if="detailData">
|
<template v-if="detailData">
|
||||||
<p>{{ t('warehouse.inbound.table.noticeNo') }}: {{ detailData.noticeNo }}</p>
|
<p>{{ t('warehouse.inbound.table.noticeNo') }}: {{ detailData.noticeNo }}</p>
|
||||||
|
<p>{{ t('warehouse.inbound.table.markReference') }}: {{ detailData.markReference || '-' }}</p>
|
||||||
<div v-for="box in detailData.boxes || []" :key="box.id" class="box-block">
|
<div v-for="box in detailData.boxes || []" :key="box.id" class="box-block">
|
||||||
<div class="box-title">{{ t('warehouse.inbound.box') }}: {{ box.markBoxNo || box.boxSeqNo }}</div>
|
<div class="box-title">{{ t('warehouse.inbound.box') }}: {{ box.markBoxNo || box.boxSeqNo }}</div>
|
||||||
<a-table :data="box.lines || []" :pagination="false" size="small">
|
<a-table :data="box.lines || []" :pagination="false" size="small">
|
||||||
@ -281,27 +363,8 @@ onMounted(async () => {
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped lang="less">
|
<style scoped lang="less">
|
||||||
.warehouse-inbound-container {
|
@import '@/styles/warehouse-list-page.less';
|
||||||
padding: 16px;
|
|
||||||
background: #f0f3f4;
|
|
||||||
min-height: 100%;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
.page-card {
|
|
||||||
background: #fff;
|
|
||||||
padding: 16px;
|
|
||||||
flex: 1;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
min-height: 0;
|
|
||||||
border-radius: 4px;
|
|
||||||
}
|
|
||||||
.list-wrap {
|
|
||||||
flex: 1;
|
|
||||||
min-height: 0;
|
|
||||||
margin-top: 12px;
|
|
||||||
}
|
|
||||||
.box-block {
|
.box-block {
|
||||||
margin-bottom: 16px;
|
margin-bottom: 16px;
|
||||||
}
|
}
|
||||||
@ -309,8 +372,4 @@ onMounted(async () => {
|
|||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
}
|
}
|
||||||
.tab-count {
|
|
||||||
margin-left: 4px;
|
|
||||||
color: var(--app-primary-color);
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -15,6 +15,12 @@ import {
|
|||||||
import { getWarehouseList } from '@/api/system/warehouse'
|
import { getWarehouseList } from '@/api/system/warehouse'
|
||||||
import { $baseMessage } from '@/utils'
|
import { $baseMessage } from '@/utils'
|
||||||
import { hasPermission } from '@/utils/rbac-permission'
|
import { hasPermission } from '@/utils/rbac-permission'
|
||||||
|
import {
|
||||||
|
buildWarehouseNameMap,
|
||||||
|
normalizeListRow,
|
||||||
|
pickField,
|
||||||
|
resolveWarehouseLabel,
|
||||||
|
} from '@/utils/warehouseListMapper'
|
||||||
|
|
||||||
defineOptions({ name: 'WarehouseInventory' })
|
defineOptions({ name: 'WarehouseInventory' })
|
||||||
|
|
||||||
@ -29,6 +35,7 @@ const totalCount = ref(0)
|
|||||||
const keyword = ref('')
|
const keyword = ref('')
|
||||||
const warehouseId = ref<number>()
|
const warehouseId = ref<number>()
|
||||||
const warehouseOptions = ref<Array<{ label: string; value: number }>>([])
|
const warehouseOptions = ref<Array<{ label: string; value: number }>>([])
|
||||||
|
const warehouseNameMap = computed(() => buildWarehouseNameMap(warehouseOptions.value))
|
||||||
|
|
||||||
const binDetailVisible = ref(false)
|
const binDetailVisible = ref(false)
|
||||||
const binDetailRows = ref<any[]>([])
|
const binDetailRows = ref<any[]>([])
|
||||||
@ -45,11 +52,26 @@ const relocateForm = ref({
|
|||||||
const hotColumns = computed(() => [
|
const hotColumns = computed(() => [
|
||||||
{ key: 'skuCode', label: t('warehouse.inventory.table.skuCode'), width: 140 },
|
{ key: 'skuCode', label: t('warehouse.inventory.table.skuCode'), width: 140 },
|
||||||
{ key: 'skuName', label: t('warehouse.inventory.table.skuName'), width: 200 },
|
{ key: 'skuName', label: t('warehouse.inventory.table.skuName'), width: 200 },
|
||||||
{ key: 'warehouseId', label: t('warehouse.inventory.table.warehouseId'), width: 100 },
|
{ key: 'warehouseName', label: t('warehouse.inventory.table.warehouse'), width: 140 },
|
||||||
{ key: 'totalQty', label: t('warehouse.inventory.table.totalQty'), width: 90 },
|
{ key: 'totalQty', label: t('warehouse.inventory.table.totalQty'), width: 90 },
|
||||||
{ key: 'binCount', label: t('warehouse.inventory.table.binCount'), width: 90 },
|
{ key: 'binCount', label: t('warehouse.inventory.table.binCount'), width: 90 },
|
||||||
])
|
])
|
||||||
|
|
||||||
|
function mapRow(raw: Record<string, unknown>) {
|
||||||
|
const row = normalizeListRow(raw)
|
||||||
|
const whId = pickField<number>(row, 'warehouseId', 'WarehouseId')
|
||||||
|
return {
|
||||||
|
skuId: pickField<number>(row, 'skuId', 'SkuId'),
|
||||||
|
skuCode: pickField(row, 'skuCode', 'SkuCode') ?? '-',
|
||||||
|
skuName: pickField(row, 'skuName', 'SkuName') ?? '-',
|
||||||
|
warehouseId: whId,
|
||||||
|
warehouseName: resolveWarehouseLabel(whId, warehouseNameMap.value),
|
||||||
|
totalQty: pickField<number>(row, 'totalQty', 'TotalQty') ?? 0,
|
||||||
|
binCount: pickField<number>(row, 'binCount', 'BinCount') ?? 0,
|
||||||
|
_raw: row,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function loadWarehouses() {
|
async function loadWarehouses() {
|
||||||
try {
|
try {
|
||||||
const { items } = await getWarehouseList({ page: 1, pageSize: 200 })
|
const { items } = await getWarehouseList({ page: 1, pageSize: 200 })
|
||||||
@ -71,7 +93,7 @@ async function loadList() {
|
|||||||
keyword: keyword.value || undefined,
|
keyword: keyword.value || undefined,
|
||||||
warehouseId: warehouseId.value,
|
warehouseId: warehouseId.value,
|
||||||
})
|
})
|
||||||
tableRows.value = items || []
|
tableRows.value = (items || []).map((item: Record<string, unknown>) => mapRow(item))
|
||||||
totalCount.value = Number(total || 0)
|
totalCount.value = Number(total || 0)
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
$baseMessage(e?.message || t('warehouse.inventory.message.loadFailed'), 'error')
|
$baseMessage(e?.message || t('warehouse.inventory.message.loadFailed'), 'error')
|
||||||
@ -82,11 +104,35 @@ 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 showBinDetail(row: any) {
|
async function showBinDetail(row: any) {
|
||||||
activeSku.value = row
|
activeSku.value = row
|
||||||
try {
|
try {
|
||||||
const data = await getWarehouseInventoryBinDetail(row.skuId, row.warehouseId)
|
const data = await getWarehouseInventoryBinDetail(row.skuId, row.warehouseId)
|
||||||
binDetailRows.value = data?.items ?? data?.Items ?? (Array.isArray(data) ? data : [])
|
const items = data?.items ?? data?.Items ?? (Array.isArray(data) ? data : [])
|
||||||
|
binDetailRows.value = (items || []).map((item: Record<string, unknown>) => {
|
||||||
|
const normalized = normalizeListRow(item)
|
||||||
|
return {
|
||||||
|
binCode: pickField(normalized, 'binCode', 'BinCode') ?? '-',
|
||||||
|
binName: pickField(normalized, 'binName', 'BinName') ?? '',
|
||||||
|
qty: pickField<number>(normalized, 'qty', 'Qty') ?? 0,
|
||||||
|
}
|
||||||
|
})
|
||||||
binDetailVisible.value = true
|
binDetailVisible.value = true
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
$baseMessage(e?.message || t('warehouse.inventory.message.binDetailFailed'), 'error')
|
$baseMessage(e?.message || t('warehouse.inventory.message.binDetailFailed'), 'error')
|
||||||
@ -148,8 +194,8 @@ onMounted(async () => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="warehouse-inventory-container">
|
<div class="warehouse-inventory-container warehouse-list-page">
|
||||||
<div class="page-card">
|
<div class="warehouse-list-page-card">
|
||||||
<div class="table-toolbar">
|
<div class="table-toolbar">
|
||||||
<div class="toolbar-left">
|
<div class="toolbar-left">
|
||||||
<a-space wrap :size="8">
|
<a-space wrap :size="8">
|
||||||
@ -160,10 +206,26 @@ onMounted(async () => {
|
|||||||
:options="warehouseOptions"
|
:options="warehouseOptions"
|
||||||
style="width: 200px"
|
style="width: 200px"
|
||||||
/>
|
/>
|
||||||
<a-input v-model="keyword" allow-clear :placeholder="t('warehouse.inventory.search.keyword')" style="width: 200px" />
|
<a-input
|
||||||
<a-button type="primary" @click="() => { currentPage = 1; loadList() }">{{ t('warehouse.common.search') }}</a-button>
|
v-model="keyword"
|
||||||
|
allow-clear
|
||||||
|
:placeholder="t('warehouse.inventory.search.keyword')"
|
||||||
|
style="width: 200px"
|
||||||
|
@press-enter="handleSearch"
|
||||||
|
/>
|
||||||
|
<a-button type="primary" @click="handleSearch">{{ t('warehouse.common.search') }}</a-button>
|
||||||
</a-space>
|
</a-space>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="toolbar-right">
|
||||||
|
<PaginationPro
|
||||||
|
:total-count="totalCount"
|
||||||
|
v-model:current-page="currentPage"
|
||||||
|
v-model:page-size="pageSize"
|
||||||
|
:page-sizes="[10, 30, 50, 100]"
|
||||||
|
@page-change="handlePageChange"
|
||||||
|
@page-size-change="handlePageSizeChange"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div ref="tableContainerRef" class="list-wrap">
|
<div ref="tableContainerRef" class="list-wrap">
|
||||||
<HandsontablePro
|
<HandsontablePro
|
||||||
@ -171,18 +233,19 @@ onMounted(async () => {
|
|||||||
:data="tableRows"
|
:data="tableRows"
|
||||||
:height="tableHeight"
|
:height="tableHeight"
|
||||||
:loading="listLoading"
|
:loading="listLoading"
|
||||||
|
:stretch-columns="true"
|
||||||
show-action-column
|
show-action-column
|
||||||
:action-renderer="actionRenderer"
|
:action-renderer="actionRenderer"
|
||||||
@action-click="handleActionClick"
|
@action-click="handleActionClick"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<PaginationPro v-model:current="currentPage" v-model:page-size="pageSize" :total="totalCount" @change="loadList" @page-size-change="() => { currentPage = 1; loadList() }" />
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<a-drawer v-model:visible="binDetailVisible" :title="t('warehouse.inventory.drawer.binDetail')" :width="520">
|
<a-drawer v-model:visible="binDetailVisible" :title="t('warehouse.inventory.drawer.binDetail')" :width="520">
|
||||||
<a-table :data="binDetailRows" :pagination="false" size="small">
|
<a-table :data="binDetailRows" :pagination="false" size="small">
|
||||||
<template #columns>
|
<template #columns>
|
||||||
<a-table-column :title="t('warehouse.inventory.table.binCode')" data-index="binCode" />
|
<a-table-column :title="t('warehouse.inventory.table.binCode')" data-index="binCode" />
|
||||||
|
<a-table-column :title="t('warehouse.inventory.table.binName')" data-index="binName" />
|
||||||
<a-table-column :title="t('warehouse.inventory.table.qty')" data-index="qty" />
|
<a-table-column :title="t('warehouse.inventory.table.qty')" data-index="qty" />
|
||||||
</template>
|
</template>
|
||||||
</a-table>
|
</a-table>
|
||||||
@ -209,25 +272,5 @@ onMounted(async () => {
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped lang="less">
|
<style scoped lang="less">
|
||||||
.warehouse-inventory-container {
|
@import '@/styles/warehouse-list-page.less';
|
||||||
padding: 16px;
|
|
||||||
background: #f0f3f4;
|
|
||||||
min-height: 100%;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
.page-card {
|
|
||||||
background: #fff;
|
|
||||||
padding: 16px;
|
|
||||||
flex: 1;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
min-height: 0;
|
|
||||||
border-radius: 4px;
|
|
||||||
}
|
|
||||||
.list-wrap {
|
|
||||||
flex: 1;
|
|
||||||
min-height: 0;
|
|
||||||
margin-top: 12px;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -12,6 +12,14 @@ import { deleteWarehouseParcel, getWarehouseParcelList, saveWarehouseParcel } fr
|
|||||||
import { getWarehouseList } from '@/api/system/warehouse'
|
import { getWarehouseList } from '@/api/system/warehouse'
|
||||||
import { $baseMessage } from '@/utils'
|
import { $baseMessage } from '@/utils'
|
||||||
import { hasPermission } from '@/utils/rbac-permission'
|
import { hasPermission } from '@/utils/rbac-permission'
|
||||||
|
import {
|
||||||
|
buildWarehouseNameMap,
|
||||||
|
formatDateTime,
|
||||||
|
normalizeListRow,
|
||||||
|
pickField,
|
||||||
|
resolveWarehouseLabel,
|
||||||
|
summarizeParcelItems,
|
||||||
|
} from '@/utils/warehouseListMapper'
|
||||||
|
|
||||||
defineOptions({ name: 'WarehouseParcel' })
|
defineOptions({ name: 'WarehouseParcel' })
|
||||||
|
|
||||||
@ -22,6 +30,7 @@ const lifecycleStatus = ref<string>()
|
|||||||
const keyword = ref('')
|
const keyword = ref('')
|
||||||
const warehouseId = ref<number>()
|
const warehouseId = ref<number>()
|
||||||
const warehouseOptions = ref<Array<{ label: string; value: number }>>([])
|
const warehouseOptions = ref<Array<{ label: string; value: number }>>([])
|
||||||
|
const warehouseNameMap = computed(() => buildWarehouseNameMap(warehouseOptions.value))
|
||||||
const listLoading = ref(false)
|
const listLoading = ref(false)
|
||||||
const tableRows = ref<any[]>([])
|
const tableRows = ref<any[]>([])
|
||||||
const currentPage = ref(1)
|
const currentPage = ref(1)
|
||||||
@ -51,10 +60,32 @@ const hotColumns = computed(() => [
|
|||||||
{ key: 'trackingNo', label: t('warehouse.parcel.table.trackingNo'), width: 160 },
|
{ key: 'trackingNo', label: t('warehouse.parcel.table.trackingNo'), width: 160 },
|
||||||
{ key: 'carrierName', label: t('warehouse.parcel.table.carrierName'), width: 120 },
|
{ key: 'carrierName', label: t('warehouse.parcel.table.carrierName'), width: 120 },
|
||||||
{ key: 'lifecycleStatus', label: t('warehouse.parcel.table.lifecycleStatus'), width: 100 },
|
{ key: 'lifecycleStatus', label: t('warehouse.parcel.table.lifecycleStatus'), width: 100 },
|
||||||
{ key: 'warehouseId', label: t('warehouse.parcel.table.warehouseId'), width: 90 },
|
{ key: 'warehouseName', label: t('warehouse.parcel.table.warehouse'), width: 140 },
|
||||||
{ key: 'remark', label: t('warehouse.parcel.table.remark'), width: 160 },
|
{ key: 'itemsSummary', label: t('warehouse.parcel.table.items'), width: 200 },
|
||||||
|
{ key: 'arrivalTime', label: t('warehouse.parcel.table.arrivalTime'), width: 160 },
|
||||||
|
{ key: 'createTime', label: t('warehouse.parcel.table.createTime'), width: 160 },
|
||||||
|
{ key: 'remark', label: t('warehouse.parcel.table.remark'), width: 140 },
|
||||||
])
|
])
|
||||||
|
|
||||||
|
function mapRow(raw: Record<string, unknown>) {
|
||||||
|
const row = normalizeListRow(raw)
|
||||||
|
const status = String(pickField(row, 'lifecycleStatus', 'LifecycleStatus') ?? '')
|
||||||
|
const whId = pickField<number>(row, 'warehouseId', 'WarehouseId')
|
||||||
|
return {
|
||||||
|
id: pickField<number>(row, 'id', 'Id'),
|
||||||
|
trackingNo: pickField(row, 'trackingNo', 'TrackingNo') ?? '-',
|
||||||
|
carrierName: pickField(row, 'carrierName', 'CarrierName') ?? '-',
|
||||||
|
lifecycleStatus: t(`warehouse.parcel.status.${status}`, status),
|
||||||
|
warehouseName: resolveWarehouseLabel(whId, warehouseNameMap.value),
|
||||||
|
itemsSummary: summarizeParcelItems(pickField(row, 'items', 'Items')),
|
||||||
|
arrivalTime: formatDateTime(pickField(row, 'arrivalTime', 'ArrivalTime')),
|
||||||
|
createTime: formatDateTime(pickField(row, 'createTime', 'CreateTime')),
|
||||||
|
remark: pickField(row, 'remark', 'Remark') ?? '',
|
||||||
|
_raw: row,
|
||||||
|
_status: status,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function loadWarehouses() {
|
async function loadWarehouses() {
|
||||||
try {
|
try {
|
||||||
const { items } = await getWarehouseList({ page: 1, pageSize: 200 })
|
const { items } = await getWarehouseList({ page: 1, pageSize: 200 })
|
||||||
@ -77,11 +108,7 @@ async function loadList() {
|
|||||||
keyword: keyword.value || undefined,
|
keyword: keyword.value || undefined,
|
||||||
warehouseId: warehouseId.value,
|
warehouseId: warehouseId.value,
|
||||||
})
|
})
|
||||||
tableRows.value = (items || []).map((r: any) => ({
|
tableRows.value = (items || []).map((item: Record<string, unknown>) => mapRow(item))
|
||||||
...r,
|
|
||||||
lifecycleStatus: t(`warehouse.parcel.status.${r.lifecycleStatus}`, r.lifecycleStatus),
|
|
||||||
_raw: r,
|
|
||||||
}))
|
|
||||||
totalCount.value = Number(total || 0)
|
totalCount.value = Number(total || 0)
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
$baseMessage(e?.message || t('warehouse.parcel.message.loadFailed'), 'error')
|
$baseMessage(e?.message || t('warehouse.parcel.message.loadFailed'), 'error')
|
||||||
@ -92,6 +119,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()
|
||||||
|
}
|
||||||
|
|
||||||
function handleAdd() {
|
function handleAdd() {
|
||||||
if (!hasPermission('Warehouse.Parcel.Save')) return
|
if (!hasPermission('Warehouse.Parcel.Save')) return
|
||||||
formModel.id = 0
|
formModel.id = 0
|
||||||
@ -126,18 +169,21 @@ function handleDelete(row: any) {
|
|||||||
if (!hasPermission('Warehouse.Parcel.Delete')) return
|
if (!hasPermission('Warehouse.Parcel.Delete')) return
|
||||||
Modal.confirm({
|
Modal.confirm({
|
||||||
title: t('warehouse.parcel.actions.delete'),
|
title: t('warehouse.parcel.actions.delete'),
|
||||||
content: row._raw?.trackingNo || row.trackingNo,
|
content: row.trackingNo,
|
||||||
onOk: async () => {
|
onOk: async () => {
|
||||||
await deleteWarehouseParcel(row._raw?.id || row.id)
|
await deleteWarehouseParcel(row.id)
|
||||||
$baseMessage(t('warehouse.parcel.message.deleteSuccess'), 'success')
|
$baseMessage(t('warehouse.parcel.message.deleteSuccess'), 'success')
|
||||||
await loadList()
|
await loadList()
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function actionRenderer() {
|
function actionRenderer(row: any) {
|
||||||
const parts: string[] = []
|
const parts: string[] = []
|
||||||
if (hasPermission('Warehouse.Parcel.Delete')) {
|
if (hasPermission('Warehouse.Parcel.Save') && row._status !== 'stored') {
|
||||||
|
parts.push(`<button type="button" class="hot-action-btn" data-action="edit">${t('warehouse.common.edit')}</button>`)
|
||||||
|
}
|
||||||
|
if (hasPermission('Warehouse.Parcel.Delete') && row._status !== 'stored') {
|
||||||
parts.push(`<button type="button" class="hot-action-btn" data-action="delete">${t('warehouse.common.delete')}</button>`)
|
parts.push(`<button type="button" class="hot-action-btn" data-action="delete">${t('warehouse.common.delete')}</button>`)
|
||||||
}
|
}
|
||||||
return parts.join('')
|
return parts.join('')
|
||||||
@ -154,25 +200,61 @@ onMounted(async () => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="warehouse-parcel-container">
|
<div class="warehouse-parcel-container warehouse-list-page">
|
||||||
<div class="page-card">
|
<div class="warehouse-list-page-card">
|
||||||
<a-tabs v-model:active-key="lifecycleStatus" @change="() => { currentPage = 1; loadList() }">
|
<a-tabs
|
||||||
|
v-model:active-key="lifecycleStatus"
|
||||||
|
class="status-tabs"
|
||||||
|
@change="() => { currentPage = 1; loadList() }"
|
||||||
|
>
|
||||||
<a-tab-pane v-for="tab in statusTabs" :key="tab.value ?? 'all'" :title="t(tab.labelKey)" />
|
<a-tab-pane v-for="tab in statusTabs" :key="tab.value ?? 'all'" :title="t(tab.labelKey)" />
|
||||||
</a-tabs>
|
</a-tabs>
|
||||||
<div class="table-toolbar">
|
<div class="table-toolbar">
|
||||||
<div class="toolbar-left">
|
<div class="toolbar-left">
|
||||||
<a-space wrap :size="8">
|
<a-space wrap :size="8">
|
||||||
<a-select v-model="warehouseId" allow-clear :placeholder="t('warehouse.common.warehouse')" :options="warehouseOptions" style="width: 200px" />
|
<a-select
|
||||||
<a-input v-model="keyword" allow-clear :placeholder="t('warehouse.parcel.search.trackingNo')" style="width: 200px" />
|
v-model="warehouseId"
|
||||||
<a-button type="primary" @click="() => { currentPage = 1; loadList() }">{{ t('warehouse.common.search') }}</a-button>
|
allow-clear
|
||||||
<a-button v-if="hasPermission('Warehouse.Parcel.Save')" type="primary" @click="handleAdd">{{ t('warehouse.common.add') }}</a-button>
|
:placeholder="t('warehouse.common.warehouse')"
|
||||||
|
:options="warehouseOptions"
|
||||||
|
style="width: 200px"
|
||||||
|
/>
|
||||||
|
<a-input
|
||||||
|
v-model="keyword"
|
||||||
|
allow-clear
|
||||||
|
:placeholder="t('warehouse.parcel.search.trackingNo')"
|
||||||
|
style="width: 200px"
|
||||||
|
@press-enter="handleSearch"
|
||||||
|
/>
|
||||||
|
<a-button type="primary" @click="handleSearch">{{ t('warehouse.common.search') }}</a-button>
|
||||||
|
<a-button v-if="hasPermission('Warehouse.Parcel.Save')" type="primary" @click="handleAdd">
|
||||||
|
{{ t('warehouse.common.add') }}
|
||||||
|
</a-button>
|
||||||
</a-space>
|
</a-space>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="toolbar-right">
|
||||||
|
<PaginationPro
|
||||||
|
:total-count="totalCount"
|
||||||
|
v-model:current-page="currentPage"
|
||||||
|
v-model:page-size="pageSize"
|
||||||
|
:page-sizes="[10, 30, 50, 100]"
|
||||||
|
@page-change="handlePageChange"
|
||||||
|
@page-size-change="handlePageSizeChange"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div ref="tableContainerRef" class="list-wrap">
|
<div ref="tableContainerRef" class="list-wrap">
|
||||||
<HandsontablePro :columns="hotColumns" :data="tableRows" :height="tableHeight" :loading="listLoading" show-action-column :action-renderer="actionRenderer" @action-click="handleActionClick" />
|
<HandsontablePro
|
||||||
|
:columns="hotColumns"
|
||||||
|
:data="tableRows"
|
||||||
|
:height="tableHeight"
|
||||||
|
:loading="listLoading"
|
||||||
|
:stretch-columns="true"
|
||||||
|
show-action-column
|
||||||
|
:action-renderer="actionRenderer"
|
||||||
|
@action-click="handleActionClick"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<PaginationPro v-model:current="currentPage" v-model:page-size="pageSize" :total="totalCount" @change="loadList" @page-size-change="() => { currentPage = 1; loadList() }" />
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ModalPro v-model:visible="modalVisible" :title="t('warehouse.parcel.modal.add')" :width="640" @cancel="closeModal">
|
<ModalPro v-model:visible="modalVisible" :title="t('warehouse.parcel.modal.add')" :width="640" @cancel="closeModal">
|
||||||
@ -202,25 +284,5 @@ onMounted(async () => {
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped lang="less">
|
<style scoped lang="less">
|
||||||
.warehouse-parcel-container {
|
@import '@/styles/warehouse-list-page.less';
|
||||||
padding: 16px;
|
|
||||||
background: #f0f3f4;
|
|
||||||
min-height: 100%;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
.page-card {
|
|
||||||
background: #fff;
|
|
||||||
padding: 16px;
|
|
||||||
flex: 1;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
min-height: 0;
|
|
||||||
border-radius: 4px;
|
|
||||||
}
|
|
||||||
.list-wrap {
|
|
||||||
flex: 1;
|
|
||||||
min-height: 0;
|
|
||||||
margin-top: 12px;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -11,6 +11,12 @@ import { useTableContainerHeight } from '@/composables/useTableContainerHeight'
|
|||||||
import { deleteWarehouseSku, getWarehouseSkuList, saveWarehouseSku } from '@/api/warehouse'
|
import { deleteWarehouseSku, getWarehouseSkuList, saveWarehouseSku } from '@/api/warehouse'
|
||||||
import { $baseMessage } from '@/utils'
|
import { $baseMessage } from '@/utils'
|
||||||
import { hasPermission } from '@/utils/rbac-permission'
|
import { hasPermission } from '@/utils/rbac-permission'
|
||||||
|
import {
|
||||||
|
formatDateTime,
|
||||||
|
formatDimensions,
|
||||||
|
normalizeListRow,
|
||||||
|
pickField,
|
||||||
|
} from '@/utils/warehouseListMapper'
|
||||||
|
|
||||||
defineOptions({ name: 'WarehouseSku' })
|
defineOptions({ name: 'WarehouseSku' })
|
||||||
|
|
||||||
@ -40,10 +46,12 @@ const formModel = reactive({
|
|||||||
|
|
||||||
const hotColumns = computed(() => [
|
const hotColumns = computed(() => [
|
||||||
{ key: 'skuCode', label: t('warehouse.sku.table.skuCode'), width: 140 },
|
{ key: 'skuCode', label: t('warehouse.sku.table.skuCode'), width: 140 },
|
||||||
{ key: 'skuName', label: t('warehouse.sku.table.skuName'), width: 200 },
|
{ key: 'skuName', label: t('warehouse.sku.table.skuName'), width: 180 },
|
||||||
{ key: 'totalQty', label: t('warehouse.sku.table.totalQty'), width: 90 },
|
{ key: 'totalQty', label: t('warehouse.sku.table.totalQty'), width: 80 },
|
||||||
{ key: 'weightG', label: t('warehouse.sku.table.weightG'), width: 90 },
|
{ key: 'weightG', label: t('warehouse.sku.table.weightG'), width: 90 },
|
||||||
{ key: 'remark', label: t('warehouse.sku.table.remark'), width: 160 },
|
{ key: 'dimensions', label: t('warehouse.sku.table.dimensions'), width: 120 },
|
||||||
|
{ key: 'remark', label: t('warehouse.sku.table.remark'), width: 140 },
|
||||||
|
{ key: 'createTime', label: t('warehouse.sku.table.createTime'), width: 160 },
|
||||||
])
|
])
|
||||||
|
|
||||||
function resetForm() {
|
function resetForm() {
|
||||||
@ -58,6 +66,21 @@ function resetForm() {
|
|||||||
formModel.remark = ''
|
formModel.remark = ''
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function mapRow(raw: Record<string, unknown>) {
|
||||||
|
const row = normalizeListRow(raw)
|
||||||
|
return {
|
||||||
|
id: pickField<number>(row, 'id', 'Id'),
|
||||||
|
skuCode: pickField(row, 'skuCode', 'SkuCode') ?? '-',
|
||||||
|
skuName: pickField(row, 'skuName', 'SkuName') ?? '-',
|
||||||
|
totalQty: pickField<number>(row, 'totalQty', 'TotalQty') ?? 0,
|
||||||
|
weightG: pickField(row, 'weightG', 'WeightG') ?? '-',
|
||||||
|
dimensions: formatDimensions(row),
|
||||||
|
remark: pickField(row, 'remark', 'Remark') ?? '',
|
||||||
|
createTime: formatDateTime(pickField(row, 'createTime', 'CreateTime')),
|
||||||
|
_raw: row,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function loadList() {
|
async function loadList() {
|
||||||
listLoading.value = true
|
listLoading.value = true
|
||||||
try {
|
try {
|
||||||
@ -66,15 +89,7 @@ async function loadList() {
|
|||||||
pageSize: pageSize.value,
|
pageSize: pageSize.value,
|
||||||
keyword: keyword.value || undefined,
|
keyword: keyword.value || undefined,
|
||||||
})
|
})
|
||||||
tableRows.value = (items || []).map((r: any) => ({
|
tableRows.value = (items || []).map((item: Record<string, unknown>) => mapRow(item))
|
||||||
id: r.id,
|
|
||||||
skuCode: r.skuCode,
|
|
||||||
skuName: r.skuName,
|
|
||||||
totalQty: r.totalQty ?? 0,
|
|
||||||
weightG: r.weightG ?? '-',
|
|
||||||
remark: r.remark ?? '',
|
|
||||||
_raw: r,
|
|
||||||
}))
|
|
||||||
totalCount.value = Number(total || 0)
|
totalCount.value = Number(total || 0)
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
$baseMessage(e?.message || t('warehouse.sku.message.loadFailed'), 'error')
|
$baseMessage(e?.message || t('warehouse.sku.message.loadFailed'), 'error')
|
||||||
@ -85,6 +100,17 @@ async function loadList() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handlePageChange(page: number) {
|
||||||
|
currentPage.value = page
|
||||||
|
loadList()
|
||||||
|
}
|
||||||
|
|
||||||
|
function handlePageSizeChange(size: number) {
|
||||||
|
pageSize.value = size
|
||||||
|
currentPage.value = 1
|
||||||
|
loadList()
|
||||||
|
}
|
||||||
|
|
||||||
function handleSearch() {
|
function handleSearch() {
|
||||||
currentPage.value = 1
|
currentPage.value = 1
|
||||||
loadList()
|
loadList()
|
||||||
@ -99,15 +125,15 @@ function handleAdd() {
|
|||||||
function handleEdit(row: any) {
|
function handleEdit(row: any) {
|
||||||
if (!hasPermission('Warehouse.Sku.Save')) return
|
if (!hasPermission('Warehouse.Sku.Save')) return
|
||||||
const raw = row._raw || row
|
const raw = row._raw || row
|
||||||
formModel.id = raw.id
|
formModel.id = Number(pickField(raw, 'id', 'Id') ?? 0)
|
||||||
formModel.skuCode = raw.skuCode || ''
|
formModel.skuCode = String(pickField(raw, 'skuCode', 'SkuCode') ?? '')
|
||||||
formModel.skuName = raw.skuName || ''
|
formModel.skuName = String(pickField(raw, 'skuName', 'SkuName') ?? '')
|
||||||
formModel.skuImageUrl = raw.skuImageUrl || ''
|
formModel.skuImageUrl = String(pickField(raw, 'skuImageUrl', 'SkuImageUrl') ?? '')
|
||||||
formModel.weightG = raw.weightG
|
formModel.weightG = pickField<number>(raw, 'weightG', 'WeightG')
|
||||||
formModel.lengthCm = raw.lengthCm
|
formModel.lengthCm = pickField<number>(raw, 'lengthCm', 'LengthCm')
|
||||||
formModel.widthCm = raw.widthCm
|
formModel.widthCm = pickField<number>(raw, 'widthCm', 'WidthCm')
|
||||||
formModel.heightCm = raw.heightCm
|
formModel.heightCm = pickField<number>(raw, 'heightCm', 'HeightCm')
|
||||||
formModel.remark = raw.remark || ''
|
formModel.remark = String(pickField(raw, 'remark', 'Remark') ?? '')
|
||||||
openModal()
|
openModal()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -146,7 +172,7 @@ function handleDelete(row: any) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function actionRenderer(row: any) {
|
function actionRenderer() {
|
||||||
const parts: string[] = []
|
const parts: string[] = []
|
||||||
if (hasPermission('Warehouse.Sku.Save')) {
|
if (hasPermission('Warehouse.Sku.Save')) {
|
||||||
parts.push(`<button type="button" class="hot-action-btn" data-action="edit">${t('warehouse.common.edit')}</button>`)
|
parts.push(`<button type="button" class="hot-action-btn" data-action="edit">${t('warehouse.common.edit')}</button>`)
|
||||||
@ -166,8 +192,8 @@ onMounted(loadList)
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="warehouse-sku-container">
|
<div class="warehouse-sku-container warehouse-list-page">
|
||||||
<div class="page-card">
|
<div class="warehouse-list-page-card">
|
||||||
<div class="table-toolbar">
|
<div class="table-toolbar">
|
||||||
<div class="toolbar-left">
|
<div class="toolbar-left">
|
||||||
<a-space wrap :size="8">
|
<a-space wrap :size="8">
|
||||||
@ -184,6 +210,16 @@ onMounted(loadList)
|
|||||||
</a-button>
|
</a-button>
|
||||||
</a-space>
|
</a-space>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="toolbar-right">
|
||||||
|
<PaginationPro
|
||||||
|
:total-count="totalCount"
|
||||||
|
v-model:current-page="currentPage"
|
||||||
|
v-model:page-size="pageSize"
|
||||||
|
:page-sizes="[10, 30, 50, 100]"
|
||||||
|
@page-change="handlePageChange"
|
||||||
|
@page-size-change="handlePageSizeChange"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div ref="tableContainerRef" class="list-wrap">
|
<div ref="tableContainerRef" class="list-wrap">
|
||||||
@ -192,22 +228,20 @@ onMounted(loadList)
|
|||||||
:data="tableRows"
|
:data="tableRows"
|
||||||
:height="tableHeight"
|
:height="tableHeight"
|
||||||
:loading="listLoading"
|
:loading="listLoading"
|
||||||
|
:stretch-columns="true"
|
||||||
show-action-column
|
show-action-column
|
||||||
:action-renderer="actionRenderer"
|
:action-renderer="actionRenderer"
|
||||||
@action-click="handleActionClick"
|
@action-click="handleActionClick"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<PaginationPro
|
|
||||||
v-model:current="currentPage"
|
|
||||||
v-model:page-size="pageSize"
|
|
||||||
:total="totalCount"
|
|
||||||
@change="loadList"
|
|
||||||
@page-size-change="() => { currentPage = 1; loadList() }"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ModalPro v-model:visible="modalVisible" :title="formModel.id ? t('warehouse.sku.modal.edit') : t('warehouse.sku.modal.add')" :width="720" @cancel="closeModal">
|
<ModalPro
|
||||||
|
v-model:visible="modalVisible"
|
||||||
|
:title="formModel.id ? t('warehouse.sku.modal.edit') : t('warehouse.sku.modal.add')"
|
||||||
|
:width="720"
|
||||||
|
@cancel="closeModal"
|
||||||
|
>
|
||||||
<FormPro :model="formModel" layout="horizontal" :row-gutter="20" :default-span="12">
|
<FormPro :model="formModel" layout="horizontal" :row-gutter="20" :default-span="12">
|
||||||
<a-form-item :label="t('warehouse.sku.form.skuCode')" field="skuCode" required>
|
<a-form-item :label="t('warehouse.sku.form.skuCode')" field="skuCode" required>
|
||||||
<a-input v-model="formModel.skuCode" />
|
<a-input v-model="formModel.skuCode" />
|
||||||
@ -231,25 +265,5 @@ onMounted(loadList)
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped lang="less">
|
<style scoped lang="less">
|
||||||
.warehouse-sku-container {
|
@import '@/styles/warehouse-list-page.less';
|
||||||
padding: 16px;
|
|
||||||
background: #f0f3f4;
|
|
||||||
min-height: 100%;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
.page-card {
|
|
||||||
background: #fff;
|
|
||||||
padding: 16px;
|
|
||||||
flex: 1;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
min-height: 0;
|
|
||||||
border-radius: 4px;
|
|
||||||
}
|
|
||||||
.list-wrap {
|
|
||||||
flex: 1;
|
|
||||||
min-height: 0;
|
|
||||||
margin-top: 12px;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -44,6 +44,38 @@ export default defineConfig(({ mode }) => {
|
|||||||
target: 'http://api.nanafa.com/admin',
|
target: 'http://api.nanafa.com/admin',
|
||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
secure: false,
|
secure: false,
|
||||||
|
bypass(req) {
|
||||||
|
const path = req.url?.split('?')[0] ?? '';
|
||||||
|
const orderSpaPrefixes = [
|
||||||
|
'/order/guide',
|
||||||
|
'/order/priceCheck',
|
||||||
|
'/order/placeOrder',
|
||||||
|
'/order/myOrders',
|
||||||
|
'/order/batchJobs',
|
||||||
|
'/order/templateMapping',
|
||||||
|
'/order/platformStore',
|
||||||
|
'/order/platformStoreOrder',
|
||||||
|
];
|
||||||
|
if (orderSpaPrefixes.some((p) => path === p || path.startsWith(`${p}/`))) {
|
||||||
|
return '/index.html';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// OMS 仓储 admin 接口:/warehouse/sku/list 等(与 Swagger OMS-Warehouse 一致)
|
||||||
|
'/warehouse': {
|
||||||
|
target: 'http://api.nanafa.com/admin',
|
||||||
|
changeOrigin: true,
|
||||||
|
secure: false,
|
||||||
|
bypass(req) {
|
||||||
|
const path = req.url?.split('?')[0] ?? '';
|
||||||
|
const warehouseSpaExact = ['/warehouse/parcel', '/warehouse/inventory', '/warehouse/sku'];
|
||||||
|
if (warehouseSpaExact.some((p) => path === p || path === `${p}/`)) {
|
||||||
|
return '/index.html';
|
||||||
|
}
|
||||||
|
if (path === '/warehouse/inboundNotice' || path.startsWith('/warehouse/inboundNotice/')) {
|
||||||
|
return '/index.html';
|
||||||
|
}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
'/monitor': {
|
'/monitor': {
|
||||||
target: 'http://api.nanafa.com/admin',
|
target: 'http://api.nanafa.com/admin',
|
||||||
@ -265,6 +297,13 @@ export default defineConfig(({ mode }) => {
|
|||||||
'/order/placeOrder',
|
'/order/placeOrder',
|
||||||
'/order/myOrders',
|
'/order/myOrders',
|
||||||
'/order/batchJobs',
|
'/order/batchJobs',
|
||||||
|
'/order/platformStore',
|
||||||
|
'/order/platformStoreOrder',
|
||||||
|
// 仓储前端路由(页面路径与 API 子路径相同时用精确匹配,见 /warehouse 代理 bypass)
|
||||||
|
'/warehouse/parcel',
|
||||||
|
'/warehouse/inventory',
|
||||||
|
'/warehouse/sku',
|
||||||
|
'/warehouse/inboundNotice',
|
||||||
// Xhs 相关前端路由(勿用 /xhs 前缀,否则 API 会被误判为 SPA)
|
// Xhs 相关前端路由(勿用 /xhs 前缀,否则 API 会被误判为 SPA)
|
||||||
'/xhs/startService',
|
'/xhs/startService',
|
||||||
'/xhs/analysis',
|
'/xhs/analysis',
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user