فهرست منبع

新增自取订单批量取货功能

ouyang 3 هفته پیش
والد
کامیت
ca8e932a74
3فایلهای تغییر یافته به همراه377 افزوده شده و 51 حذف شده
  1. 368 51
      hdApp/src/admin/home/order.vue
  2. 8 0
      hdApp/src/api/order/index.js
  3. 1 0
      hdApp/src/static/icons/order-printer-green.svg

+ 368 - 51
hdApp/src/admin/home/order.vue

@@ -9,7 +9,7 @@
       </view>
     </view>
 
-    <view class="control-panel">
+    <view class="control-panel" @click="closeBatchMenu">
         <view class="filter-row">
           <view class="filter-chip date-chip">
             <image class="filter-icon" :src="iconSrc('order-calendar')" mode="aspectFit" />
@@ -32,6 +32,13 @@
             />
             <view v-if="customId != 0 || searchText" class="search-clear" @click.stop="clearFn">清空</view>
           </view>
+          <view class="batch-op-wrap">
+            <view class="batch-op-btn" @click.stop="toggleBatchMenu">操作</view>
+            <view v-if="showBatchMenu" class="batch-menu">
+              <view class="batch-menu-item" @click.stop="openBatchFetchModal">批量发货</view>
+              <view class="batch-menu-item batch-menu-close" @click.stop="closeBatchMenu">关闭</view>
+            </view>
+          </view>
           <view class="filter-btn" @click.stop="openFilterPanel">
             <image class="filter-icon filter-icon-lg" :src="iconSrc('order-filter')" mode="aspectFit" />
             <view v-if="hasActiveFilters" class="filter-dot"></view>
@@ -66,7 +73,7 @@
 
     <view class="list-wrap">
       <block v-if="!$util.isEmpty(list.data)">
-        <!-- 
+        <!--
           使用 item.id 作为唯一 key。
           注意:在 uni-app 小程序端,:key 表达式中不能包含逻辑运算符(如 ||),
           否则在事件传参(如 @click)时,小程序编译器生成的 getExtraValue 无法解析 key,
@@ -162,52 +169,70 @@
             <text class="merge-arrow">›</text>
           </view>
 
-          <view class="action-row">
-            <view
-              class="outline-action"
-              :class="{ disabled: !item._canSendWork }"
-              @click.stop="item._canSendWork ? sendWork(index) : ''"
-            >
-              <image
-                class="action-icon"
-                :class="{ 'action-icon-muted': !item._isPaid }"
-                :src="iconSrc('order-bell')"
-                mode="aspectFit"
-              />
-              <text>通知制作</text>
-            </view>
+          <view class="action-row" @click.stop="closeActionMoreMenu">
+            <view class="action-row-main">
+              <view
+                class="outline-action"
+                :class="{ disabled: !item._showDeliveryAction }"
+                @click.stop="item._showDeliveryAction ? handleDeliveryAction(index) : ''"
+              >
+                {{ item._deliveryActionText }}
+              </view>
 
-            <view class="outline-action" v-if="item._showFetchAction" @click.stop="getOrderFn(index)">已取货</view>
-            <view class="outline-action" v-if="item._showSelfDeliveryAction" @click.stop="sendOrderFn(index)">自配送</view>
-            <view class="outline-action" v-if="item._showConfirmReachAction" @click.stop="confirmReach(index)">确认送达</view>
+              <view
+                class="outline-action"
+                :class="{ disabled: !item._isPaid }"
+                @click.stop="item._isPaid ? printOrder(index) : ''"
+              >
+                <image
+                  class="action-icon"
+                  :class="{ 'action-icon-muted': !item._isPaid }"
+                  :src="iconSrc('order-printer-green')"
+                  mode="aspectFit"
+                />
+                <text>打印</text>
+              </view>
 
