Compare commits
2 Commits
3661c0e86e
...
52071ecf47
| Author | SHA1 | Date | |
|---|---|---|---|
| 52071ecf47 | |||
| 6ec41b2dbe |
@ -2222,6 +2222,19 @@ export async function postOrderUpdate(body: Record<string, any>): Promise<any> {
|
|||||||
return res.data?.data ?? res.data?.Data ?? res.data
|
return res.data?.data ?? res.data?.Data ?? res.data
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** POST /order/change-product — 改销售产品并自动重排货/重算费 */
|
||||||
|
export async function postOrderChangeProduct(body: {
|
||||||
|
id: number | string
|
||||||
|
productCode: string
|
||||||
|
}): Promise<any> {
|
||||||
|
const res = await axiosInstance.post(orderUrl('/order/change-product'), {
|
||||||
|
id: Number(body.id),
|
||||||
|
productCode: String(body.productCode ?? '').trim(),
|
||||||
|
})
|
||||||
|
assertBusinessOk(res.data)
|
||||||
|
return res.data?.data ?? res.data?.Data ?? res.data
|
||||||
|
}
|
||||||
|
|
||||||
/** POST /order/weight/update */
|
/** POST /order/weight/update */
|
||||||
export async function postOrderWeightUpdate(body: {
|
export async function postOrderWeightUpdate(body: {
|
||||||
id: number | string
|
id: number | string
|
||||||
|
|||||||
@ -26,6 +26,22 @@ export const deleteWarehouseSku = (id: number) => postOk('/warehouse/sku/delete'
|
|||||||
export const bindWarehouseSkuMapping = (body: Record<string, unknown>) =>
|
export const bindWarehouseSkuMapping = (body: Record<string, unknown>) =>
|
||||||
postOk('/warehouse/sku/bind-mapping', body)
|
postOk('/warehouse/sku/bind-mapping', body)
|
||||||
|
|
||||||
|
/** POST /warehouse/sku/print-label → TemplateType=sku 单页 PDF */
|
||||||
|
export async function postWarehouseSkuPrintLabel(body: {
|
||||||
|
id: number
|
||||||
|
customerId?: number
|
||||||
|
}): Promise<{ printUrl: string; skuCode: string }> {
|
||||||
|
const data = await postOk('/warehouse/sku/print-label', {
|
||||||
|
id: body.id,
|
||||||
|
customerId: body.customerId,
|
||||||
|
})
|
||||||
|
const d = (data ?? {}) as Record<string, unknown>
|
||||||
|
return {
|
||||||
|
printUrl: String(d.printUrl ?? d.PrintUrl ?? '').trim(),
|
||||||
|
skuCode: String(d.skuCode ?? d.SkuCode ?? '').trim(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** 上传 SKU 图片:优先 COS 直传,落库存 key;返回 key + 预览 URL */
|
/** 上传 SKU 图片:优先 COS 直传,落库存 key;返回 key + 预览 URL */
|
||||||
export async function uploadWarehouseSkuImage(
|
export async function uploadWarehouseSkuImage(
|
||||||
file: File,
|
file: File,
|
||||||
@ -119,6 +135,23 @@ export const confirmWarehouseInboundReceive = (body: Record<string, unknown>) =>
|
|||||||
export const getWarehouseInboundSummary = (params?: Record<string, unknown>) =>
|
export const getWarehouseInboundSummary = (params?: Record<string, unknown>) =>
|
||||||
getData('/warehouse/inbound/summary', params)
|
getData('/warehouse/inbound/summary', params)
|
||||||
|
|
||||||
|
/** POST /warehouse/inbound/print-labels → 多箱合并为一个 PDF */
|
||||||
|
export async function postWarehouseInboundPrintLabels(body: {
|
||||||
|
id: number
|
||||||
|
customerId?: number
|
||||||
|
}): Promise<{ printUrl: string; noticeNo: string; pageCount: number }> {
|
||||||
|
const data = await postOk('/warehouse/inbound/print-labels', {
|
||||||
|
id: body.id,
|
||||||
|
customerId: body.customerId,
|
||||||
|
})
|
||||||
|
const d = (data ?? {}) as Record<string, unknown>
|
||||||
|
return {
|
||||||
|
printUrl: String(d.printUrl ?? d.PrintUrl ?? '').trim(),
|
||||||
|
noticeNo: String(d.noticeNo ?? d.NoticeNo ?? '').trim(),
|
||||||
|
pageCount: Number(d.pageCount ?? d.PageCount ?? 0) || 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Parcel
|
// Parcel
|
||||||
export const getWarehouseParcelList = (body: Record<string, unknown>) => postList('/warehouse/parcel/list', body)
|
export const getWarehouseParcelList = (body: Record<string, unknown>) => postList('/warehouse/parcel/list', body)
|
||||||
export const getWarehouseParcelSummary = (params?: Record<string, unknown>) =>
|
export const getWarehouseParcelSummary = (params?: Record<string, unknown>) =>
|
||||||
|
|||||||
@ -96,6 +96,7 @@ export const PERMISSION_ACTION_LABELS: Record<string, string> = {
|
|||||||
PackVersions: '打包版本',
|
PackVersions: '打包版本',
|
||||||
BinDetail: '库位明细',
|
BinDetail: '库位明细',
|
||||||
History: '出入库记录',
|
History: '出入库记录',
|
||||||
|
PrintLabel: '打印箱唛',
|
||||||
BatchPay: '批量支付',
|
BatchPay: '批量支付',
|
||||||
CreatePickup: '创建揽收',
|
CreatePickup: '创建揽收',
|
||||||
AddMapping: '添加映射',
|
AddMapping: '添加映射',
|
||||||
@ -644,6 +645,7 @@ const CORE_GROUPS: PageButtonGroup[] = [
|
|||||||
{ code: 'Query', name: '查询' },
|
{ code: 'Query', name: '查询' },
|
||||||
{ code: 'Save', name: '新增' },
|
{ code: 'Save', name: '新增' },
|
||||||
{ code: 'Update', name: '编辑' },
|
{ code: 'Update', name: '编辑' },
|
||||||
|
{ code: 'PrintLabel', name: '打印标签' },
|
||||||
{ code: 'Delete', name: '删除' },
|
{ code: 'Delete', name: '删除' },
|
||||||
]),
|
]),
|
||||||
B('Warehouse.Inventory', [
|
B('Warehouse.Inventory', [
|
||||||
@ -663,6 +665,7 @@ const CORE_GROUPS: PageButtonGroup[] = [
|
|||||||
{ code: 'Save', name: '新增' },
|
{ code: 'Save', name: '新增' },
|
||||||
{ code: 'View', name: '详情' },
|
{ code: 'View', name: '详情' },
|
||||||
{ code: 'ConfirmReceive', name: '确认收货' },
|
{ code: 'ConfirmReceive', name: '确认收货' },
|
||||||
|
{ code: 'PrintLabel', name: '打印箱唛' },
|
||||||
{ code: 'Delete', name: '删除' },
|
{ code: 'Delete', name: '删除' },
|
||||||
]),
|
]),
|
||||||
B('Warehouse.BinLocation', [
|
B('Warehouse.BinLocation', [
|
||||||
|
|||||||
@ -213,6 +213,7 @@ export default {
|
|||||||
'warehouse.sku.modal.add': 'Add SKU',
|
'warehouse.sku.modal.add': 'Add SKU',
|
||||||
'warehouse.sku.modal.edit': 'Edit SKU',
|
'warehouse.sku.modal.edit': 'Edit SKU',
|
||||||
'warehouse.sku.actions.delete': 'Delete SKU',
|
'warehouse.sku.actions.delete': 'Delete SKU',
|
||||||
|
'warehouse.sku.actions.printLabel': 'Print Label',
|
||||||
'warehouse.sku.message.loadFailed': 'Failed to load SKU list',
|
'warehouse.sku.message.loadFailed': 'Failed to load SKU list',
|
||||||
'warehouse.sku.message.saveSuccess': 'Saved',
|
'warehouse.sku.message.saveSuccess': 'Saved',
|
||||||
'warehouse.sku.message.saveFailed': 'Save failed',
|
'warehouse.sku.message.saveFailed': 'Save failed',
|
||||||
@ -220,6 +221,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.sku.message.printFailed': 'Failed to print SKU label',
|
||||||
|
'warehouse.sku.message.printEmpty': 'No SKU label file available',
|
||||||
'warehouse.inventory.table.warehouse': 'Warehouse',
|
'warehouse.inventory.table.warehouse': 'Warehouse',
|
||||||
'warehouse.inventory.table.binName': 'Bin Name',
|
'warehouse.inventory.table.binName': 'Bin Name',
|
||||||
'warehouse.inventory.table.skuCode': 'SKU Code',
|
'warehouse.inventory.table.skuCode': 'SKU Code',
|
||||||
@ -409,7 +412,7 @@ export default {
|
|||||||
'warehouse.inbound.search.keyword': 'Notice / Mark',
|
'warehouse.inbound.search.keyword': 'Notice / Mark',
|
||||||
'warehouse.inbound.tip': 'Forecast inbound goods. Stock increases after receive & putaway. Status: Forecasted → Received → Completed.',
|
'warehouse.inbound.tip': 'Forecast inbound goods. Stock increases after receive & putaway. Status: Forecasted → Received → Completed.',
|
||||||
'warehouse.inbound.tip.currentWarehouse': 'Notice belongs to the current warehouse; switch it from the top bar',
|
'warehouse.inbound.tip.currentWarehouse': 'Notice belongs to the current warehouse; switch it from the top bar',
|
||||||
'warehouse.inbound.tip.markReference': 'Usually the tracking number or master mark for receiving checks',
|
'warehouse.inbound.tip.markReference': 'Optional; usually the tracking number or master mark for receiving checks',
|
||||||
'warehouse.inbound.tip.inboundType': 'Normal or return inbound; used for later reporting',
|
'warehouse.inbound.tip.inboundType': 'Normal or return inbound; used for later reporting',
|
||||||
'warehouse.inbound.tip.boxSkuDetail': 'Maintain SKUs and planned qty per box; add one box or five at once',
|
'warehouse.inbound.tip.boxSkuDetail': 'Maintain SKUs and planned qty per box; add one box or five at once',
|
||||||
'warehouse.inbound.tip.markBoxNo': 'Per-box mark / box number (optional)',
|
'warehouse.inbound.tip.markBoxNo': 'Per-box mark / box number (optional)',
|
||||||
@ -419,7 +422,7 @@ export default {
|
|||||||
'warehouse.inbound.form.markBoxNoPlaceholder': 'Box mark (optional)',
|
'warehouse.inbound.form.markBoxNoPlaceholder': 'Box mark (optional)',
|
||||||
'warehouse.inbound.form.sku': 'SKU',
|
'warehouse.inbound.form.sku': 'SKU',
|
||||||
'warehouse.inbound.form.plannedQty': 'Planned Qty',
|
'warehouse.inbound.form.plannedQty': 'Planned Qty',
|
||||||
'warehouse.inbound.form.markPlaceholder': 'Usually the tracking number',
|
'warehouse.inbound.form.markPlaceholder': 'Usually the tracking number (optional)',
|
||||||
'warehouse.inbound.form.remarkPlaceholder': 'Optional',
|
'warehouse.inbound.form.remarkPlaceholder': 'Optional',
|
||||||
'warehouse.inbound.form.optional': 'Optional',
|
'warehouse.inbound.form.optional': 'Optional',
|
||||||
'warehouse.inbound.form.boxSkuDetail': 'Boxes & SKU lines',
|
'warehouse.inbound.form.boxSkuDetail': 'Boxes & SKU lines',
|
||||||
@ -441,6 +444,8 @@ export default {
|
|||||||
'warehouse.inbound.actions.removeCurrentBox': 'Remove current box',
|
'warehouse.inbound.actions.removeCurrentBox': 'Remove current box',
|
||||||
'warehouse.inbound.actions.addSku': 'Add SKU line',
|
'warehouse.inbound.actions.addSku': 'Add SKU line',
|
||||||
'warehouse.inbound.modal.add': 'Add Inbound',
|
'warehouse.inbound.modal.add': 'Add Inbound',
|
||||||
|
'warehouse.inbound.modal.edit': 'Edit Inbound',
|
||||||
|
'warehouse.inbound.modal.view': 'View Inbound',
|
||||||
'warehouse.inbound.drawer.detail': 'Inbound Detail',
|
'warehouse.inbound.drawer.detail': 'Inbound Detail',
|
||||||
'warehouse.inbound.box': 'Box',
|
'warehouse.inbound.box': 'Box',
|
||||||
'warehouse.inbound.line.skuCode': 'SKU',
|
'warehouse.inbound.line.skuCode': 'SKU',
|
||||||
@ -450,6 +455,9 @@ export default {
|
|||||||
'warehouse.inbound.form.selectBin': 'Select bin',
|
'warehouse.inbound.form.selectBin': 'Select bin',
|
||||||
'warehouse.inbound.actions.delete': 'Delete',
|
'warehouse.inbound.actions.delete': 'Delete',
|
||||||
'warehouse.inbound.actions.confirmReceive': 'Confirm Receive',
|
'warehouse.inbound.actions.confirmReceive': 'Confirm Receive',
|
||||||
|
'warehouse.inbound.actions.printLabel': 'Print Box Label',
|
||||||
|
'warehouse.inbound.message.printFailed': 'Failed to print box label',
|
||||||
|
'warehouse.inbound.message.printEmpty': 'No box label file available',
|
||||||
'warehouse.inbound.modal.confirmReceive': 'Confirm Receive & Putaway',
|
'warehouse.inbound.modal.confirmReceive': 'Confirm Receive & Putaway',
|
||||||
'warehouse.inbound.message.loadFailed': 'Failed to load inbound list',
|
'warehouse.inbound.message.loadFailed': 'Failed to load inbound list',
|
||||||
'warehouse.inbound.message.saveSuccess': 'Saved',
|
'warehouse.inbound.message.saveSuccess': 'Saved',
|
||||||
@ -2533,6 +2541,8 @@ export default {
|
|||||||
'order.myOrders.drawer.editBasicTitle': 'Edit Order Details',
|
'order.myOrders.drawer.editBasicTitle': 'Edit Order Details',
|
||||||
'order.myOrders.drawer.editWeightTitle': 'Edit Weight & Dimensions',
|
'order.myOrders.drawer.editWeightTitle': 'Edit Weight & Dimensions',
|
||||||
'order.myOrders.drawer.message.saveSuccess': 'Saved successfully',
|
'order.myOrders.drawer.message.saveSuccess': 'Saved successfully',
|
||||||
|
'order.myOrders.drawer.message.changeProductSuccess': 'Sales product updated',
|
||||||
|
'order.myOrders.drawer.placeholder.productName': 'Select sales product',
|
||||||
'order.myOrders.drawer.message.recalculateSuccess': 'Recalculated successfully',
|
'order.myOrders.drawer.message.recalculateSuccess': 'Recalculated successfully',
|
||||||
'order.myOrders.drawer.costItem': 'Cost Item',
|
'order.myOrders.drawer.costItem': 'Cost Item',
|
||||||
'order.myOrders.drawer.amount': 'Amount',
|
'order.myOrders.drawer.amount': 'Amount',
|
||||||
@ -4248,6 +4258,7 @@ export default {
|
|||||||
'operation.printLabel.labelTemplate.inbound': 'Inbound Label',
|
'operation.printLabel.labelTemplate.inbound': 'Inbound Label',
|
||||||
'operation.printLabel.labelTemplate.outbound': 'Outbound Label',
|
'operation.printLabel.labelTemplate.outbound': 'Outbound Label',
|
||||||
'operation.printLabel.labelTemplate.sku': 'SKU Label',
|
'operation.printLabel.labelTemplate.sku': 'SKU Label',
|
||||||
|
'operation.printLabel.labelTemplate.box': 'Box Mark Label',
|
||||||
'operation.printLabel.preview.title': 'Label preview',
|
'operation.printLabel.preview.title': 'Label preview',
|
||||||
'operation.printLabel.preview.empty': 'Scan a number to show order info; open Preview when needed',
|
'operation.printLabel.preview.empty': 'Scan a number to show order info; open Preview when needed',
|
||||||
'operation.printLabel.preview.product': 'Product',
|
'operation.printLabel.preview.product': 'Product',
|
||||||
|
|||||||
@ -214,6 +214,7 @@ export default {
|
|||||||
'warehouse.sku.modal.add': '新增 SKU',
|
'warehouse.sku.modal.add': '新增 SKU',
|
||||||
'warehouse.sku.modal.edit': '编辑 SKU',
|
'warehouse.sku.modal.edit': '编辑 SKU',
|
||||||
'warehouse.sku.actions.delete': '删除 SKU',
|
'warehouse.sku.actions.delete': '删除 SKU',
|
||||||
|
'warehouse.sku.actions.printLabel': '打印标签',
|
||||||
'warehouse.sku.message.loadFailed': '加载 SKU 列表失败',
|
'warehouse.sku.message.loadFailed': '加载 SKU 列表失败',
|
||||||
'warehouse.sku.message.saveSuccess': '保存成功',
|
'warehouse.sku.message.saveSuccess': '保存成功',
|
||||||
'warehouse.sku.message.saveFailed': '保存失败',
|
'warehouse.sku.message.saveFailed': '保存失败',
|
||||||
@ -221,6 +222,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.sku.message.printFailed': '打印 SKU 标签失败',
|
||||||
|
'warehouse.sku.message.printEmpty': '未获取到 SKU 标签文件',
|
||||||
'warehouse.inventory.table.warehouse': '仓库',
|
'warehouse.inventory.table.warehouse': '仓库',
|
||||||
'warehouse.inventory.table.binName': '库位名称',
|
'warehouse.inventory.table.binName': '库位名称',
|
||||||
'warehouse.inventory.table.skuCode': 'SKU 编码',
|
'warehouse.inventory.table.skuCode': 'SKU 编码',
|
||||||
@ -410,7 +413,7 @@ export default {
|
|||||||
'warehouse.inbound.search.keyword': '单号/唛头',
|
'warehouse.inbound.search.keyword': '单号/唛头',
|
||||||
'warehouse.inbound.tip': '预报到仓货物;收货上架后库存会增加。关注状态:预报 → 待入库 → 已完成。',
|
'warehouse.inbound.tip': '预报到仓货物;收货上架后库存会增加。关注状态:预报 → 待入库 → 已完成。',
|
||||||
'warehouse.inbound.tip.currentWarehouse': '预报归属当前仓库;可在顶部切换仓库',
|
'warehouse.inbound.tip.currentWarehouse': '预报归属当前仓库;可在顶部切换仓库',
|
||||||
'warehouse.inbound.tip.markReference': '一般填物流快递单号或主唛头,便于到仓核对',
|
'warehouse.inbound.tip.markReference': '选填;一般填物流快递单号或主唛头,便于到仓核对',
|
||||||
'warehouse.inbound.tip.inboundType': '普通入库或退货入库,影响后续统计分类',
|
'warehouse.inbound.tip.inboundType': '普通入库或退货入库,影响后续统计分类',
|
||||||
'warehouse.inbound.tip.boxSkuDetail': '按箱维护 SKU 与计划数量;可加箱或一次加 5 箱',
|
'warehouse.inbound.tip.boxSkuDetail': '按箱维护 SKU 与计划数量;可加箱或一次加 5 箱',
|
||||||
'warehouse.inbound.tip.markBoxNo': '单箱唛头/箱号,选填',
|
'warehouse.inbound.tip.markBoxNo': '单箱唛头/箱号,选填',
|
||||||
@ -420,7 +423,7 @@ export default {
|
|||||||
'warehouse.inbound.form.markBoxNoPlaceholder': '箱唛/箱号(选填)',
|
'warehouse.inbound.form.markBoxNoPlaceholder': '箱唛/箱号(选填)',
|
||||||
'warehouse.inbound.form.sku': 'SKU',
|
'warehouse.inbound.form.sku': 'SKU',
|
||||||
'warehouse.inbound.form.plannedQty': '计划数量',
|
'warehouse.inbound.form.plannedQty': '计划数量',
|
||||||
'warehouse.inbound.form.markPlaceholder': '一般填写物流快递单号',
|
'warehouse.inbound.form.markPlaceholder': '一般填写物流快递单号(选填)',
|
||||||
'warehouse.inbound.form.remarkPlaceholder': '选填',
|
'warehouse.inbound.form.remarkPlaceholder': '选填',
|
||||||
'warehouse.inbound.form.optional': '选填',
|
'warehouse.inbound.form.optional': '选填',
|
||||||
'warehouse.inbound.form.boxSkuDetail': '箱子与 SKU 明细',
|
'warehouse.inbound.form.boxSkuDetail': '箱子与 SKU 明细',
|
||||||
@ -442,6 +445,8 @@ export default {
|
|||||||
'warehouse.inbound.actions.removeCurrentBox': '删除当前箱',
|
'warehouse.inbound.actions.removeCurrentBox': '删除当前箱',
|
||||||
'warehouse.inbound.actions.addSku': '选择库内 SKU',
|
'warehouse.inbound.actions.addSku': '选择库内 SKU',
|
||||||
'warehouse.inbound.modal.add': '新增入库预报',
|
'warehouse.inbound.modal.add': '新增入库预报',
|
||||||
|
'warehouse.inbound.modal.edit': '编辑入库预报',
|
||||||
|
'warehouse.inbound.modal.view': '查看入库预报',
|
||||||
'warehouse.inbound.drawer.detail': '入库详情',
|
'warehouse.inbound.drawer.detail': '入库详情',
|
||||||
'warehouse.inbound.box': '箱',
|
'warehouse.inbound.box': '箱',
|
||||||
'warehouse.inbound.line.skuCode': 'SKU',
|
'warehouse.inbound.line.skuCode': 'SKU',
|
||||||
@ -451,6 +456,9 @@ export default {
|
|||||||
'warehouse.inbound.form.selectBin': '请选择库位',
|
'warehouse.inbound.form.selectBin': '请选择库位',
|
||||||
'warehouse.inbound.actions.delete': '删除预报',
|
'warehouse.inbound.actions.delete': '删除预报',
|
||||||
'warehouse.inbound.actions.confirmReceive': '确认收货',
|
'warehouse.inbound.actions.confirmReceive': '确认收货',
|
||||||
|
'warehouse.inbound.actions.printLabel': '打印箱唛',
|
||||||
|
'warehouse.inbound.message.printFailed': '打印箱唛失败',
|
||||||
|
'warehouse.inbound.message.printEmpty': '未获取到箱唛文件',
|
||||||
'warehouse.inbound.modal.confirmReceive': '确认收货上架',
|
'warehouse.inbound.modal.confirmReceive': '确认收货上架',
|
||||||
'warehouse.inbound.message.loadFailed': '加载入库预报失败',
|
'warehouse.inbound.message.loadFailed': '加载入库预报失败',
|
||||||
'warehouse.inbound.message.saveSuccess': '保存成功',
|
'warehouse.inbound.message.saveSuccess': '保存成功',
|
||||||
@ -2392,6 +2400,8 @@ export default {
|
|||||||
'order.myOrders.drawer.editBasicTitle': '编辑订单详情',
|
'order.myOrders.drawer.editBasicTitle': '编辑订单详情',
|
||||||
'order.myOrders.drawer.editWeightTitle': '编辑件重尺',
|
'order.myOrders.drawer.editWeightTitle': '编辑件重尺',
|
||||||
'order.myOrders.drawer.message.saveSuccess': '保存成功',
|
'order.myOrders.drawer.message.saveSuccess': '保存成功',
|
||||||
|
'order.myOrders.drawer.message.changeProductSuccess': '销售产品已更新',
|
||||||
|
'order.myOrders.drawer.placeholder.productName': '请选择销售产品',
|
||||||
'order.myOrders.drawer.message.recalculateSuccess': '重算成功',
|
'order.myOrders.drawer.message.recalculateSuccess': '重算成功',
|
||||||
'order.myOrders.drawer.costItem': '费用项',
|
'order.myOrders.drawer.costItem': '费用项',
|
||||||
'order.myOrders.drawer.amount': '金额',
|
'order.myOrders.drawer.amount': '金额',
|
||||||
@ -4273,6 +4283,7 @@ export default {
|
|||||||
'operation.printLabel.labelTemplate.inbound': '入库标签',
|
'operation.printLabel.labelTemplate.inbound': '入库标签',
|
||||||
'operation.printLabel.labelTemplate.outbound': '出库标签',
|
'operation.printLabel.labelTemplate.outbound': '出库标签',
|
||||||
'operation.printLabel.labelTemplate.sku': 'sku标签',
|
'operation.printLabel.labelTemplate.sku': 'sku标签',
|
||||||
|
'operation.printLabel.labelTemplate.box': '箱唛标签',
|
||||||
'operation.printLabel.preview.title': '面单预览',
|
'operation.printLabel.preview.title': '面单预览',
|
||||||
'operation.printLabel.preview.empty': '扫描单号后显示订单信息,需要时可点预览查看面单',
|
'operation.printLabel.preview.empty': '扫描单号后显示订单信息,需要时可点预览查看面单',
|
||||||
'operation.printLabel.preview.product': '销售产品',
|
'operation.printLabel.preview.product': '销售产品',
|
||||||
|
|||||||
@ -73,13 +73,27 @@ const EMPTY_LABEL_TEMPLATE = {
|
|||||||
|
|
||||||
const initialTemplate = ref<unknown>(EMPTY_LABEL_TEMPLATE)
|
const initialTemplate = ref<unknown>(EMPTY_LABEL_TEMPLATE)
|
||||||
|
|
||||||
const BARCODE_FIELDS = new Set(['serviceNo', 'trackNo', 'clientNo', 'requestId'])
|
const BARCODE_FIELDS = new Set([
|
||||||
|
'serviceNo',
|
||||||
|
'trackNo',
|
||||||
|
'clientNo',
|
||||||
|
'requestId',
|
||||||
|
'noticeNo',
|
||||||
|
'markReference',
|
||||||
|
'markBoxNo',
|
||||||
|
'boxSeqNo',
|
||||||
|
'barcode',
|
||||||
|
'skuCode',
|
||||||
|
])
|
||||||
const LONGTEXT_FIELDS = new Set([
|
const LONGTEXT_FIELDS = new Set([
|
||||||
'recipientAddressFull',
|
'recipientAddressFull',
|
||||||
'senderAddressFull',
|
'senderAddressFull',
|
||||||
'recipientStreet1',
|
'recipientStreet1',
|
||||||
'recipientStreet2',
|
'recipientStreet2',
|
||||||
'recipientStreet3',
|
'recipientStreet3',
|
||||||
|
'skuSummary',
|
||||||
|
'declareElement',
|
||||||
|
'remark',
|
||||||
])
|
])
|
||||||
|
|
||||||
const MOCK_BY_FIELD: Record<string, string> = {
|
const MOCK_BY_FIELD: Record<string, string> = {
|
||||||
@ -93,6 +107,40 @@ const MOCK_BY_FIELD: Record<string, string> = {
|
|||||||
actualWeight: '1.25',
|
actualWeight: '1.25',
|
||||||
salesProductName: '专线-普货',
|
salesProductName: '专线-普货',
|
||||||
warehouseName: 'SZ-01',
|
warehouseName: 'SZ-01',
|
||||||
|
noticeNo: 'IN202610220001',
|
||||||
|
markReference: 'SF1234567890',
|
||||||
|
markBoxNo: 'BOX-001',
|
||||||
|
boxSeqNo: '1',
|
||||||
|
barcode: 'BOX-001',
|
||||||
|
boxIndexText: '1/2',
|
||||||
|
skuSummary: 'SKU001 T-Shirt×2\nSKU002 Pants×1',
|
||||||
|
skuCode: 'SKU001',
|
||||||
|
skuName: 'T-Shirt',
|
||||||
|
skuNameEn: 'T-Shirt',
|
||||||
|
skuQty: '2',
|
||||||
|
unitPrice: '9.90',
|
||||||
|
exHsCode: '610910',
|
||||||
|
imHsCode: '610910',
|
||||||
|
declareElement: '棉质 T 恤',
|
||||||
|
weightG: '200',
|
||||||
|
lengthCm: '30',
|
||||||
|
widthCm: '20',
|
||||||
|
heightCm: '15',
|
||||||
|
dimensions: '30×20×15',
|
||||||
|
remark: 'fragile',
|
||||||
|
printTime: '2026-10-22 18:00:00',
|
||||||
|
operatorName: 'preview',
|
||||||
|
pageIndex: '1',
|
||||||
|
pageCount: '2',
|
||||||
|
customerCode: 'AF0A1',
|
||||||
|
customerName: 'Demo Customer',
|
||||||
|
}
|
||||||
|
|
||||||
|
function groupPanelTitle(g: LabelFieldGroup): string {
|
||||||
|
if (g.code === 'inboundBox') return '入库预报'
|
||||||
|
if (g.code === 'warehouseSku') return '库内SKU'
|
||||||
|
if (g.code === 'system') return '系统'
|
||||||
|
return `订单·${g.title}`
|
||||||
}
|
}
|
||||||
|
|
||||||
let hiprintRegistered = false
|
let hiprintRegistered = false
|
||||||
@ -183,12 +231,14 @@ function buildFieldElements(groups: LabelFieldGroup[]) {
|
|||||||
|
|
||||||
for (const g of normalizeFieldGroups(groups)) {
|
for (const g of normalizeFieldGroups(groups)) {
|
||||||
const items: any[] = []
|
const items: any[] = []
|
||||||
|
const groupKey = g.code || 'default'
|
||||||
for (const f of g.fields) {
|
for (const f of g.fields) {
|
||||||
const testData = MOCK_BY_FIELD[f.field] || f.title
|
const testData = MOCK_BY_FIELD[f.field] || f.title
|
||||||
const isLong = LONGTEXT_FIELDS.has(f.field)
|
const isLong = LONGTEXT_FIELDS.has(f.field)
|
||||||
|
const tidBase = `omsLabelFields.${groupKey}.${f.field}`
|
||||||
|
|
||||||
items.push({
|
items.push({
|
||||||
tid: `omsLabelFields.${f.field}`,
|
tid: tidBase,
|
||||||
title: f.title,
|
title: f.title,
|
||||||
type: isLong ? 'longText' : 'text',
|
type: isLong ? 'longText' : 'text',
|
||||||
options: {
|
options: {
|
||||||
@ -204,7 +254,7 @@ function buildFieldElements(groups: LabelFieldGroup[]) {
|
|||||||
|
|
||||||
if (BARCODE_FIELDS.has(f.field)) {
|
if (BARCODE_FIELDS.has(f.field)) {
|
||||||
items.push({
|
items.push({
|
||||||
tid: `omsLabelFields.${f.field}.barcode`,
|
tid: `${tidBase}.barcode`,
|
||||||
title: `${f.title}·条码`,
|
title: `${f.title}·条码`,
|
||||||
type: 'text',
|
type: 'text',
|
||||||
options: {
|
options: {
|
||||||
@ -218,7 +268,7 @@ function buildFieldElements(groups: LabelFieldGroup[]) {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
items.push({
|
items.push({
|
||||||
tid: `omsLabelFields.${f.field}.qrcode`,
|
tid: `${tidBase}.qrcode`,
|
||||||
title: `${f.title}·二维码`,
|
title: `${f.title}·二维码`,
|
||||||
type: 'text',
|
type: 'text',
|
||||||
options: {
|
options: {
|
||||||
@ -236,7 +286,7 @@ function buildFieldElements(groups: LabelFieldGroup[]) {
|
|||||||
|
|
||||||
if (items.length) {
|
if (items.length) {
|
||||||
if (!hiprint?.PrintElementTypeGroup) continue
|
if (!hiprint?.PrintElementTypeGroup) continue
|
||||||
typeGroups.push(new hiprint.PrintElementTypeGroup(`订单·${g.title}`, items))
|
typeGroups.push(new hiprint.PrintElementTypeGroup(groupPanelTitle(g), items))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -200,6 +200,7 @@ const typeOptions = computed(() => [
|
|||||||
{ value: 'inbound', label: t('operation.printLabel.labelTemplate.inbound') },
|
{ value: 'inbound', label: t('operation.printLabel.labelTemplate.inbound') },
|
||||||
{ value: 'outbound', label: t('operation.printLabel.labelTemplate.outbound') },
|
{ value: 'outbound', label: t('operation.printLabel.labelTemplate.outbound') },
|
||||||
{ value: 'sku', label: t('operation.printLabel.labelTemplate.sku') },
|
{ value: 'sku', label: t('operation.printLabel.labelTemplate.sku') },
|
||||||
|
{ value: 'box', label: t('operation.printLabel.labelTemplate.box') },
|
||||||
])
|
])
|
||||||
|
|
||||||
function typeLabel(type?: string) {
|
function typeLabel(type?: string) {
|
||||||
|
|||||||
@ -143,7 +143,23 @@
|
|||||||
<div class="detail-field-value">
|
<div class="detail-field-value">
|
||||||
<template v-if="basicEditing && isOrderFieldEditable(fieldKey)">
|
<template v-if="basicEditing && isOrderFieldEditable(fieldKey)">
|
||||||
<a-select
|
<a-select
|
||||||
v-if="fieldKey === 'warehouse'"
|
v-if="fieldKey === 'productName'"
|
||||||
|
v-model="basicEditForm.productCode"
|
||||||
|
allow-search
|
||||||
|
size="small"
|
||||||
|
:loading="salesProductOptionsLoading"
|
||||||
|
:placeholder="$t('order.myOrders.drawer.placeholder.productName')"
|
||||||
|
>
|
||||||
|
<a-option
|
||||||
|
v-for="opt in salesProductOptions"
|
||||||
|
:key="opt.value"
|
||||||
|
:value="opt.value"
|
||||||
|
>
|
||||||
|
{{ opt.label }}
|
||||||
|
</a-option>
|
||||||
|
</a-select>
|
||||||
|
<a-select
|
||||||
|
v-else-if="fieldKey === 'warehouse'"
|
||||||
v-model="basicEditForm.warehouseId"
|
v-model="basicEditForm.warehouseId"
|
||||||
allow-clear
|
allow-clear
|
||||||
allow-search
|
allow-search
|
||||||
@ -719,12 +735,14 @@ import useDrawer from '@/components/DrawerPro/useDrawer';
|
|||||||
import { postGetDetailTimeline } from '@/api/TrackOrder/trackOrder';
|
import { postGetDetailTimeline } from '@/api/TrackOrder/trackOrder';
|
||||||
import {
|
import {
|
||||||
postOrderUpdate,
|
postOrderUpdate,
|
||||||
|
postOrderChangeProduct,
|
||||||
postOrderWeightUpdate,
|
postOrderWeightUpdate,
|
||||||
postOrderRecalculateReceivable,
|
postOrderRecalculateReceivable,
|
||||||
postOrderRecalculatePayable,
|
postOrderRecalculatePayable,
|
||||||
getOrderOperationLogList,
|
getOrderOperationLogList,
|
||||||
type OrderOperationLogItem,
|
type OrderOperationLogItem,
|
||||||
} from '@/api/order';
|
} from '@/api/order';
|
||||||
|
import { getSalesProductList } from '@/api/channel/salesProduct';
|
||||||
import { getDictListAll, type DictDto } from '@/api/system/dict';
|
import { getDictListAll, type DictDto } from '@/api/system/dict';
|
||||||
import { getWarehouseList } from '@/api/system/warehouse';
|
import { getWarehouseList } from '@/api/system/warehouse';
|
||||||
import { getOrderStatusMeta, getOrderStatusTagStyle, resolveOrderStatusCode } from '@/constants/orderStatus';
|
import { getOrderStatusMeta, getOrderStatusTagStyle, resolveOrderStatusCode } from '@/constants/orderStatus';
|
||||||
@ -893,6 +911,12 @@ const orderEditFieldDefs: FormFieldDef[] = [
|
|||||||
|
|
||||||
/** 入库前(草稿/已预报)才允许客户编辑 */
|
/** 入库前(草稿/已预报)才允许客户编辑 */
|
||||||
const MY_ORDERS_EDITABLE_STATUSES = new Set(['draft', 'predeclared']);
|
const MY_ORDERS_EDITABLE_STATUSES = new Set(['draft', 'predeclared']);
|
||||||
|
const CHANGE_PRODUCT_STATUSES = new Set([
|
||||||
|
'draft',
|
||||||
|
'predeclared',
|
||||||
|
'pendingAllocation',
|
||||||
|
'pendingPayment',
|
||||||
|
]);
|
||||||
|
|
||||||
const orderEditMetaMap = Object.fromEntries(
|
const orderEditMetaMap = Object.fromEntries(
|
||||||
orderEditFieldDefs.map((def) => [def.key, def])
|
orderEditFieldDefs.map((def) => [def.key, def])
|
||||||
@ -902,7 +926,16 @@ const SENDER_READONLY_KEYS = new Set(['isDefault']);
|
|||||||
const SENDER_FORM_KEY_MAP: Record<string, string> = { country: 'countryCode' };
|
const SENDER_FORM_KEY_MAP: Record<string, string> = { country: 'countryCode' };
|
||||||
const RECIPIENT_FORM_KEY_MAP: Record<string, string> = { country: 'countryCode' };
|
const RECIPIENT_FORM_KEY_MAP: Record<string, string> = { country: 'countryCode' };
|
||||||
|
|
||||||
|
const canChangeProduct = computed(() => {
|
||||||
|
const o = detail.value as any;
|
||||||
|
const paid = o?.isPaid ?? o?.IsPaid;
|
||||||
|
if (paid === true || paid === 1 || paid === '1') return false;
|
||||||
|
const code = resolveOrderStatusCode(detail.value);
|
||||||
|
return !!code && CHANGE_PRODUCT_STATUSES.has(code) && MY_ORDERS_EDITABLE_STATUSES.has(code);
|
||||||
|
});
|
||||||
|
|
||||||
function isOrderFieldEditable(key: string) {
|
function isOrderFieldEditable(key: string) {
|
||||||
|
if (key === 'productName') return canChangeProduct.value;
|
||||||
return key in orderEditMetaMap;
|
return key in orderEditMetaMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -936,12 +969,36 @@ const basicEditForm = reactive({
|
|||||||
codCurrency: '',
|
codCurrency: '',
|
||||||
declaredCurrency: '',
|
declaredCurrency: '',
|
||||||
declaredSum: undefined as number | undefined,
|
declaredSum: undefined as number | undefined,
|
||||||
|
productCode: '',
|
||||||
});
|
});
|
||||||
|
const initialProductCode = ref('');
|
||||||
|
|
||||||
const currencyOptions = ref<Array<{ value: string; label: string }>>([]);
|
const currencyOptions = ref<Array<{ value: string; label: string }>>([]);
|
||||||
const currencyOptionsLoading = ref(false);
|
const currencyOptionsLoading = ref(false);
|
||||||
const warehouseOptions = ref<Array<{ value: number; label: string }>>([]);
|
const warehouseOptions = ref<Array<{ value: number; label: string }>>([]);
|
||||||
const warehouseOptionsLoading = ref(false);
|
const warehouseOptionsLoading = ref(false);
|
||||||
|
const salesProductOptions = ref<Array<{ value: string; label: string }>>([]);
|
||||||
|
const salesProductOptionsLoading = ref(false);
|
||||||
|
|
||||||
|
async function ensureSalesProductOptions() {
|
||||||
|
if (salesProductOptions.value.length > 0) return;
|
||||||
|
salesProductOptionsLoading.value = true;
|
||||||
|
try {
|
||||||
|
const res = await getSalesProductList({ page: 1, pageSize: 500, isActive: 2 });
|
||||||
|
salesProductOptions.value = (res.items ?? [])
|
||||||
|
.map((row: any) => {
|
||||||
|
const code = String(row?.productCode ?? row?.ProductCode ?? '').trim();
|
||||||
|
const name = String(row?.productName ?? row?.ProductName ?? '').trim();
|
||||||
|
if (!code) return null;
|
||||||
|
return { value: code, label: name ? `${name} ${code}` : code };
|
||||||
|
})
|
||||||
|
.filter(Boolean) as Array<{ value: string; label: string }>;
|
||||||
|
} catch {
|
||||||
|
salesProductOptions.value = [];
|
||||||
|
} finally {
|
||||||
|
salesProductOptionsLoading.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function ensureWarehouseOptions(preferred?: {
|
async function ensureWarehouseOptions(preferred?: {
|
||||||
id?: number;
|
id?: number;
|
||||||
@ -1988,6 +2045,9 @@ function fillBasicEditForm() {
|
|||||||
declaredSumRaw != null && declaredSumRaw !== '' && Number.isFinite(Number(declaredSumRaw))
|
declaredSumRaw != null && declaredSumRaw !== '' && Number.isFinite(Number(declaredSumRaw))
|
||||||
? Number(declaredSumRaw)
|
? Number(declaredSumRaw)
|
||||||
: undefined;
|
: undefined;
|
||||||
|
const productCode = String(o.productCode ?? o.ProductCode ?? '').trim();
|
||||||
|
basicEditForm.productCode = productCode;
|
||||||
|
initialProductCode.value = productCode;
|
||||||
|
|
||||||
senderEditForm.senderName = String(sender.value.senderName ?? '');
|
senderEditForm.senderName = String(sender.value.senderName ?? '');
|
||||||
senderEditForm.company = String(sender.value.company ?? '');
|
senderEditForm.company = String(sender.value.company ?? '');
|
||||||
@ -2051,6 +2111,9 @@ function startBasicEdit() {
|
|||||||
id: basicEditForm.warehouseId,
|
id: basicEditForm.warehouseId,
|
||||||
name: String(detail.value?.warehouse ?? detail.value?.warehouseName ?? ''),
|
name: String(detail.value?.warehouse ?? detail.value?.warehouseName ?? ''),
|
||||||
});
|
});
|
||||||
|
if (canChangeProduct.value) {
|
||||||
|
void ensureSalesProductOptions();
|
||||||
|
}
|
||||||
basicEditing.value = true;
|
basicEditing.value = true;
|
||||||
void focusFirstEditableField('.order-detail-drawer .basic-info');
|
void focusFirstEditableField('.order-detail-drawer .basic-info');
|
||||||
}
|
}
|
||||||
@ -2082,6 +2145,18 @@ async function handleBasicEditSave() {
|
|||||||
if (id == null) return;
|
if (id == null) return;
|
||||||
basicSaving.value = true;
|
basicSaving.value = true;
|
||||||
try {
|
try {
|
||||||
|
const nextProductCode = String(basicEditForm.productCode ?? '').trim();
|
||||||
|
const prevProductCode = String(initialProductCode.value ?? '').trim();
|
||||||
|
if (
|
||||||
|
canChangeProduct.value
|
||||||
|
&& nextProductCode
|
||||||
|
&& nextProductCode.toLowerCase() !== prevProductCode.toLowerCase()
|
||||||
|
) {
|
||||||
|
const changed = await postOrderChangeProduct({ id, productCode: nextProductCode });
|
||||||
|
applyDetailFromApi(changed);
|
||||||
|
initialProductCode.value = nextProductCode;
|
||||||
|
}
|
||||||
|
|
||||||
const result = await postOrderUpdate({
|
const result = await postOrderUpdate({
|
||||||
id,
|
id,
|
||||||
clientNo: basicEditForm.clientNo,
|
clientNo: basicEditForm.clientNo,
|
||||||
|
|||||||
@ -134,7 +134,23 @@
|
|||||||
<div class="detail-field-value">
|
<div class="detail-field-value">
|
||||||
<template v-if="basicEditing && isOrderFieldEditable(fieldKey)">
|
<template v-if="basicEditing && isOrderFieldEditable(fieldKey)">
|
||||||
<a-select
|
<a-select
|
||||||
v-if="fieldKey === 'warehouse'"
|
v-if="fieldKey === 'productName'"
|
||||||
|
v-model="basicEditForm.productCode"
|
||||||
|
allow-search
|
||||||
|
size="small"
|
||||||
|
:loading="salesProductOptionsLoading"
|
||||||
|
:placeholder="$t('order.myOrders.drawer.placeholder.productName')"
|
||||||
|
>
|
||||||
|
<a-option
|
||||||
|
v-for="opt in salesProductOptions"
|
||||||
|
:key="opt.value"
|
||||||
|
:value="opt.value"
|
||||||
|
>
|
||||||
|
{{ opt.label }}
|
||||||
|
</a-option>
|
||||||
|
</a-select>
|
||||||
|
<a-select
|
||||||
|
v-else-if="fieldKey === 'warehouse'"
|
||||||
v-model="basicEditForm.warehouseId"
|
v-model="basicEditForm.warehouseId"
|
||||||
allow-clear
|
allow-clear
|
||||||
allow-search
|
allow-search
|
||||||
@ -994,6 +1010,7 @@ import useDrawer from '@/components/DrawerPro/useDrawer';
|
|||||||
import { postGetDetailTimeline } from '@/api/TrackOrder/trackOrder';
|
import { postGetDetailTimeline } from '@/api/TrackOrder/trackOrder';
|
||||||
import {
|
import {
|
||||||
postOrderUpdate,
|
postOrderUpdate,
|
||||||
|
postOrderChangeProduct,
|
||||||
postOrderWeightUpdate,
|
postOrderWeightUpdate,
|
||||||
postOrderRecalculateReceivable,
|
postOrderRecalculateReceivable,
|
||||||
postOrderRecalculatePayable,
|
postOrderRecalculatePayable,
|
||||||
@ -1158,7 +1175,21 @@ const SENDER_READONLY_KEYS = new Set(['isDefault']);
|
|||||||
const SENDER_FORM_KEY_MAP: Record<string, string> = { country: 'countryCode' };
|
const SENDER_FORM_KEY_MAP: Record<string, string> = { country: 'countryCode' };
|
||||||
const RECIPIENT_FORM_KEY_MAP: Record<string, string> = { country: 'countryCode' };
|
const RECIPIENT_FORM_KEY_MAP: Record<string, string> = { country: 'countryCode' };
|
||||||
|
|
||||||
|
const CHANGE_PRODUCT_STATUSES = new Set([
|
||||||
|
'draft',
|
||||||
|
'predeclared',
|
||||||
|
'pendingAllocation',
|
||||||
|
'pendingPayment',
|
||||||
|
]);
|
||||||
|
|
||||||
|
const canChangeProduct = computed(() => {
|
||||||
|
if (!isOrderUnpaid.value) return false;
|
||||||
|
const code = resolveOrderStatusCode(detail.value);
|
||||||
|
return !!code && CHANGE_PRODUCT_STATUSES.has(code);
|
||||||
|
});
|
||||||
|
|
||||||
function isOrderFieldEditable(key: string) {
|
function isOrderFieldEditable(key: string) {
|
||||||
|
if (key === 'productName') return canChangeProduct.value;
|
||||||
return key in orderEditMetaMap;
|
return key in orderEditMetaMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1193,12 +1224,36 @@ const basicEditForm = reactive({
|
|||||||
codCurrency: '',
|
codCurrency: '',
|
||||||
declaredCurrency: '',
|
declaredCurrency: '',
|
||||||
declaredSum: undefined as number | undefined,
|
declaredSum: undefined as number | undefined,
|
||||||
|
productCode: '',
|
||||||
});
|
});
|
||||||
|
const initialProductCode = ref('');
|
||||||
|
|
||||||
const currencyOptions = ref<Array<{ value: string; label: string }>>([]);
|
const currencyOptions = ref<Array<{ value: string; label: string }>>([]);
|
||||||
const currencyOptionsLoading = ref(false);
|
const currencyOptionsLoading = ref(false);
|
||||||
const warehouseOptions = ref<Array<{ value: number; label: string }>>([]);
|
const warehouseOptions = ref<Array<{ value: number; label: string }>>([]);
|
||||||
const warehouseOptionsLoading = ref(false);
|
const warehouseOptionsLoading = ref(false);
|
||||||
|
const salesProductOptions = ref<Array<{ value: string; label: string }>>([]);
|
||||||
|
const salesProductOptionsLoading = ref(false);
|
||||||
|
|
||||||
|
async function ensureSalesProductOptions() {
|
||||||
|
if (salesProductOptions.value.length > 0) return;
|
||||||
|
salesProductOptionsLoading.value = true;
|
||||||
|
try {
|
||||||
|
const res = await getSalesProductList({ page: 1, pageSize: 500, isActive: 2 });
|
||||||
|
salesProductOptions.value = (res.items ?? [])
|
||||||
|
.map((row: any) => {
|
||||||
|
const code = String(row?.productCode ?? row?.ProductCode ?? '').trim();
|
||||||
|
const name = String(row?.productName ?? row?.ProductName ?? '').trim();
|
||||||
|
if (!code) return null;
|
||||||
|
return { value: code, label: name ? `${name} ${code}` : code };
|
||||||
|
})
|
||||||
|
.filter(Boolean) as Array<{ value: string; label: string }>;
|
||||||
|
} catch {
|
||||||
|
salesProductOptions.value = [];
|
||||||
|
} finally {
|
||||||
|
salesProductOptionsLoading.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function ensureWarehouseOptions(preferred?: {
|
async function ensureWarehouseOptions(preferred?: {
|
||||||
id?: number;
|
id?: number;
|
||||||
@ -2481,6 +2536,9 @@ function fillBasicEditForm() {
|
|||||||
declaredSumRaw != null && declaredSumRaw !== '' && Number.isFinite(Number(declaredSumRaw))
|
declaredSumRaw != null && declaredSumRaw !== '' && Number.isFinite(Number(declaredSumRaw))
|
||||||
? Number(declaredSumRaw)
|
? Number(declaredSumRaw)
|
||||||
: undefined;
|
: undefined;
|
||||||
|
const productCode = String(o.productCode ?? o.ProductCode ?? '').trim();
|
||||||
|
basicEditForm.productCode = productCode;
|
||||||
|
initialProductCode.value = productCode;
|
||||||
|
|
||||||
senderEditForm.senderName = String(sender.value.senderName ?? '');
|
senderEditForm.senderName = String(sender.value.senderName ?? '');
|
||||||
senderEditForm.company = String(sender.value.company ?? '');
|
senderEditForm.company = String(sender.value.company ?? '');
|
||||||
@ -2540,6 +2598,9 @@ function startBasicEdit() {
|
|||||||
id: basicEditForm.warehouseId,
|
id: basicEditForm.warehouseId,
|
||||||
name: String(detail.value?.warehouse ?? detail.value?.warehouseName ?? ''),
|
name: String(detail.value?.warehouse ?? detail.value?.warehouseName ?? ''),
|
||||||
});
|
});
|
||||||
|
if (canChangeProduct.value) {
|
||||||
|
void ensureSalesProductOptions();
|
||||||
|
}
|
||||||
basicEditing.value = true;
|
basicEditing.value = true;
|
||||||
void focusFirstEditableField('.order-detail-drawer .basic-info');
|
void focusFirstEditableField('.order-detail-drawer .basic-info');
|
||||||
}
|
}
|
||||||
@ -2572,6 +2633,18 @@ async function handleBasicEditSave() {
|
|||||||
if (id == null) return;
|
if (id == null) return;
|
||||||
basicSaving.value = true;
|
basicSaving.value = true;
|
||||||
try {
|
try {
|
||||||
|
const nextProductCode = String(basicEditForm.productCode ?? '').trim();
|
||||||
|
const prevProductCode = String(initialProductCode.value ?? '').trim();
|
||||||
|
if (
|
||||||
|
canChangeProduct.value
|
||||||
|
&& nextProductCode
|
||||||
|
&& nextProductCode.toLowerCase() !== prevProductCode.toLowerCase()
|
||||||
|
) {
|
||||||
|
const changed = await postOrderChangeProduct({ id, productCode: nextProductCode });
|
||||||
|
applyDetailFromApi(changed);
|
||||||
|
initialProductCode.value = nextProductCode;
|
||||||
|
}
|
||||||
|
|
||||||
const result = await postOrderUpdate({
|
const result = await postOrderUpdate({
|
||||||
id,
|
id,
|
||||||
clientNo: basicEditForm.clientNo,
|
clientNo: basicEditForm.clientNo,
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, onMounted, reactive, ref, watch } from 'vue'
|
import { computed, defineAsyncComponent, onMounted, reactive, ref, watch } from 'vue'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
import { Modal } from '@arco-design/web-vue'
|
import { Modal } from '@arco-design/web-vue'
|
||||||
import { IconDelete, IconPlus, IconQuestionCircle } from '@arco-design/web-vue/es/icon'
|
import { IconDelete, IconPlus, IconQuestionCircle } from '@arco-design/web-vue/es/icon'
|
||||||
@ -16,11 +16,13 @@ import {
|
|||||||
getWarehouseInboundList,
|
getWarehouseInboundList,
|
||||||
getWarehouseInboundSummary,
|
getWarehouseInboundSummary,
|
||||||
getWarehouseSkuList,
|
getWarehouseSkuList,
|
||||||
|
postWarehouseInboundPrintLabels,
|
||||||
saveWarehouseInbound,
|
saveWarehouseInbound,
|
||||||
} from '@/api/warehouse'
|
} from '@/api/warehouse'
|
||||||
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 { useHiprintClientState } from '@/utils/hiprint/hiprintClientState'
|
||||||
import {
|
import {
|
||||||
buildWarehouseNameMap,
|
buildWarehouseNameMap,
|
||||||
formatDateTime,
|
formatDateTime,
|
||||||
@ -35,6 +37,10 @@ import { useCurrentWarehouseStore } from '@/store'
|
|||||||
|
|
||||||
defineOptions({ name: 'WarehouseInboundNotice' })
|
defineOptions({ name: 'WarehouseInboundNotice' })
|
||||||
|
|
||||||
|
const OrderPrintModal = defineAsyncComponent(
|
||||||
|
() => import('@/components/PrintTemplate/OrderPrintModal.vue'),
|
||||||
|
)
|
||||||
|
|
||||||
interface InboundLineForm {
|
interface InboundLineForm {
|
||||||
skuId?: number
|
skuId?: number
|
||||||
skuCode: string
|
skuCode: string
|
||||||
@ -78,6 +84,10 @@ const {
|
|||||||
resolveSelectedCustomerId,
|
resolveSelectedCustomerId,
|
||||||
} = useWarehouseAdminCustomer()
|
} = useWarehouseAdminCustomer()
|
||||||
const currentWarehouseStore = useCurrentWarehouseStore()
|
const currentWarehouseStore = useCurrentWarehouseStore()
|
||||||
|
const printConnectionState = useHiprintClientState()
|
||||||
|
const printVisible = ref(false)
|
||||||
|
const printItems = ref<Array<{ id: number | string; label: string; url: string }>>([])
|
||||||
|
const printPreparing = ref(false)
|
||||||
|
|
||||||
/** Tab active-key;全部用 'all',请求时再转成后端 receiptStatus */
|
/** Tab active-key;全部用 'all',请求时再转成后端 receiptStatus */
|
||||||
const STATUS_TAB_ALL = 'all'
|
const STATUS_TAB_ALL = 'all'
|
||||||
@ -111,11 +121,10 @@ const slidingStatusTabs = computed(() =>
|
|||||||
})),
|
})),
|
||||||
)
|
)
|
||||||
|
|
||||||
const detailVisible = ref(false)
|
|
||||||
const detailData = ref<any>(null)
|
|
||||||
|
|
||||||
const [registerFormModal, { openModal, closeModal }] = useModal() as any
|
const [registerFormModal, { openModal, closeModal }] = useModal() as any
|
||||||
const [registerReceiveModal, { openModal: openReceiveModal, closeModal: closeReceiveModal }] = useModal() as any
|
const [registerReceiveModal, { openModal: openReceiveModal, closeModal: closeReceiveModal }] = useModal() as any
|
||||||
|
/** 已完成等不可改状态:同一弹窗只读查看 */
|
||||||
|
const formReadonly = ref(false)
|
||||||
const saving = ref(false)
|
const saving = ref(false)
|
||||||
const receiveSaving = ref(false)
|
const receiveSaving = ref(false)
|
||||||
const receiveNotice = ref<any>(null)
|
const receiveNotice = ref<any>(null)
|
||||||
@ -145,11 +154,49 @@ const boxTabItems = computed(() =>
|
|||||||
|
|
||||||
const canRemoveBox = computed(() => boxRows.value.length > 1)
|
const canRemoveBox = computed(() => boxRows.value.length > 1)
|
||||||
|
|
||||||
|
const formModalTitle = computed(() => {
|
||||||
|
if (!formModel.id) return t('warehouse.inbound.modal.add')
|
||||||
|
if (formReadonly.value) return t('warehouse.inbound.modal.view')
|
||||||
|
return t('warehouse.inbound.modal.edit')
|
||||||
|
})
|
||||||
|
|
||||||
|
const formInboundTypeLabel = computed(() =>
|
||||||
|
t(`warehouse.inbound.type.${formModel.inboundType}`, formModel.inboundType || '-'),
|
||||||
|
)
|
||||||
|
|
||||||
const modalWarehouseName = computed(() => {
|
const modalWarehouseName = computed(() => {
|
||||||
const wh = warehouseOptions.value.find((w) => w.value === formModel.warehouseId)
|
const wh = warehouseOptions.value.find((w) => w.value === formModel.warehouseId)
|
||||||
return wh?.label || ''
|
return wh?.label || ''
|
||||||
})
|
})
|
||||||
|
|
||||||
|
function displayOrDash(value: unknown) {
|
||||||
|
const text = String(value ?? '').trim()
|
||||||
|
return text || '-'
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatBoxWeight(box: InboundBoxForm) {
|
||||||
|
if (box.weightG == null || Number.isNaN(Number(box.weightG))) return '-'
|
||||||
|
return `${box.weightG} g`
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatBoxSize(box: InboundBoxForm) {
|
||||||
|
const l = box.lengthCm
|
||||||
|
const w = box.widthCm
|
||||||
|
const h = box.heightCm
|
||||||
|
if (l == null && w == null && h == null) return '-'
|
||||||
|
return `${l ?? '-'} × ${w ?? '-'} × ${h ?? '-'} cm`
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveSkuLabel(line: InboundLineForm) {
|
||||||
|
if (!line.skuId) return '-'
|
||||||
|
const opt = skuOptions.value.find((s) => s.value === line.skuId)
|
||||||
|
return opt?.label || line.skuCode || `#${line.skuId}`
|
||||||
|
}
|
||||||
|
|
||||||
|
function viewSkuLines(box: InboundBoxForm) {
|
||||||
|
return box.lines.filter((l) => l.skuId)
|
||||||
|
}
|
||||||
|
|
||||||
const receiveWarehouseName = computed(() => {
|
const receiveWarehouseName = computed(() => {
|
||||||
const whId = receiveNotice.value?.warehouseId
|
const whId = receiveNotice.value?.warehouseId
|
||||||
return resolveWarehouseLabel(whId, warehouseNameMap.value)
|
return resolveWarehouseLabel(whId, warehouseNameMap.value)
|
||||||
@ -261,6 +308,7 @@ function onSkuChange(line: InboundLineForm, skuId?: number) {
|
|||||||
watch(
|
watch(
|
||||||
boxRows,
|
boxRows,
|
||||||
(boxes) => {
|
(boxes) => {
|
||||||
|
if (formReadonly.value) return
|
||||||
for (const box of boxes) {
|
for (const box of boxes) {
|
||||||
const last = box.lines[box.lines.length - 1]
|
const last = box.lines[box.lines.length - 1]
|
||||||
if (last?.skuId) {
|
if (last?.skuId) {
|
||||||
@ -374,18 +422,6 @@ function handleSearch() {
|
|||||||
loadList()
|
loadList()
|
||||||
}
|
}
|
||||||
|
|
||||||
async function showDetail(row: any) {
|
|
||||||
try {
|
|
||||||
detailData.value = await getWarehouseInboundDetail(
|
|
||||||
row._raw?.id || row.id,
|
|
||||||
isAdmin.value ? resolveSelectedCustomerId() : undefined,
|
|
||||||
)
|
|
||||||
detailVisible.value = true
|
|
||||||
} catch (e: any) {
|
|
||||||
$baseMessage(e?.message || t('warehouse.inbound.message.detailFailed'), 'error')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function loadReceiveBinOptions(whId?: number) {
|
async function loadReceiveBinOptions(whId?: number) {
|
||||||
if (!whId) {
|
if (!whId) {
|
||||||
receiveBinOptions.value = []
|
receiveBinOptions.value = []
|
||||||
@ -496,9 +532,14 @@ async function submitConfirmReceive() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function canEditInbound(row: any) {
|
||||||
|
return row?._status === 'forecasted' || row?._status === 'received'
|
||||||
|
}
|
||||||
|
|
||||||
function handleAdd() {
|
function handleAdd() {
|
||||||
if (!hasPermission('Warehouse.InboundNotice.Save')) return
|
if (!hasPermission('Warehouse.InboundNotice.Save')) return
|
||||||
if (isAdmin.value && ensureMutationCustomerId() == null) return
|
if (isAdmin.value && ensureMutationCustomerId() == null) return
|
||||||
|
formReadonly.value = false
|
||||||
formModel.id = 0
|
formModel.id = 0
|
||||||
formModel.warehouseId = currentWarehouseStore.resolveDefaultWarehouseId(warehouseId.value)
|
formModel.warehouseId = currentWarehouseStore.resolveDefaultWarehouseId(warehouseId.value)
|
||||||
formModel.markReference = ''
|
formModel.markReference = ''
|
||||||
@ -512,7 +553,82 @@ function handleAdd() {
|
|||||||
openModal(true)
|
openModal(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 查看/编辑融合:同一弹窗;预报/待入库可改,已完成只读 */
|
||||||
|
async function openInboundForm(row: any) {
|
||||||
|
const editable =
|
||||||
|
hasPermission('Warehouse.InboundNotice.Save') && canEditInbound(row)
|
||||||
|
if (editable && isAdmin.value && ensureMutationCustomerId() == null) return
|
||||||
|
try {
|
||||||
|
const detail = await getWarehouseInboundDetail(
|
||||||
|
row._raw?.id || row.id,
|
||||||
|
isAdmin.value ? resolveSelectedCustomerId() : undefined,
|
||||||
|
)
|
||||||
|
const normalized = normalizeListRow(detail || {})
|
||||||
|
formReadonly.value = !editable
|
||||||
|
formModel.id = Number(pickField(normalized, 'id', 'Id') || 0)
|
||||||
|
formModel.warehouseId = pickField<number>(normalized, 'warehouseId', 'WarehouseId')
|
||||||
|
formModel.markReference = String(pickField(normalized, 'markReference', 'MarkReference') ?? '')
|
||||||
|
formModel.inboundType = String(pickField(normalized, 'inboundType', 'InboundType') ?? 'normal')
|
||||||
|
formModel.remark = String(pickField(normalized, 'remark', 'Remark') ?? '')
|
||||||
|
|
||||||
|
const boxesRaw = pickField(normalized, 'boxes', 'Boxes')
|
||||||
|
const boxes = Array.isArray(boxesRaw) ? boxesRaw : []
|
||||||
|
if (!boxes.length) {
|
||||||
|
if (editable) resetBoxes()
|
||||||
|
else {
|
||||||
|
boxRows.value = []
|
||||||
|
activeBoxKey.value = ''
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
boxRows.value = boxes.map((box: any, index: number) => {
|
||||||
|
const b = normalizeListRow(box)
|
||||||
|
const linesRaw = pickField(b, 'lines', 'Lines')
|
||||||
|
const lines = Array.isArray(linesRaw) ? linesRaw : []
|
||||||
|
const mappedLines: InboundLineForm[] = lines.map((line: any) => {
|
||||||
|
const l = normalizeListRow(line)
|
||||||
|
return {
|
||||||
|
skuId: Number(pickField(l, 'skuId', 'SkuId') || 0) || undefined,
|
||||||
|
skuCode: String(pickField(l, 'skuCode', 'SkuCode') ?? ''),
|
||||||
|
plannedQty: Number(pickField(l, 'plannedQty', 'PlannedQty') ?? 1) || 1,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if (editable && (!mappedLines.length || mappedLines.every((l) => l.skuId))) {
|
||||||
|
mappedLines.push({ skuId: undefined, skuCode: '', plannedQty: 1 })
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
key: `box-open-${pickField(b, 'id', 'Id') || index}-${Date.now()}`,
|
||||||
|
boxSeqNo: String(pickField(b, 'boxSeqNo', 'BoxSeqNo') ?? index + 1),
|
||||||
|
markBoxNo: String(pickField(b, 'markBoxNo', 'MarkBoxNo') ?? ''),
|
||||||
|
weightG: pickField<number>(b, 'weightG', 'WeightG') ?? undefined,
|
||||||
|
lengthCm: pickField<number>(b, 'lengthCm', 'LengthCm') ?? undefined,
|
||||||
|
widthCm: pickField<number>(b, 'widthCm', 'WidthCm') ?? undefined,
|
||||||
|
heightCm: pickField<number>(b, 'heightCm', 'HeightCm') ?? undefined,
|
||||||
|
lines: mappedLines.length
|
||||||
|
? mappedLines
|
||||||
|
: [{ skuId: undefined, skuCode: '', plannedQty: 1 }],
|
||||||
|
}
|
||||||
|
})
|
||||||
|
selectedBoxKeys.value = []
|
||||||
|
activeBoxKey.value = boxRows.value[0]?.key || ''
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!formModel.warehouseId) {
|
||||||
|
$baseMessage(t('warehouse.common.noCurrentWarehouse'), 'warning')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
openModal(true)
|
||||||
|
} catch (e: any) {
|
||||||
|
$baseMessage(e?.message || t('warehouse.inbound.message.detailFailed'), 'error')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeFormModal() {
|
||||||
|
formReadonly.value = false
|
||||||
|
closeModal()
|
||||||
|
}
|
||||||
|
|
||||||
async function handleSave() {
|
async function handleSave() {
|
||||||
|
if (formReadonly.value) return
|
||||||
if (!formModel.warehouseId) {
|
if (!formModel.warehouseId) {
|
||||||
$baseMessage(t('warehouse.inbound.message.required'), 'warning')
|
$baseMessage(t('warehouse.inbound.message.required'), 'warning')
|
||||||
return
|
return
|
||||||
@ -552,7 +668,7 @@ async function handleSave() {
|
|||||||
boxes,
|
boxes,
|
||||||
}))
|
}))
|
||||||
$baseMessage(t('warehouse.inbound.message.saveSuccess'), 'success')
|
$baseMessage(t('warehouse.inbound.message.saveSuccess'), 'success')
|
||||||
closeModal()
|
closeFormModal()
|
||||||
await loadSummary()
|
await loadSummary()
|
||||||
await loadList()
|
await loadList()
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
@ -577,8 +693,62 @@ function handleDelete(row: any) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isUsablePrintUrl(raw: unknown) {
|
||||||
|
const value = String(raw ?? '').trim()
|
||||||
|
if (!value) return false
|
||||||
|
if (value.includes('获取中')) return false
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handlePrintLabel(row: any) {
|
||||||
|
if (!hasPermission('Warehouse.InboundNotice.PrintLabel')) return
|
||||||
|
const id = Number(row?._raw?.id || row?.id || 0)
|
||||||
|
if (!id) return
|
||||||
|
|
||||||
|
printVisible.value = false
|
||||||
|
printItems.value = []
|
||||||
|
printPreparing.value = true
|
||||||
|
try {
|
||||||
|
const customerId = isAdmin.value ? resolveSelectedCustomerId() ?? undefined : undefined
|
||||||
|
const { printUrl, noticeNo, pageCount } = await postWarehouseInboundPrintLabels({
|
||||||
|
id,
|
||||||
|
customerId,
|
||||||
|
})
|
||||||
|
if (!isUsablePrintUrl(printUrl)) {
|
||||||
|
$baseMessage(t('warehouse.inbound.message.printEmpty'), 'warning')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const label = noticeNo || row.noticeNo || String(id)
|
||||||
|
printItems.value = [
|
||||||
|
{
|
||||||
|
id,
|
||||||
|
label: pageCount > 1 ? `${label}(${pageCount}页)` : label,
|
||||||
|
url: printUrl,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
printVisible.value = true
|
||||||
|
if (!printConnectionState.enabled || !printConnectionState.connected) {
|
||||||
|
$baseMessage('已打开箱唛预览;静默打印需先在顶部连接打印开关', 'info')
|
||||||
|
}
|
||||||
|
} catch (e: any) {
|
||||||
|
$baseMessage(e?.message || t('warehouse.inbound.message.printFailed'), 'error')
|
||||||
|
} finally {
|
||||||
|
printPreparing.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function actionRenderer(row: any) {
|
function actionRenderer(row: any) {
|
||||||
const parts = [renderWarehouseActionBtn('detail', t('warehouse.common.detail'), 'view')]
|
const canEdit = hasPermission('Warehouse.InboundNotice.Save') && canEditInbound(row)
|
||||||
|
const parts = [
|
||||||
|
canEdit
|
||||||
|
? renderWarehouseActionBtn('open', t('warehouse.common.edit'), 'edit')
|
||||||
|
: renderWarehouseActionBtn('open', t('warehouse.common.detail'), 'view'),
|
||||||
|
]
|
||||||
|
if (hasPermission('Warehouse.InboundNotice.PrintLabel')) {
|
||||||
|
parts.push(
|
||||||
|
renderWarehouseActionBtn('printLabel', t('warehouse.inbound.actions.printLabel'), 'print'),
|
||||||
|
)
|
||||||
|
}
|
||||||
if (
|
if (
|
||||||
(hasPermission('Warehouse.InboundNotice.ConfirmReceive')
|
(hasPermission('Warehouse.InboundNotice.ConfirmReceive')
|
||||||
|| hasPermission('Warehouse.InboundNotice.Save'))
|
|| hasPermission('Warehouse.InboundNotice.Save'))
|
||||||
@ -593,7 +763,8 @@ function actionRenderer(row: any) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function handleActionClick(row: any, _i: number, action?: string) {
|
function handleActionClick(row: any, _i: number, action?: string) {
|
||||||
if (action === 'detail') showDetail(row)
|
if (action === 'open') void openInboundForm(row)
|
||||||
|
if (action === 'printLabel') void handlePrintLabel(row)
|
||||||
if (action === 'receive') openConfirmReceive(row)
|
if (action === 'receive') openConfirmReceive(row)
|
||||||
if (action === 'delete') handleDelete(row)
|
if (action === 'delete') handleDelete(row)
|
||||||
}
|
}
|
||||||
@ -676,22 +847,13 @@ onMounted(async () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<a-drawer v-model:visible="detailVisible" :title="t('warehouse.inbound.drawer.detail')" :width="640">
|
<OrderPrintModal
|
||||||
<template v-if="detailData">
|
v-if="printVisible"
|
||||||
<p>{{ t('warehouse.inbound.table.noticeNo') }}: {{ detailData.noticeNo || detailData.NoticeNo }}</p>
|
v-model:visible="printVisible"
|
||||||
<p>{{ t('warehouse.inbound.table.markReference') }}: {{ detailData.markReference || detailData.MarkReference || '-' }}</p>
|
mode="single"
|
||||||
<div v-for="box in (detailData.boxes || detailData.Boxes || [])" :key="box.id || box.Id" class="box-block">
|
:items="printItems"
|
||||||
<div class="box-title">{{ t('warehouse.inbound.box') }}: {{ box.markBoxNo || box.MarkBoxNo || box.boxSeqNo || box.BoxSeqNo }}</div>
|
:preparing="printPreparing"
|
||||||
<a-table :data="box.lines || box.Lines || []" :pagination="false" size="small">
|
/>
|
||||||
<template #columns>
|
|
||||||
<a-table-column :title="t('warehouse.inbound.line.skuCode')" data-index="skuCode" />
|
|
||||||
<a-table-column :title="t('warehouse.inbound.line.plannedQty')" data-index="plannedQty" />
|
|
||||||
<a-table-column :title="t('warehouse.inbound.line.receivedQty')" data-index="receivedQty" />
|
|
||||||
</template>
|
|
||||||
</a-table>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</a-drawer>
|
|
||||||
|
|
||||||
<ModalPro
|
<ModalPro
|
||||||
:title="t('warehouse.inbound.modal.confirmReceive')"
|
:title="t('warehouse.inbound.modal.confirmReceive')"
|
||||||
@ -754,11 +916,11 @@ onMounted(async () => {
|
|||||||
</ModalPro>
|
</ModalPro>
|
||||||
|
|
||||||
<ModalPro
|
<ModalPro
|
||||||
:title="t('warehouse.inbound.modal.add')"
|
:title="formModalTitle"
|
||||||
:width="860"
|
:width="860"
|
||||||
:mask-closable="false"
|
:mask-closable="false"
|
||||||
@register="registerFormModal"
|
@register="registerFormModal"
|
||||||
@cancel="closeModal"
|
@cancel="closeFormModal"
|
||||||
>
|
>
|
||||||
<div class="wh-form-modal">
|
<div class="wh-form-modal">
|
||||||
<div class="wh-form-info-bar">
|
<div class="wh-form-info-bar">
|
||||||
@ -770,17 +932,33 @@ onMounted(async () => {
|
|||||||
|| formModel.warehouseId
|
|| formModel.warehouseId
|
||||||
|| '-'
|
|| '-'
|
||||||
}}
|
}}
|
||||||
<a-tooltip :content="t('warehouse.inbound.tip.currentWarehouse')">
|
<a-tooltip v-if="!formReadonly" :content="t('warehouse.inbound.tip.currentWarehouse')">
|
||||||
<icon-question-circle class="tip-icon" />
|
<icon-question-circle class="tip-icon" />
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-section-title">{{ t('warehouse.modal.section.basic') }}</div>
|
<div class="form-section-title">{{ t('warehouse.modal.section.basic') }}</div>
|
||||||
<a-form :model="formModel" layout="vertical" class="inbound-form-basic edit-form">
|
<div v-if="formReadonly" class="inbound-view-basic">
|
||||||
|
<div class="inbound-view-row">
|
||||||
|
<div class="inbound-view-field inbound-view-field--grow">
|
||||||
|
<div class="inbound-view-label">{{ t('warehouse.inbound.form.markReference') }}</div>
|
||||||
|
<div class="wh-form-readonly">{{ displayOrDash(formModel.markReference) }}</div>
|
||||||
|
</div>
|
||||||
|
<div class="inbound-view-field">
|
||||||
|
<div class="inbound-view-label">{{ t('warehouse.inbound.table.inboundType') }}</div>
|
||||||
|
<div class="wh-form-readonly">{{ formInboundTypeLabel }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="inbound-view-field">
|
||||||
|
<div class="inbound-view-label">{{ t('warehouse.parcel.form.remark') }}</div>
|
||||||
|
<div class="wh-form-readonly">{{ displayOrDash(formModel.remark) }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<a-form v-else :model="formModel" layout="vertical" class="inbound-form-basic edit-form">
|
||||||
<a-row :gutter="16">
|
<a-row :gutter="16">
|
||||||
<a-col :span="14">
|
<a-col :span="14">
|
||||||
<a-form-item>
|
<a-form-item :required="false">
|
||||||
<template #label>
|
<template #label>
|
||||||
<span class="label-with-tip">
|
<span class="label-with-tip">
|
||||||
{{ t('warehouse.inbound.form.markReference') }}
|
{{ t('warehouse.inbound.form.markReference') }}
|
||||||
@ -837,7 +1015,7 @@ onMounted(async () => {
|
|||||||
<icon-question-circle class="tip-icon" />
|
<icon-question-circle class="tip-icon" />
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
</span>
|
</span>
|
||||||
<a-space :size="8">
|
<a-space v-if="!formReadonly" :size="8">
|
||||||
<a-button size="mini" @click="addBox">
|
<a-button size="mini" @click="addBox">
|
||||||
<template #icon><IconPlus :size="12" /></template>
|
<template #icon><IconPlus :size="12" /></template>
|
||||||
{{ t('warehouse.inbound.actions.addBox') }}
|
{{ t('warehouse.inbound.actions.addBox') }}
|
||||||
@ -866,7 +1044,21 @@ onMounted(async () => {
|
|||||||
:key="box.key"
|
:key="box.key"
|
||||||
class="wh-form-box-panel inbound-box-panel"
|
class="wh-form-box-panel inbound-box-panel"
|
||||||
>
|
>
|
||||||
<div class="inbound-box-meta">
|
<div v-if="formReadonly" class="inbound-box-meta inbound-box-meta--view">
|
||||||
|
<div class="inbound-box-meta__field inbound-box-meta__field--mark">
|
||||||
|
<span class="inbound-box-meta__label">{{ t('warehouse.inbound.form.markBoxNo') }}</span>
|
||||||
|
<div class="wh-form-readonly">{{ displayOrDash(box.markBoxNo) }}</div>
|
||||||
|
</div>
|
||||||
|
<div class="inbound-box-meta__field inbound-box-meta__field--weight">
|
||||||
|
<span class="inbound-box-meta__label">{{ t('warehouse.inbound.form.boxWeight') }}</span>
|
||||||
|
<div class="wh-form-readonly">{{ formatBoxWeight(box) }}</div>
|
||||||
|
</div>
|
||||||
|
<div class="inbound-box-meta__field inbound-box-meta__field--size">
|
||||||
|
<span class="inbound-box-meta__label">{{ t('warehouse.inbound.form.boxSize') }}</span>
|
||||||
|
<div class="wh-form-readonly">{{ formatBoxSize(box) }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-else class="inbound-box-meta">
|
||||||
<div class="inbound-box-meta__field inbound-box-meta__field--mark">
|
<div class="inbound-box-meta__field inbound-box-meta__field--mark">
|
||||||
<span class="inbound-box-meta__label label-with-tip">
|
<span class="inbound-box-meta__label label-with-tip">
|
||||||
{{ t('warehouse.inbound.form.markBoxNo') }}
|
{{ t('warehouse.inbound.form.markBoxNo') }}
|
||||||
@ -925,7 +1117,7 @@ onMounted(async () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="inbound-sku-head">
|
<div v-if="!formReadonly" class="inbound-sku-head">
|
||||||
<span class="wh-form-hint label-with-tip">
|
<span class="wh-form-hint label-with-tip">
|
||||||
{{ t('warehouse.inbound.form.autoAddSkuRow') }}
|
{{ t('warehouse.inbound.form.autoAddSkuRow') }}
|
||||||
<a-tooltip :content="t('warehouse.inbound.tip.skuLines')">
|
<a-tooltip :content="t('warehouse.inbound.tip.skuLines')">
|
||||||
@ -935,6 +1127,32 @@ onMounted(async () => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<a-table
|
<a-table
|
||||||
|
v-if="formReadonly"
|
||||||
|
class="inbound-sku-table"
|
||||||
|
:data="viewSkuLines(box)"
|
||||||
|
:pagination="false"
|
||||||
|
size="small"
|
||||||
|
:bordered="{ cell: true }"
|
||||||
|
:scroll="{ x: '100%', y: 240 }"
|
||||||
|
>
|
||||||
|
<template #columns>
|
||||||
|
<a-table-column :title="'#'" :width="44">
|
||||||
|
<template #cell="{ rowIndex }">{{ rowIndex + 1 }}</template>
|
||||||
|
</a-table-column>
|
||||||
|
<a-table-column :title="t('warehouse.inbound.form.sku')" :min-width="220">
|
||||||
|
<template #cell="{ record }">
|
||||||
|
<span class="wh-form-readonly">{{ resolveSkuLabel(record) }}</span>
|
||||||
|
</template>
|
||||||
|
</a-table-column>
|
||||||
|
<a-table-column :title="t('warehouse.inbound.line.plannedQty')" :width="120">
|
||||||
|
<template #cell="{ record }">
|
||||||
|
<span class="wh-form-readonly">{{ record.plannedQty }}</span>
|
||||||
|
</template>
|
||||||
|
</a-table-column>
|
||||||
|
</template>
|
||||||
|
</a-table>
|
||||||
|
<a-table
|
||||||
|
v-else
|
||||||
class="inbound-sku-table"
|
class="inbound-sku-table"
|
||||||
:data="box.lines"
|
:data="box.lines"
|
||||||
:pagination="false"
|
:pagination="false"
|
||||||
@ -991,8 +1209,15 @@ onMounted(async () => {
|
|||||||
|
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<a-space :size="8">
|
<a-space :size="8">
|
||||||
<a-button @click="closeModal">{{ t('warehouse.common.cancel') }}</a-button>
|
<a-button @click="closeFormModal">{{ t('warehouse.common.cancel') }}</a-button>
|
||||||
<a-button type="primary" :loading="saving" @click="handleSave">{{ t('warehouse.common.save') }}</a-button>
|
<a-button
|
||||||
|
v-if="!formReadonly"
|
||||||
|
type="primary"
|
||||||
|
:loading="saving"
|
||||||
|
@click="handleSave"
|
||||||
|
>
|
||||||
|
{{ t('warehouse.common.save') }}
|
||||||
|
</a-button>
|
||||||
</a-space>
|
</a-space>
|
||||||
</template>
|
</template>
|
||||||
</ModalPro>
|
</ModalPro>
|
||||||
@ -1003,16 +1228,40 @@ onMounted(async () => {
|
|||||||
@import '@/styles/warehouse-list-page.less';
|
@import '@/styles/warehouse-list-page.less';
|
||||||
@import '@/styles/warehouse-form-modal.less';
|
@import '@/styles/warehouse-form-modal.less';
|
||||||
|
|
||||||
.box-block {
|
|
||||||
margin-bottom: 16px;
|
|
||||||
}
|
|
||||||
.box-title {
|
|
||||||
font-weight: 500;
|
|
||||||
margin-bottom: 8px;
|
|
||||||
}
|
|
||||||
.wh-form-box-tabs {
|
.wh-form-box-tabs {
|
||||||
margin-bottom: 12px;
|
margin-bottom: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.inbound-view-basic {
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inbound-view-row {
|
||||||
|
display: flex;
|
||||||
|
gap: 24px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inbound-view-field {
|
||||||
|
min-width: 140px;
|
||||||
|
|
||||||
|
&--grow {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.inbound-view-label {
|
||||||
|
margin-bottom: 4px;
|
||||||
|
font-size: 13px;
|
||||||
|
color: var(--color-text-3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.inbound-box-meta--view {
|
||||||
|
.inbound-box-meta__label {
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
.receive-box-block {
|
.receive-box-block {
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -69,7 +69,7 @@ const historyLoading = ref(false)
|
|||||||
const historyTab = ref<'all' | 'inbound' | 'deduct'>('all')
|
const historyTab = ref<'all' | 'inbound' | 'deduct'>('all')
|
||||||
const historyRows = ref<any[]>([])
|
const historyRows = ref<any[]>([])
|
||||||
const historyPage = ref(1)
|
const historyPage = ref(1)
|
||||||
const historypageSize = ref(50)
|
const historyPageSize = ref(50)
|
||||||
const historyTotal = ref(0)
|
const historyTotal = ref(0)
|
||||||
|
|
||||||
const [registerRelocateModal, { openModal: openRelocate, closeModal: closeRelocate }] = useModal() as any
|
const [registerRelocateModal, { openModal: openRelocate, closeModal: closeRelocate }] = useModal() as any
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, onMounted, reactive, ref } from 'vue'
|
import { computed, defineAsyncComponent, onMounted, reactive, ref } from 'vue'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
import { Modal } from '@arco-design/web-vue'
|
import { Modal } from '@arco-design/web-vue'
|
||||||
import { IconDelete, IconImage, IconPlus, IconQuestionCircle } from '@arco-design/web-vue/es/icon'
|
import { IconDelete, IconImage, IconPlus, IconQuestionCircle } from '@arco-design/web-vue/es/icon'
|
||||||
@ -11,6 +11,7 @@ import { useTableContainerHeight } from '@/composables/useTableContainerHeight'
|
|||||||
import {
|
import {
|
||||||
deleteWarehouseSku,
|
deleteWarehouseSku,
|
||||||
getWarehouseSkuList,
|
getWarehouseSkuList,
|
||||||
|
postWarehouseSkuPrintLabel,
|
||||||
saveWarehouseSku,
|
saveWarehouseSku,
|
||||||
uploadWarehouseSkuImage,
|
uploadWarehouseSkuImage,
|
||||||
} from '@/api/warehouse'
|
} from '@/api/warehouse'
|
||||||
@ -19,6 +20,7 @@ import { resolveOmsAssetUrl } from '@/api/oms/common'
|
|||||||
import { streamFileBlob } from '@/api/fileStorage'
|
import { streamFileBlob } from '@/api/fileStorage'
|
||||||
import { $baseMessage } from '@/utils'
|
import { $baseMessage } from '@/utils'
|
||||||
import { hasPermission } from '@/utils/rbac-permission'
|
import { hasPermission } from '@/utils/rbac-permission'
|
||||||
|
import { useHiprintClientState } from '@/utils/hiprint/hiprintClientState'
|
||||||
import {
|
import {
|
||||||
formatDateTime,
|
formatDateTime,
|
||||||
formatDimensions,
|
formatDimensions,
|
||||||
@ -35,6 +37,10 @@ import {
|
|||||||
|
|
||||||
defineOptions({ name: 'WarehouseSku' })
|
defineOptions({ name: 'WarehouseSku' })
|
||||||
|
|
||||||
|
const OrderPrintModal = defineAsyncComponent(
|
||||||
|
() => import('@/components/PrintTemplate/OrderPrintModal.vue'),
|
||||||
|
)
|
||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
const { tableContainerRef, tableHeight } = useTableContainerHeight()
|
const { tableContainerRef, tableHeight } = useTableContainerHeight()
|
||||||
const {
|
const {
|
||||||
@ -43,8 +49,13 @@ const {
|
|||||||
customerOptions,
|
customerOptions,
|
||||||
withCustomerId,
|
withCustomerId,
|
||||||
ensureMutationCustomerId,
|
ensureMutationCustomerId,
|
||||||
|
resolveSelectedCustomerId,
|
||||||
} = useWarehouseAdminCustomer()
|
} = useWarehouseAdminCustomer()
|
||||||
const currentWarehouseStore = useCurrentWarehouseStore()
|
const currentWarehouseStore = useCurrentWarehouseStore()
|
||||||
|
const printConnectionState = useHiprintClientState()
|
||||||
|
const printVisible = ref(false)
|
||||||
|
const printItems = ref<Array<{ id: number | string; label: string; url: string }>>([])
|
||||||
|
const printPreparing = ref(false)
|
||||||
|
|
||||||
const listLoading = ref(false)
|
const listLoading = ref(false)
|
||||||
const tableRows = ref<any[]>([])
|
const tableRows = ref<any[]>([])
|
||||||
@ -428,8 +439,56 @@ function handleDelete(row: any) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isUsablePrintUrl(raw: unknown) {
|
||||||
|
const value = String(raw ?? '').trim()
|
||||||
|
if (!value) return false
|
||||||
|
if (value.includes('获取中')) return false
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handlePrintLabel(row: any) {
|
||||||
|
if (!hasPermission('Warehouse.Sku.PrintLabel')) return
|
||||||
|
const id = Number(row?.id || 0)
|
||||||
|
if (!id) return
|
||||||
|
|
||||||
|
printVisible.value = false
|
||||||
|
printItems.value = []
|
||||||
|
printPreparing.value = true
|
||||||
|
try {
|
||||||
|
const adminCustomerId = isAdmin.value ? resolveSelectedCustomerId() ?? undefined : undefined
|
||||||
|
const { printUrl, skuCode } = await postWarehouseSkuPrintLabel({
|
||||||
|
id,
|
||||||
|
customerId: adminCustomerId,
|
||||||
|
})
|
||||||
|
if (!isUsablePrintUrl(printUrl)) {
|
||||||
|
$baseMessage(t('warehouse.sku.message.printEmpty'), 'warning')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
printItems.value = [
|
||||||
|
{
|
||||||
|
id,
|
||||||
|
label: skuCode || row.skuCode || String(id),
|
||||||
|
url: printUrl,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
printVisible.value = true
|
||||||
|
if (!printConnectionState.enabled || !printConnectionState.connected) {
|
||||||
|
$baseMessage('已打开 SKU 标签预览;静默打印需先在顶部连接打印开关', 'info')
|
||||||
|
}
|
||||||
|
} catch (e: any) {
|
||||||
|
$baseMessage(e?.message || t('warehouse.sku.message.printFailed'), 'error')
|
||||||
|
} finally {
|
||||||
|
printPreparing.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function actionRenderer() {
|
function actionRenderer() {
|
||||||
const parts: string[] = []
|
const parts: string[] = []
|
||||||
|
if (hasPermission('Warehouse.Sku.PrintLabel')) {
|
||||||
|
parts.push(
|
||||||
|
renderWarehouseActionBtn('printLabel', t('warehouse.sku.actions.printLabel'), 'print'),
|
||||||
|
)
|
||||||
|
}
|
||||||
if (hasPermission('Warehouse.Sku.Save')) {
|
if (hasPermission('Warehouse.Sku.Save')) {
|
||||||
parts.push(renderWarehouseActionBtn('edit', t('warehouse.common.edit')))
|
parts.push(renderWarehouseActionBtn('edit', t('warehouse.common.edit')))
|
||||||
}
|
}
|
||||||
@ -440,6 +499,7 @@ function actionRenderer() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function handleActionClick(row: any, _idx: number, action?: string) {
|
function handleActionClick(row: any, _idx: number, action?: string) {
|
||||||
|
if (action === 'printLabel') void handlePrintLabel(row)
|
||||||
if (action === 'edit') handleEdit(row)
|
if (action === 'edit') handleEdit(row)
|
||||||
if (action === 'delete') handleDelete(row)
|
if (action === 'delete') handleDelete(row)
|
||||||
}
|
}
|
||||||
@ -514,6 +574,14 @@ onMounted(async () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<OrderPrintModal
|
||||||
|
v-if="printVisible"
|
||||||
|
v-model:visible="printVisible"
|
||||||
|
mode="single"
|
||||||
|
:items="printItems"
|
||||||
|
:preparing="printPreparing"
|
||||||
|
/>
|
||||||
|
|
||||||
<ModalPro
|
<ModalPro
|
||||||
:title="modalTitle"
|
:title="modalTitle"
|
||||||
:width="720"
|
:width="720"
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user