shizhongqi 1 день назад
Родитель
Сommit
4c381469c2

+ 138 - 12
hdApp/src/admin/billing/affirm.vue

@@ -303,8 +303,10 @@
             <view v-else-if="form.hasPay == 3" class="tui-title">余额扣减</view>
             <view v-else class="tui-title">实际金额</view>
             <input style="border:1upx solid #eee;color:#3385FF;width:150upx;height:70upx;" type="digit" @focus="modifyPrice=''" v-model="modifyPrice" placeholder-class="tui-placeholder"/>
+            <!-- 有会员折扣/门店满减时右侧展示具体优惠;人工加价仍显示包装费;其它降价显示通用优惠 -->
             <text v-if="modifyPrice > 0 && modifyPrice > systemPrice" style="font-size:25upx;margin-left:20upx;color:red;">人工包装费 ¥{{parseFloat((modifyPrice-systemPrice).toFixed(2))}}</text>
-            <text v-if="modifyPrice > 0 && modifyPrice < systemPrice" style="font-size:25upx;margin-left:20upx;color:green;">优惠 ¥{{parseFloat((systemPrice-modifyPrice).toFixed(2))}}</text>
+            <text v-else-if="wholeDiscountLabel" style="font-size:25upx;margin-left:20upx;color:#ff2842;">{{ wholeDiscountLabel }} {{ wholeDiscountAmountText }}</text>
+            <text v-else-if="modifyPrice > 0 && modifyPrice < systemPrice" style="font-size:25upx;margin-left:20upx;color:green;">优惠 ¥{{parseFloat((systemPrice-modifyPrice).toFixed(2))}}</text>
           </tui-list-cell>
 
 				<tui-list-cell class="line-cell" :hover="false" :arrow="true">
@@ -545,6 +547,7 @@ export default {
 			reachDatePickerShow: false,
 			reachDatePickerValue: '',
       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}],
+      /** 0=未选手动打折(走会员/门店互斥);>0 已选手动折扣,1=不打折(也不套会员/门店) */
       discountValue:0,
       customDiscount:'',
       autoLoad: false,
@@ -751,11 +754,22 @@ export default {
         this.getDeliveryQuotes();
       }
     },
+    // 合计金额变化时,默认实际金额扣掉互斥整单优惠(会员折扣/门店满减取大)
     systemPrice: {
       handler(val) {
-        this.modifyPrice = val
+        this.syncModifyPriceWithWholeDiscount(val)
       },
       immediate: true
+    },
+    // 客户折扣或门店满减配置变化时同步默认实际金额
+    'customInfo.discount'() {
+      this.syncModifyPriceWithWholeDiscount(this.systemPrice)
+    },
+    currentShop: {
+      handler() {
+        this.syncModifyPriceWithWholeDiscount(this.systemPrice)
+      },
+      deep: true
     }
   },
   computed: {
@@ -795,7 +809,7 @@ export default {
         return price >= min
       })
     },