-            <view
-              class="solid-action"
-              :class="{ disabled: !item._isPaid }"
-              @click.stop="item._isPaid ? printOrder(index) : ''"
-            >
-              <image
-                class="action-icon action-icon-white"
-                :class="{ 'action-icon-muted': !item._isPaid }"
-                :src="iconSrc('order-printer')"
-                mode="aspectFit"
-              />
-              <text>打印</text>
+              <view
+                v-if="item._expressText"
+                class="outline-action"
+                :class="{ disabled: item._expressDisabled }"
+                @click.stop="item._expressDisabled ? '' : openExpressPage(item.id)"
+              >
+                <image
+                  class="action-icon"
+                  :class="{ 'action-icon-muted': item._expressDisabled }"
+                  :src="iconSrc('delivery-outline')"
+                  mode="aspectFit"
+                />
+                <text>{{ item._expressText }}</text>
+              </view>
             </view>
 
-            <view
-              v-if="item._expressText"
-              class="outline-action"
-              :class="{ disabled: item._expressDisabled }"
-              @click.stop="item._expressDisabled ? '' : openExpressPage(item.id)"
-            >
-              <image
-                class="action-icon"
-                :class="{ 'action-icon-muted': item._expressDisabled }"
-                :src="iconSrc('delivery-outline')"
-                mode="aspectFit"
-              />
-              <text>{{ item._expressText }}</text>
+            <view class="action-more-wrap">
+              <view class="outline-action" @click.stop="toggleActionMore(index)">更多</view>
+              <view v-if="activeMoreIndex === index" class="action-more-menu" @click.stop>
+                <view
+                  class="action-more-item"
+                  :class="{ disabled: !item._canSendWork }"
+                  @click.stop="handleMoreSendWork(index)"
+                >
+                  <image
+                    class="action-icon"
+                    :class="{ 'action-icon-muted': !item._canSendWork }"
+                    :src="iconSrc('order-bell')"
+                    mode="aspectFit"
+                  />
+                  <text>通知制作</text>
+                </view>
+                <view
+                  class="action-more-item"
+                  :class="{ disabled: !item._showConfirmReachAction }"
+                  @click.stop="handleMoreConfirmReach(index)"
+                >
+                  确认送达
+                </view>
+              </view>
             </view>
           </view>
         </view>
@@ -227,6 +252,17 @@
       </view>
     </uni-popup>
 
+    <view v-if="showBatchFetchModal" class="batch-fetch-overlay" @click="closeBatchFetchModal">
+      <view class="batch-fetch-modal" @click.stop>
+        <view class="batch-fetch-close" @click="closeBatchFetchModal">×</view>
+        <view class="batch-fetch-content">待发货订单是否全部变更为配送中?</view>
+        <view class="batch-fetch-actions">
+          <view class="batch-fetch-btn cancel-btn" @click="closeBatchFetchModal">取消</view>
+          <view class="batch-fetch-btn confirm-btn" @click="confirmBatchFetch">确定</view>
+        </view>
+      </view>
+    </view>
+
     <view v-if="showFilterPanel" class="filter-overlay" @click="closeFilterPanel">
       <view class="filter-panel" @click.stop>
         <view class="filter-header">筛选订单</view>
@@ -271,7 +307,7 @@
 <script>
 import AppWrapperEmpty from "@/components/app-wrapper-empty";
 import { list } from "@/mixins";
-import { onlinePrintOrder, getListB, sendOrder, fetchOrder, sendReach } from "@/api/order";
+import { onlinePrintOrder, getListB, sendOrder, fetchOrder, sendReach, batchFetchOrder } from "@/api/order";
 import NotLogin from "@/components/not-login";
 import { getStaffList } from "@/api/staff";
 import AppSearchModule from "@/components/module/app-search";
