Просмотр исходного кода

Merge branch 'redesign‌-260706' of http://git.huaml.com/zhh/front-end into redesign‌-260706

shish 13 часов назад
Родитель
Сommit
eadab27b08

+ 122 - 1
hdApp/src/admin/home/psMethod.vue

@@ -156,6 +156,48 @@
             <view class="tui-title">{{ form.unMeet == 2 ? '包装费' : '运费' }}</view>
             <input type="digit" v-model="form.unMeetFee" @focus="form.unMeetFee = ''" class="tui-input" placeholder="请填写金额" />
           </tui-list-cell>
+          <!-- 附加费重收:自然日锁一天 或 按小时间隔冷却后再收 -->
+          <tui-list-cell class="line-cell" :hover="false" v-if="form.unMeet != 1">
+            <view class="tui-title">附加费重收</view>
+            <view class="btn-group">
+              <button
+                class="admin-button-com middle"
+                :class="[form.unMeetLockType == 0 ? 'blue' : 'default']"
+                @click="form.unMeetLockType = 0"
+              >
+                按自然日
+              </button>
+              <button
+                class="admin-button-com middle"
+                :class="[form.unMeetLockType == 1 ? 'blue' : 'default']"
+                @click="form.unMeetLockType = 1"
+                style="margin-left: 10upx;"
+              >
+                按时间间隔
+              </button>
+            </view>
+          </tui-list-cell>
+          <tui-list-cell class="line-cell" :hover="false" v-if="form.unMeet != 1 && form.unMeetLockType == 1">
+            <view class="tui-title">间隔时长</view>
+            <view class="input-row">
+              <input
+                type="digit"
+                v-model="form.unMeetLockHours"
+                @focus="form.unMeetLockHours = ''"
+                class="tui-input-sm"
+                placeholder="0.5"
+              />
+              <text class="unit-text">小时后再收</text>
+            </view>
+          </tui-list-cell>
+          <view
+            class="section-tip"
+            v-if="form.unMeet != 1"
+            style="padding: 0 30upx 20upx;"
+          >
+            <text v-if="form.unMeetLockType == 0">按自然日:同一客户当天收过该附加费后,当日不再收取。</text>
+            <text v-else>按时间间隔:收过附加费后,需间隔填写的小时数才会再次收取(支持小数,如 0.5)</text>
+          </view>
         </block>
 
         <!-- 跑腿 特有字段(花束运费、满减、自定义计费等) -->
@@ -349,6 +391,7 @@
  * 花店配送方式设置
  * 路由:admin/home/psMethod
  * 跑腿保存:无论用跑腿还是用自定义,都必须完整填写自定义计费;启用还需绑定跑腿或自定义完整
+ * 送货/快递:支持附加费重收规则(按自然日 或 按小时间隔冷却)
  */
 import TuiListCell from '@/components/plugin/list-cell'
 import { getPsMethodConfig, savePsMethodConfig, getPsMethodSorts } from '@/api/ps-method'
