فهرست منبع

零售与批发的收银菜单下,结算按钮添加跑腿报价与跑腿发单功能

shizhongqi 7 ماه پیش
والد
کامیت
658534d26e

+ 103 - 31
ghsApp/src/admin/billing/affirm.vue

@@ -285,26 +285,41 @@
 
               <view v-show="displaySendCost == true">
                 <view v-if="openIntraCity == 1" class="delivery-section">
-                  <tui-list-cell v-if="openIntraCity == 1" class="line-cell" :hover="false">
-                    <!-- 跑腿平台报价列表 -->
+                  <!-- 客户地址信息 -->
+                  <tui-list-cell class="line-cell" :hover="false" :arrow="true"  @click.stop="updateCustomAddress()">
+                    <view class="tui-title" style="width: 120upx">客户地址</view>
+                    <view class="address-box">
+                      <view v-if="$util.isEmpty(customInfo.long) || $util.isEmpty(customInfo.lat) || $util.isEmpty(customInfo.address)" class="addr-main" style="color: red; font-size: 28rpx;">
+                        客户地址不完善,请修改
+                      </view>
+                      <view v-else>
+                        <view class="addr-main">{{ customInfo.address }}</view>
+                        <view class="addr-sub">{{ customInfo.showAddress }}</view>
+                      </view>
+                    </view>
+                  </tui-list-cell>
+                  <!-- 跑腿平台报价列表 -->
+                  <tui-list-cell class="line-cell" :hover="false">
                     <view v-if="deliveryQuotes.length > 0" class="delivery-quotes-container">
-                      <view class="delivery-quotes-grid">
-                        <view
-                          v-for="(item, index) in deliveryQuotes"
-                          :key="index"
-                          class="delivery-quote-item"
-                          :class="{
-                            'active': selectedDeliveryIndex === index,
-                            'disabled': !item.isAble,
-                            'fixed-width': deliveryQuotes.length <= 2
-                          }"
-                          @click="selectDelivery(item, index)"
-                        >
-                          <view class="platform-name">{{ item.displayName || item.name }}</view>
-                          <view class="platform-price">{{ item.priceText }}元</view>
-                          <view v-if="item.type" class="platform-type">{{ item.type }}</view>
+                      <scroll-view scroll-y class="delivery-quotes-scroll">
+                        <view class="delivery-quotes-grid">
+                          <view
+                            v-for="(item, index) in deliveryQuotes"
+                            :key="index"
+                            class="delivery-quote-item"
+                            :class="{
+                              'active': selectedDeliveryIndex === index,
+                              'disabled': !item.isAble,
+                              'fixed-width': deliveryQuotes.length <= 2
+                            }"
+                            @click="selectDelivery(item, index)"
+                          >
+                            <view class="platform-name">{{ item.displayName || item.name }}</view>
+                            <view class="platform-price">{{ item.priceText }}元</view>
+                            <view v-if="item.type" class="platform-type">{{ item.type }}</view>
+                          </view>
                         </view>
-                      </view>
+                      </scroll-view>
                     </view>
                     <view v-else class="empty-quotes-view">
                       <text>正在获取运费...</text>
@@ -326,11 +341,18 @@
                   </tui-list-cell>
                   <tui-list-cell class="line-cell" :hover="false" :arrow="false">
                     <view class="tui-title">运费</view>
-                    <text v-if="form.reductionRule == 0" class="blue-text">¥{{ form.sendCost }}</text>
+                    <!-- <text v-if="form.reductionRule == 0" class="blue-text">¥{{ form.sendCost }}</text>
                     <text v-if="form.reductionRule == 1" class="free-delivery-tag">
                       <text v-if="isHcMapRule()"><text class="strike-through">¥{{ form.sendCost }}</text>【花材免跑腿费】</text>
                       <text v-else>¥{{ form.sendCost }}</text>
-                    </text>
+                    </text> -->
+                    
+                    <view v-if="form.reductionRule == 1 && isHcMapRule()">
+                      <input type="digit" placeholder="请填写运费" v-model="form.customSendCost" placeholder-class="tui-placeholder" style="border:1upx solid #eee;color:#3385FF;width:150upx;height:70upx;"/>
+                    </view>
+                    <view v-else>
+                      <input type="digit" placeholder="请填写运费" v-model="form.sendCost" placeholder-class="tui-placeholder" style="border:1upx solid #eee;color:#3385FF;width:150upx;height:70upx;"/>
+                    </view>
                   </tui-list-cell>
                   <tui-list-cell class="line-cell" :hover="false" :arrow="false">
                     <view class="tui-title">呼叫跑腿</view>