-    // 合计金额
+    // 合计金额:商品+运费-红包(互斥整单优惠前的基数)
     systemPrice(){
       let finalPrice = this.preDiscountPrice
 
@@ -807,18 +821,123 @@ export default {
 
       return parseFloat(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
-    // }
+    /**
+     * 会员折扣与门店满减互斥结果,口径对齐 WholeOrderDiscountClass。
+     * bigNum 用花材扎数,与 create-order 的 bigNum/totalNum 一致。
+     */
+    wholeDiscountPick() {
+      const book = this.option && this.option.book ? Number(this.option.book) : 0
+      const bigNum = Number(this.allCount && this.allCount.bigLength) || 0
+      return this.pickWholeOrderDiscount(this.systemPrice, bigNum, book)
+    },
+    /** 实际金额右侧文案:会员折扣 / 门店满减优惠;手动选打折或不打折后不再展示 */
+    wholeDiscountLabel() {
+      // 店员已选手动打折(含「不打折」),不再展示会员/门店优惠
+      if (this.isManualDiscountActive) return ''
+      const type = this.wholeDiscountPick.type
+      if (!(Number(this.wholeDiscountPick.amount) > 0)) return ''
+      if (type === 'member') return '会员折扣'
+      if (type === 'shop') return '门店满减优惠'
+      return ''
+    },
+    /** 实际金额右侧金额,如 -¥10 */
+    wholeDiscountAmountText() {
+      if (this.isManualDiscountActive) return ''
+      const amount = Number(this.wholeDiscountPick.amount) || 0
+      if (amount <= 0) return ''
+      return `-¥${parseFloat(amount.toFixed(2))}`
+    },
+    /**
+     * 是否已手动操作打折弹层:discountValue>0(含 1=不打折)。
+     * 不打折时实际金额=合计原价,也不再套会员折扣/门店满减。
+     */
+    isManualDiscountActive() {
+      return Number(this.discountValue) > 0
+    },
   },
   methods: {
     init () {},
     ...mapActions(["initMerchantInfo"]),
+    /**
+     * 回写默认实际金额:
+     * - 已手动选打折/不打折:systemPrice * discountValue(不打折时为 1,即原价)
+     * - 未手动选:systemPrice - 互斥整单优惠额
+     */
+    syncModifyPriceWithWholeDiscount(systemPrice) {
+      const base = Number(systemPrice) || 0
+      if (this.isManualDiscountActive) {
+        const rate = Number(this.discountValue) || 1
+        this.modifyPrice = parseFloat((base * rate).toFixed(2))
+        return
+      }
+      const book = this.option && this.option.book ? Number(this.option.book) : 0
+      const bigNum = Number(this.allCount && this.allCount.bigLength) || 0
+      const pick = this.pickWholeOrderDiscount(base, bigNum, book)
+      let price = base - (Number(pick.amount) || 0)
+      if (price < 0) price = 0
+      this.modifyPrice = parseFloat(price.toFixed(2))
+    },
+    /**
+     * 会员折扣与门店满减互斥取大,对齐后端 WholeOrderDiscountClass。
+     * @param {number} basePrice 合计金额(已减红包)
+     * @param {number} bigNum 花材扎数
+     * @param {number} book 预订单 book!=0 时不算门店满减
+     */
+    pickWholeOrderDiscount(basePrice, bigNum, book) {
+      const memberSave = this.calcMemberDiscountSave(basePrice)
+      const shopSave = Number(book) !== 0 ? 0 : this.calcShopMeetCutSave(basePrice, bigNum)
+      // 相等时用会员折扣,只扣一笔
+      if (memberSave >= shopSave && memberSave > 0) {
+        return { type: 'member', amount: memberSave }
+      }
+      if (shopSave > 0) {
+        return { type: 'shop', amount: shopSave }
+      }
+      return { type: 'none', amount: 0 }
+    },
+    /**
+     * 会员折扣额:custom.discount ∈ (0,1) 时 base*(1-discount);折后≤0 按后端保底 0.01。
+     */
+    calcMemberDiscountSave(basePrice) {
+      const base = Number(Number(basePrice || 0).toFixed(2))
+      const discount = Number(this.customInfo && this.customInfo.discount)
+      if (!(discount > 0 && discount < 1) || base <= 0) {
+        return 0
+      }
+      let after = Number((base * discount).toFixed(2))
+      if (after <= 0) {
+        after = 0.01
+      }
+      const save = Number((base - after).toFixed(2))
+      return save > 0 ? save : 0
+    },
+    /**
+     * 门店满减额:件数+金额双门槛,cutStyle=0 固定额、=1 按折扣乘算;满减不得 ≥ 应付。
+     */
+    calcShopMeetCutSave(basePrice, bigNum) {
+      const base = Number(Number(basePrice || 0).toFixed(2))
+      const shop = this.currentShop || {}
+      const meetNum = Number(shop.meetNum) || 0
+      const meetAmount = Number(shop.meetAmount) || 0
+      const cutOption = Number(shop.cutAmount) || 0
+      const cutStyle = Number(shop.cutStyle) || 0
+      let cutAmount = 0
+      if (cutStyle === 0) {
+        cutAmount = Number(cutOption.toFixed(2))
+      } else {
+        cutAmount = Number((base * (1 - cutOption)).toFixed(2))
+      }
+      if (!(cutAmount > 0 && meetNum >= 0 && meetAmount > 0)) {
+        return 0
+      }
+      if (Number(bigNum || 0) < meetNum || base < meetAmount) {
+        return 0
+      }
+      if (cutAmount >= base) {
+        return 0
+      }
+      return cutAmount
+    },
     checkMapRule(type){
       const distanceInMeters = Number(this.showDistance) || 0
       const freeKm = type === 'hc' ? this.hcFreeKm : this.hsFreeKm
@@ -945,8 +1064,12 @@ export default {
       this.$refs.discountRef.open('center')
     },
     setDiscount(num){
+      // 含「不打折」(value=1):实际金额按手选倍率,不再套会员折扣/门店满减
       this.discountValue = Number(num)
-      let price = Number(this.systemPrice)*Number(num)
+      if (!(this.discountValue > 0)) {
+        this.discountValue = 1
+      }
+      let price = Number(this.systemPrice) * Number(this.discountValue)
       price = parseFloat(price.toFixed(2))
       this.modifyPrice = price
       this.customDiscount = ''
@@ -1068,6 +1191,9 @@ export default {
       formData.shopId = this.getMerchantInfo.id;
 
       let params = {...formData,modifyPrice: this.modifyPrice}; //labourCost:this.labourCost
+      // 本页已把会员折扣/门店满减算进 modifyPrice,禁止服务端再折/再满减
+      params.skipMemberDiscount = 1
+      params.skipShopMeetCut = 1
 
       // 如果选择了跑腿配送并且有选中的平台数据,则添加配送平台信息
       if (this.form.sendType == 2 && this.selectedDeliveryData) {

+ 35 - 5
hdApp/src/admin/tools/calculator.vue

@@ -252,6 +252,10 @@
 </template>
 
 <script>
+/**
+ * hdApp 工具-计算器直接收款:用户输入(及手选折)即应收。
+ * 不套会员折扣、门店满减;createOrder 传 skipMemberDiscount / skipShopMeetCut,避免服务端二次扣减。
+ */
 import { createOrder } from '@/api/order/index'
 import { getCustomInfo } from '@/api/custom'
 import { mapActions, mapGetters } from 'vuex'
@@ -299,6 +303,11 @@ export default {
 				{ name: '银行卡', id: 5 },
 			],
 			customInfo: { discount: 1, balance: 0 },
+			/**
+			 * 是否已在打折弹层选手动折扣(含「无折扣」)。
+			 * 仅影响手选折展示;会员折扣/门店满减不再参与应收。
+			 */
+			manualDiscountLocked: false,
 		}
 	},
 	computed: {
@@ -336,13 +345,17 @@ export default {
 			if (isNaN(z) || z <= 0) return 1
 			return Math.min(10, Math.max(0.01, z)) / 10
 		},
+		/**
+		 * 应收金额:算式合计 × 手选折扣(默认 10 折即原价)。
+		 * 不套会员折扣、门店满减;提交时 skip* 避免服务端再扣。
+		 */
 		stackDiscountedAmount() {
 			const raw = Number(this.stackTotal)
-			if (isNaN(raw)) return 0
+			if (isNaN(raw) || raw <= 0) return 0
 			return parseFloat((raw * this.discountRate).toFixed(2))
 		},
 		isDiscountActive() {
-			return Number(this.discountZhe) < 10 - 1e-6
+			return this.manualDiscountLocked && Number(this.discountZhe) < 10 - 1e-6
 		},
 		zl() {
 			if (!(this.selHasPay === 1 && this.selPayWay === 4)) return 0
@@ -525,7 +538,11 @@ export default {
 				const sel = uni.getStorageSync(DISCOUNT_SELECTED_STORAGE_KEY)
 				if (sel !== undefined && sel !== null && sel !== '') {
 					const z = Number(sel)
-					if (!isNaN(z) && z > 0 && z <= 10) this.discountZhe = parseFloat(z.toFixed(2))
+					if (!isNaN(z) && z > 0 && z <= 10) {
+						this.discountZhe = parseFloat(z.toFixed(2))
+						// 本地存过小于 10 折视为曾选手动折扣;10 折不锁,应收即用户输入
+						this.manualDiscountLocked = z < 10 - 1e-6
+					}
 				}
 				this.normalizeDiscountSelectionToCustomOrNone()
 			} catch (e) {
@@ -536,16 +553,19 @@ export default {
 			const z = Number(this.discountZhe)
 			if (isNaN(z)) {
 				this.discountZhe = 10
+				this.manualDiscountLocked = false
 				this.persistDiscountSelected()
 				return
 			}
 			if (z >= 10 - 1e-6) {
 				this.discountZhe = 10
+				// 未选手动折或仅为默认 10:应收即用户输入;若曾在弹层点过「无折扣」则 locked 仍为 true
 				return
 			}
 			const ok = this.customDiscountZheList.some((x) => Math.abs(Number(x) - z) < 1e-4)
 			if (!ok) {
 				this.discountZhe = 10
+				this.manualDiscountLocked = false
 				this.persistDiscountSelected()
 			}
 		},
@@ -564,7 +584,9 @@ export default {
 			}
 		},
 		resetActiveDiscountToNone() {
+			// 清空手选折扣,应收回到用户输入原价(不套会员折扣/门店满减)
 			this.discountZhe = 10
+			this.manualDiscountLocked = false
 			this.persistDiscountSelected()
 		},
 		formatDiscountZheLabel(z) {
@@ -630,6 +652,8 @@ export default {
 				}
 			}
 			this.discountZhe = parseFloat(n.toFixed(2))
+			// 含「无折扣」:锁定手选折;会员折扣/门店满减不参与应收
+			this.manualDiscountLocked = true
 			this.persistDiscountSelected()
 			this.closeDiscountPopup()
 		},
@@ -987,10 +1011,13 @@ export default {
 			}
 			if (z >= 10 - 1e-6) {
 				this.discountZhe = 10
+				this.manualDiscountLocked = false
 				return
 			}
 			const ok = this.customDiscountZheList.some((x) => Math.abs(Number(x) - z) < 1e-4)
 			this.discountZhe = ok ? z : 10
+			// 暂存里带了有效手选折则锁上手选,避免只改数字却不显示「X折后」
+			this.manualDiscountLocked = ok
 		},
 		applyHistoryRow(row) {
 			if (!row) return
@@ -1184,7 +1211,7 @@ export default {
 				emitHasPay !== 4
 			) {
 				const that = this
-				that.$util.confirmModal({content:'余额充,可以使用余额支付'},() => {
+				that.$util.confirmModal({content:'余额充,可以使用余额支付'},() => {
 					that._submitCashierPayCore(true)
 				})
 				return
@@ -1208,6 +1235,9 @@ export default {
 				getGoods: 1,
 				hasPay: emitHasPay != null ? emitHasPay : 0,
 				payWay: Number(emitHasPay) === 1 ? (emitPayWay != null ? emitPayWay : 0) : 0,
+				// 计算器按用户输入(及手选折)收款,禁止服务端再套会员折扣/门店满减
+				skipMemberDiscount: 1,
+				skipShopMeetCut: 1,
 			}
 			const finalHasPay = payload.hasPay
 			uni.showLoading({ mask: true })
@@ -2067,4 +2097,4 @@ export default {
 	color: #c5ccd6;
 	padding: 60upx 0;
 }
-</style>
+</style>

+ 5 - 4
mallApp/src/components/home/navGrid.vue

@@ -103,11 +103,12 @@ export default {
   box-sizing: border-box;
   padding: 0 11upx;
 }
+/* 金刚区图标:无底色,图片铺满圆形裁切(小程序需 overflow:hidden 才能圆角生效) */
 .nav-icon-wrap {
   width: 117upx;
   height: 117upx;
   border-radius: 50%;
-  background: #FFF3F5;
+  overflow: hidden;
   display: flex;
   align-items: center;
   justify-content: center;
@@ -118,9 +119,9 @@ export default {
 }
 /* display:block 保证小程序端自定义组件包裹节点能接受宽高,否则内部图片会塌陷 */
 .nav-icon-img {
-  display: block;
-  width: 80upx;
-  height: 80upx;
+  width: 117upx;
+  height: 117upx;
+  border-radius: 50%;
 }
 .nav-name {
   font-size: 24upx;

+ 209 - 52
mallApp/src/pages/billing/affirmMix.vue

@@ -1,6 +1,8 @@
 <!--
   混合结算页(花束+花材),立即购买
   用途:商城混合购物车确认下单,支持多种配送方式、红包、贺卡与匿名配送
+  整单优惠:会员折扣与门店满减互斥,左侧名称、右侧 -¥N,只展示优惠金额更大的一项
+  布局:贺卡/匿名/备注后为运费+折扣+红包同一卡片;贺卡默认不需要,匿名默认否,备注单行
   顶部公告:/shop-notice/show-list position=8(订单提交),公告条与分类页 shop-category 同款(noticeBar)
 -->
 <template>
@@ -296,12 +298,6 @@
 						</view>
 					</view>
 				</view>
-				<view class="affirm-card fee-card" v-if="Number(form.unMeetSendCost) > 0">
-					<view class="fee-row">
-						<text class="fee-label">运费</text>
-						<text class="accent-text">¥{{ form.unMeetSendCost }}</text>
-					</view>
-				</view>
 				<view class="affirm-card fee-card" v-if="(form.sendType == 2 || form.sendType == 0 || form.sendType == 3 || form.sendType == 4) && Number(form.packCost) > 0">
 					<view class="fee-row">
 						<text class="fee-label">包装费</text>
@@ -309,7 +305,7 @@
 					</view>
 				</view>
 
-				<!-- 贺卡:标题 + 切换按钮 + 多行输入,样式对齐设计稿 -->
+				<!-- 贺卡:默认不需要 -->
 				<view class="affirm-card card-section">
 					<text class="plain-section-title">贺卡</text>
 					<view class="card-toggle">
@@ -338,49 +334,65 @@
 					</view>
 				</view>
 
-				<!-- 红包/红包行:样式对齐设计稿,整行可点(弹层组件放页面级,避免点击被父级拦截) -->
-				<view class="affirm-card hb-row-card" @click="openHbPopup">
-					<view class="hb-row-left">
-						<sprite-icon name="youhuiquan" sheet="order" :height="35" custom-class="hb-row-icon" />
-						<text class="hb-row-title">红包</text>
-						<text class="hb-row-desc">选择红包</text>
-					</view>
-					<view class="hb-row-right">
-						<text
-							class="hb-row-value"
-							:class="{
-								'hb-row-value--highlight': selectedHbId,
-								'hb-row-value--available': hasAvailableHb && !selectedHbId
-							}"
-						>{{ hbRowText }}</text>
-						<text class="iconfont iconxiangyou hb-row-arrow"></text>
-					</view>
-				</view>
-
-				<!-- 匿名配送:文案左、当前是/否 + 开关右,默认开启 -->
+				<!-- 匿名配送:默认否 -->
 				<view class="affirm-card anonymity-row">
 					<text class="anonymity-label">是否匿名</text>
 					<view class="anonymity-control">
-						<!-- 开关旁展示当前选择,避免只靠颜色判断是或否 -->
 						<text class="anonymity-value">{{ form.anonymity == 1 ? '是' : '否' }}</text>
 						<switch :checked="form.anonymity == 1" @change="onAnonymityChange" color="#77c34f" />
 					</view>
 				</view>
 
-				<!-- 备注 -->
+				<!-- 备注:单行输入 -->
 				<view class="affirm-card remark-section">
 					<text class="plain-section-title">备注</text>
-					<view class="remark-wrap textarea-box">
-						<textarea
-							class="remark-input"
+					<view class="remark-wrap remark-line-box">
+						<input
+							class="remark-line-input"
+							type="text"
 							v-model="form.remark"
-							maxlength="200"
+							maxlength="50"
 							placeholder="请输入备注信息"
 							placeholder-class="input-placeholder"
-							:auto-height="false"
-							:show-confirm-bar="false"
+							confirm-type="done"
 						/>
-						<text class="char-count char-count--inner">{{ remarkLength }}/200</text>
+					</view>
+				</view>
+
+				<!-- 运费、会员折扣、红包同一卡片;红包行单独可点,避免点运费也弹层 -->
+				<view class="affirm-card fee-card fee-hb-card">
+					<view class="fee-row" v-if="Number(form.unMeetSendCost) > 0">
+						<text class="fee-label">运费</text>
+						<text class="accent-text">¥{{ form.unMeetSendCost }}</text>
+					</view>
+					<view
+						class="fee-row"
+						:class="{ 'fee-row--follow': Number(form.unMeetSendCost) > 0 }"
+						v-if="wholeDiscountLabel"
+					>
+						<text class="fee-label">{{ wholeDiscountLabel }}</text>
+						<text :class="wholeDiscountAmountClass">{{ wholeDiscountAmountText }}</text>
+					</view>
+					<view
+						class="hb-row-inner"
+						:class="{ 'fee-row--follow': Number(form.unMeetSendCost) > 0 || !!wholeDiscountLabel }"
+						@click="openHbPopup"
+					>
+						<view class="hb-row-left">
+							<sprite-icon name="youhuiquan" sheet="order" :height="35" custom-class="hb-row-icon" />
+							<text class="hb-row-title">红包</text>
+							<text class="hb-row-desc">选择红包</text>
+						</view>
+						<view class="hb-row-right">
+							<text
+								class="hb-row-value"
+								:class="{
+									'hb-row-value--highlight': selectedHbId,
+									'hb-row-value--available': hasAvailableHb && !selectedHbId
+								}"
+							>{{ hbRowText }}</text>
+							<text class="iconfont iconxiangyou hb-row-arrow"></text>
+						</view>
 					</view>
 				</view>
 			</form>
@@ -548,12 +560,14 @@ export default {
 				openIntraCity: 0,
 				receiveUserName: "",
 				receiveMobile: "",
-				needCard: 1,
+				needCard: 0,
 				cardInfo: "",
-				anonymity: 1
+				anonymity: 0
 			},
 			userInfo: {},
 			shopInfo: {},
+			/** 花店客户折扣 0~1,1 表示无折扣;与后端 custom.discount 对齐 */
+			customDiscount: 1,
 			account: 0,
 			showDistance: 0,
 			deliveryQuotes: [],
@@ -712,7 +726,11 @@ export default {
 				return isHbAvailable(item, this.hbProductList, this.allPriceFun);
 			});
 		},
-		modifyPrice() {
+		/**
+		 * 整单互斥优惠前的应付:商品合计 + 运费/包装 - 红包。
+		 * 与后端 actionCreateMixOrder 红包后的 modifyPrice 对齐,作为会员折扣/门店满减比较基数。
+		 */
+		wholeDiscountBasePrice() {
 			let price = Number(this.mixGoodsPrice) || 0;
 
 			if (this.hasAvailableHb && this.selectedHbId) {
@@ -728,8 +746,37 @@ export default {
 			} else if ([0, 3, 4].includes(Number(this.form.sendType))) {
 				price = price + unMeetSend + packCost;
 			}
-			price = price.toFixed(2);
-			return parseFloat(price);
+			return parseFloat(price.toFixed(2));
+		},
+		/**
+		 * 会员折扣与门店满减互斥结果:type=member|shop|none,amount 为优惠额。
+		 */
+		wholeDiscountPick() {
+			return this.pickWholeOrderDiscount(this.wholeDiscountBasePrice, this.mixOrderBigNum);
+		},
+		/** 确认页费用行左侧:会员折扣 / 门店满减优惠 */
+		wholeDiscountLabel() {
+			const type = this.wholeDiscountPick.type;
+			if (!(Number(this.wholeDiscountPick.amount) > 0)) return "";
+			if (type === "member") return "会员折扣";
+			if (type === "shop") return "门店优惠";
+			return "";
+		},
+		/** 确认页费用行右侧优惠金额,如 -¥10 */
+		wholeDiscountAmountText() {
+			const amount = Number(this.wholeDiscountPick.amount) || 0;
+			if (amount <= 0) return "";
+			return `-¥${parseFloat(amount.toFixed(2))}`;
+		},
+		/** 会员折扣、门店满减右侧金额统一用 #ff2842 */
+		wholeDiscountAmountClass() {
+			return "fee-discount-amount";
+		},
+		modifyPrice() {
+			let price = Number(this.wholeDiscountBasePrice) || 0;
+			price = price - (Number(this.wholeDiscountPick.amount) || 0);
+			if (price < 0) price = 0;
+			return parseFloat(price.toFixed(2));
 		},
 		/** 混合单花材扎数(与提交 product.num 一致:仅支数时按 smallCount/ratio 折算) */
 		mixItemBigNum() {
@@ -904,9 +951,6 @@ export default {
 		cardInfoLength() {
 			return (this.form.cardInfo || "").length;
 		},
-		remarkLength() {
-			return (this.form.remark || "").length;
-		},
 		/** 是否已选择有效收货地址 */
 		hasSelectedAddress() {
 			return this.validAddress && !this.$util.isEmpty(this.userInfo.lat) && !this.$util.isEmpty(this.userInfo.long);
@@ -1008,6 +1052,67 @@ export default {
 		}
 	},
 	methods: {
+		/**
+		 * 会员折扣与门店满减互斥取大,口径对齐后端 WholeOrderDiscountClass。
+		 * @param {number} basePrice 商品+运费包装-红包
+		 * @param {number} bigNum 花材扎数+花束份数
+		 * @returns {{ type: string, amount: number }}
+		 */
+		pickWholeOrderDiscount(basePrice, bigNum) {
+			const memberSave = this.calcMemberDiscountSave(basePrice);
+			const shopSave = this.calcShopMeetCutSave(basePrice, bigNum);
+			// 相等时用会员折扣,只扣一笔
+			if (memberSave >= shopSave && memberSave > 0) {
+				return { type: "member", amount: memberSave };
+			}
+			if (shopSave > 0) {
+				return { type: "shop", amount: shopSave };
+			}
+			return { type: "none", amount: 0 };
+		},
+		/**
+		 * 会员折扣额:custom.discount ∈ (0,1) 时 base*(1-discount);折后≤0 按后端保底 0.01。
+		 */
+		calcMemberDiscountSave(basePrice) {
+			const base = Number(Number(basePrice || 0).toFixed(2));
+			const discount = Number(this.customDiscount);
+			if (!(discount > 0 && discount < 1) || base <= 0) {
+				return 0;
+			}
+			let after = Number((base * discount).toFixed(2));
+			if (after <= 0) {
+				after = 0.01;
+			}
+			const save = Number((base - after).toFixed(2));
+			return save > 0 ? save : 0;
+		},
+		/**
+		 * 门店满减额:件数+金额双门槛,cutStyle=0 固定额、=1 按折扣乘算;满减不得 ≥ 应付。
+		 */
+		calcShopMeetCutSave(basePrice, bigNum) {
+			const base = Number(Number(basePrice || 0).toFixed(2));
+			const shop = this.shopInfo || {};
+			const meetNum = Number(shop.meetNum) || 0;
+			const meetAmount = Number(shop.meetAmount) || 0;
+			const cutOption = Number(shop.cutAmount) || 0;
+			const cutStyle = Number(shop.cutStyle) || 0;
+			let cutAmount = 0;
+			if (cutStyle === 0) {
+				cutAmount = Number(cutOption.toFixed(2));
+			} else {
+				cutAmount = Number((base * (1 - cutOption)).toFixed(2));
+			}
+			if (!(cutAmount > 0 && meetNum >= 0 && meetAmount > 0)) {
+				return 0;
+			}
+			if (Number(bigNum || 0) < meetNum || base < meetAmount) {
+				return 0;
+			}
+			if (cutAmount >= base) {
+				return 0;
+			}
+			return cutAmount;
+		},
 		/**
 		 * 将购物车 selectList 按行唯一键合并,得到结算明细(购物车结算用)。
 		 * 立即购买模式不走这里,直接使用详情页传入的单商品明细。
@@ -1210,6 +1315,8 @@ export default {
 						this.mergePickupShopInfo(res.data.info, res.data.hd);
 						uni.setStorageSync("currentShop", res.data.info);
 					}
+					// 客户折扣用于整单互斥优惠;无客户时按 1(不打折)
+					this.applyCustomDiscount(res.data.custom, res.data.hd);
 					this.getHbData();
 				}
 			}).catch(() => {});
@@ -1227,6 +1334,23 @@ export default {
 				}
 			}).catch(() => {});
 		},
+		/**
+		 * 写入花店客户折扣,供会员折扣与门店满减互斥比较。
+		 * 优先 custom.discount,其次 hd.discount;无效则视为 1(不打折)。
+		 */
+		applyCustomDiscount(custom, hd) {
+			const fromCustom = custom && custom.discount != null ? Number(custom.discount) : NaN;
+			const fromHd = hd && hd.discount != null ? Number(hd.discount) : NaN;
+			if (fromCustom > 0) {
+				this.customDiscount = fromCustom;
+				return;
+			}
+			if (fromHd > 0) {
+				this.customDiscount = fromHd;
+				return;
+			}
+			this.customDiscount = 1;
+		},
 		/** 合并门店详情,供到店自取信息展示 */
 		mergePickupShopInfo(info, hd) {
 			if (!info) return;
@@ -2825,6 +2949,10 @@ export default {
 	justify-content: space-between;
 }
 
+.fee-row--follow {
+	margin-top: 20upx;
+}
+
 .fee-label {
 	font-size: 28upx;
 	color: #333333;
@@ -2848,6 +2976,17 @@ export default {
 	font-weight: bold;
 }
 
+.fee-discount-amount {
+	color: #ff2842;
+	font-size: 28upx;
+	font-weight: bold;
+}
+
+/* 运费+折扣+红包卡片下方留 20px,避免贴底栏 */
+.fee-hb-card {
+	margin-bottom: 60upx;
+}
+
 /* 时间选择 */
 .time-select-group {
 	display: flex;
@@ -3083,6 +3222,28 @@ export default {
 	margin-top: 24upx;
 }
 
+/* 备注单行:高度约一行文字,不占用贺卡多行输入框 */
+.remark-line-box {
+	background-color: #fafafa;
+	border: 2upx solid #e8e8e8;
+	border-radius: 12upx;
+	box-sizing: border-box;
+	overflow: hidden;
+}
+
+.remark-line-input {
+	display: block;
+	width: 100%;
+	height: 72upx;
+	padding: 0 24upx;
+	font-size: 28upx;
+	line-height: 72upx;
+	color: #333333;
+	background-color: transparent;
+	border: none;
+	box-sizing: border-box;
+}
+
 /* 贺卡/备注输入区:外框统一边框,固定约两行输入高度 */
 .textarea-box {
 	position: relative;
@@ -3095,8 +3256,7 @@ export default {
 	overflow: hidden;
 }
 
-.card-info-input,
-.remark-input {
+.card-info-input {
 	display: block;
 	width: 100%;
 	height: 80upx;
@@ -3141,16 +3301,13 @@ export default {
 	z-index: 1000;
 }
 
-/* 红包行:设计稿单行布局,整行可点 */
-.hb-row-card {
+/* 红包行:与运费同卡时作为卡片内一行,不再单独成卡 */
+.hb-row-inner {
 	display: flex;
 	flex-direction: row;
 	align-items: center;
 	justify-content: space-between;
-	position: relative;
-	min-height: 96upx;
-	padding-top: 28upx;
-	padding-bottom: 28upx;
+	min-height: 56upx;
 	box-sizing: border-box;
 }
 

+ 2 - 1
mallApp/src/pages/interest/level.vue

@@ -16,7 +16,8 @@
     <view class="rank-bottom-wrap">
       <view class="rank-text">
         <view>1、充值或消费1元得1成长值,余额消费没有成长值</view>
-        <view>2、会员在本店享受以下权益:</view>
+		<view>2、会员折扣不可与店内其他优惠活动同时享受</view>
+        <view>3、会员在本店享受以下权益:</view>
       </view>
       <view class="level-list-wrap">
         <view class="level-list-tit">

+ 1 - 1
mallApp/src/pages/order/detail.vue

@@ -115,7 +115,7 @@
             </view>
             <view class="msg-list">
               <view class="label">创建时间</view>
-              <view class="value">{{ data.addTime ? data.addTime.substr(5,11) : ''}}</view>
+              <view class="value">{{ data.addTime ? data.addTime.substr(0,13) : ''}}</view>
             </view>
             <view class="msg-list">
               <view class="label">支付方式</view>