From 3b665f6d8cc6eb08a2de6849dd77e24a72b49793 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=94=B0=E9=A6=99=E7=90=AA?= <624506849@qq.com> Date: Thu, 13 Aug 2026 17:19:06 +0800 Subject: [PATCH] =?UTF-8?q?feat(orderFulfill):=20=E5=A2=9E=E5=BC=BA?= =?UTF-8?q?=E8=AE=A2=E5=8D=95=E5=8F=91=E8=B4=A7=E9=80=BB=E8=BE=91=E4=B8=8E?= =?UTF-8?q?=E7=95=8C=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增无需配送场景及发货模式相关工具函数 - 更新订单详情与订单管理页,支持整单发货、分包裹发货、无需配送 - 优化物流信息与订单日志展示 - 重构订单发货 API 以适配新逻辑 - 增强订单列表与详情页的发货选项与操作展示 --- .../pages/home/orderCenter/OrderDetail.vue | 92 ++- buyer/src/pages/payment/Pay.vue | 2 +- buyer/src/utils/orderFulfill.js | 18 + seller/src/api/order.js | 15 +- seller/src/api/shops.js | 4 +- seller/src/utils/orderFulfill.js | 72 +++ seller/src/views/order/order/orderDetail.vue | 577 +++++++++++------- seller/src/views/order/order/orderList.vue | 27 +- 8 files changed, 549 insertions(+), 258 deletions(-) create mode 100644 buyer/src/utils/orderFulfill.js create mode 100644 seller/src/utils/orderFulfill.js diff --git a/buyer/src/pages/home/orderCenter/OrderDetail.vue b/buyer/src/pages/home/orderCenter/OrderDetail.vue index 1ce29e45..b66e9536 100644 --- a/buyer/src/pages/home/orderCenter/OrderDetail.vue +++ b/buyer/src/pages/home/orderCenter/OrderDetail.vue @@ -72,23 +72,29 @@

配送信息

-

配送方式:{{ order.deliveryMethodValue }}

-

配送状态:{{ order.deliverStatusValue }}

-

- 物流信息:{{ logistics.shipper || "暂无物流信息" }} -

-

- 物流单号:{{ logistics.logisticCode || "暂无物流单号" }} -

-
-
-

订单日志:

-
- {{ item.AcceptTime }} - {{ item.AcceptStation }} +

配送方式:{{ displayDeliveryMethodValue }}

+

配送状态:{{ order.deliverStatusValue }}

+ +

发票信息

@@ -313,12 +319,28 @@ - - - - - - + +
+ + 整单发货 + 拆单发货 + 无需配送 + +
+
继续拆单发货
+ + + + + + - - - - + +
- - - + + + + + + + + + + + + + + + + + + + + + + + + + + +
暂无可发货商品
+
已有包裹
- - + + + + + + +
@@ -611,6 +710,21 @@ :key="packageIndex" >
+ +
  • @@ -628,7 +743,7 @@