@@ -629,7 +651,8 @@ export default {
         sendDate: '', // 保留以防 API 需要
         sendTimeWant: '',
         historyDate: '',
-        sendCost: '',
+        sendCost: '', //系统默认运费
+        customSendCost: 0, //自设置的运费
         customId: '',
         packCost: '',
         product: '',
@@ -646,6 +669,7 @@ export default {
       },
       showCustomer: false,
       customInfo: { discount: 1 },
+      isUpdateAddress: false,
       customData: { bigCount: 0, smallCount: 0, userPrice: 0 },
       isAddModel: false,
       modifyPrice: '',
@@ -685,13 +709,14 @@ export default {
 
     if (Number(option.customId) > 0) {
       this.form.customId = option.customId;
-      let that = this;
-      getUserDet({ id: option.customId }).then((res) => {
-        that.form.customName = res.data.name;
-        that.customInfo = res.data;
-        that.form.wlName = res.data.wlName ? res.data.wlName : '';
-        that.$forceUpdate();
-      });
+      // let that = this;
+      // getUserDet({ id: option.customId }).then((res) => {
+      //   that.form.customName = res.data.name;
+      //   that.customInfo = res.data;
+      //   that.form.wlName = res.data.wlName ? res.data.wlName : '';
+      //   that.$forceUpdate();
+      // });
+      this.getCustomDetail();
 
       //用于混入product的已选中花材!!!!!
       this.selectJobId = option.customId;
@@ -741,7 +766,13 @@ export default {
 
     //获取跑腿平台报价
     if (this.openIntraCity == 1 && this.form.sendType == 2) {
-      this.getDeliveryQuotes();
+      if(this.isUpdateAddress){
+        this.getCustomDetail().then(() => {
+          this.getDeliveryQuotes()
+        });
+      }else{
+        this.getDeliveryQuotes();
+      }
     }
   },
   onUnload() {
@@ -795,7 +826,7 @@ export default {
           // 花材满减
           if (this.isHcMapRule()) {
              // 满足条件免运费,不加 sendCost
-             finalPrice = Number(finalPrice);
+             finalPrice = Number(finalPrice) + Number(this.form.customSendCost);
           } else {
              finalPrice = Number(finalPrice) + sendCost;
           }
@@ -1075,6 +1106,12 @@ export default {
       return this.checkMapRule('hc');
     },
     getDeliveryQuotes() {
+      //如果客户地址缺少经度、纬度和address,任何一个,则不要请求后端询价接口
+      if (this.$util.isEmpty(this.customInfo.long) || this.$util.isEmpty(this.customInfo.lat) || this.$util.isEmpty(this.customInfo.address)) {
+        this.$msg('客户地址缺少经纬度或地址,任何一个,无法获取运费', 'none', 2200);
+        return;
+      }
+      
       this.deliveryQuotes = [];
       this.selectedDeliveryIndex = -1;
       this.selectedDeliveryData = null;
@@ -1148,6 +1185,19 @@ export default {
       if (this.form.sendType == 2) {
         this.getDeliveryQuotes();
       }
+    },
+    getCustomDetail() {
+      let that = this;
+      return getUserDet({ id: that.option.customId }).then((res) => {
+        that.form.customName = res.data.name;
+        that.customInfo = res.data;
+        that.form.wlName = res.data.wlName ? res.data.wlName : '';
+        that.$forceUpdate();
+      });
+    },
+    updateCustomAddress(){
+      this.isUpdateAddress = true;
+      this.$util.pageTo({ url: '/pagesClient/member/modify?id='+this.customInfo.id+'&name='+this.customInfo.name })
     }
   }
 };
@@ -1412,6 +1462,10 @@ export default {
   margin-top: 10upx;
 }
 
+.delivery-quotes-scroll {
+  max-height: 450upx;
+}
+
 .delivery-quotes-grid {
   display: flex;
   flex-wrap: wrap;
@@ -1511,7 +1565,8 @@ export default {
 }
 
 .delivery-section {
-    border: 1upx solid #eee;
+    border: 1upx solid #80B3FF;
+    box-shadow: 0 0 12upx #6ba3f8;
     margin: 8upx 20upx;
     border-radius: 8upx;
 }
@@ -1565,4 +1620,21 @@ export default {
     color: #3385ff;
     margin-left: 5upx;
 }
+.address-box {
+    flex: 1;
+    text-align: center;
+    display: flex;
+    flex-direction: column;
+    justify-content: center;
+    align-items: center;
+    .addr-main {
+        font-size: 32upx;
+        color: #333;
+    }
+    .addr-sub {
+        font-size: 28upx;
+        color: #999;
+        margin-top: 6upx;
+    }
+}
 </style>

+ 31 - 11
ghsPad/src/pages/home/components/settlePop.vue

@@ -46,17 +46,30 @@
                       <text style="font-size:12upx;">正在获取运费...</text>
                     </view>
 
-                    <view class="select-item_box">
+                    <view class="select-item_box" style="margin-top:8upx;">
                         <view class="title">
                              <text>运费:</text>
-                             <text v-if="form.reductionRule == 0" class="blue-text">¥{{ form.sendCost }}</text>
+                        </view>
+                        <view class="item-list_box">
+                            <!-- <text v-if="form.reductionRule == 0" class="blue-text">¥{{ form.sendCost }}</text>
                              <text v-if="form.reductionRule == 1" class="free-delivery-tag">
                                <text v-if="isHcMapRule()"><text class="strike-through">¥{{ form.sendCost }}</text>【花材免跑腿费】</text>
                                <text v-else>¥{{ form.sendCost }}</text>
-                             </text>
-                             <text style="margin-left: 40upx;">距离:</text>
-                             <text v-if="showDistance > 100" class="blue-text">{{ (showDistance/1000).toFixed(1) }}公里</text>
-                             <text v-else class="blue-text">{{ showDistance }} 米</text>
+                             </text> -->
+                            <view v-if="form.reductionRule == 1 && isHcMapRule()">
+                                <input type="digit" placeholder="请填写运费" v-model="form.customSendCost" placeholder-class="tui-placeholder" />
+                             </view>
+                             <view v-else>
+                                <input type="digit" placeholder="请填写运费" v-model="form.sendCost" placeholder-class="tui-placeholder" />
+                             </view>
+                        </view>
+                    </view>
+
+                    <view class="select-item_box">
+                        <view class="title">距离:</view>
+                        <view class="item-list_box">
+                            <text v-if="showDistance > 100" class="blue-text" style="font-size:12upx;">{{ (showDistance/1000).toFixed(1) }}公里</text>
+                             <text v-else class="blue-text" style="font-size:12upx;">{{ showDistance }} 米</text>
                         </view>
                     </view>
 
@@ -231,7 +244,8 @@ export default {
                 groupName:'',
                 remark:'',
                 sendCost:0,
-                labourCost:0,
+                customSendCost:0, //自设置的运费
+                labourCost:0, //废弃字段 -- 原本是花材满减规则,现在用 reductionRule 代替
                 modifyPrice:0,
                 getStaffId:0,
                 getStaffName:'',
@@ -239,7 +253,7 @@ export default {
                 shopAdminName:'',
                 cash:0,
                 dealPrice:0,
-                reductionRule: 0,
+                reductionRule: 0, // 0 没有满减 1 用花材
                 callErrand: 0 //现在呼叫 0 晚点呼叫 1
             },
             proceeds: '0',
@@ -295,7 +309,11 @@ export default {
             if(!this.$util.isEmpty(this.groupInfo)){
                 price = this.groupInfo.price ? parseFloat(this.groupInfo.price) : 0
             }else{
-                price = Number(this.allPrice)+Number(this.form.sendCost)+Number(this.form.labourCost)
+                if(this.form.sendType == 2 && this.form.reductionRule == 1 && this.isHcMapRule()){
+                    price = Number(this.allPrice)+Number(this.form.customSendCost)
+                }else{
+                    price = Number(this.allPrice)+Number(this.form.sendCost)+Number(this.form.labourCost)
+                }
                 price = parseFloat(price.toFixed(2))
             }
             this.form.modifyPrice = price
@@ -564,6 +582,7 @@ export default {
                 this.form.deliveryPrice = this.selectedDeliveryData.priceNumber;
                 this.form.deliveryDistance = this.showDistance;
                 this.form.deliveryBracketContent = this.selectedDeliveryData.bracketContent;
+                this.form.deliveryAllParams = this.selectedDeliveryData
                 if (this.form.callErrand == 1) {
                     this.form.pickupTime = this.pickupTime.value;
                 }
@@ -707,7 +726,6 @@ export default {
                     background:blue;
                     color:white;
                 }
-
             }
             & .inpup_box{
                 display: flex;
@@ -802,6 +820,7 @@ export default {
 .delivery-quotes-scroll {
   width: 100%;
   max-height: 210upx;
+  border-bottom: 1upx solid #eee;
 }
 .delivery-quotes-grid {
   display: flex;
@@ -898,7 +917,8 @@ export default {
 }
 
 .delivery-section {
-    border: 1px solid #eee;
+    border: 1px solid #80B3FF;
+    box-shadow: 0 0 8upx #80B3FF;
     margin: 5upx 0;
     border-radius: 4upx;
     padding: 5upx;

+ 181 - 63
hdApp/src/admin/billing/affirm.vue

@@ -157,26 +157,40 @@
 
           <view v-if="form.sendType == 2">
             <view v-if="openIntraCity == 1" class="delivery-section">
-              <tui-list-cell v-if="openIntraCity == 1" class="line-cell" :hover="false">
-                <!-- 跑腿平台报价列表 -->
+              <!-- 客户地址信息 -->
+              <tui-list-cell class="line-cell" :hover="false" :arrow="true" @click.stop="updateCustomAddress()">
+                <view class="tui-title" style="width:120rpx;">客户地址</view>
+                <view class="address-box">
+                      <view v-if="$util.isEmpty(customInfo.long) || $util.isEmpty(customInfo.lat) || $util.isEmpty(customInfo.address)" class="addr-main" style="color: red; font-size: 28rpx;">
+                        客户地址不完善,请修改
+                      </view>
+                      <view v-else>
+                        <view class="addr-main">{{ customInfo.address }}</view>
+                        <view class="addr-sub">{{ customInfo.showAddress }}</view>
+                      </view>
+                    </view>
+              </tui-list-cell>
+              <tui-list-cell class="line-cell" :hover="false">
                 <view v-if="deliveryQuotes.length > 0" class="delivery-quotes-container">
-                  <view class="delivery-quotes-grid">
-                    <view
-                      v-for="(item, index) in deliveryQuotes"
-                      :key="index"
-                      class="delivery-quote-item"
-                      :class="{
-                        'active': selectedDeliveryIndex === index,
-                        'disabled': !item.isAble,
-                        'fixed-width': deliveryQuotes.length <= 2
-                      }"
-                      @click="selectDelivery(item, index)"
-                    >
-                      <view class="platform-name">{{ item.displayName || item.name }}</view>
-                      <view class="platform-price">{{ item.priceText }}元</view>
-                      <view v-if="item.type" class="platform-type">{{ item.type }}</view>
+                  <scroll-view scroll-y class="delivery-quotes-scroll">
+                    <view class="delivery-quotes-grid">
+                      <view
+                        v-for="(item, index) in deliveryQuotes"
+                        :key="index"
+                        class="delivery-quote-item"
+                        :class="{
+                          'active': selectedDeliveryIndex === index,
+                          'disabled': !item.isAble,
+                          'fixed-width': deliveryQuotes.length <= 2
+                        }"
+                        @click="selectDelivery(item, index)"
+                      >
+                        <view class="platform-name">{{ item.displayName || item.name }}</view>
+                        <view class="platform-price">{{ item.priceText }}元</view>
+                        <view v-if="item.type" class="platform-type">{{ item.type }}</view>
+                      </view>
                     </view>
-                  </view>
+                  </scroll-view>
                 </view>
                 <view v-else class="empty-quotes-view">
                   <text>正在获取运费...</text>
@@ -193,13 +207,14 @@
               </tui-list-cell>
               <tui-list-cell class="line-cell" :hover="false" :arrow="false">
                 <view class="tui-title title-reduction" style="width: 150upx;">满减规则</view>
-                <button @tap="form.reductionRule = 0" class="admin-button-com mini-btn btn-reduction" :class="form.reductionRule == 0 ? 'active' : 'default'">没有满减</button>
-                <button @tap="form.reductionRule = 1" class="admin-button-com mini-btn btn-reduction" :class="form.reductionRule == 1 ? 'active' : 'default'">用花材</button>
-                <button @tap="form.reductionRule = 2" class="admin-button-com mini-btn btn-reduction-last" :class="form.reductionRule == 2 ? 'active' : 'default'">用花束</button>
+                <button @tap="changeReductionRule(0)" class="admin-button-com mini-btn btn-reduction" :class="form.reductionRule == 0 ? 'active' : 'default'">没有满减</button>
+                <button @tap="changeReductionRule(1)" class="admin-button-com mini-btn btn-reduction" :class="form.reductionRule == 1 ? 'active' : 'default'">用花材</button>
+                <button @tap="changeReductionRule(2)" class="admin-button-com mini-btn btn-reduction-last" :class="form.reductionRule == 2 ? 'active' : 'default'">用花束</button>
               </tui-list-cell>
               <tui-list-cell class="line-cell" :hover="false" :arrow="false">
                 <view class="tui-title">运费</view>
-                <text v-if="form.reductionRule == 0" class="blue-text">¥{{ form.sendCost }}</text>
+                
+                <!-- <text v-if="form.reductionRule == 0" class="blue-text">¥{{ form.sendCost }}</text>
                 <text v-if="form.reductionRule == 1" class="free-delivery-tag">
                   <text v-if="isHcMapRule()"><text class="strike-through">¥{{ form.sendCost }}</text>【花材免跑腿费】</text>
                   <text v-else>¥{{ form.sendCost }}</text>
@@ -207,7 +222,21 @@
                 <text v-if="form.reductionRule == 2" class="free-delivery-tag">
                   <text v-if="isHsMapRule()"><text class="strike-through">¥{{ form.sendCost }}</text>【花束免跑腿费】</text>
                   <text v-else>¥{{ form.sendCost }}</text>
-                </text>
+                </text> -->
+                <view v-if="form.reductionRule == 1 && isHcMapRule()">
+                  <input type="digit" placeholder="请填写运费" v-model="form.customSendCost" placeholder-class="tui-placeholder" style="border:1upx solid #eee;color:#3385FF;width:150upx;height:70upx;" />
+                </view>
+                <view v-else-if="form.reductionRule == 2">
+                  <view v-if="isHsMapRule()">
+                    <input type="digit" placeholder="请填写运费" v-model="form.customSendCost" placeholder-class="tui-placeholder" style="border:1upx solid #eee;color:#3385FF;width:150upx;height:70upx;" />
+                  </view>
+                  <view v-else>
+                    <input type="digit" placeholder="请填写运费" v-model="form.sendCost" placeholder-class="tui-placeholder" style="border:1upx solid #eee;color:#3385FF;width:150upx;height:70upx;" />
+                  </view>
+                </view>
+                <view v-else>
+                  <input type="digit" placeholder="请填写运费" v-model="form.sendCost" placeholder-class="tui-placeholder" style="border:1upx solid #eee;color:#3385FF;width:150upx;height:70upx;" />
+                </view>
               </tui-list-cell>
               <tui-list-cell class="line-cell" :hover="false" :arrow="false">
                 <view class="tui-title">呼叫跑腿</view>
@@ -225,7 +254,7 @@
             <view v-else class="delivery-section">
               <tui-list-cell class="line-cell" :hover="false" :arrow="false">
                 <view class="tui-title">运费</view>
-                <input type="digit" placeholder="请填写运费" v-model="form.sendCost" placeholder-class="tui-placeholder"/>
+                <input type="digit" placeholder="请填写运费" v-model="form.sendCost" />
               </tui-list-cell>
             </view>
           </view>
@@ -520,7 +549,8 @@ export default {
         reachDate: "",
 		    reachPeriod: "12:00",
         historyDate:"",
-        sendCost: "",
+        sendCost: "", //系统默认运费
+        customSendCost: 0, //自设置的运费
         customId: "",
         packCost:"",
         product: "",
@@ -540,6 +570,7 @@ export default {
       },
       showCustomer: false,
       customInfo:{discount:1,balance:0},
+      isUpdateAddress: false,
       customData: { bigCount: 0, smallCount: 0, userPrice: 0 },
       isAddModel:false,
       checkStock:true,
@@ -554,7 +585,7 @@ export default {
       hcFreeKm: 0,  //跑腿免费公里数
       hcMap: [],    //花材满减规则
       hsFreeKm: 0,  //花束免费公里数
-      hsMap: [],    //花束满减规则
+      hsAddFee: 0,    //花束满减规则
       deliveryQuotes: [], // 跑腿平台报价列表
       selectedDeliveryIndex: -1,
       selectedDeliveryData: null,
@@ -587,20 +618,21 @@ export default {
         }
         that.hsFreeKm = data.hsFreeKm
         try {
-           that.hsMap = typeof data.hsMap === 'string' ? JSON.parse(data.hsMap) : data.hsMap
+           that.hsAddFee = data.hsAddFee
         } catch (e) {
-           that.hsMap = []
+           that.hsAddFee = 0
         }
     })
 
     if(Number(option.customId) > 0){
       this.form.customId = option.customId
-      let that = this
-      getCustomInfo({ id: option.customId }).then(res => {
-        that.form.customName = res.data.custom.name
-        that.customInfo = res.data.custom
-        that.$forceUpdate()
-      })
+      // let that = this
+      // getCustomInfo({ id: option.customId }).then(res => {
+      //   that.form.customName = res.data.custom.name
+      //   that.customInfo = res.data.custom
+      //   that.$forceUpdate()
+      // })
+      this.getCustomDetail();
 
       //用于混入product的已选中花材!!!!!
       this.selectJobId = option.customId
@@ -623,7 +655,6 @@ export default {
     if(this.reduceStock == 1){
       this.form.hasPay = 2
     }
-
   },
 	onShow(){
 		if(!this.$util.isEmpty(uni.getStorageSync("newClient"))){
@@ -638,8 +669,14 @@ export default {
     })
 
     //获取跑腿平台报价
-    if(this.form.sendType == 2){
-      this.getDeliveryQuotes()
+    if(this.openIntraCity == 1 && this.form.sendType == 2){
+      if(this.isUpdateAddress){
+        this.getCustomDetail().then(() => {
+          this.getDeliveryQuotes()
+        });
+      }else{
+        this.getDeliveryQuotes()
+      }
     }
 	},
 	onUnload(){
@@ -647,7 +684,7 @@ export default {
       uni.$off('listenGetScanCode')
 	},
   computed: {
-    ...mapGetters(["getMerchantInfo","getMyShopInfo", "getLoginInfo"]),
+    ...mapGetters(["getMerchantInfo", "getMyShopInfo", "getLoginInfo"]),
     systemPrice(){
       let totalPrice = this.allPrice.toFixed(2)
       let finalPrice = Number(totalPrice)
@@ -658,28 +695,35 @@ export default {
           finalPrice = Number(finalPrice) + sendCost
         }else if(this.form.reductionRule == 1){
           if(this.isHcMapRule()){
-            finalPrice = Number(finalPrice)
+            finalPrice = Number(finalPrice) + Number(this.form.customSendCost)
           }else{
             finalPrice = Number(finalPrice) + sendCost
           }
         }else if(this.form.reductionRule == 2){
           if(this.isHsMapRule()){
-            finalPrice = Number(finalPrice)
+            finalPrice = Number(finalPrice) + Number(this.form.customSendCost)
           }else{
+            const distanceInMeters = Number(this.showDistance) || 0
+            const freeKmInMeters = (Number(this.hsFreeKm) || 0) * 1000
+            const diff = distanceInMeters - freeKmInMeters
+            //花束满减规则 -- 每超出1公里,加收 hsAddFee 元(不足1公里按1公里计算)
+            const addFeePrice = Math.ceil(diff / 1000) * this.hsAddFee
+            console.log('addFeePrice', addFeePrice)
+
             finalPrice = Number(finalPrice) + sendCost
           }
         }
       }
       return parseFloat(Number(finalPrice).toFixed(2))
     },
-    labourCost(){
-      let cost = 0
-      if(Number(this.modifyPrice)>Number(this.systemPrice)){
-        cost = Number(this.modifyPrice) - Number(this.systemPrice)
-        cost = Number(cost).toFixed(2)
-      }
-      return cost
-    }
+    // labourCost(){
+    //   let cost = 0
+    //   if(Number(this.modifyPrice) > Number(this.systemPrice)){
+    //     cost = Number(this.modifyPrice) - Number(this.systemPrice)
+    //     cost = Number(cost).toFixed(2)
+    //   }
+    //   return cost
+    // }
   },
   watch: {
     systemPrice: {
@@ -696,22 +740,36 @@ export default {
       const distanceInMeters = Number(this.showDistance) || 0
       const freeKm = type === 'hc' ? this.hcFreeKm : this.hsFreeKm
       const freeKmInMeters = (Number(freeKm) || 0) * 1000
-      if(distanceInMeters < freeKmInMeters){
+      if(distanceInMeters <= freeKmInMeters){
+        console.log('满足免费公里数条件', type,distanceInMeters,freeKmInMeters)
         return true
       }
-      let map = type === 'hc' ? this.hcMap : this.hsMap
+
+      let map = type === 'hc' ? this.hcMap : ''
       if(map && Array.isArray(map) && map.length > 0){
-          const flowerCount = this.allCountFun.bigLength || 0
-          const flowerPrice = this.allPriceFun || 0
-          for(const rule of map){
-            const ruleNum = Number(rule.num) || 0
-            const rulePrice = Number(rule.price) || 0
-            const ruleDistance = (Number(rule.distance) || 0) * 1000 
-            if(flowerCount >= ruleNum && flowerPrice >= rulePrice && distanceInMeters <= ruleDistance){
-              return true
-            }
+        const flowerCount = this.allCount.bigLength || 0
+        const flowerPrice = this.allPrice || 0
+        for(const rule of map){
+          const ruleNum = Number(rule.num) || 0
+          const rulePrice = Number(rule.price) || 0
+          const ruleDistance = (Number(rule.distance) || 0) * 1000 
+          if(flowerCount >= ruleNum && flowerPrice >= rulePrice && distanceInMeters <= ruleDistance){
+            console.log('满足满减条件',flowerCount,ruleNum,flowerPrice, rulePrice,distanceInMeters,ruleDistance)
+            return true
           }
+        }
+      }else{
+        const addFee = distanceInMeters - freeKmInMeters
+        if(addFee > 0){
+          //花束满减规则 -- 每超出1公里,加收 hsAddFee 元(不足1公里按1公里计算)
+          const addFeePrice = Math.ceil(addFee / 1000) * this.hsAddFee
+          console.log('addFeePrice---', addFeePrice)
+          //this.form.sendCost = addFeePrice
+          return false
+        }
+        return true
       }
+
       return false
     },
     isHcMapRule(){
@@ -720,6 +778,28 @@ export default {
     isHsMapRule(){
       return this.checkMapRule('hs')
     },
+    changeReductionRule(rule) {
+      this.form.reductionRule = rule;
+      if(rule == 0){
+        // 选中报价卡片
+        this.form.sendCost = this.selectedDeliveryData.priceNumber
+      }else if(rule == 1){
+        if(this.isHcMapRule()){
+          this.form.sendCost = this.selectedDeliveryData.priceNumber
+        }else{
+          this.form.sendCost = 0
+        }
+      }else if(rule == 2) {
+         const distanceInMeters = Number(this.showDistance) || 0
+         const freeKmInMeters = (Number(this.hsFreeKm) || 0) * 1000
+         const diff = distanceInMeters - freeKmInMeters
+         let addFeePrice = 0
+         if(diff > 0){
+           addFeePrice = Math.ceil(diff / 1000) * this.hsAddFee
+         }
+         this.form.sendCost = addFeePrice
+      }
+    },
     setSendType (type) {
       this.form.sendType = type
       if (type !== 2) {
@@ -918,7 +998,7 @@ export default {
       }
       formData.shopId = this.getMerchantInfo.id;
 
-      let params = {...formData,modifyPrice: this.modifyPrice,labourCost:this.labourCost};
+      let params = {...formData,modifyPrice: this.modifyPrice}; //labourCost:this.labourCost
 
       // 如果选择了跑腿配送并且有选中的平台数据,则添加配送平台信息
       if (this.form.sendType == 2 && this.selectedDeliveryData) {
@@ -989,6 +1069,11 @@ export default {
       
     },
     getDeliveryQuotes(){
+      //如果客户地址缺少经纬度或地址,任何一个,则不要请求后端询价接口
+      if (this.$util.isEmpty(this.customInfo.long) || this.$util.isEmpty(this.customInfo.lat) || this.$util.isEmpty(this.customInfo.address)) {
+        this.$msg('客户地址缺少经纬度或地址,任何一个,无法获取运费', 'none', 2200);
+        return;
+      }
       this.deliveryQuotes = []
       this.selectedDeliveryIndex = -1
       this.selectedDeliveryData = null
@@ -1062,6 +1147,18 @@ export default {
           this.getDeliveryQuotes();
       }
     },
+    getCustomDetail(){
+      let that = this
+      return getCustomInfo({ id: that.option.customId }).then(res => {
+        that.form.customName = res.data.custom.name
+        that.customInfo = res.data.custom
+        that.$forceUpdate()
+      })
+    },
+    updateCustomAddress(){
+      this.isUpdateAddress = true;
+      this.$util.pageTo({ url: '/admin/member/modify?id='+this.customInfo.id+'&name='+this.customInfo.name })
+    }
   }
 };
 </script>
@@ -1561,7 +1658,11 @@ export default {
 	/* 跑腿平台报价样式 */
 .delivery-quotes-container {
 	width: 100%;
-	margin-top: 20upx;
+	margin-top: 10upx;
+}
+
+.delivery-quotes-scroll {
+  max-height: 450upx;
 }
 
 .delivery-quotes-grid {
@@ -1735,7 +1836,8 @@ export default {
 }
 
 .delivery-section {
-    border: 1upx solid #eee;
+    border: 1upx solid #2ECE7E;
+    box-shadow: 0 0 12upx #09C567;
     margin: 8upx 20upx;
     border-radius: 8upx;
 }
@@ -1785,5 +1887,21 @@ export default {
     color: #049E2C;
     margin-left: 5upx;
 }
-
+.address-box {
+    flex: 1;
+    text-align: center;
+    display: flex;
+    flex-direction: column;
+    justify-content: center;
+    align-items: center;
+    .addr-main {
+        font-size: 32upx;
+        color: #333;
+    }
+    .addr-sub {
+        font-size: 28upx;
+        color: #999;
+        margin-top: 6upx;
+    }
+}
 </style>

+ 8 - 0
hdPad/src/api/delivery/index.js

@@ -0,0 +1,8 @@
+import https from '@/plugins/luch-request_0.0.7/request'
+
+/**
+ * 零售快捷开单 -- 跑腿 -- 获取配送报价
+ */
+export const quickOrderAllDeliveryQuotes = data => {
+	return https.post('/delivery/quick-order-delivery-quotes', data)
+}

+ 4 - 0
hdPad/src/api/merchant/index.js

@@ -0,0 +1,4 @@
+import https from '@/plugins/luch-request_0.0.7/request'
+
+// 商户信息
+export const merchantDetail = param => https.get("/merchant/detail", param);

+ 295 - 0
hdPad/src/components/TimePicker.vue

@@ -0,0 +1,295 @@
+<template>
+  <uni-popup ref="popup" type="center" :safe-area="false">
+    <view class="time-picker-container">
+      <view class="picker-header">
+        <text class="title">期望取件时间</text>
+        <view class="close-btn" @click="close">
+          <zui-svg-icon icon="general-close" :width="18" :height="18" color="#666" />
+        </view>
+      </view>
+
+      <picker-view v-if="visible" class="picker-view" :indicator-style="indicatorStyle" :value="pickerValue" @change="handleChange" :immediate-change="true">
+        <picker-view-column>
+          <view class="picker-item" v-for="(item, index) in days" :key="index">{{ item.label }}</view>
+        </picker-view-column>
+        <picker-view-column>
+          <view class="picker-item" v-for="(item, index) in hours" :key="index">{{ item.label }}</view>
+        </picker-view-column>
+        <picker-view-column>
+          <view class="picker-item" v-for="(item, index) in minutes" :key="index">{{ item.label }}</view>
+        </picker-view-column>
+      </picker-view>
+
+      <view class="confirm-button-wrapper">
+        <button class="confirm-btn" @click="handleConfirm">确认</button>
+      </view>
+    </view>
+  </uni-popup>
+</template>
+
+<script>
+import dayjs from 'dayjs';
+
+const minutes = [0, 10, 20, 30, 40, 50];
+
+export default {
+  name: 'TimePicker',
+  data() {
+    return {
+      visible: false, // 控制picker-view的渲染
+      pickerValue: [0, 0, 0],
+      tempPickerValue: [0, 0, 0], // 临时存储滚动过程中的值
+      debounceTimer: null,
+      days: [],
+      hours: [],
+      minutes: [],
+      indicatorStyle: `height: 70rpx;`,
+    };
+  },
+  created() {
+    this.initTime();
+  },
+  methods: {
+    open() {
+      this.visible = true;
+      this.initTime();
+      this.$refs.popup.open();
+    },
+
+    close() {
+      this.visible = false;
+      this.$refs.popup.close();
+    },
+
+    initTime() {
+      const now = dayjs(); // Use current time
+      this.days = this.generateDays();
+
+      const isToday = true;
+      let availableHoursToday = this.generateHours(now, isToday);
+
+      // If no hours available today, switch to tomorrow
+      if (availableHoursToday.length <= 1) { // Only "立即"
+        const tomorrow = dayjs().add(1, 'day').startOf('day');
+        this.days = this.generateDays(); // Regenerate days if needed
+        this.hours = this.generateHours(tomorrow, false);
+        this.minutes = this.generateMinutes(tomorrow, false, this.hours[0].value);
+        this.pickerValue = [1, 0, 0]; // Select "Tomorrow", first hour, first minute
+        this.tempPickerValue = [1, 0, 0];
+        return;
+      }
+
+      this.hours = availableHoursToday;
+
+      // Default to "立即"
+      const initialHourIndex = 0;
+      const selectedHour = this.hours[initialHourIndex].value;
+      this.minutes = this.generateMinutes(now, isToday, selectedHour);
+      const initialMinuteIndex = 0;
+      
+      this.pickerValue = [0, initialHourIndex, initialMinuteIndex];
+      this.tempPickerValue = [0, initialHourIndex, initialMinuteIndex];
+    },
+
+    generateDays() {
+      return [
+        { label: '今天', value: dayjs().format('YYYY-MM-DD') },
+        { label: '明天', value: dayjs().add(1, 'day').format('YYYY-MM-DD') },
+      ];
+    },
+
+    generateHours(now, isToday) {
+      const hours = [];
+      if (isToday) {
+        hours.push({ label: '立即', value: -1 });
+
+        let startHour = now.hour() + 1; // 当前小时的下一个小时
+        // Find if any minutes are available in the current hour
+        const firstAvailableMinute = minutes.find(m => m > now.minute());
+        
+        // If no minutes available in current hour, or if the last minute has passed, start from the next hour
+        if (firstAvailableMinute === undefined) {
+          startHour++;
+        }
+        
+        for (let i = startHour; i <= 23; i++) {
+          hours.push({ label: `${i}时`, value: i });
+        }
+      } else {
+        for (let i = 0; i <= 23; i++) {
+          hours.push({ label: `${i}时`, value: i });
+        }
+      }
+      return hours;
+    },
+
+    generateMinutes(now, isToday, selectedHour) {
+      if (selectedHour === -1) {
+        return [{ label: '', value: -1 }];
+      }
+      
+      let availableMinutes = minutes;
+      
+      if (isToday && selectedHour === now.hour()) {
+        const currentMinute = now.minute();
+        availableMinutes = minutes.filter(m => m > currentMinute);
+      }
+      
+      return availableMinutes.map(m => ({ label: `${m}分`, value: m }));
+    },
+
+    handleChange(e) {
+      const val = e.detail.value;
+      this.tempPickerValue = val;
+      
+      if (this.debounceTimer) {
+        clearTimeout(this.debounceTimer);
+      }
+      
+      this.debounceTimer = setTimeout(() => {
+        this.updatePickers(val);
+      }, 100);
+    },
+
+    updatePickers(val, isSync = false) {
+      const [dayIndex, hourIndex, minuteIndex] = val;
+      const oldPickerValue = this.pickerValue;
+      
+      const now = dayjs();
+      const isToday = dayIndex === 0;
+
+      let newHours = this.hours;
+      let newMinutes = this.minutes;
+      
+      let finalHourIndex = hourIndex;
+      let finalMinuteIndex = minuteIndex;
+
+      // Day changed
+      if (oldPickerValue[0] !== dayIndex) {
+        newHours = this.generateHours(now, isToday);
+        // 修正越界,尽量保持用户的选择,而不是强制归零
+        if (finalHourIndex >= newHours.length) {
+            finalHourIndex = 0;
+        }
+      }
+      
+      // Calculate selected hour based on current indices
+      const selectedHour = newHours[finalHourIndex] ? newHours[finalHourIndex].value : -1;
+
+      // Always regenerate minutes to ensure correctness (e.g. today/tomorrow switch, or hour switch)
+      // Optimization: we could check if generation parameters actually changed, but it's fast enough.
+      newMinutes = this.generateMinutes(now, isToday, selectedHour);
+      
+      // 修正越界
+      if (finalMinuteIndex >= newMinutes.length) {
+          finalMinuteIndex = 0;
+      }
+      
+      this.hours = newHours;
+      this.minutes = newMinutes;
+      this.debounceTimer = null;
+
+      const newValue = [dayIndex, finalHourIndex, finalMinuteIndex];
+      
+      if (isSync) {
+        this.pickerValue = newValue;
+      } else {
+        // Use setTimeout to avoid visual glitch on picker reset
+        setTimeout(() => {
+            this.pickerValue = newValue;
+        }, 0);
+      }
+    },
+
+    handleConfirm() {
+      if (this.debounceTimer) {
+        clearTimeout(this.debounceTimer);
+        this.updatePickers(this.tempPickerValue, true);
+      }
+      
+      const [dayIndex, hourIndex, minuteIndex] = this.pickerValue;
+      const selectedDay = this.days[dayIndex];
+      const selectedHour = this.hours[hourIndex];
+      const selectedMinute = this.minutes[minuteIndex];
+
+      let result = {};
+      if (selectedHour && selectedHour.value === -1) {
+        result = {
+          label: '立即取件',
+          value: dayjs().format('YYYY-MM-DD HH:mm'),
+        };
+      } else {
+        const date = selectedDay.value;
+        const hour = selectedHour?.value ?? 0;
+        const minute = selectedMinute?.value ?? 0;
+        const finalDate = dayjs(`${date} ${hour}:${minute}`).toDate();
+        result = {
+          label: dayjs(finalDate).format('YYYY-MM-DD HH:mm'),
+          value: dayjs(finalDate).format('YYYY-MM-DD HH:mm'),
+        };
+      }
+
+      this.$emit('confirm', result);
+      this.close();
+    },
+  },
+};
+</script>
+
+<style lang="scss" scoped>
+.time-picker-container {
+  background-color: #fff;
+  border-radius: 20rpx;
+  width: 300rpx;
+  height: 400rpx;
+  display: flex;
+  flex-direction: column;
+  overflow: hidden;
+}
+.picker-header {
+  position: relative;
+  text-align: center;
+  padding: 20rpx;
+  flex-shrink: 0;
+
+  .title {
+    font-size: 26rpx;
+    font-weight: 350;
+  }
+
+  .close-btn {
+    position: absolute;
+    right: 24rpx;
+    top: 50%;
+    transform: translateY(-50%);
+    padding: 10rpx;
+  }
+}
+.picker-view {
+  width: 100%;
+  height: 100%;
+}
+.picker-item {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  font-size: 22rpx;
+}
+.confirm-button-wrapper {
+  padding: 15rpx 20rpx;
+  padding-bottom: 25rpx;
+  flex-shrink: 0;
+}
+.confirm-btn {
+  background: linear-gradient(to right, #409eff, #67c2ff);
+  color: white;
+  border-radius: 50rpx;
+  font-size: 24rpx;
+  height: 50rpx;
+  line-height: 50rpx;
+  border: none;
+  &::after {
+    border: none;
+  }
+}
+</style>

+ 1 - 1
hdPad/src/pages/home/cash.vue

@@ -61,7 +61,7 @@ import { customList } from '@/api/home/index'
 import customerList from './components/customerList.vue'
 export default {
 	name: "cash",
-	components: { rightItem,consoleButton,  loginComponent, leftArea,leftGroup, rightGoods,navComponent,customerList},
+	components: { rightItem, consoleButton, loginComponent, leftArea, leftGroup, rightGoods, navComponent,customerList},
 	mixins: [autoUpdateApp,productMins],
 	data() {
 		return {

+ 1 - 1
hdPad/src/pages/home/components/console.vue

@@ -42,7 +42,7 @@
 
     <!-- 结算弹框 -->
     <uni-popup ref="settlePopRef" background-color="#fff" type="center" :animation="false">
-        <settlePop @settleCommit="settleCommit" @cancelSettle="cancelSettle" ref="settlePop" :settleOpenStatus.sync="settleOpenStatus" :balance.sync="balance"></settlePop>
+        <settlePop @settleCommit="settleCommit" @cancelSettle="cancelSettle" ref="settlePop" :settleOpenStatus.sync="settleOpenStatus" :balance.sync="balance" :customId.sync="customId"></settlePop>
     </uni-popup>
 
     <!-- 扫码付款 -->

+ 252 - 210
hdPad/src/pages/home/components/leftGroup.vue

@@ -1,236 +1,278 @@
 <template>
-    <view class="itemList-box">
-        <view class="title">
-            列表
-            <text style="float:right;">{{ getMyShopInfo.shopName=='首店'? getMyShopInfo.merchantName: getMyShopInfo.merchantName+' '+getMyShopInfo.shopName}}</text>
-        </view>
-        <scroll-view class="itemList-item_box" scroll-y="true" @scrolltolower="reachBottom" lower-threshold="50">
-            <view v-for="item in list.data" :key="item.id" class="show-item_box">
-                <view class="item-body_box not-active" @click="getDetail(item)">
-                    <view class="flower-name_box">
-                        <view class="current-item-title">
-                            <text class="name">{{item.name}}</text>
-                        </view>
-                        <image class="flower-image" :src="item.cover"></image>
-                    </view>
-                    <view class="item-price-num"> <view>¥{{parseFloat(item.price)}}</view> <view>{{item.num}}次</view> <view>快捷开单</view>  <view><text @click.stop="delCombination(item.id)" class="del">删除</text></view> </view>
-                </view>
-            </view>
-        </scroll-view>
+	<view class="itemList-box">
+		<view class="title">
+			列表
+			<text style="float: right">{{
+				getMyShopInfo.shopName == "首店"
+					? getMyShopInfo.merchantName
+					: getMyShopInfo.merchantName + " " + getMyShopInfo.shopName
+			}}</text>
+		</view>
+		<scroll-view
+			class="itemList-item_box"
+			scroll-y="true"
+			@scrolltolower="reachBottom"
+			lower-threshold="50"
+		>
+			<view v-for="item in list.data" :key="item.id" class="show-item_box">
+				<view class="item-body_box not-active" @click="getDetail(item)">
+					<view class="flower-name_box">
+						<view class="current-item-title">
+							<text class="name">{{ item.name }}</text>
+						</view>
+						<image class="flower-image" :src="item.cover"></image>
+					</view>
+					<view class="item-price-num">
+						<view>¥{{ parseFloat(item.price) }}</view> <view>{{ item.num }}次</view>
+						<view>快捷开单</view>
+						<view
+							><text @click.stop="delCombination(item.id)" class="del"
+								>删除</text
+							></view
+						>
+					</view>
+				</view>
+			</view>
+		</scroll-view>
 
-        <!-- 结算弹框 -->
-        <uni-popup ref="settlePopRef" background-color="#fff" type="center" :animation="false">
-            <settlePop @settleCommit="settleCommit" @cancelSettle="cancelSettle" ref="settlePop" :groupInfo.sync="groupInfo" :settleOpenStatus.sync="settleOpenStatus"></settlePop>
-        </uni-popup>
+		<!-- 结算弹框 -->
+		<uni-popup
+			ref="settlePopRef"
+			background-color="#fff"
+			type="center"
+			:animation="false"
+		>
+			<settlePop
+				@settleCommit="settleCommit"
+				@cancelSettle="cancelSettle"
+				ref="settlePop"
+				:groupInfo.sync="groupInfo"
+				:settleOpenStatus.sync="settleOpenStatus"
+				:customId.sync="customId"
+			></settlePop>
+		</uni-popup>
 
-        <!-- 扫码付款 -->
-        <uni-popup ref="toPayRef" background-color="#fff" type="center" :animation="false" :is-mask-click="false">
-            <toPayWay @cancelPay="cancelPay()" @payFinish="payFinish()" @selectPayWayBack="selectPayWayBack" :orderId.sync="orderId" :price.sync="price"></toPayWay>
-        </uni-popup>
-
-    </view>
+		<!-- 扫码付款 -->
+		<uni-popup
+			ref="toPayRef"
+			background-color="#fff"
+			type="center"
+			:animation="false"
+			:is-mask-click="false"
+		>
+			<toPayWay
+				@cancelPay="cancelPay()"
+				@payFinish="payFinish()"
+				@selectPayWayBack="selectPayWayBack"
+				:orderId.sync="orderId"
+				:price.sync="price"
+			></toPayWay>
+		</uni-popup>
+	</view>
 </template>
 <script>
 import { mapGetters } from "vuex";
-import settlePop from './settlePop'
-import toPayWay from './toPay'
-import {getGroupList,detail,del} from "@/api/group"
-import { list } from "@/mixins"
-import { createOrder} from '@/api/order'
-import productMins from "@/mixins/product"
+import settlePop from "./settlePop";
+import toPayWay from "./toPay";
+import { getGroupList, detail, del } from "@/api/group";
+import { list } from "@/mixins";
+import { createOrder } from "@/api/order";
+import productMins from "@/mixins/product";
 export default {
-    name:'leftItem',
-    components:{settlePop,toPayWay},
-	mixins: [list,productMins],
+	name: "leftItem",
+	components: { settlePop, toPayWay },
+	mixins: [list, productMins],
 	props: {
 		customId: {
 			type: Number,
-			default: 0
-		}
-    },
-    data(){
-        return {
-            totalShop:{ bigUnit: 0, smallUnit:0, totalPrice: 0 },
-            itemList:[],
-            currentJobType:'bill',
-            currentJobId:0,
-            groupList:[],
-            settleOpenStatus:0,
-            groupInfo:{},
-            orderId:0,
-            price:0
-        }
-    },
-    watch:{
-        customId:{
-            handler(newCustomId){
-                this.currentJobId = newCustomId
-            },
-            immediate:true
-        }
-    },
-	computed:{
+			default: 0,
+		},
+	},
+	data() {
+		return {
+			totalShop: { bigUnit: 0, smallUnit: 0, totalPrice: 0 },
+			itemList: [],
+			currentJobType: "bill",
+			currentJobId: 0,
+			groupList: [],
+			settleOpenStatus: 0,
+			groupInfo: {},
+			orderId: 0,
+			price: 0,
+		};
+	},
+	watch: {
+		customId: {
+			handler(newCustomId) {
+				this.currentJobId = newCustomId;
+			},
+			immediate: true,
+		},
+	},
+	computed: {
 		...mapGetters(["getMyShopInfo"]),
 	},
-    created(){
-        this.getGroupList()
-    },
-    methods:{
-		payFinish(){
-			this.$util.hitVoice()
-			this.$refs.toPayRef.close()
-			this.listenScanItem()
+	created() {
+		this.getGroupList();
+	},
+	methods: {
+		payFinish() {
+			this.$util.hitVoice();
+			this.$refs.toPayRef.close();
+			this.listenScanItem();
+		},
+		selectPayWayBack() {
+			this.$util.hitVoice();
 		},
-		selectPayWayBack(){
-			this.$util.hitVoice()
+		getDetail(item) {
+			this.$util.hitVoice();
+			uni.showLoading({ mask: true });
+			detail({ id: item.id }).then((res) => {
+				uni.hideLoading();
+				if (res.code == 1) {
+					this.groupInfo = res.data.info || [];
+					this.settleOpenStatus = 1;
+					this.$refs.settlePopRef.open("center");
+				}
+			});
 		},
-        getDetail(item){
-            this.$util.hitVoice()
-            uni.showLoading({mask:true})
-            detail({id:item.id}).then(res=>{
-                uni.hideLoading()
-                if(res.code == 1){
-                    this.groupInfo = res.data.info || []
-                    this.settleOpenStatus = 1
-                    this.$refs.settlePopRef.open('center')
-                }
-            })
-        },
-		settleCommit(params){
-			uni.showLoading({mask:true})
-			createOrder({...params,groupId:this.groupInfo.id,isCashier:1}).then(res=>{
-				uni.hideLoading()
-				if(res.code == 1){
-					if(res.data.status == 1){
-						this.orderId = res.data.id
-                        this.price = res.data.mainPay ? Number(res.data.mainPay) : 0
-						this.$refs.toPayRef.open('center')
+		settleCommit(params) {
+			uni.showLoading({ mask: true });
+			createOrder({ ...params, groupId: this.groupInfo.id, isCashier: 1 }).then(
+				(res) => {
+					uni.hideLoading();
+					if (res.code == 1) {
+						if (res.data.status == 1) {
+							this.orderId = res.data.id;
+							this.price = res.data.mainPay ? Number(res.data.mainPay) : 0;
+							this.$refs.toPayRef.open("center");
+						}
+						this.settleOpenStatus = 0;
+						this.$refs.settlePopRef.close();
 					}
-					this.settleOpenStatus = 0
-					this.$refs.settlePopRef.close()
 				}
-			})
+			);
+		},
+		cancelPay() {
+			this.$util.hitVoice();
+			this.$refs.toPayRef.close();
+			this.listenScanItem();
+		},
+		reachBottom() {
+			if (!this.list.finished) {
+				this.getGroupList().then((res) => {
+					uni.stopPullDownRefresh();
+				});
+			} else {
+				uni.stopPullDownRefresh();
+			}
 		},
-		cancelPay(){
-			this.$util.hitVoice()
-			this.$refs.toPayRef.close()
-			this.listenScanItem()
+		getGroupList() {
+			return getGroupList({ page: this.list.page }).then((res) => {
+				if (res.code == 1) {
+					this.completes(res);
+				}
+			});
 		},
-        reachBottom() {
-            if (!this.list.finished) {
-                this.getGroupList().then((res) => {
-                    uni.stopPullDownRefresh();
-                });
-            } else {
-                uni.stopPullDownRefresh();
-            }
-        },
-        getGroupList(){
-            return getGroupList({page: this.list.page}).then(res=>{
-                if(res.code == 1){
-                    this.completes(res);
-                }
-            })
-        },
-		cancelSettle(){
-			this.$util.hitVoice()
-			this.settleOpenStatus = 0
-			this.$refs.settlePopRef.close()
+		cancelSettle() {
+			this.$util.hitVoice();
+			this.settleOpenStatus = 0;
+			this.$refs.settlePopRef.close();
 		},
-        delCombination(itemId){
-            let that = this
-            del({id:itemId}).then(res=>{
-				if(res.code == 1){
-				    that.$msg(res.msg)
+		delCombination(itemId) {
+			let that = this;
+			del({ id: itemId }).then((res) => {
+				if (res.code == 1) {
+					that.$msg(res.msg);
 				}
 
-                getGroupList({page: this.list.page}).then(res=>{
-                    if(res.code == 1){
-                        this.completes(res);
-                    }
-                })
-			})
-        }
-    }
-}
+				getGroupList({ page: this.list.page }).then((res) => {
+					if (res.code == 1) {
+						this.completes(res);
+					}
+				});
+			});
+		},
+	},
+};
 </script>
 <style lang="scss" scoped>
 .itemList-box {
-    height: 100%;
-    display: flex;
-    flex-direction: column;
-    & .title {
-        padding: 3upx;
-        font-size: 12upx;
-        background-color: #09C567;
-        color: #FFFFFF;
-    }
-    & .itemList-item_box {
-        margin-bottom:75upx;
-        overflow-y: auto;
-        flex: 1;
-        border: 1px solid #eee;
-        & .show-item_box {
-            padding: 2upx;
-            & .item-body_box {
-                border-radius: 2upx;
-                padding: 2upx;
-                font-size: 14upx;
-                color:#666666;
-                height:58upx;
-                &.active {
-                    background-color: #d4d6d6;
-                }
-                &.not-active {
-                    background-color: #e7e9e9;
-                }
-                & .flower-name_box {
-                    padding: 0 5upx;
-                    position: relative;
-                    .current-item-title {
-                        .name{
-                            color: #333333;
-                            font-weight: bold;
-                            overflow: hidden;
-                            text-overflow:ellipsis;
-                            white-space: nowrap;
-                            width:60upx;
-                            font-size:11upx;
-                        }
-                    }
-                    .flower-image{
-                        position: absolute;
-                        right:1upx;
-                        top:1upx;
-                        width:32upx;
-                        height:32upx;
-                        border-radius:5upx;
-                    }
-                }
-                .item-price-num{
-                    color:#428369;
-                    font-size:10upx;
-                    display: flex;
-                    margin-top:5upx;
-                    align-items: center;
-                    justify-content: space-between;
-                    padding: 15upx 2upx 0 2upx;
+	height: 100%;
+	display: flex;
+	flex-direction: column;
+	& .title {
+		padding: 3upx;
+		font-size: 12upx;
+		background-color: #09c567;
+		color: #ffffff;
+	}
+	& .itemList-item_box {
+		margin-bottom: 75upx;
+		overflow-y: auto;
+		flex: 1;
+		border: 1px solid #eee;
+		& .show-item_box {
+			padding: 2upx;
+			& .item-body_box {
+				border-radius: 2upx;
+				padding: 2upx;
+				font-size: 14upx;
+				color: #666666;
+				height: 58upx;
+				&.active {
+					background-color: #d4d6d6;
+				}
+				&.not-active {
+					background-color: #e7e9e9;
+				}
+				& .flower-name_box {
+					padding: 0 5upx;
+					position: relative;
+					.current-item-title {
+						.name {
+							color: #333333;
+							font-weight: bold;
+							overflow: hidden;
+							text-overflow: ellipsis;
+							white-space: nowrap;
+							width: 60upx;
+							font-size: 11upx;
+						}
+					}
+					.flower-image {
+						position: absolute;
+						right: 1upx;
+						top: 1upx;
+						width: 32upx;
+						height: 32upx;
+						border-radius: 5upx;
+					}
+				}
+				.item-price-num {
+					color: #428369;
+					font-size: 10upx;
+					display: flex;
+					margin-top: 5upx;
+					align-items: center;
+					justify-content: space-between;
+					padding: 15upx 2upx 0 2upx;
 
-                    .del{
-                        color:orange;
-                        font-size:12upx; 
-                    }
-                }
-            }
-        }
-    }
-    & .itemList-footer_box {
-        height: 20upx;
-        border: 1px solid #eee;
-        font-size: 10upx;
-        display: flex;
-        align-items: center;
-        justify-content: space-around;
-    }
+					.del {
+						color: orange;
+						font-size: 12upx;
+					}
+				}
+			}
+		}
+	}
+	& .itemList-footer_box {
+		height: 20upx;
+		border: 1px solid #eee;
+		font-size: 10upx;
+		display: flex;
+		align-items: center;
+		justify-content: space-around;
+	}
 }
-</style>
+</style>

+ 389 - 7
hdPad/src/pages/home/components/settlePop.vue

@@ -35,6 +35,91 @@
                     <view :key="item.id" @tap="changeSendType(item.id)" v-for="item in sendTypeList" :class="[form.sendType===item.id?'active':'']"> {{item.name}} </view>     
                 </view>
             </view>
+            <view class="select-item_box" v-if="form.sendType == 2">
+                <view v-if="openIntraCity == 1" class="delivery-section">
+                    <!-- 跑腿平台报价列表 -->
+                    <view class="select-item_box" v-if="deliveryQuotes.length > 0">
+                        <!-- <view class="title">选择跑腿</view> -->
+                        <scroll-view scroll-y class="delivery-quotes-scroll">
+                            <view class="delivery-quotes-grid">
+                                <view
+                                  v-for="(item, index) in deliveryQuotes"
+                                  :key="index"
+                                  class="delivery-quote-item"
+                                  :class="{
+                                    'active': selectedDeliveryIndex === index,
+                                    'disabled': !item.isAble,
+                                    'fixed-width': deliveryQuotes.length <= 2
+                                  }"
+                                  @click="selectDelivery(item, index)"
+                                >
+                                  <view class="platform-name">{{ item.displayName || item.name }}</view>
+                                  <view class="platform-price">{{ item.priceText }}元</view>
+                                  <view v-if="item.type" class="platform-type">{{ item.type }}</view>
+                                </view>
+                            </view>
+                        </scroll-view>
+                    </view>
+                    <view v-else class="empty-quotes-view">
+                      <text style="font-size:12upx;">正在获取运费...</text>
+                    </view>
+
+                    <view class="select-item_box" style="margin-top:8upx;">
+                        <view class="title">
+                             <text>运费:</text>
+                        </view>
+                        <view class="item-list_box">
+                            <view v-if="form.reductionRule == 1 && isHcMapRule()">
+                                <input type="digit" placeholder="请填写运费" v-model="form.customSendCost" placeholder-class="tui-placeholder" />
+                             </view>
+                             <view v-else>
+                                <input type="digit" placeholder="请填写运费" v-model="form.sendCost" placeholder-class="tui-placeholder" />
+                             </view>
+                        </view>
+                    </view>
+
+                    <view class="select-item_box">
+                        <view class="title">距离:</view>
+                        <view class="item-list_box">
+                            <text v-if="showDistance > 100" class="blue-text" style="font-size:12upx;">{{ (showDistance/1000).toFixed(1) }}公里</text>
+                             <text v-else class="blue-text" style="font-size:12upx;">{{ showDistance }} 米</text>
+                        </view>
+                    </view>
+
+                    <view class="select-item_box">
+                        <view class="title">满减规则</view>
+                        <view class="item-list_box">
+                            <view @tap="form.reductionRule = 0" :class="[form.reductionRule == 0 ? 'active' : '']">没有满减</view>
+                            <view @tap="form.reductionRule = 1" :class="[form.reductionRule == 1 ? 'active' : '']">用花材</view>
+                        </view>
+                    </view>
+
+                    
+
+                    <view class="select-item_box">
+                        <view class="title">呼叫跑腿</view>
+                        <view class="item-list_box">
+                            <view @tap="form.callErrand = 0" :class="[form.callErrand == 0 ? 'active' : '']">晚点呼叫</view>
+                            <view @tap="form.callErrand = 1" :class="[form.callErrand == 1 ? 'active' : '']">现在呼叫</view>
+                        </view>
+                    </view>
+
+                    <view class="select-item_box" v-if="form.callErrand == 1">
+                        <view class="title" @click.stop="openTimePicker">
+                            取件时间:<text class="blue-text">{{ pickupTime.label || '立即取件' }}</text>
+                        </view>
+                    </view>
+
+                </view>
+                <view v-else class="delivery-section">
+                    <view class="select-item_box">
+                        <view class="title">运费</view>
+                        <view class="inpup_box">
+                            <view><input type="digit" placeholder="请填写运费" v-model="form.sendCost" placeholder-class="tui-placeholder"/></view>
+                        </view>
+                    </view>
+                </view>
+            </view>
             <view class="select-item_box" v-if="form.sendType == 1" style="border:1upx solid #CCCCCC;">
                 <view class="title" style="margin-top:8upx;">取货状态</view>
                 <view class="item-list_box">
@@ -128,13 +213,16 @@
             </view>
         </uni-popup>
 
-
+        <TimePicker ref="timePicker" @confirm="handleTimeConfirm" />
     </view>
 </template>
 <script>
 import VKeyboard from '@/components/vKeyboard/VKeyboard.vue'
+import TimePicker from '@/components/TimePicker';
 import productMins from "@/mixins/product";
 import { getAllStaff} from '@/api/shop-admin'
+import { merchantDetail } from '@/api/merchant' 
+import { quickOrderAllDeliveryQuotes } from '@/api/delivery'
 import { mapGetters } from "vuex";
 export default {
     name:'selectCustomer',
@@ -154,7 +242,8 @@ export default {
                 groupName:'',
                 remark:'',
                 sendCost:0,
-                labourCost:0,
+                customSendCost:0, //自设置的运费
+                labourCost: 0, // 工时费用
                 modifyPrice:0,
                 getStaffId:0,
                 getStaffName:'',
@@ -163,7 +252,10 @@ export default {
                 cash:0,
                 dealPrice:0,
                 reachDate: "",
-		        reachPeriod: "12:00"
+		        reachPeriod: "12:00",
+                merchantInfo: {}, // 商户信息
+                reductionRule: 0, // 0 没有满减 1 用花材
+                callErrand: 0 //现在呼叫 0 晚点呼叫 1
             },
             proceeds: '0',
             hasPayList:[ { name:'扫码', id:0, },{name:'余额',id:4},{ name:'赊账', id:2, },{ name:'线下', id:1, }],
@@ -178,7 +270,20 @@ export default {
             diffData:[],
             discountList:[{name:'1折',value:0.1},{name:'2折',value:0.2},{name:'3折',value:0.3},{name:'4折',value:0.4},{name:'5折',value:0.5},{name:'6折',value:0.6},{name:'7折',value:0.7},{name:'8折',value:0.8},{name:'9折',value:0.9},{name:'不打折',value:1}],
             discountValue:0,
-            showDiscount:false
+            showDiscount:false,
+            deliveryQuotes: [], // 跑腿平台报价列表
+            selectedDeliveryIndex: -1,
+            selectedDeliveryData: null,
+            showDistance: 0,
+            openIntraCity: 0,
+            hcFreeKm: 0,
+            hcMap: [],
+            hsFreeKm: 0,
+            hsAddFee: 0,
+            pickupTime: {
+                label: '立即取件',
+                value: ''
+            },
         };
     },
     props:{
@@ -197,9 +302,13 @@ export default {
 		balance:{
 			type: Number,
 			default: 0
-		}
+		},
+        customId:{
+            type:Number,
+            default:0
+        }
     },
-    components:{ VKeyboard },
+    components:{ VKeyboard, TimePicker },
 	mixins: [productMins],
     computed:{
         ...mapGetters(["getLoginInfo"]),
@@ -249,6 +358,20 @@ export default {
                         this.changeHasPay(4)
                     }
 
+                    merchantDetail().then(res=>{
+                        if(res.code == 1){
+                            this.openIntraCity = res.data.openIntraCity
+                            this.merchantInfo = res.data
+                            this.hcFreeKm = res.data.hcFreeKm
+                            try {
+                                this.hcMap = typeof res.data.hcMap === 'string' ? JSON.parse(res.data.hcMap) : res.data.hcMap;
+                            } catch (e) {
+                                this.hcMap = [];
+                            }
+                            this.hsFreeKm = res.data.hsFreeKm
+                            this.hsAddFee = res.data.hsAddFee
+                        }
+                    })
                 }
             },
             immediate:true
@@ -324,8 +447,128 @@ export default {
             this.form.getGoods = val
         },
         changeSendType(val){
-            this.$util.hitVoice()
             this.form.sendType = val
+            if (val !== 2) {
+                this.$util.hitVoice()
+
+                this.deliveryQuotes = [];
+                this.selectedDeliveryIndex = -1;
+                this.selectedDeliveryData = null;
+            } else {
+                if (this.openIntraCity == 1) {
+                    this.getDeliveryQuotes();
+                }
+            }
+        },
+        checkMapRule(type) {
+            const distanceInMeters = Number(this.showDistance) || 0;
+            const freeKm = type === 'hc' ? this.hcFreeKm : 0; // 只处理花材
+            const freeKmInMeters = (Number(freeKm) || 0) * 1000;
+            if (distanceInMeters < freeKmInMeters) {
+                return true;
+            }
+            let map = type === 'hc' ? this.hcMap : [];
+            if (map && Array.isArray(map) && map.length > 0) {
+                let big = 0;
+                if (!this.$util.isEmpty(this.selectList)) {
+                    for (const ele of this.selectList) {
+                        if (Number(ele.bigCount) > 0) {
+                            big = Number(big) + Number(ele.bigCount);
+                        }
+                    }
+                }
+                const flowerCount = big || 0; 
+                const flowerPrice = this.allPrice || 0;
+                for (const rule of map) {
+                    const ruleNum = Number(rule.num) || 0;
+                    const rulePrice = Number(rule.price) || 0;
+                    const ruleDistance = (Number(rule.distance) || 0) * 1000;
+                    if (flowerCount >= ruleNum && flowerPrice >= rulePrice && distanceInMeters <= ruleDistance) {
+                        return true;
+                    }
+                }
+            }
+            return false;
+        },
+        isHcMapRule() {
+            return this.checkMapRule('hc');
+        },
+        getDeliveryQuotes() {
+            console.log('getDeliveryQuotes', Number(this.customId));
+            if(Number(this.customId) === 0){
+                this.$msg('请先选择客户');
+                return false;
+            }
+            quickOrderAllDeliveryQuotes({
+                totalPrice: this.allPrice,
+                weight: 1,
+                customId: this.customId, 
+                pickupTime: this.pickupTime.value
+            })
+            .then(res => {
+                console.log('getDeliveryQuotes res', res);
+                if (res.code == 1 && res.data && res.data.deliveryList) {
+                    this.deliveryQuotes = res.data.deliveryList
+                        .filter((item) => item.isAble !== false)
+                        .map((item) => {
+                            const price = item.price ? (item.price / 100).toFixed(1) : '0.0';
+                            let displayName = item.name || '';
+                            let bracketContent = '';
+                            if (displayName && displayName.indexOf('(') !== -1) {
+                                const parts = displayName.split('(');
+                                if (parts.length > 1) {
+                                    displayName = parts[0].trim();
+                                    bracketContent = parts[1].replace(')', '');
+                                }
+                            }
+                            if (item.en_name == 'didi' && bracketContent != '') {
+                                item.type = bracketContent;
+                            }
+                            return {
+                                ...item,
+                                displayName: displayName,
+                                priceText: price,
+                                priceNumber: parseFloat(price),
+                                bracketContent: bracketContent
+                            }
+                        })
+                    
+                    const firstAvailableIndex = this.deliveryQuotes.findIndex((item) => item.isAble);
+                    if (firstAvailableIndex !== -1) {
+                        this.selectDelivery(this.deliveryQuotes[firstAvailableIndex], firstAvailableIndex);
+                    }
+                    
+                    // this.selectedDeliveryIndex = -1;
+                    // this.selectedDeliveryData = null;
+                }
+            }).catch((err) => {
+                console.log('获取跑腿平台报价失败:', err);
+                if(err.code == 0){
+                    uni.showToast({ title: err.msg || '获取跑腿平台报价失败', icon: 'none', duration: 2500});
+                } else {
+                    uni.showToast({ title: '获取跑腿平台报价失败', icon: 'none' });
+                }
+                this.form.sendType = 1; // 切换回自取
+                this.deliveryQuotes = [];
+            })
+            .catch(err => {
+                console.log('getDeliveryQuotes err', err);
+            })
+        },
+        selectDelivery(item, index) {
+            this.selectedDeliveryIndex = index;
+            this.selectedDeliveryData = item;
+            this.form.sendCost = item.priceNumber;
+            this.showDistance = item.distance || 0;
+        },
+        openTimePicker() {
+            this.$refs.timePicker.open();
+        },
+        handleTimeConfirm(time) {
+            this.pickupTime = time;
+            if (this.form.sendType == 2) {
+                this.getDeliveryQuotes();
+            }
         },
         changeNeedPrint(val){
             this.$util.hitVoice()
@@ -435,6 +678,19 @@ export default {
                 this.$msg('应付金额不能大于总金额')
                 return false
             }
+
+            // 跑腿信息
+            if (this.form.sendType == 2 && this.selectedDeliveryData) {
+                this.form.deliveryPlatform = this.selectedDeliveryData.en_name;
+                this.form.deliveryPrice = this.selectedDeliveryData.priceNumber;
+                this.form.deliveryDistance = this.showDistance;
+                this.form.deliveryBracketContent = this.selectedDeliveryData.bracketContent;
+                this.form.deliveryAllParams = this.selectedDeliveryData
+                if (this.form.callErrand == 1) {
+                    this.form.pickupTime = this.pickupTime.value;
+                }
+            }
+
             this.$emit('settleCommit',this.form)
         },
         cancel(){
@@ -548,4 +804,130 @@ export default {
         overflow: auto;
     }
 }
+
+/* 跑腿平台报价样式 */
+.delivery-quotes-container {
+  width: 100%;
+  margin-top: 2upx;
+}
+.delivery-quotes-scroll {
+  width: 100%;
+  max-height: 210upx;
+  border-bottom: 1upx solid #eee;
+}
+.delivery-quotes-grid {
+  display: flex;
+  flex-wrap: wrap;
+  gap: 4upx;
+  padding: 0;
+  width: 100%;
+}
+.delivery-quote-item {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  flex: 0 0 calc(33% - 4upx);
+  width: calc(33% - 4upx);
+  min-height: 50upx;
+  padding: 4upx;
+  margin-bottom: 2upx;
+  background: #FFFFFF;
+  border: 1px solid #DDDDDD;
+  border-radius: 4upx;
+  box-sizing: border-box;
+  transition: all 0.3s;
+  
+  &.fixed-width {
+    flex: 0 0 100upx;
+    width: 100upx;
+  }
+  
+  .platform-name {
+    font-size: 11upx;
+    color: #333;
+    font-weight: bold;
+    margin-bottom: 2upx;
+    text-align: center;
+    line-height: 1.3;
+    max-width: 100%;
+    overflow: hidden;
+    text-overflow: ellipsis;
+    display: -webkit-box;
+    -webkit-line-clamp: 2;
+    line-clamp: 2;
+    -webkit-box-orient: vertical;
+    word-break: break-all;
+  }
+  
+  .platform-price {
+    font-size: 13upx;
+    color: #333;
+    font-weight: bold;
+    margin-bottom: 1upx;
+  }
+  
+  .platform-type {
+    font-size: 9upx;
+    color: #868686;
+    margin-top: 1upx;
+    text-align: center;
+    line-height: 1.3;
+    max-width: 100%;
+    overflow: hidden;
+    text-overflow: ellipsis;
+    display: -webkit-box;
+    -webkit-line-clamp: 2;
+    line-clamp: 2;
+    -webkit-box-orient: vertical;
+    word-break: break-all;
+  }
+  
+  &.active {
+    background: #09C567;
+    border-color: #09C567;
+    
+    .platform-name,
+    .platform-price {
+      color: #FFFFFF;
+    }
+    
+    .platform-type {
+      color: rgba(255, 255, 255, 0.85);
+    }
+  }
+  
+  &.disabled {
+    opacity: 0.5;
+    background: #F5F5F5;
+    
+    .platform-name,
+    .platform-price,
+    .platform-type {
+      color: #CCCCCC;
+    }
+  }
+}
+
+.delivery-section {
+    border: 1px solid #2ECE7E;
+    box-shadow: 0 0 8upx #2ECE7E;
+    margin: 5upx 0;
+    border-radius: 4upx;
+    padding: 5upx;
+}
+.empty-quotes-view {
+    text-align: center;
+    padding: 10upx 0;
+}
+.blue-text {
+    color: #3385FF;
+}
+.strike-through {
+    text-decoration: line-through;
+}
+.free-delivery-tag {
+    color: #3385FF;
+    margin-left: 5upx;
+}
 </style>