@@ -340,6 +376,10 @@ export default {
       lastSearchText: "",
       orderListRequestId: 0,
       showFilterPanel: false,
+      showBatchMenu: false,
+      showBatchFetchModal: false,
+      batchFetchLoading: false,
+      activeMoreIndex: -1,
       repeatOptions: [
         { label: "全部", value: -1 },
         { label: "扫码单", value: 1 }
@@ -409,12 +449,107 @@ export default {
         this.statusBarHeight = 0;
       }
     },
+    /**
+     * 切换订单卡片「更多」操作菜单
+     * @param {number} index - 订单列表索引
+     */
+    toggleActionMore (index) {
+      this.activeMoreIndex = this.activeMoreIndex === index ? -1 : index;
+    },
+    /**
+     * 关闭订单卡片「更多」操作菜单
+     */
+    closeActionMoreMenu () {
+      this.activeMoreIndex = -1;
+    },
+    /**
+     * 更多菜单:通知制作
+     * @param {number} index - 订单列表索引
+     */
+    handleMoreSendWork (index) {
+      const item = this.list.data[index];
+      if (!item || !item._canSendWork) {
+        return;
+      }
+      this.closeActionMoreMenu();
+      this.sendWork(index);
+    },
+    /**
+     * 更多菜单:确认送达
+     * @param {number} index - 订单列表索引
+     */
+    handleMoreConfirmReach (index) {
+      const item = this.list.data[index];
+      if (!item || !item._showConfirmReachAction) {
+        return;
+      }
+      this.closeActionMoreMenu();
+      this.confirmReach(index);
+    },
     openFilterPanel () {
+      this.closeActionMoreMenu();
+      this.closeBatchMenu();
       this.showFilterPanel = true;
     },
     closeFilterPanel () {
       this.showFilterPanel = false;
     },
+    /**
+     * 切换批量操作下拉菜单
+     */
+    toggleBatchMenu () {
+      this.showBatchMenu = !this.showBatchMenu;
+    },
+    /**
+     * 关闭批量操作下拉菜单
+     */
+    closeBatchMenu () {
+      this.showBatchMenu = false;
+    },
+    /**
+     * 打开批量发货确认弹窗
+     */
+    openBatchFetchModal () {
+      this.closeBatchMenu();
+      this.showBatchFetchModal = true;
+    },
+    /**
+     * 关闭批量发货确认弹窗
+     */
+    closeBatchFetchModal () {
+      if (this.batchFetchLoading) {
+        return;
+      }
+      this.showBatchFetchModal = false;
+    },
+    /**
+     * 确认批量发货:待发货订单全部变更为配送中
+     */
+    confirmBatchFetch () {
+      if (this.batchFetchLoading) {
+        return;
+      }
+      this.batchFetchLoading = true;
+      batchFetchOrder({
+        searchTime: this.searchTime,
+        startTime: this.startTime,
+        endTime: this.endTime,
+        shopAdminId: this.shopAdminId,
+        searchText: this.searchText,
+        customId: this.customId,
+        repeat: this.repeat,
+        searchStyle: this.searchStyle
+      }).then(res => {
+        if (res.code == 1) {
+          this.$msg(res.msg || "操作成功");
+          this.showBatchFetchModal = false;
+          this.resetList();
+          this.getOrderList();
+        }
+      }).finally(() => {
+        this.batchFetchLoading = false;
+      });
+    },
     selectRepeat (value) {
       this.repeat = value;
     },
@@ -504,6 +639,21 @@ export default {
         });
       });
     },