-
+
  • {{ item.AcceptTime || item.acceptTime }} @@ -666,9 +781,8 @@ - + - - - - - - - - - - @@ -744,6 +837,13 @@ import { cardKeyFulfillAlertType, resolveCardKeyFulfillMessage, } from "@/constants/goodsType"; +import { + validateFulfillForm, + isNoDeliveryPackage, + getFulfillmentModeLabel, + resolveDefaultFulfillmentMode, + canShowShipButton, +} from "@/utils/orderFulfill"; export default { name: "orderDetail", components: { @@ -764,9 +864,8 @@ export default { faceSheetFormValidate: { logisticsId: [{ required: true, message: "请选择物流公司" }], }, - facesheetFlag: false, logisticsModal: false, - orderDeliverModal: false, + faceSheetModal: false, logisticsInfo: { shipper: "", }, @@ -792,16 +891,14 @@ export default { modifyRemarkForm: { sellerRemark: "", }, - //订单发货 - orderDeliveryForm: { - logisticsNo: "", //发货单号 - logisticsId: "", //物流公司 - }, - partDeliveryModal: false, - partDeliverySubmitting: false, - partDeliveryForm: { + fulfillModal: false, + fulfillSubmitting: false, + autoOpenFulfill: true, + fulfillForm: { + fulfillmentMode: "WHOLE", logisticsNo: "", logisticsId: "", + deliveryRemark: "", selectedOrderItemIds: [], deliveryNumMap: {}, }, @@ -835,11 +932,7 @@ export default { consigneeAddressPath: "", consigneeAddressIdPath: "", }, - orderDeliverFormValidate: { - logisticsNo: [{ required: true, message: "发货单号不能为空", trigger: "change" }], - logisticsId: [{ required: true, message: "请选择物流公司", trigger: "blur" }], - }, - partDeliveryRules: { + fulfillRules: { logisticsNo: [{ required: true, message: "发货单号不能为空", trigger: "change" }], logisticsId: [{ required: true, message: "请选择物流公司", trigger: "change" }], }, @@ -888,14 +981,14 @@ export default { ecouponCardKeyRows() { return flattenOrderCardKeys(this.data, true); }, - canPartDelivery() { - if (!this.allowOperation.ship) return false; - const status = this.orderInfo.order && this.orderInfo.order.orderStatus; - return ( - !this.isNonPhysicalOrder && - ["UNDELIVERED", "PARTS_DELIVERED"].includes(status) && - this.deliverableOrderItems.length > 0 - ); + canShip() { + return canShowShipButton(this.allowOperation, this.orderInfo?.order); + }, + showFulfillModeSelector() { + const status = this.orderInfo?.order?.orderStatus; + if (status === "PARTS_DELIVERED") return false; + const op = this.allowOperation || {}; + return [op.fulfillWhole, op.fulfillSplit, op.fulfillNoDelivery].filter(Boolean).length > 1; }, deliverableOrderItems() { return (this.data || []).filter((item) => { @@ -1011,16 +1104,16 @@ export default { return pkgKeys.some((key) => orderKeys.includes(key)); }, getRemainingDeliveryNum(item) { + if (item != null && item.remainDeliverNumber != null && item.remainDeliverNumber !== "") { + const remain = Number(item.remainDeliverNumber); + if (Number.isFinite(remain)) return Math.max(remain, 0); + } const total = Number(item && item.num) || 0; return Math.max(total - this.getShippedPackageNum(item), 0); }, - getPartDeliverySubtotal(row) { - const id = this.getOrderItemId(row); - const qty = Number(this.partDeliveryForm.deliveryNumMap[id]) || 0; - const unitPrice = Number(row.unitPrice) || 0; - return unitPrice * qty; - }, - initPartDeliveryNumMap() { + isNoDeliveryPackage, + getFulfillmentModeLabel, + initFulfillNumMap() { const map = {}; this.deliverableOrderItems.forEach((item) => { const id = this.getOrderItemId(item); @@ -1028,10 +1121,10 @@ export default { map[id] = this.getRemainingDeliveryNum(item); } }); - this.partDeliveryForm.deliveryNumMap = map; + this.fulfillForm.deliveryNumMap = map; }, - refreshPartDeliveryAfterSubmit() { - this.partDeliveryModal = false; + refreshFulfillAfterSubmit() { + this.fulfillModal = false; this.loading = true; return Promise.all([ API_Order.getOrderDetail(this.sn).then((res) => { @@ -1049,80 +1142,132 @@ export default { this.getOrderPackages(), ]).finally(() => { this.loading = false; - this.partDeliveryForm.logisticsNo = ""; - this.partDeliveryForm.logisticsId = ""; - this.partDeliveryForm.selectedOrderItemIds = []; - this.initPartDeliveryNumMap(); + this.resetFulfillForm(false); + this.initFulfillNumMap(); this.$nextTick(() => { - this.$refs.partDeliveryTable?.clearSelection(); - this.$refs.partDeliveryForm?.clearValidate(); + this.$refs.fulfillTable?.clearSelection(); + this.$refs.fulfillFormRef?.clearValidate(); }); }); }, - handlePartDeliveryNumChange(row, value) { + handleFulfillNumChange(row, value) { const id = this.getOrderItemId(row); if (!id) return; const num = Number(value); if (!Number.isFinite(num) || num < 1) return; - if (!this.partDeliveryForm.selectedOrderItemIds.includes(id)) { - this.partDeliveryForm.selectedOrderItemIds = [ - ...this.partDeliveryForm.selectedOrderItemIds, - id, - ]; + if (!this.fulfillForm.selectedOrderItemIds.includes(id)) { + this.fulfillForm.selectedOrderItemIds = [...this.fulfillForm.selectedOrderItemIds, id]; this.$nextTick(() => { - this.$refs.partDeliveryTable?.toggleRowSelection(row, true); + this.$refs.fulfillTable?.toggleRowSelection(row, true); }); } }, isPartDeliveryRowSelectable(row) { return this.getRemainingDeliveryNum(row) > 0; }, - handlePartDeliverySelectionChange(selection) { + handleFulfillSelectionChange(selection) { const selectedIds = []; selection.forEach((item) => { const id = this.getOrderItemId(item); if (!id) return; selectedIds.push(id); - if (this.partDeliveryForm.deliveryNumMap[id] == null) { - this.partDeliveryForm.deliveryNumMap[id] = this.getRemainingDeliveryNum(item); + if (this.fulfillForm.deliveryNumMap[id] == null) { + this.fulfillForm.deliveryNumMap[id] = this.getRemainingDeliveryNum(item); } }); - this.partDeliveryForm.selectedOrderItemIds = selectedIds; + this.fulfillForm.selectedOrderItemIds = selectedIds; }, - isPartDeliveryItemSelected(item) { - const id = this.getOrderItemId(item); - return !!id && this.partDeliveryForm.selectedOrderItemIds.includes(id); - }, - buildPartDeliveryDTOList() { - return this.partDeliveryForm.selectedOrderItemIds + buildFulfillItems() { + return this.fulfillForm.selectedOrderItemIds .map((orderItemId) => ({ orderItemId, - deliveryNum: Number(this.partDeliveryForm.deliveryNumMap[orderItemId]), + deliveryNum: Number(this.fulfillForm.deliveryNumMap[orderItemId]), })) .filter((item) => item.orderItemId && item.deliveryNum > 0); }, - resetPartDeliveryForm() { - this.partDeliveryForm = { + resetFulfillForm(clearMode = true) { + const mode = clearMode + ? resolveDefaultFulfillmentMode( + this.allowOperation, + this.orderInfo?.order?.orderStatus + ) || "WHOLE" + : this.fulfillForm.fulfillmentMode; + this.fulfillForm = { + fulfillmentMode: mode, logisticsNo: "", logisticsId: "", + deliveryRemark: "", selectedOrderItemIds: [], deliveryNumMap: {}, }; this.$nextTick(() => { - if (this.$refs.partDeliveryForm) { - this.$refs.partDeliveryForm.clearValidate(); - } - if (this.$refs.partDeliveryTable) { - this.$refs.partDeliveryTable.clearSelection(); - } + this.$refs.fulfillFormRef?.clearValidate(); + this.$refs.fulfillTable?.clearSelection(); }); }, - openPartDelivery() { - this.resetPartDeliveryForm(); - this.partDeliveryModal = true; + openFulfill(presetMode) { + this.resetFulfillForm(true); + if (presetMode === "SPLIT" || this.$route.query.fulfillMode === "SPLIT") { + this.fulfillForm.fulfillmentMode = "SPLIT"; + } else { + const mode = resolveDefaultFulfillmentMode( + this.allowOperation, + this.orderInfo?.order?.orderStatus + ); + if (mode) this.fulfillForm.fulfillmentMode = mode; + } + this.fulfillModal = true; this.getCheckedLogistics(); this.getOrderPackages(); }, + buildFulfillPayload() { + const mode = this.fulfillForm.fulfillmentMode; + const payload = { fulfillmentMode: mode }; + if (mode === "WHOLE" || mode === "SPLIT") { + payload.logisticsId = this.fulfillForm.logisticsId; + payload.logisticsNo = this.fulfillForm.logisticsNo; + } + if (mode === "NO_DELIVERY") { + const remark = (this.fulfillForm.deliveryRemark || "").trim(); + if (remark) payload.deliveryRemark = remark; + } + if (mode === "SPLIT") { + payload.items = this.buildFulfillItems(); + } + return payload; + }, + submitFulfill() { + const mode = this.fulfillForm.fulfillmentMode; + const payload = this.buildFulfillPayload(); + const validateMsg = validateFulfillForm(mode, payload); + if (validateMsg) { + this.$Message.error(validateMsg); + return; + } + if (mode !== "NO_DELIVERY") { + this.$refs.fulfillFormRef.validate((valid) => { + if (!valid) return; + this.doSubmitFulfill(payload); + }); + } else { + this.doSubmitFulfill(payload); + } + }, + doSubmitFulfill(payload) { + this.fulfillSubmitting = true; + API_Order.orderFulfill(this.sn, payload) + .then((res) => { + if (res.success) { + this.$Message.success("发货成功"); + this.refreshFulfillAfterSubmit(); + } else if (res.code === 1003) { + this.$Message.warning(res.message || "访问过于频繁,请稍后再试"); + } + }) + .finally(() => { + this.fulfillSubmitting = false; + }); + }, getCheckedLogistics() { API_Order.getLogisticsChecked().then((res) => { if (res.success) { @@ -1135,33 +1280,7 @@ export default { if (res.success) { this.orderPackages = Array.isArray(res.result) ? res.result : []; } - this.initPartDeliveryNumMap(); - }); - }, - submitPartDelivery() { - this.$refs.partDeliveryForm.validate((valid) => { - if (!valid) return; - const partDeliveryDTOList = this.buildPartDeliveryDTOList(); - if (!partDeliveryDTOList.length) { - this.$Message.error("请选择本次发货商品并填写发货数量"); - return; - } - this.partDeliverySubmitting = true; - API_Order.partDelivery(this.sn, { - orderSn: this.sn, - logisticsNo: this.partDeliveryForm.logisticsNo, - logisticsId: this.partDeliveryForm.logisticsId, - partDeliveryDTOList, - }) - .then((res) => { - if (res.success) { - this.$Message.success("发货成功"); - this.refreshPartDeliveryAfterSubmit(); - } - }) - .finally(() => { - this.partDeliverySubmitting = false; - }); + this.initFulfillNumMap(); }); }, @@ -1180,6 +1299,14 @@ export default { : []; this.getContentPrice(); this.getOrderPrice(); + if ( + this.autoOpenFulfill && + this.canShip && + (this.$route.query.openFulfill || this.$route.query.fulfillMode) + ) { + this.autoOpenFulfill = false; + this.$nextTick(() => this.openFulfill(this.$route.query.fulfillMode)); + } } }); }, @@ -1240,11 +1367,10 @@ export default { }); }, toPrint () { - this.facesheetFlag = true; API_Logistics.getCheckedOn().then((res) => { if (res.success) { this.checkedLogistics = res.result; - this.orderDeliverModal = true; + this.faceSheetModal = true; } }); }, @@ -1275,7 +1401,7 @@ export default { }); }, toPrints (printWindow) { - this.orderDeliverModal = false; + this.faceSheetModal = false; if (!this.someJSONdata) { printWindow && printWindow.close(); return; @@ -1290,39 +1416,26 @@ export default { win.focus(); this.$Message.success("电子面单发货成功"); this.getDataDetail(); + this.getOrderPackages(); }, - orderDeliverySubmit () { - if (this.facesheetFlag) { - this.$refs.faceSheetForm.validate((valid) => { - if (valid) { - const printWindow = window.open("", "_blank"); - API_Order.getOrderFaceSheet(this.sn, this.faceSheetForm).then((res) => { - if (res.success && res.result.printTemplate) { - this.someJSONdata = res.result.printTemplate; - this.toPrints(printWindow); - } else { - printWindow && printWindow.close(); - this.$Message.error("电子面单发货失败!"); - } - }).catch(() => { + submitFaceSheet () { + this.$refs.faceSheetForm.validate((valid) => { + if (valid) { + const printWindow = window.open("", "_blank"); + API_Order.getOrderFaceSheet(this.sn, this.faceSheetForm).then((res) => { + if (res.success && res.result.printTemplate) { + this.someJSONdata = res.result.printTemplate; + this.toPrints(printWindow); + } else { printWindow && printWindow.close(); this.$Message.error("电子面单发货失败!"); - }); - } - }); - } else { - this.$refs.orderDeliveryForm.validate((valid) => { - if (valid) { - API_Order.orderDelivery(this.sn, this.orderDeliveryForm).then((res) => { - if (res.success) { - this.$Message.success("订单发货成功"); - this.orderDeliverModal = false; - this.getDataDetail(); - } - }); - } - }); - } + } + }).catch(() => { + printWindow && printWindow.close(); + this.$Message.error("电子面单发货失败!"); + }); + } + }); }, getLogisticsSetting () { API_Logistics.getLogisticsSetting().then((res) => { @@ -1605,6 +1718,24 @@ export default { font-weight: bold; } +.fulfill-mode-selector { + margin-bottom: 16px; +} + +.fulfill-mode-tip { + margin-bottom: 12px; + color: #606266; +} + +.package-item-line { + line-height: 1.8; +} + +.delivery-remark-text { + color: #909399; + margin-left: 4px; +} + .flex-card { display: flex; height: 600px; diff --git a/seller/src/views/order/order/orderList.vue b/seller/src/views/order/order/orderList.vue index bcaa04da..a7027bd1 100644 --- a/seller/src/views/order/order/orderList.vue +++ b/seller/src/views/order/order/orderList.vue @@ -108,9 +108,14 @@ - + @@ -133,6 +138,7 @@