@@ -381,6 +424,11 @@ export default {
         minNum: 0,
         unMeet: 0,
         unMeetFee: 0.00,
+        /** 附加费重收:0 按自然日;1 按冷却秒(页面用小时录入) */
+        unMeetLockType: 0,
+        unMeetLockSeconds: 1800,
+        /** 仅编辑展示:小时,支持小数如 0.5 */
+        unMeetLockHours: 0.5,
         calcType: 0,
         hsFreeKm: 0.00,
         hsPerKmPrice: 0.00,
@@ -555,6 +603,11 @@ export default {
               minNum: data.minNum != null ? Number(data.minNum) : 0,
               unMeet: data.unMeet != null ? Number(data.unMeet) : 0,
               unMeetFee: data.unMeetFee != null ? parseFloat(data.unMeetFee) : 0.00,
+              unMeetLockType: data.unMeetLockType != null ? Number(data.unMeetLockType) : 0,
+              unMeetLockSeconds: data.unMeetLockSeconds != null ? Number(data.unMeetLockSeconds) : 1800,
+              unMeetLockHours: this.secondsToLockHours(
+                data.unMeetLockSeconds != null ? Number(data.unMeetLockSeconds) : 1800
+              ),
               calcType: data.calcType != null ? Number(data.calcType) : 0,
               hsFreeKm: data.hsFreeKm != null ? parseFloat(data.hsFreeKm) : 0.00,
               hsPerKmPrice: data.hsPerKmPrice != null ? parseFloat(data.hsPerKmPrice) : 0.00,
@@ -699,6 +752,64 @@ export default {
       this.$util.pageTo({ url: "/admin/shop/wl" });
     },
 
+    /**
+     * 冷却秒数 → 配置页小时(保留小数,去掉多余 0)
+     * @param {number} seconds
+     * @returns {number}
+     */
+    secondsToLockHours(seconds) {
+      const s = Number(seconds);
+      if (!(s > 0)) {
+        return 0.5;
+      }
+      return parseFloat((s / 3600).toFixed(4));
+    },
+
+    /**
+     * 配置页小时 → 冷却秒(四舍五入)
+     * @param {number|string} hours
+     * @returns {number}
+     */
+    hoursToLockSeconds(hours) {
+      const h = Number(hours);
+      if (!(h > 0)) {
+        return 0;
+      }
+      return Math.round(h * 3600);
+    },
+
+    /**
+     * 送货/快递附加费重收规则校验;通过则把小时写入 form.unMeetLockSeconds
+     * @returns {string} 错误文案,空表示通过
+     */
+    validateUnMeetLockBeforeSave() {
+      const style = Number(this.form.style);
+      if (style !== 0 && style !== 4) {
+        return "";
+      }
+      if (Number(this.form.unMeet) === 1) {
+        this.form.unMeetLockType = 0;
+        return "";
+      }
+      const lockType = Number(this.form.unMeetLockType) === 1 ? 1 : 0;
+      this.form.unMeetLockType = lockType;
+      if (lockType !== 1) {
+        // 自然日模式仍保留秒数字段默认值,便于切回间隔时有底
+        if (!(Number(this.form.unMeetLockSeconds) > 0)) {
+          this.form.unMeetLockSeconds = 1800;
+          this.form.unMeetLockHours = 0.5;
+        }
+        return "";
+      }
+      const seconds = this.hoursToLockSeconds(this.form.unMeetLockHours);
+      if (!(seconds > 0)) {
+        return "请填写大于 0 的间隔小时(支持小数,如 0.5)";
+      }
+      this.form.unMeetLockSeconds = seconds;
+      this.form.unMeetLockHours = this.secondsToLockHours(seconds);
+      return "";
+    },
+
     /**
      * 保存当前配置
      */
@@ -720,6 +831,12 @@ export default {
         }
       }
 
+      const lockErr = this.validateUnMeetLockBeforeSave();
+      if (lockErr) {
+        this.$msg(lockErr);
+        return;
+      }
+
       const runnerErr = this.validateRunnerBeforeSave()
       if (runnerErr) {
         this.$msg(runnerErr)
@@ -728,7 +845,11 @@ export default {
 
       savePsMethodConfig(this.form).then(res => {
           if (res.code == 1) {
-            this.$msg("保存成功");
+            if (Number(this.form.unMeetLockType) === 1 && (Number(this.form.style) === 0 || Number(this.form.style) === 4)) {
+              this.$msg(`保存成功,间隔 ${this.form.unMeetLockHours} 小时后再收`);
+            } else {
+              this.$msg("保存成功");
+            }
             // 重新加载配置刷新数据
             this.loadConfig(this.form.style);
             // 重新加载排序和别名信息

+ 8 - 2
hdPad/src/pages/home/components/console.vue

@@ -386,7 +386,10 @@ export default {
 		cancelSelectPrice(){
 			this.$refs.gatheringRef.close()
 		},
-		//直接收款
+		/**
+		 * 计算器直接收款:用户输入(及手选折)即最终应收。
+		 * 提交后不再套会员折扣/门店满减;传 skip* 避免 createHdOrder/addOrder 二次扣减。
+		 */
 		zjGathering(params){
 			uni.showLoading()
 			const payload = {
@@ -402,7 +405,10 @@ export default {
 				sendType: 1,
 				getGoods: 1,
 				hasPay: params.hasPay != null ? params.hasPay : 0,
-				payWay: Number(params.hasPay) === 1 ? (params.payWay != null ? params.payWay : 0) : 0
+				payWay: Number(params.hasPay) === 1 ? (params.payWay != null ? params.payWay : 0) : 0,
+				// 金额已是最终订单金额,禁止服务端再套会员折扣/门店满减(对齐 hdApp calculator)
+				skipMemberDiscount: 1,
+				skipShopMeetCut: 1
 			}
 			createOrder(payload).then(res=>{
 				uni.hideLoading()

+ 1 - 0
hdPad/src/pages/home/components/selectPrice.vue

@@ -1234,6 +1234,7 @@ export default {
                 source: 'submit',
                 orderOnly: !hasCalc,
             })
+            // 提交价即最终应收(含计算器手选折);父级 createOrder 须 skip 会员折扣/门店满减
             this.$emit('zjGathering', {
                 price: p,
                 name: this.name,

+ 29 - 16
mallApp/src/pages/billing/affirmMix.vue

@@ -737,10 +737,11 @@ export default {
 			return Math.round(price * 100) / 100;
 		},
 		/**
-		 * 整单互斥优惠前的应付:先按商品+运费包装-红包得到应付,再剔除秒杀额。
-		 * 与后端 actionCreateMixOrder 红包后的 modifyPrice,经 capSeckillExcludePrice 后的优惠基数对齐。
+		 * 整单互斥优惠前的基数(剔秒杀前):商品+运费包装-红包。
+		 * 跑腿费(sendType=2 的 sendCost)不参与会员折/门店满减基数,实付时折后原额加回;
+		 * 红包只抵商品不抵运费。与后端 actionCreateMixOrder 的 wholeDiscountPrice 口径对齐。
 		 */
-		wholeDiscountBasePrice() {
+		wholePriceBeforeSeckill() {
 			let price = Number(this.mixGoodsPrice) || 0;
 
 			if (this.hasAvailableHb && this.selectedHbId) {
@@ -748,21 +749,27 @@ export default {
 			}
 			if (price < 0) price = 0;
 
-			const unMeetSend = Number(this.form.unMeetSendCost) || 0;
-			const packCost = Number(this.form.packCost) || 0;
-
-			if (this.form.sendType == 2) {
-				price = price + Number(this.isFreeDelivery ? 0 : this.form.sendCost || 0) + unMeetSend + packCost;
-			} else if ([0, 3, 4].includes(Number(this.form.sendType))) {
-				price = price + unMeetSend + packCost;
+			// 跑腿费不计入折扣基数,各配送方式仅加运费与包装费
+			if ([0, 2, 3, 4].includes(Number(this.form.sendType))) {
+				price = price + (Number(this.form.unMeetSendCost) || 0) + (Number(this.form.packCost) || 0);
 			}
-
-			// 秒杀价不参与会员折/门店满减;剔除额不超过当前应付,避免红包已吃进秒杀时把基数打成负数
+			return parseFloat(price.toFixed(2));
+		},
+		/**
+		 * 从折扣基数剔除的秒杀额(不超过基数),实付时原额加回。
+		 * 与后端 capSeckillExcludePrice 对齐。
+		 */
+		seckillExcludePrice() {
 			let seckillPrice = Number(this.mixSeckillGoodsPrice) || 0;
-			if (seckillPrice > price) seckillPrice = price;
-			if (seckillPrice > 0) {
-				price = price - seckillPrice;
-			}
+			const base = Number(this.wholePriceBeforeSeckill) || 0;
+			if (seckillPrice > base) seckillPrice = base;
+			return seckillPrice > 0 ? parseFloat(seckillPrice.toFixed(2)) : 0;
+		},
+		/**
+		 * 整单互斥优惠基数:商品+运费包装-红包-秒杀额(不含跑腿费)。
+		 */
+		wholeDiscountBasePrice() {
+			let price = (Number(this.wholePriceBeforeSeckill) || 0) - (Number(this.seckillExcludePrice) || 0);
 			if (price < 0) price = 0;
 			return parseFloat(price.toFixed(2));
 		},
@@ -793,6 +800,12 @@ export default {
 		modifyPrice() {
 			let price = Number(this.wholeDiscountBasePrice) || 0;
 			price = price - (Number(this.wholeDiscountPick.amount) || 0);
+			// 秒杀额不参与折扣但计入实付,原额加回
+			price = price + (Number(this.seckillExcludePrice) || 0);
+			// 跑腿费不参与整单折扣,折后原额加回计入实付(免跑腿时 sendCost 已为 0)
+			if (Number(this.form.sendType) === 2 && !this.isFreeDelivery) {
+				price = price + (Number(this.form.sendCost) || 0);
+			}
 			if (price < 0) price = 0;
 			return parseFloat(price.toFixed(2));
 		},