+    /**
+     * 取货/自配送:到店自取走取货,配送单走自配送
+     * @param {number} index - 订单列表中的索引
+     */
+    handleDeliveryAction (index) {
+      const item = this.list.data[index];
+      if (!item || Number(item.status) !== 2) {
+        return;
+      }
+      if (Number(item.sendType) === 1) {
+        this.getOrderFn(index);
+        return;
+      }
+      this.sendOrderFn(index);
+    },
     /**
      * 确认取货
      * @param {number} index - 订单列表中的索引。传递索引而非 item 对象,是为了避免微信小程序端在 v-for 循环中直接传递对象导致 getExtraValue 报错。
@@ -646,6 +796,8 @@ export default {
       if (this.tabIndex == index) {
         return false;
       }
+      this.closeBatchMenu();
+      this.closeActionMoreMenu();
       this.tabIndex = index;
       this.resetList();
       this.getOrderList();
@@ -709,8 +861,9 @@ export default {
         _actPriceText: this.formatMoney(item.actPrice),
         _isPaid: isPaid,
         _canSendWork: isPaid && (Number(item.hasWork) === 0 || Number(item.hasWork) === 2),
-        _showFetchAction: status === 2 && sendType === 1,
-        _showSelfDeliveryAction: status === 2 && sendType !== 1,
+        // 待发货时展示取货/配送操作:到店自取显示「已取货」,配送单显示「自配送」,二者互斥
+        _showDeliveryAction: status === 2,
+        _deliveryActionText: sendType === 1 ? "已取货" : "自配送",
         _showConfirmReachAction: status === 3,
         _expressText: express.text,
         _expressDisabled: express.disabled,
@@ -734,7 +887,7 @@ export default {
       if (SENT_EXPRESS_STATUS_MAP[sendStatus]) {
         return { text: "已发跑腿", disabled: true, iconColor: "#09C567" };
       }
-      return { text: "", disabled: true, iconColor: "#09C567" };
+      return { text: "呼叫跑腿", disabled: true, iconColor: "#09C567" };
     },
     orderStatusText (item) {
       if (item.status == 2 && item.sendType == 1) {
@@ -872,6 +1025,121 @@ export default {
   min-width: 0;
 }
 
+.batch-op-wrap {
+  position: relative;
+  flex-shrink: 0;
+}
+
+.batch-op-btn {
+  height: 72upx;
+  padding: 0 14upx;
+  border: 1upx solid #09c567;
+  border-radius: 16upx;
+  background: #fff;
+  color: #09c567;
+  font-size: 24upx;
+  font-weight: 700;
+  line-height: 72upx;
+  white-space: nowrap;
+  box-sizing: border-box;
+}
+
+.batch-menu {
+  position: absolute;
+  right: 0;
+  top: 82upx;
+  z-index: 50;
+  min-width: 180upx;
+  padding: 8upx 0;
+  border: 1upx solid #09c567;
+  border-radius: 16upx;
+  background: #fff;
+  box-shadow: 0 12upx 28upx rgba(22, 33, 28, 0.12);
+}
+
+.batch-menu-item {
+  padding: 18upx 28upx;
+  color: #09c567;
+  font-size: 28upx;
+  font-weight: 700;
+  text-align: center;
+  white-space: nowrap;
+}
+
+.batch-menu-close {
+  color: #666;
+  font-weight: 500;
+}
+
+.batch-fetch-overlay {
+  position: fixed;
+  left: 0;
+  right: 0;
+  top: 0;
+  bottom: 0;
+  z-index: 120;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  background: rgba(0, 0, 0, 0.45);
+}
+
+.batch-fetch-modal {
+  position: relative;
+  width: 620upx;
+  padding: 56upx 40upx 40upx;
+  border: 2upx solid #09c567;
+  border-radius: 24upx;
+  background: #fff;
+  box-sizing: border-box;
+}
+
+.batch-fetch-close {
+  position: absolute;
+  right: 24upx;
+  top: 16upx;
+  width: 48upx;
+  height: 48upx;
+  color: #999;
+  font-size: 40upx;
+  line-height: 48upx;
+  text-align: center;
+}
+
+.batch-fetch-content {
+  padding: 20upx 10upx 48upx;
+  color: #333;
+  font-size: 34upx;
+  line-height: 48upx;
+  text-align: center;
+}
+
+.batch-fetch-actions {
+  display: flex;
+  align-items: center;
+}
+
+.batch-fetch-btn {
+  flex: 1;
+  height: 80upx;
+  border-radius: 12upx;
+  font-size: 32upx;
+  font-weight: 700;
+  line-height: 80upx;
+  text-align: center;
+}
+
+.batch-fetch-btn.cancel-btn {
+  margin-right: 20upx;
+  background: #b7bfc0;
+  color: #fff;
+}
+
+.batch-fetch-btn.confirm-btn {
+  background: #09c567;
+  color: #fff;
+}
+
 .search-box ::v-deep .app-search-module .map-search {
   height: 72upx !important;
   border-radius: 16upx;
@@ -1221,12 +1489,61 @@ export default {
 
 .action-row {
   display: flex;
-  justify-content: flex-end;
-  flex-wrap: nowrap;
+  align-items: center;
+  justify-content: space-between;
   margin-top: 22upx;
+}
+
+.action-row-main {
+  display: flex;
+  flex: 1;
+  justify-content: flex-end;
+  min-width: 0;
   overflow-x: auto;
 }
 
+.action-row-main > view + view {
+  margin-left: 8upx;
+}
+
+.action-more-wrap {
+  position: relative;
+  flex-shrink: 0;
+  margin-left: 8upx;
+}
+
+.action-more-menu {
+  position: absolute;
+  right: 0;
+  bottom: 82upx;
+  z-index: 20;
+  min-width: 200upx;
+  padding: 8upx 0;
+  border: 1upx solid #09c567;
+  border-radius: 16upx;
+  background: #fff;
+  box-shadow: 0 12upx 28upx rgba(22, 33, 28, 0.12);
+}
+
+.action-more-item {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  padding: 18upx 24upx;
+  color: #09c567;
+  font-size: 26upx;
+  font-weight: 700;
+  white-space: nowrap;
+}
+
+.action-more-item text {
+  margin-left: 6upx;
+}
+
+.action-more-item.disabled {
+  color: #b7bfc0;
+}
+
 .action-row > view + view {
   margin-left: 8upx;
 }
@@ -1455,4 +1772,4 @@ export default {
   color: #fff;
   background: #09c567;
 }
-</style>
+</style>

+ 8 - 0
hdApp/src/api/order/index.js

@@ -27,6 +27,14 @@ export const fetchOrder = data => {
 	return https.get("/order/fetch", data);
 };
 
+/**
+ * 批量发货:自取待发货订单全部变更为配送中
+ * @param {Object} data - 可选筛选参数,与列表查询保持一致
+ */
+export const batchFetchOrder = data => {
+	return https.post("/order/batch-fetch", data);
+};
+
 export const onlinePrintOrder = data => {
 	return https.get("/order/print-order", data);
 };

+ 1 - 0
hdApp/src/static/icons/order-printer-green.svg

@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1783474302342" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5212" xmlns:xlink="http://www.w3.org/1999/xlink" width="256" height="256"><path d="M786.3 382.5m-40.9 0a40.9 40.9 0 1 0 81.8 0 40.9 40.9 0 1 0-81.8 0Z" fill="#09c567" p-id="5213"></path><path d="M125.4 689.9V649v16.2c0.8 13.8 12.2 24.7 26.2 24.7h-26.2zM266.4 888.9V848v16.2c0.8 13.8 12.2 24.7 26.2 24.7h-26.2zM886.4 689.7h-40.9 16.2c13.8-0.8 24.7-12.2 24.7-26.2v26.2zM745.8 888.4h-40.9 16.2c13.8-0.8 24.7-12.2 24.7-26.2v26.2zM745.5 139.8v40.9-16.2c-0.8-13.8-12.2-24.7-26.2-24.7h26.2zM745.5 619.9v40.9-16.2c-0.8-13.8-12.2-24.7-26.2-24.7h26.2zM265.9 139.8h40.9-16.2c-13.8 0.8-24.7 12.2-24.7 26.2v-26.2zM266 619.7h41-16.2c-13.8 0.9-24.8 12.3-24.8 26.2v-26.2zM125.4 289.1h40.9-16.2c-13.8 0.8-24.7 12.2-24.7 26.2v-26.2zM886.1 289.2v40.9-16.2c-0.8-13.8-12.2-24.7-26.2-24.7h26.2z" fill="#09c567" p-id="5214"></path><path d="M967.8 710.1V266c-4.7-33-33.1-58.5-67.4-58.5h-73.1v-89.6c-3.9-32.8-31.1-58.4-64.5-59.8H250.2c-34.8 1-63.1 28.2-65.8 62.6v86.8h-78.8c-33.2 3-59.5 29.7-61.8 63.1v438.7c2.4 31.7 26.4 57.3 57.4 62.1h83.2v133.1c1.2 34.8 28.5 62.9 62.8 65.5h519.2c32.6-3.3 58.4-29.7 60.9-62.6v-136h80.5c31.8-3.6 57-29.2 60-61.3zM266.3 140h479.1v67.5H266.3V140z m479.1 590.4V888H266.3V620h479.1v110.4z m140.5-40.9h-58.6v-92c-3.9-31.9-29.8-56.9-62.1-59.4H248.3c-34.4 2-61.9 29.6-63.9 64v87.4h-58.6V289.4H886v400.1z" fill="#09c567" p-id="5215"></path></svg>