shish hai 2 meses
pai
achega
f107df9c01

+ 67 - 7
ghsPad/src/pages/home/components/console.vue

@@ -8,6 +8,7 @@
 	<view class="active" @click="showCustomPop" style="background-color:#3385FF;color:white;">选客</view>
 	<view class="active" @click="part()" style="border:1upx solid black;color:black">拆散</view>
 	<view class="active" @click="fastCreate" style="font-size:8upx;">快捷开单</view>
+	<view class="active" @click="gatheringFn" style="color:#3385FF;border:1upx solid #3385FF;font-size:8upx;">计算器</view>
     <view class="active" @click="clearItemFn" style="border:1upx solid #636161;color:#636161;">清空</view>
 
 	<view v-if="Number(customId)>0" :class="[fastAction==1 ? 'custom-fast':'custom']">{{customName}}</view>
@@ -69,9 +70,15 @@
         <toPayWay @cancelPay="cancelPay()" @payFinish="payFinish()" @selectPayWayBack="selectPayWayBack" :orderId.sync="orderId" :price.sync="price" :reachDiscountPrice.sync="reachDiscountPrice" :orderReachDiscountPrice.sync="orderReachDiscountPrice"></toPayWay>
     </uni-popup>
 
-	<!-- 临时收款 -->
+	<!-- 临时收款 / 计算器 -->
 	<uni-popup ref="gatheringRef" background-color="#fff" type="center" :animation="false">
-		<selectPrice @confirmSelectPrice="confirmSelectPrice" @cancelSelectPrice="cancelSelectPrice"></selectPrice>
+		<selectPrice
+			ref="gatheringSelectPriceRef"
+			:custom-id="customId"
+			:custom-name="customName"
+			@zjGathering="zjGathering"
+			@cancelSelectPrice="cancelSelectPrice"
+		></selectPrice>
 	</uni-popup>
 
 	<!-- 开单选花材输入数量和金额 -->
@@ -112,6 +119,8 @@ import selectPrice from './selectPrice'
 import selectNumPrice from './selectNumPrice'
 import { mapGetters } from "vuex";
 import { partItem } from '@/api/part'
+/** 计算器置顶:须与 selectPrice.vue 中 GATHERING_PIN_TOP_STORAGE_KEY 一致 */
+const GATHERING_PIN_TOP_STORAGE_KEY = 'ghsPad_selectPrice_gatheringPinTop'
 // #ifdef APP-PLUS
 let sunmiCashboxPlugin = uni.requireNativePlugin('Tellsea-SunmiCashboxPlugin')
 // #endif
@@ -186,6 +195,16 @@ export default {
 	created(){
 		this.refreshHandOrderList()
 	},
+	mounted(){
+		this.$nextTick(() => {
+			this.reopenGatheringIfPinned()
+		})
+	},
+	activated() {
+		this.$nextTick(() => {
+			this.reopenGatheringIfPinned()
+		})
+	},
 	destroyed(){
 	},
     methods:{
@@ -301,10 +320,45 @@ export default {
 		cancelSelectPrice(){
 			this.$refs.gatheringRef.close()
 		},
-		confirmSelectPrice(params){
-			uni.showLoading({mask:true})
-			//isCashier=1时收银台播放 总金额139元,请扫码付款
-			createOrder({orderName:params.name,lsGoodsPrice:params.price,modifyPrice:params.price,isCashier:1,cash:params.cash}).then(res=>{
+		_isGatheringPinnedInStorage() {
+			try {
+				const v = uni.getStorageSync(GATHERING_PIN_TOP_STORAGE_KEY)
+				return v === true || v === 1 || v === '1' || String(v) === 'true'
+			} catch (e) {
+				return false
+			}
+		},
+		/** 置顶开启时重新打开计算器弹层(不改变 storage) */
+		reopenGatheringIfPinned() {
+			if (!this._isGatheringPinnedInStorage()) {
+				return
+			}
+			this.$nextTick(() => {
+				const pop = this.$refs.gatheringRef
+				if (pop && typeof pop.open === 'function') {
+					pop.open('center')
+				}
+			})
+		},
+		/** 直接收款 / 计算器提交 */
+		zjGathering(params){
+			uni.showLoading({ mask: true })
+			const payload = {
+				orderName: params.name,
+				lsGoodsPrice: params.price,
+				modifyPrice: params.price,
+				isCashier: 1,
+				cash: params.cash,
+				customId: this.customId,
+				version: 3,
+				zjGathering: 1,
+				dealPrice: 1,
+				sendType: 1,
+				getGoods: 1,
+				hasPay: params.hasPay != null ? params.hasPay : 0,
+				payWay: Number(params.hasPay) === 1 ? (params.payWay != null ? params.payWay : 0) : 0
+			}
+			createOrder(payload).then(res=>{
 				uni.hideLoading()
 				if(res.code == 1){
 					if(res.data.status == 1){
@@ -314,7 +368,8 @@ export default {
 						this.price = res.data.actPrice ? Number(res.data.actPrice) : 0
 					}else{
 						this.$refs.gatheringRef.close()
-						uni.showToast({title: '操作成功' })
+						uni.showToast({ title: '操作成功' })
+						this.reopenGatheringIfPinned()
 					}
 					if(params.cash && parseFloat(params.cash)>0){
 						this.openBox()
@@ -322,12 +377,17 @@ export default {
 				}
 			})
 		},
+		gatheringFn(){
+			this.$util.hitVoice()
+			this.$refs.gatheringRef.open('center')
+		},
 		payFinish(){
 			this.$util.hitVoice()
 			this.$refs.toPayRef.close()
 			this.removeFromSaveDirect()
 			//开单完成之后更新花材库存
 			this.$emit('updateRightItem')
+			this.reopenGatheringIfPinned()
 		},
 		calcFn(){
 			this.calc = this.calc == 'add' ? 'sub' : 'add'

+ 1908 - 133
ghsPad/src/pages/home/components/selectPrice.vue

@@ -1,232 +1,2007 @@
 <template>
     <view class="boardset-flowert">
-        <view class="flower-input_box">
-            <view class="flower-open_box">
-                <view @tap="checkCurrent(1)">
-                    <view class="desc">订单金额</view>
-                    <div class="flower-unit_box">
-                        <text :class="{selected:checkType == 1}">{{price}}</text>
-                    </div>
+        <view class="register-main">
+            <scroll-view class="register-left-scroll" scroll-y :enable-back-to-top="false">
+                <view class="flower-input_box">
+                    <view class="flower-input_box-wrap">
+                        <view
+                            class="discount-entry"
+                            @tap.stop="openDiscountPopup"
+                            hover-class="discount-entry--hover"
+                        >
+                            <text class="discount-entry__glyph">折</text>
+                        </view>
+                        <view class="history-entry" @tap.stop="openCalcHistory" hover-class="history-entry--hover">
+                            <view class="iconfont iconmingxi1 history-entry__icon"></view>
+                        </view>
+                        <!-- 计算器:多笔累加 -->
+                        <view class="calc-section" :class="{ 'calc-section-active': keyboardTarget === 'calc' }" @tap="focusCalc">
+                            <view class="calc-title">计算器</view>
+                        <view class="calc-formula">
+                            <block v-if="tokens.length === 0 && isEmptyCalcInput">
+                                <text class="calc-placeholder">输入数字</text>
+                            </block>
+                            <view v-else class="calc-formula-inner">
+                                <block v-for="(t, idx) in tokens" :key="'tok-' + idx">
+                                    <text v-if="t.type === 'num'" class="calc-num">{{ t.s }}</text>
+                                    <text v-else class="calc-op">{{ t.o }}</text>
+                                </block>
+                                <text v-if="!isEmptyCalcInput" class="calc-num">{{ calcInput }}</text>
+                            </view>
+                        </view>
+                        <view class="calc-total-row">
+                            <text class="calc-total-label">订单金额</text>
+                            <text class="calc-total-val">{{ formatMoney(stackDiscountedAmount) }}</text>
+                        </view>
+                        <view v-if="isDiscountActive" class="calc-discount-row">
+                            <text class="calc-discount-label">{{ formatDiscountZheLabel(discountZhe) }}后</text>
+                            <text class="calc-discount-val">{{ formatMoney(stackDiscountedAmount) }}</text>
+                        </view>
+                        <view
+                            class="gather-pin-entry"
+                            :class="{ 'gather-pin-entry--on': gatheringPinTop }"
+                            @tap.stop="toggleGatheringPin"
+                            hover-class="gather-pin-entry--hover"
+                        >
+                            <view class="iconfont iconzhiding gather-pin-entry__icon"></view>
+                        </view>
+                    </view>
+
+                    <view class="rcv-pay-block">
+                        <view class="rcv-pay-title">收款方式</view>
+                        <view class="rcv-item-list_box">
+                            <view
+                                v-for="item in optHasPayList"
+                                :key="'hp-' + item.id"
+                                class="rcv-item-chip"
+                                :data-hpid="String(item.id)"
+                                :class="{ active: selHasPay === item.id }"
+                                @tap.stop="onHasPayChipTap"
+                            >
+                                {{ item.name }}
+                            </view>
+                        </view>
+                        <view class="rcv-customer-line">
+                            <text class="rcv-customer-line__name">{{ customerDisplayName }}</text>
+                        </view>
+                        <view v-if="selHasPay === 1" class="rcv-sub-block">
+                            <view class="rcv-pay-title rcv-pay-title-sub">请选择线下收款方式</view>
+                            <view class="rcv-item-list_box">
+                                <view
+                                    v-for="item in optPayWayList"
+                                    :key="'pw-' + item.id"
+                                    class="rcv-item-chip"
+                                    :data-pwid="String(item.id)"
+                                    :class="{ active: selPayWay === item.id }"
+                                    @tap.stop="onPayWayChipTap"
+                                >
+                                    {{ item.name }}
+                                </view>
+                            </view>
+                        </view>
+                    </view>
+
+                    <view class="pay-amount-row" v-if="showCashPayCols">
+                        <view class="flower-open_box pay-amount-col">
+                            <view @tap="checkCurrent(2)">
+                                <view class="desc">收现金</view>
+                                <view class="flower-unit_box">
+                                    <text :class="{ selected: keyboardTarget !== 'calc' && checkType == 2 }">{{ formatMoney(cash) }}</text>
+                                </view>
+                            </view>
+                        </view>
+                        <view class="flower-open_box pay-amount-col">
+                            <view class="pay-change-inner">
+                                <view class="desc pay-change-desc">
+                                    <text v-if="Number(zl) > 0" class="change-label-ok">找零</text>
+                                    <text v-else-if="Number(zl) < 0" class="change-label-bad">还差</text>
+                                    <text v-else class="change-label-neutral">找零</text>
+                                </view>
+                                <view class="flower-unit_box flower-unit_box-change">
+                                    <text v-if="Number(zl) > 0" class="change-hint-ok">{{ formatChangeAmount(zl) }}</text>
+                                    <text v-else-if="Number(zl) < 0" class="change-hint-bad">{{ formatChangeAmount(-zl) }}</text>
+                                    <text v-else class="change-hint-neutral">—</text>
+                                </view>
+                            </view>
+                        </view>
+                    </view>
+                    <view class="pay-submit-row">
+                        <view class="pay-submit-btn pay-submit-btn-stash" @tap.stop="stashCurrent">暂存</view>
+                        <view class="pay-submit-btn" @tap.stop="submitCashierPay">提交</view>
+                    </view>
+                    </view>
+                </view>
+            </scroll-view>
+            <view class="register-right">
+                <view class="keyboard-body_box">
+                    <view class="numpad">
+                        <view class="numpad-grid-wrap">
+                            <view class="numpad-digits">
+                                <view class="numpad-row">
+                                    <view class="numpad-key" data-char="1" @tap="onNumpadTap">1</view>
+                                    <view class="numpad-key" data-char="2" @tap="onNumpadTap">2</view>
+                                    <view class="numpad-key" data-char="3" @tap="onNumpadTap">3</view>
+                                </view>
+                                <view class="numpad-row">
+                                    <view class="numpad-key" data-char="4" @tap="onNumpadTap">4</view>
+                                    <view class="numpad-key" data-char="5" @tap="onNumpadTap">5</view>
+                                    <view class="numpad-key" data-char="6" @tap="onNumpadTap">6</view>
+                                </view>
+                                <view class="numpad-row">
+                                    <view class="numpad-key" data-char="7" @tap="onNumpadTap">7</view>
+                                    <view class="numpad-key" data-char="8" @tap="onNumpadTap">8</view>
+                                    <view class="numpad-key" data-char="9" @tap="onNumpadTap">9</view>
+                                </view>
+                                <view class="numpad-row">
+                                    <view class="numpad-key numpad-key-muted" data-char="." @tap="onNumpadTap">.</view>
+                                    <view class="numpad-key" data-char="0" @tap="onNumpadTap">0</view>
+                                    <view class="numpad-key" data-char="00" @tap="onNumpadTap">00</view>
+                                </view>
+                                <view class="numpad-row numpad-row-actions">
+                                    <view class="numpad-key numpad-key-action numpad-key-action-dual" @tap.stop="cancel">
+                                        <text class="numpad-key-text">取消</text>
+                                    </view>
+                                    <view class="numpad-key numpad-key-danger numpad-key-action-dual" data-action="backspace" @tap="onNumpadTap">
+                                        <text class="numpad-key-text">删除</text>
+                                    </view>
+                                </view>
+                            </view>
+                            <view class="numpad-op-col">
+                                <view class="numpad-key numpad-key-op" data-action="calc-mul" @tap="onNumpadTap">×</view>
+                                <view class="numpad-key numpad-key-op" data-action="calc-add" @tap="onNumpadTap">+</view>
+                                <view class="numpad-key numpad-key-op" data-action="calc-sub" @tap="onNumpadTap">−</view>
+                                <view class="numpad-key numpad-key-op" data-action="calc-div" @tap="onNumpadTap">÷</view>
+                                <view class="numpad-key numpad-key-op numpad-key-op-muted" data-action="calc-clear" @tap="onNumpadTap">清空</view>
+                            </view>
+                        </view>
+                    </view>
                 </view>
             </view>
-            <input class="goods-name" v-model="name" placeholder="商品名称" placeholder-class="grey-color" />
-            <view class="flower-open_box">
-                <view @tap="checkCurrent(2)" style="position:relative;">
-                    <view class="desc">
-                        收现金
+        </view>
+        <uni-popup ref="calcHistoryPopup" type="center" background-color="#ffffff" @change="onCalcHistoryPopupChange">
+            <view class="calc-history-popup">
+                <view class="calc-history-popup-hd">
+                    <text class="calc-history-popup-title">历史记录</text>
+                    <text class="calc-history-popup-close" @tap.stop="onHistoryPopupCloseTap">关闭</text>
+                </view>
+                <scroll-view scroll-y class="calc-history-popup-body">
+                    <view
+                        v-for="(row, idx) in calcHistory"
+                        :key="row.id || 'hist-' + idx"
+                        class="history-item"
+                    >
+                        <view class="history-item-body" :data-idx="idx" @tap.stop="onHistoryRowTap">
+                            <view class="history-line">
+                                <text class="history-time">{{ row.timeLabel }}</text>
+                                <text class="history-total">合计 {{ formatMoney(row.total) }}</text>
+                            </view>
+                            <text class="history-detail">{{ row.detailText }}</text>
+                        </view>
+                        <text class="history-del" :data-idx="idx" @tap.stop="onHistoryDeleteTap">删除</text>
                     </view>
-                    <view style="position:absolute;top:20upx;left:95upx;width:300upx;text-align:left;font-size:13upx;">
-                        <text v-if="Number(zl)>0" style="color:green;">找零 {{zl}}</text>
-                        <text style="color:red;" v-if="Number(zl)<0">还差 {{-zl}}</text>
+                    <view v-if="calcHistory.length === 0" class="history-empty">暂无记录</view>
+                </scroll-view>
+            </view>
+        </uni-popup>
+        <uni-popup ref="discountPopup" type="center" background-color="#ffffff" @change="onDiscountPopupChange">
+            <view class="calc-history-popup discount-popup">
+                <view class="calc-history-popup-hd">
+                    <text class="calc-history-popup-title">选择折扣</text>
+                    <text class="calc-history-popup-close" @tap.stop="onDiscountPopupCloseTap">关闭</text>
+                </view>
+                <view class="discount-popup-toolbar">
+                    <view class="discount-custom-row">
+                        <input
+                            class="discount-custom-input"
+                            v-model="customZheInput"
+                            type="text"
+                            placeholder="如 8.8(八点八折)"
+                            confirm-type="done"
+                        />
+                        <view class="discount-custom-add-btn" @tap.stop="addCustomDiscountZhe">添加</view>
                     </view>
-                    <div class="flower-unit_box">
-                        <text :class="{selected:checkType == 2}">{{cash}}</text>
-                    </div>
+                    <text v-if="customDiscountZheList.length > 0" class="discount-toolbar-hint">
+                        已保存 {{ customDiscountZheList.length }} 条,点下方选用;无折扣不参与打折
+                    </text>
+                    <text v-else class="discount-toolbar-hint discount-toolbar-hint--muted">无默认折扣,请先输入数字点「添加」</text>
                 </view>
+                <scroll-view scroll-y enable-flex="true" class="calc-history-popup-body discount-popup-body-scroll">
+                    <view
+                        class="discount-option"
+                        :class="{ 'discount-option--active': isZheActive(10) }"
+                        data-zhe="10"
+                        @tap.stop="onDiscountRowPickTap"
+                    >
+                        <text class="discount-option-text">无折扣</text>
+                        <text v-if="isZheActive(10)" class="discount-option-check">✓</text>
+                    </view>
+                    <view
+                        v-for="(z, cidx) in customDiscountZheList"
+                        :key="'d-' + cidx + '-' + z"
+                        class="discount-option discount-option--with-del"
+                        :class="{ 'discount-option--active': isZheActive(z) }"
+                    >
+                        <view class="discount-option-main" :data-zhe="String(z)" @tap.stop="onDiscountRowPickTap">
+                            <text class="discount-option-text">{{ formatDiscountRowLabel(z) }}</text>
+                            <text v-if="isZheActive(z)" class="discount-option-check">✓</text>
+                        </view>
+                        <text class="discount-option-del" :data-cidx="String(cidx)" @tap.stop="onCustomDiscountDeleteTap">删除</text>
+                    </view>
+                </scroll-view>
             </view>
-        </view>
-        <view class="keyboard-body_box">
-         <VKeyboard ref="keyboard" :pointIsShow="true" :digital="true" :disorderly="false" @typing="typing" @enter="enter" @cancel="cancel"> </VKeyboard>
-        </view>
+        </uni-popup>
     </view>
 </template>
 <script>
-import VKeyboard from '@/components/vKeyboard/VKeyboard.vue'
+const CALC_HISTORY_STORAGE_KEY = 'ghsPad_selectPrice_calcHistory'
+const DISCOUNT_CUSTOM_STORAGE_KEY = 'ghsPad_selectPrice_customDiscountZhe'
+const DISCOUNT_SELECTED_STORAGE_KEY = 'ghsPad_selectPrice_discountZhe'
+/** 计算器弹层置顶:须在 console.vue 中使用同名常量 */
+const GATHERING_PIN_TOP_STORAGE_KEY = 'ghsPad_selectPrice_gatheringPinTop'
 export default {
-    name:'selectPrice',
-    components:{ VKeyboard },
+    name: 'selectPrice',
+    props: {
+        customId: {
+            type: Number,
+            default: 0
+        },
+        customName: {
+            type: String,
+            default: ''
+        }
+    },
     data() {
         return {
-            price:0,
-            checkType:1,
-            fillType:1,
-            name:'',
-            cash:0
-        };
-    },
-    mounted(){
-        this.activeKeyboard()
+            price: 0,
+            checkType: 3,
+            keyboardTarget: 'calc',
+            name: '',
+            cash: 0,
+            /** 表达式:数字与运算符交替,运算符一点即显示 */
+            tokens: [],
+            calcInput: '',
+            calcHistory: [],
+            maxHistory: 20,
+            /** 通过代码调用 uni-popup.close 时为 true,避免与遮罩关闭的 @change 重复播点击音 */
+            _historyPopupCloseByApi: false,
+            /** 10 = 无折扣,9 = 九折,实付 = 合计 × (zhe/10) */
+            discountZhe: 10,
+            customDiscountZheList: [],
+            customZheInput: '',
+            _discountPopupCloseByApi: false,
+            selHasPay: 0,
+            selPayWay: 0,
+            /** 与 settlePop:扫码(0)、赊账(2)、线下(1),无余额 */
+            optHasPayList: [
+                { name: '扫码', id: 0 },
+                { name: '赊账', id: 2 },
+                { name: '线下', id: 1 }
+            ],
+            optPayWayList: [
+                { name: '线下微信', id: 0 },
+                { name: '线下支付宝', id: 1 },
+                { name: '现金', id: 4 },
+                { name: '银行卡', id: 5 }
+            ],
+            /** 计算器置顶:persist 见 GATHERING_PIN_TOP_STORAGE_KEY */
+            gatheringPinTop: false
+        }
     },
-    computed:{
-        zl(){
+    computed: {
+        /** 当前选客名称(供赊账等场景识别) */
+        customerDisplayName() {
+            const n = (this.customName || '').trim()
+            if (n) {
+                return n
+            }
+            if (Number(this.customId) > 0) {
+                return '—'
+            }
+            return '未选择客户'
+        },
+        isEmptyCalcInput() {
+            return this.$util.isEmpty(this.calcInput)
+        },
+        stackTotal() {
+            const { nums, ops } = this.buildEvalState()
+            if (!nums.length) {
+                return 0
+            }
+            const r = this.evalExpr(nums, ops)
+            return isNaN(r) ? 0 : r
+        },
+        discountRate() {
+            const z = Number(this.discountZhe)
+            if (isNaN(z) || z <= 0) {
+                return 1
+            }
+            return Math.min(10, Math.max(0.01, z)) / 10
+        },
+        stackDiscountedAmount() {
+            const raw = Number(this.stackTotal)
+            if (isNaN(raw)) {
+                return 0
+            }
+            return parseFloat((raw * this.discountRate).toFixed(2))
+        },
+        isDiscountActive() {
+            return Number(this.discountZhe) < 10 - 1e-6
+        },
+        zl() {
+            if (!(this.selHasPay === 1 && this.selPayWay === 4)) {
+                return 0
+            }
             let amount = 0
-            if(Number(this.cash) > 0 && Number(this.price) > 0){
+            if (Number(this.cash) > 0 && Number(this.price) > 0) {
                 amount = Number(this.cash) - Number(this.price)
                 amount = parseFloat(amount.toFixed(2))
             }
             return Number(amount)
-        }
+        },
+        showCashPayCols() {
+            return this.selHasPay === 1 && this.selPayWay === 4
+        },
+    },
+    watch: {
+        stackDiscountedAmount: {
+            handler(v) {
+                if (this.keyboardTarget === 'calc') {
+                    const n = parseFloat(Number(v).toFixed(2))
+                    this.price = isNaN(n) ? 0 : n
+                }
+            },
+            immediate: true,
+        },
+    },
+    mounted() {
+        this.loadCalcHistoryFromStorage()
+        this.loadDiscountFromStorage()
+        this.loadGatheringPinFromStorage()
     },
     methods: {
-        checkCurrent(n){
+        loadGatheringPinFromStorage() {
+            try {
+                const v = uni.getStorageSync(GATHERING_PIN_TOP_STORAGE_KEY)
+                this.gatheringPinTop = v === true || v === 1 || v === '1' || String(v) === 'true'
+            } catch (e) {
+                this.gatheringPinTop = false
+            }
+        },
+        persistGatheringPinTop() {
+            try {
+                if (this.gatheringPinTop) {
+                    uni.setStorageSync(GATHERING_PIN_TOP_STORAGE_KEY, '1')
+                } else {
+                    uni.removeStorageSync(GATHERING_PIN_TOP_STORAGE_KEY)
+                }
+            } catch (e) {
+                console.warn('[selectPrice] persistGatheringPinTop', e)
+            }
+        },
+        toggleGatheringPin() {
+            this.tapVoice()
+            this.gatheringPinTop = !this.gatheringPinTop
+            this.persistGatheringPinTop()
+        },
+        loadCalcHistoryFromStorage() {
+            try {
+                const raw = uni.getStorageSync(CALC_HISTORY_STORAGE_KEY)
+                if (raw === undefined || raw === null || raw === '') {
+                    return
+                }
+                const list = typeof raw === 'string' ? JSON.parse(raw) : raw
+                if (!Array.isArray(list) || !list.length) {
+                    return
+                }
+                const max = this.maxHistory
+                this.calcHistory = list.slice(0, max).map((row, i) => this.normalizeHistoryRow(row, i))
+            } catch (e) {
+                console.warn('[selectPrice] loadCalcHistoryFromStorage', e)
+            }
+        },
+        normalizeHistoryRow(row, idx) {
+            const r = row && typeof row === 'object' ? row : {}
+            const id =
+                r.id != null && String(r.id) !== ''
+                    ? String(r.id)
+                    : 'hist-' + Date.now() + '-' + idx
+            return {
+                id,
+                tokens: Array.isArray(r.tokens) ? r.tokens : null,
+                addends: Array.isArray(r.addends) ? r.addends : null,
+                calcInput: r.calcInput != null ? String(r.calcInput) : '',
+                total: r.total,
+                timeLabel: r.timeLabel != null ? String(r.timeLabel) : '--:--',
+                detailText: r.detailText != null ? String(r.detailText) : '',
+                cashSnapshot: r.cashSnapshot,
+                priceSnapshot: r.priceSnapshot,
+                orderOnly: !!r.orderOnly,
+                source: r.source === 'stash' ? 'stash' : 'submit',
+                discountZhe:
+                    r.discountZhe != null && r.discountZhe !== '' && !isNaN(Number(r.discountZhe))
+                        ? parseFloat(Number(r.discountZhe).toFixed(2))
+                        : undefined,
+            }
+        },
+        persistCalcHistoryToStorage() {
+            try {
+                uni.setStorageSync(CALC_HISTORY_STORAGE_KEY, JSON.stringify(this.calcHistory))
+            } catch (e) {
+                console.warn('[selectPrice] persistCalcHistoryToStorage', e)
+            }
+        },
+        loadDiscountFromStorage() {
+            try {
+                const customRaw = uni.getStorageSync(DISCOUNT_CUSTOM_STORAGE_KEY)
+                if (customRaw !== undefined && customRaw !== null && customRaw !== '') {
+                    const parsed = typeof customRaw === 'string' ? JSON.parse(customRaw) : customRaw
+                    if (Array.isArray(parsed)) {
+                        this.customDiscountZheList = parsed
+                            .map((x) => Number(x))
+                            .filter((n) => !isNaN(n) && n > 0 && n < 10 - 1e-6)
+                            .map((n) => parseFloat(n.toFixed(2)))
+                    }
+                }
+                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))
+                    }
+                }
+                this.normalizeDiscountSelectionToCustomOrNone()
+            } catch (e) {
+                console.warn('[selectPrice] loadDiscountFromStorage', e)
+            }
+        },
+        /** 仅允许无折扣(10)或自定义列表中的折数 */
+        normalizeDiscountSelectionToCustomOrNone() {
+            const z = Number(this.discountZhe)
+            if (isNaN(z)) {
+                this.discountZhe = 10
+                this.persistDiscountSelected()
+                return
+            }
+            if (z >= 10 - 1e-6) {
+                this.discountZhe = 10
+                return
+            }
+            const ok = this.customDiscountZheList.some((x) => Math.abs(Number(x) - z) < 1e-4)
+            if (!ok) {
+                this.discountZhe = 10
+                this.persistDiscountSelected()
+            }
+        },
+        persistDiscountSelected() {
+            try {
+                uni.setStorageSync(DISCOUNT_SELECTED_STORAGE_KEY, String(this.discountZhe))
+            } catch (e) {
+                console.warn('[selectPrice] persistDiscountSelected', e)
+            }
+        },
+        persistCustomDiscountList() {
+            try {
+                uni.setStorageSync(DISCOUNT_CUSTOM_STORAGE_KEY, JSON.stringify(this.customDiscountZheList))
+            } catch (e) {
+                console.warn('[selectPrice] persistCustomDiscountList', e)
+            }
+        },
+        /** 当前结算恢复为无折扣(不删除自定义折扣列表) */
+        resetActiveDiscountToNone() {
+            this.discountZhe = 10
+            this.persistDiscountSelected()
+        },
+        formatDiscountZheLabel(z) {
+            const n = Number(z)
+            if (isNaN(n) || n >= 10 - 1e-6) {
+                return ''
+            }
+            return parseFloat(n.toFixed(2)) + '折'
+        },
+        formatDiscountRowLabel(z) {
+            const n = Number(z)
+            if (isNaN(n)) {
+                return '—'
+            }
+            return parseFloat(n.toFixed(2)) + '折'
+        },
+        isZheActive(z) {
+            const a = Number(this.discountZhe)
+            const b = Number(z)
+            if (isNaN(a) || isNaN(b)) {
+                return false
+            }
+            return Math.abs(a - b) < 1e-4
+        },
+        openDiscountPopup() {
+            this.tapVoice()
+            this.$refs.discountPopup && this.$refs.discountPopup.open()
+        },
+        onDiscountPopupCloseTap() {
+            this.tapVoice()
+            this.closeDiscountPopup()
+        },
+        closeDiscountPopup() {
+            this._discountPopupCloseByApi = true
+            this.$refs.discountPopup && this.$refs.discountPopup.close()
+        },
+        onDiscountPopupChange(e) {
+            if (!e || e.show !== false) {
+                return
+            }
+            if (this._discountPopupCloseByApi) {
+                this._discountPopupCloseByApi = false
+                return
+            }
+            this.tapVoice()
+        },
+        /** 微信小程序 data-event-opts 对 @tap 带参编译易报错,统一用 dataset 取参 */
+        onDiscountRowPickTap(e) {
+            const ds = e.currentTarget.dataset || {}
+            const z = Number(ds.zhe)
+            if (isNaN(z)) {
+                return
+            }
+            this.pickDiscountZhe(z)
+        },
+        onCustomDiscountDeleteTap(e) {
+            const ds = e.currentTarget.dataset || {}
+            const idx = Number(ds.cidx)
+            if (isNaN(idx)) {
+                return
+            }
+            this.removeCustomDiscountByIndex(idx)
+        },
+        pickDiscountZhe(z) {
+            this.tapVoice()
+            const n = Number(z)
+            if (isNaN(n) || n <= 0 || n > 10) {
+                this.$msg('折扣无效')
+                return
+            }
+            if (n < 10 - 1e-6) {
+                const ok = this.customDiscountZheList.some((x) => Math.abs(Number(x) - n) < 1e-4)
+                if (!ok) {
+                    this.$msg('请先从列表选择或添加该折扣')
+                    return
+                }
+            }
+            this.discountZhe = parseFloat(n.toFixed(2))
+            this.persistDiscountSelected()
+            this.closeDiscountPopup()
+        },
+        addCustomDiscountZhe() {
+            this.tapVoice()
+            const raw = String(this.customZheInput || '').trim()
+            if (!raw) {
+                this.$msg('请输入折扣')
+                return
+            }
+            const n = parseFloat(raw)
+            if (isNaN(n) || n <= 0 || n > 10) {
+                this.$msg('请输入 0.1~10 的数值(如 8.8)')
+                return
+            }
+            const rounded = parseFloat(n.toFixed(2))
+            if (rounded >= 10 - 1e-6) {
+                this.$msg('无需添加,请直接选「无折扣」')
+                return
+            }
+            if (this.customDiscountZheList.some((x) => Math.abs(x - rounded) < 1e-4)) {
+                this.$msg('该折扣已在列表中')
+                return
+            }
+            this.customDiscountZheList = [rounded].concat(this.customDiscountZheList)
+            this.persistCustomDiscountList()
+            this.customZheInput = ''
+            this.$msg('已添加')
+        },
+        removeCustomDiscountByIndex(cidx) {
+            const idx = Number(cidx)
+            if (isNaN(idx) || idx < 0 || idx >= this.customDiscountZheList.length) {
+                return
+            }
+            this.tapVoice()
+            const removed = this.customDiscountZheList[idx]
+            this.customDiscountZheList = this.customDiscountZheList.filter((_, i) => i !== idx)
+            this.persistCustomDiscountList()
+            if (this.isZheActive(removed)) {
+                this.discountZhe = 10
+                this.persistDiscountSelected()
+            }
+        },
+        /** 全页可点区域统一按键音(依赖 $util.hitVoice) */
+        tapVoice() {
             this.$util.hitVoice()
+        },
+        /** 构造用于求值的数字列与运算符列(末尾仅挂运算符、无下一操作数时不参与运算) */
+        buildEvalState() {
+            const nums = []
+            const ops = []
+            for (let i = 0; i < this.tokens.length; i++) {
+                const t = this.tokens[i]
+                if (t.type === 'num') {
+                    const v = Number(t.s)
+                    if (!isNaN(v)) {
+                        nums.push(v)
+                    }
+                } else {
+                    ops.push(t.o)
+                }
+            }
+            const hasTrailingOp =
+                this.tokens.length > 0 &&
+                this.tokens[this.tokens.length - 1].type === 'op'
+            const ci = this.calcInput
+            const hasPartial =
+                ci &&
+                !this.$util.isEmpty(ci) &&
+                ci !== '.' &&
+                !isNaN(parseFloat(ci))
+
+            let evalNums = nums.slice()
+            let evalOps = ops.slice()
+
+            if (hasPartial) {
+                const c = parseFloat(ci)
+                if (!isNaN(c) && c > 0) {
+                    evalNums.push(parseFloat(c.toFixed(2)))
+                }
+            } else if (hasTrailingOp && evalOps.length > 0) {
+                evalOps = evalOps.slice(0, -1)
+            }
+
+            while (evalOps.length > 0 && evalNums.length <= evalOps.length) {
+                evalOps.pop()
+            }
+            return { nums: evalNums, ops: evalOps }
+        },
+        opToInternal(o) {
+            if (o === '×') {
+                return '*'
+            }
+            if (o === '÷') {
+                return '/'
+            }
+            if (o === '−') {
+                return '-'
+            }
+            return '+'
+        },
+        /** 先 ×÷ 再 +− */
+        evalExpr(nums, ops) {
+            if (!nums.length) {
+                return 0
+            }
+            const nArr = nums.map((x) => Number(x))
+            const oArr = ops.map((o) => this.opToInternal(o))
+            while (oArr.length > nArr.length - 1) {
+                oArr.pop()
+            }
+            if (nArr.length === 1) {
+                return parseFloat(nArr[0].toFixed(2))
+            }
+            if (oArr.length !== nArr.length - 1) {
+                return nArr[0] || 0
+            }
+            let i = 0
+            while (i < oArr.length) {
+                if (oArr[i] === '*' || oArr[i] === '/') {
+                    const a = nArr[i]
+                    const b = nArr[i + 1]
+                    if (oArr[i] === '/' && b === 0) {
+                        return NaN
+                    }
+                    const r = oArr[i] === '*' ? a * b : a / b
+                    nArr.splice(i, 2, r)
+                    oArr.splice(i, 1)
+                } else {
+                    i++
+                }
+            }
+            let s = nArr[0]
+            for (let k = 0; k < oArr.length; k++) {
+                const b = nArr[k + 1]
+                if (oArr[k] === '+') {
+                    s += b
+                } else {
+                    s -= b
+                }
+            }
+            return parseFloat(s.toFixed(2))
+        },
+        cloneTokens(arr) {
+            return arr.map((t) =>
+                t.type === 'num' ? { type: 'num', s: t.s } : { type: 'op', o: t.o }
+            )
+        },
+        getExprDisplayString(includePartial) {
+            let s = ''
+            this.tokens.forEach((t) => {
+                s += t.type === 'num' ? t.s : t.o
+            })
+            if (includePartial && !this.$util.isEmpty(this.calcInput)) {
+                s += this.calcInput
+            }
+            return s
+        },
+        legacyAddendsToTokens(addends) {
+            const tokens = []
+            if (!addends || !addends.length) {
+                return tokens
+            }
+            addends.forEach((raw, i) => {
+                const v = Number(raw)
+                if (i === 0) {
+                    tokens.push({ type: 'num', s: String(Math.abs(v)) })
+                } else {
+                    tokens.push({ type: 'op', o: v >= 0 ? '+' : '−' })
+                    tokens.push({ type: 'num', s: String(Math.abs(v)) })
+                }
+            })
+            return tokens
+        },
+        pressCalcOp(displayOp) {
+            if (!this.ensureCalcModeForOps()) {
+                return
+            }
+            if (!this.$util.isEmpty(this.calcInput) && this.calcInput !== '.') {
+                const n = Number(this.calcInput)
+                if (isNaN(n) || n <= 0) {
+                    this.$msg('请输入有效金额')
+                    return
+                }
+                const fs = parseFloat(n.toFixed(2))
+                this.tokens.push({ type: 'num', s: String(fs) })
+                this.calcInput = ''
+            }
+            const last = this.tokens.length > 0 ? this.tokens[this.tokens.length - 1] : null
+            if (last && last.type === 'op') {
+                last.o = displayOp
+                return
+            }
+            if (last && last.type === 'num') {
+                this.tokens.push({ type: 'op', o: displayOp })
+                return
+            }
+            this.$msg('请先输入数字')
+        },
+        ensureCalcModeForOps() {
+            if (this.keyboardTarget !== 'calc') {
+                this.$msg('请先在左侧点击「金额累加」')
+                return false
+            }
+            return true
+        },
+        formatMoney(n) {
+            const x = Number(n)
+            if (isNaN(x)) {
+                return '0.00'
+            }
+            return x.toFixed(2)
+        },
+        /** 找零/差额展示:不保留小数点后多余的 0 */
+        formatChangeAmount(n) {
+            const x = Number(n)
+            if (isNaN(x)) {
+                return '0'
+            }
+            const s = (Math.round(x * 100) / 100).toFixed(2)
+            return parseFloat(s).toString()
+        },
+        focusCalc() {
+            this.tapVoice()
+            this.keyboardTarget = 'calc'
+            this.checkType = 3
+        },
+        checkCurrent(n) {
+            this.tapVoice()
             this.checkType = n
-            this.fillType = n
+            this.keyboardTarget = n === 1 ? 'order' : 'cash'
         },
-        cancel(){
-            this.$util.hitVoice()
+        onHasPayChipTap(e) {
+            const ds = e.currentTarget.dataset || {}
+            const v = ds.hpid
+            if (v === undefined || v === '') {
+                return
+            }
+            const val = Number(v)
+            if (isNaN(val)) {
+                return
+            }
+            this.applySelHasPay(val)
+        },
+        onPayWayChipTap(e) {
+            const ds = e.currentTarget.dataset || {}
+            const v = ds.pwid
+            if (v === undefined || v === '') {
+                return
+            }
+            const val = Number(v)
+            if (isNaN(val)) {
+                return
+            }
+            this.applySelPayWay(val)
+        },
+        applySelHasPay(val) {
+            this.tapVoice()
+            this.selHasPay = val
+            this.cash = 0
+            if (Number(val) !== 1) {
+                this.selPayWay = 0
+            }
+            this.blurCashIfNoColumn()
+        },
+        applySelPayWay(val) {
+            this.tapVoice()
+            this.selPayWay = val
+            if (Number(val) !== 4) {
+                this.cash = 0
+            } else {
+                this.checkCurrent(2)
+            }
+            this.blurCashIfNoColumn()
+        },
+        blurCashIfNoColumn() {
+            if (this.keyboardTarget === 'cash' && !this.showCashPayCols) {
+                this.focusCalc()
+            }
+        },
+        openCalcHistory() {
+            this.tapVoice()
+            this.$refs.calcHistoryPopup && this.$refs.calcHistoryPopup.open()
+        },
+        onHistoryPopupCloseTap() {
+            this.tapVoice()
+            this.closeCalcHistory()
+        },
+        closeCalcHistory() {
+            this._historyPopupCloseByApi = true
+            this.$refs.calcHistoryPopup && this.$refs.calcHistoryPopup.close()
+        },
+        onCalcHistoryPopupChange(e) {
+            if (!e || e.show !== false) {
+                return
+            }
+            if (this._historyPopupCloseByApi) {
+                this._historyPopupCloseByApi = false
+                return
+            }
+            this.tapVoice()
+        },
+        onHistoryRowTap(e) {
+            this.tapVoice()
+            const idx = Number(e.currentTarget.dataset.idx)
+            if (isNaN(idx) || idx < 0) {
+                return
+            }
+            const row = this.calcHistory[idx]
+            this.applyHistoryRow(row)
+            this.closeCalcHistory()
+        },
+        onHistoryDeleteTap(e) {
+            this.tapVoice()
+            const idx = Number(e.currentTarget.dataset.idx)
+            if (isNaN(idx) || idx < 0 || idx >= this.calcHistory.length) {
+                return
+            }
+            this.calcHistory.splice(idx, 1)
+            this.persistCalcHistoryToStorage()
+        },
+        pushHistorySnapshot(total, opts) {
+            opts = opts || {}
+            const now = new Date()
+            const hh = String(now.getHours())
+            const m = now.getMinutes() < 10 ? '0' + now.getMinutes() : String(now.getMinutes())
+            const timeLabel = hh + ':' + m
+            const hasCalc =
+                this.tokens.length > 0 ||
+                (this.calcInput != null && !this.$util.isEmpty(this.calcInput))
+            const orderOnly =
+                opts.orderOnly !== undefined
+                    ? !!opts.orderOnly
+                    : !hasCalc
+            const expr = this.getExprDisplayString(true)
+            let detailText = orderOnly
+                ? '订单 ' + this.formatMoney(total)
+                : expr || '订单 ' + this.formatMoney(total)
+            if (!orderOnly && hasCalc && this.isDiscountActive && expr) {
+                detailText =
+                    expr +
+                    '(原价' +
+                    this.formatMoney(this.stackTotal) +
+                    ' ' +
+                    this.formatDiscountZheLabel(this.discountZhe) +
+                    '→' +
+                    this.formatMoney(total) +
+                    ')'
+            }
+            const row = this.normalizeHistoryRow(
+                {
+                    id: Date.now() + '-' + Math.random().toString(16).slice(2, 10),
+                    tokens: this.cloneTokens(this.tokens),
+                    calcInput: this.calcInput != null ? String(this.calcInput) : '',
+                    total,
+                    timeLabel,
+                    detailText,
+                    cashSnapshot: Number(this.cash) || 0,
+                    priceSnapshot: Number(this.price) || 0,
+                    orderOnly,
+                    source: opts.source === 'stash' ? 'stash' : 'submit',
+                    discountZhe: parseFloat(Number(this.discountZhe).toFixed(2)),
+                },
+                0
+            )
+            this.calcHistory.unshift(row)
+            if (this.calcHistory.length > this.maxHistory) {
+                this.calcHistory.pop()
+            }
+            this.persistCalcHistoryToStorage()
+        },
+        stashCurrent() {
+            this.tapVoice()
+            const hasCalc =
+                this.tokens.length > 0 ||
+                (this.calcInput != null && !this.$util.isEmpty(this.calcInput))
+            const st = this.stackTotal
+            const orderP = Number(this.price)
+            let total = null
+            let orderOnly = false
+            if (hasCalc) {
+                if (!Number.isFinite(st) || isNaN(st) || st <= 0) {
+                    this.$msg('请先完成有效算式后再暂存')
+                    return
+                }
+                const payable = this.stackDiscountedAmount
+                if (!Number.isFinite(payable) || isNaN(payable) || payable <= 0) {
+                    this.$msg('请先完成有效算式后再暂存')
+                    return
+                }
+                total = payable
+                orderOnly = false
+            } else if (!isNaN(orderP) && orderP > 0) {
+                total = orderP
+                orderOnly = true
+            } else {
+                this.$msg('没有可暂存的内容')
+                return
+            }
+            this.pushHistorySnapshot(total, { source: 'stash', orderOnly })
+            this.$msg('已暂存')
+            this.clearStack()
+            this.keyboardTarget = 'calc'
+            this.checkType = 3
+        },
+        clearStack() {
+            this.tokens = []
+            this.calcInput = ''
+            this.resetActiveDiscountToNone()
+        },
+        /** 从历史记录还原折扣(不写全局折扣 storage,避免覆盖用户默认偏好) */
+        restoreHistoryDiscountFromRow(row) {
+            let z = 10
+            const raw = row && row.discountZhe
+            if (raw != null && raw !== '' && !isNaN(Number(raw))) {
+                const n = Number(raw)
+                if (n > 0 && n <= 10) {
+                    z = parseFloat(n.toFixed(2))
+                }
+            }
+            if (z >= 10 - 1e-6) {
+                this.discountZhe = 10
+                return
+            }
+            const ok = this.customDiscountZheList.some((x) => Math.abs(Number(x) - z) < 1e-4)
+            this.discountZhe = ok ? z : 10
+        },
+        applyHistoryRow(row) {
+            if (!row) {
+                return
+            }
+            this.restoreHistoryDiscountFromRow(row)
+            const ci = row.calcInput != null ? String(row.calcInput) : ''
+            const hasTok = row.tokens && row.tokens.length > 0
+            const hasLegacy = row.addends && row.addends.length
+
+            const restoreCash = () => {
+                this.$nextTick(() => {
+                    if (row.cashSnapshot != null && !isNaN(Number(row.cashSnapshot))) {
+                        this.cash = Number(row.cashSnapshot)
+                    }
+                })
+            }
+
+            if (hasLegacy) {
+                this.tokens = this.legacyAddendsToTokens(row.addends)
+                this.calcInput = ci
+                this.keyboardTarget = 'calc'
+                this.checkType = 3
+                restoreCash()
+                return
+            }
+            if (hasTok) {
+                this.tokens = this.cloneTokens(row.tokens)
+                this.calcInput = ci
+                this.keyboardTarget = 'calc'
+                this.checkType = 3
+                restoreCash()
+                return
+            }
+            if (!this.$util.isEmpty(ci)) {
+                this.tokens = []
+                this.calcInput = ci
+                this.keyboardTarget = 'calc'
+                this.checkType = 3
+                restoreCash()
+                return
+            }
+            const orderAmt =
+                row.priceSnapshot != null && !isNaN(Number(row.priceSnapshot)) && Number(row.priceSnapshot) > 0
+                    ? Number(row.priceSnapshot)
+                    : Number(row.total)
+            if (!isNaN(orderAmt) && orderAmt > 0) {
+                this.tokens = []
+                this.calcInput = ''
+                this.price = orderAmt
+                this.cash =
+                    row.cashSnapshot != null && !isNaN(Number(row.cashSnapshot))
+                        ? Number(row.cashSnapshot)
+                        : 0
+                this.keyboardTarget = 'order'
+                this.checkType = 1
+                return
+            }
+        },
+        cancel() {
+            this.tapVoice()
             this.$emit('cancelSelectPrice')
         },
-        activeKeyboard() {
-            this.$refs.keyboard.activate();
+        onNumpadTap(e) {
+            this.tapVoice()
+            const ds = e.currentTarget.dataset || {}
+            const action = ds.action
+            const char = ds.char
+            if (action === 'calc-add') {
+                this.pressCalcOp('+')
+                return
+            }
+            if (action === 'calc-sub') {
+                this.pressCalcOp('−')
+                return
+            }
+            if (action === 'calc-mul') {
+                this.pressCalcOp('×')
+                return
+            }
+            if (action === 'calc-div') {
+                this.pressCalcOp('÷')
+                return
+            }
+            if (action === 'calc-clear') {
+                this.clearStack()
+                this.keyboardTarget = 'calc'
+                this.checkType = 3
+                return
+            }
+            if (action === 'backspace') {
+                this.typing({ backspace: true })
+            } else if (char !== undefined && char !== null && char !== '') {
+                this.typing({ backspace: false, char: String(char) })
+            }
         },
         typing(e) {
+            const field = this.keyboardTarget
             if (e.backspace) {
-                if(this.fillType == 1){
-                    if(this.checkType == 1){
-                        this.price = ''
-                    }else{
-                        if (this.price.length > 0) {
-                            this.price = this.price.substring(0, this.price.length - 1)   
+                if (field === 'calc') {
+                    if (this.calcInput && String(this.calcInput).length > 0) {
+                        this.calcInput = String(this.calcInput).substring(0, String(this.calcInput).length - 1)
+                    } else if (this.tokens.length > 0) {
+                        const last = this.tokens[this.tokens.length - 1]
+                        if (last.type === 'op') {
+                            this.tokens.pop()
+                        } else if (last.s.length > 1) {
+                            last.s = String(last.s).substring(0, String(last.s).length - 1)
+                        } else {
+                            this.tokens.pop()
                         }
                     }
-                }else if(this.fillType == 2){
-                    if(this.checkType == 2){
+                } else if (field === 'order') {
+                    if (this.checkType === 1) {
+                        this.price = ''
+                    } else if (String(this.price).length > 0) {
+                        this.price = String(this.price).substring(0, String(this.price).length - 1)
+                    }
+                } else if (field === 'cash') {
+                    if (!this.showCashPayCols) {
+                        return
+                    }
+                    if (this.checkType === 2) {
                         this.cash = ''
-                    }else{
-                        if (this.cash.length > 0) {
-                            this.cash = this.cash.substring(0, this.cash.length - 1)   
+                    } else if (String(this.cash).length > 0) {
+                        this.cash = String(this.cash).substring(0, String(this.cash).length - 1)
+                    }
+                }
+            } else {
+                const ch = e.char
+                if (field === 'calc') {
+                    if (this.$util.isEmpty(this.calcInput) || this.checkType === 3) {
+                        this.calcInput = ch
+                    } else {
+                        if (ch === '.' && String(this.calcInput).indexOf('.') >= 0) {
+                            return
                         }
+                        this.calcInput += ch
                     }
-                }else{
-
-                }                
-            }else {
-                if(this.fillType == 1){
-                    if(this.$util.isEmpty(this.price) || this.checkType == 1) {
-                        this.price = e.char
-                    }else {
-                        this.price += e.char
+                } else if (field === 'order') {
+                    if (this.$util.isEmpty(this.price) || this.checkType === 1) {
+                        this.price = ch
+                    } else {
+                        if (ch === '.' && String(this.price).indexOf('.') >= 0) {
+                            return
+                        }
+                        this.price += ch
                     }
-                }else if(this.fillType == 2){
-                    if(this.$util.isEmpty(this.cash) || this.checkType == 2) {
-                        this.cash = e.char
-                    }else {
-                        this.cash += e.char
+                } else if (field === 'cash') {
+                    if (!this.showCashPayCols) {
+                        return
+                    }
+                    if (this.$util.isEmpty(this.cash) || this.checkType === 2) {
+                        this.cash = ch
+                    } else {
+                        if (ch === '.' && String(this.cash).indexOf('.') >= 0) {
+                            return
+                        }
+                        this.cash += ch
                     }
-                }else{
-
                 }
             }
             this.checkType = 0
         },
-        enter() {
-            if(Number(this.price)  <= 0) {
-                this.$msg('请填写金额')
+        submitCashierPay() {
+            this._submitCashierPayCore()
+        },
+        _submitCashierPayCore() {
+            this.tapVoice()
+            if (this.keyboardTarget === 'calc') {
+                const st = this.stackTotal
+                if (!Number.isFinite(st) || isNaN(st)) {
+                    this.$msg('算式无效(如除数为 0)')
+                    return
+                }
+                this.price = this.stackDiscountedAmount
+            }
+            const p = Number(this.price)
+            if (isNaN(p) || p <= 0) {
+                this.$msg('订单金额须大于 0')
                 return
             }
-            this.$emit('confirmSelectPrice', {price:Number(this.price),name:this.name,cash:Number(this.cash)})
-        }
-    }
+            if (this.selHasPay === 2 && (!this.customId || Number(this.customId) <= 0)) {
+                this.$msg('请先选择客户')
+                return
+            }
+            if (this.showCashPayCols) {
+                const cx = Number(this.cash) || 0
+                if (cx <= 0) {
+                    this.$msg('请输入收到的现金金额')
+                    return
+                }
+                if (Number(this.zl) < 0) {
+                    this.$msg('现金不足')
+                    return
+                }
+            }
+            const hasCalc =
+                this.tokens.length > 0 ||
+                (this.calcInput != null && !this.$util.isEmpty(this.calcInput))
+            const cashSubmit = this.showCashPayCols ? (Number(this.cash) || 0) : 0
+            const emitHasPay = this.selHasPay
+            const emitPayWay = this.selHasPay === 1 ? this.selPayWay : 0
+            this.pushHistorySnapshot(p, {
+                source: 'submit',
+                orderOnly: !hasCalc,
+            })
+            this.$emit('zjGathering', {
+                price: p,
+                name: this.name,
+                cash: cashSubmit,
+                hasPay: emitHasPay,
+                payWay: emitPayWay
+            })
+            this.price = 0
+            this.checkType = 3
+            this.keyboardTarget = 'calc'
+            this.cash = 0
+            this.selHasPay = 0
+            this.selPayWay = 0
+            this.tokens = []
+            this.calcInput = ''
+            this.resetActiveDiscountToNone()
+        },
+    },
 }
 </script>
 <style lang="scss" scoped>
-.pay-style{
-    text-align:center;
-    margin-top:10upx;
+.pay-style {
+    text-align: center;
+    margin-top: 10upx;
     button {
-        width:80upx;
-        height:30upx;
-        font-size:12upx;
-        display:inline-block;
-        color:#777575;
+        width: 80upx;
+        height: 30upx;
+        font-size: 12upx;
+        display: inline-block;
+        color: #777575;
     }
-    button:first-child{
-        margin-right:8upx;
+    button:first-child {
+        margin-right: 8upx;
     }
-    .selected{
-        color:#3385FF;
-        border:1upx solid #3385FF;
+    .selected {
+        color: #09c567;
+        border: 1upx solid #09c567;
     }
 }
-.grey-color{
-    color:#d1cfcf;
+.grey-color {
+    color: #d1cfcf;
 }
 .boardset-flowert {
-    width:34vw;
-    height: 90vh;
+    width: 91vw;
+    height: 99vh;
+    max-height: 960px;
     background-color: #ffffff;
     margin: 0 auto;
     border-radius: 5upx;
     padding: 10upx;
-    overflow-y: auto;
+    overflow: hidden;
     display: flex;
     flex-direction: column;
     box-sizing: border-box;
-    & .flower-input_box {
+
+    .register-main {
         flex: 1;
-        & .flower-title {
-            text-align: center;
-            color: #333333;
-            font-size: 14upx;
+        display: flex;
+        flex-direction: row;
+        align-items: stretch;
+        min-height: 0;
+    }
+    .register-left-scroll {
+        flex: 6;
+        min-width: 0;
+        width: 0;
+        height: 100%;
+        box-sizing: border-box;
+        padding-right: 10upx;
+    }
+    .register-right {
+        flex: 5;
+        min-width: 0;
+        width: 0;
+        height: 100%;
+        display: flex;
+        flex-direction: column;
+        box-sizing: border-box;
+        padding-left: 10upx;
+        border-left: 1upx solid #e8e8e8;
+    }
+    .flower-input_box {
+        padding-bottom: 16upx;
+    }
+    .flower-input_box-wrap {
+        position: relative;
+    }
+    .discount-entry {
+        position: absolute;
+        top: 6upx;
+        left: 6upx;
+        z-index: 3;
+        width: 44upx;
+        height: 44upx;
+        padding: 0;
+        margin: 0;
+        display: flex;
+        flex-direction: row;
+        align-items: center;
+        justify-content: center;
+        border-radius: 8upx;
+        background: rgba(255, 251, 245, 0.98);
+        border: 1upx solid #f0dcc8;
+        box-sizing: border-box;
+        pointer-events: auto;
+    }
+    .discount-entry--hover {
+        opacity: 0.82;
+    }
+    .discount-entry__glyph {
+        font-size: 17upx;
+        font-weight: 700;
+        color: #d97706;
+        line-height: 1;
+    }
+    .history-entry {
+        position: absolute;
+        top: 6upx;
+        right: 6upx;
+        z-index: 3;
+        width: 44upx;
+        height: 44upx;
+        padding: 0;
+        margin: 0;
+        display: flex;
+        flex-direction: row;
+        align-items: center;
+        justify-content: center;
+        border-radius: 8upx;
+        background: rgba(248, 255, 248, 0.95);
+        border: 1upx solid #c8ecd8;
+        box-sizing: border-box;
+        pointer-events: auto;
+    }
+    .history-entry--hover {
+        opacity: 0.82;
+    }
+    .history-entry__icon {
+        font-size: 22upx;
+        color: #09c567;
+        line-height: 1;
+    }
+    .rcv-pay-block {
+        margin-top: 10upx;
+        padding: 10upx 8upx 8upx;
+        border: 1upx solid #eeeeee;
+        border-radius: 6upx;
+        box-sizing: border-box;
+    }
+    .rcv-pay-title {
+        font-size: 12upx;
+        color: #333333;
+        text-align: left;
+        margin-bottom: 6upx;
+    }
+    .rcv-pay-title-sub {
+        margin-top: 8upx;
+    }
+    .rcv-sub-block {
+        margin-top: 4upx;
+    }
+    .rcv-item-list_box {
+        display: flex;
+        align-items: center;
+        justify-content: flex-start;
+        flex-wrap: wrap;
+        padding-bottom: 2upx;
+    }
+    .rcv-item-chip {
+        min-width: 78upx;
+        padding-left: 6upx;
+        padding-right: 6upx;
+        height: 26upx;
+        text-align: center;
+        line-height: 26upx;
+        font-size: 10upx;
+        border: 1upx solid #eeeeee;
+        margin-right: 6upx;
+        margin-bottom: 6upx;
+        margin-top: 0;
+        color: #333333;
+        border-radius: 3upx;
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        box-sizing: border-box;
+    }
+    .rcv-item-chip:first-child {
+        margin-left: 0;
+    }
+    .rcv-item-chip.active {
+        background-color: #09c567;
+        color: #ffffff;
+        border-color: #09c567;
+    }
+    .rcv-customer-line {
+        font-size: 13upx;
+        margin-bottom: 4upx;
+        margin-top: 2upx;
+    }
+    .rcv-customer-line__name {
+        color:black;
+        font-weight: bold;
+    }
+    .rcv-customer-line__amt {
+        color: #3385ff;
+        font-weight: bold;
+        margin-left:20upx;
+    }
+    .pay-amount-row {
+        display: flex;
+        flex-direction: row;
+        margin-top: 10upx;
+        align-items: flex-start;
+    }
+    .pay-amount-col {
+        flex: 1;
+        min-width: 0;
+        margin-top: 0;
+        & + .pay-amount-col {
+            margin-left: 8upx;
         }
-        & .flower-ratio{
-            text-align: center;
-            color: #CCCCCC;
-            font-size: 9upx;
-            margin-bottom:10upx;
-            margin-top:6upx;
+    }
+    .pay-change-desc {
+        display: flex;
+        flex-direction: row;
+        align-items: center;
+        justify-content: center;
+    }
+    .change-label-ok {
+        font-size: 13upx;
+        color: #09c567;
+    }
+    .change-label-bad {
+        font-size: 13upx;
+        color: #e54545;
+    }
+    .change-label-neutral {
+        font-size: 13upx;
+        color: #cccccc;
+    }
+    .change-hint-ok {
+        font-size: 15upx;
+        color: #09c567;
+    }
+    .change-hint-bad {
+        font-size: 15upx;
+        color: #e54545;
+    }
+    .change-hint-neutral {
+        font-size: 15upx;
+        color: #cccccc;
+    }
+    .flower-unit_box-change {
+        min-height: 25upx;
+    }
+    .pay-submit-row {
+        display: flex;
+        flex-direction: row;
+        margin-top: 12upx;
+        align-items: stretch;
+    }
+    .pay-submit-row .pay-submit-btn {
+        flex: 1;
+    }
+    .pay-submit-row .pay-submit-btn + .pay-submit-btn {
+        margin-left: 8upx;
+    }
+    .pay-submit-btn {
+        text-align: center;
+        font-size: 16upx;
+        padding: 14upx 16upx;
+        background: #09c567;
+        color: #ffffff;
+        border-radius: 8upx;
+        border: 1upx solid #09c567;
+        font-weight: 600;
+        box-sizing: border-box;
+    }
+    .pay-submit-btn-secondary {
+        background: #f7f7f7;
+        color: #333333;
+        border-color: #dddddd;
+        font-weight: 500;
+    }
+    .pay-submit-btn-stash {
+        background: #ffffff;
+        color: #09c567;
+        border-color: #09c567;
+        font-weight: 600;
+    }
+    .pay-submit-btn:active {
+        opacity: 0.9;
+    }
+    .calc-section {
+        position: relative;
+        border: 1upx solid #eeeeee;
+        border-radius: 6upx;
+        padding: 10upx;
+        margin-top: 0;
+        margin-bottom: 10upx;
+        &.calc-section-active {
+            border-color: #09c567;
+            background: #f8fff8;
         }
-        & .set-residue_box {
-            font-size: 12upx;
-            color: #CCCCCC;
-            margin-top: 10upx;
-            text-align: left;
+    }
+    .gather-pin-entry {
+        position: absolute;
+        left: 8upx;
+        bottom: 8upx;
+        z-index: 2;
+        width: 40upx;
+        height: 40upx;
+        display: flex;
+        flex-direction: row;
+        align-items: center;
+        justify-content: center;
+        border-radius: 8upx;
+        background: rgba(250, 250, 250, 0.98);
+        border: 1upx solid #dddddd;
+        box-sizing: border-box;
+    }
+    .gather-pin-entry--on {
+        border-color: #09c567;
+        background: rgba(240, 255, 245, 0.95);
+    }
+    .gather-pin-entry--hover {
+        opacity: 0.85;
+    }
+    .gather-pin-entry__icon {
+        font-size: 20upx;
+        color: #999999;
+        line-height: 1;
+    }
+    .gather-pin-entry--on .gather-pin-entry__icon {
+        color: #09c567;
+    }
+    .calc-title {
+        text-align: center;
+        font-size: 13upx;
+        color: #333333;
+        margin-bottom: 8upx;
+    }
+    .calc-formula {
+        min-height: 36upx;
+        font-size: 14upx;
+        color: #333333;
+        text-align: center;
+        line-height: 1.4;
+        word-break: break-all;
+    }
+    .calc-formula-inner {
+        display: flex;
+        flex-direction: row;
+        flex-wrap: wrap;
+        align-items: center;
+        justify-content: center;
+    }
+    .calc-placeholder {
+        font-size: 12upx;
+        color: #999999;
+    }
+    .calc-num {
+        color: #333333;
+    }
+    .calc-op {
+        color: #09c567;
+    }
+    .calc-total-row {
+        display: flex;
+        flex-direction: row;
+        align-items: center;
+        justify-content: center;
+        margin-top: 10upx;
+    }
+    .calc-total-label {
+        font-size: 12upx;
+        color: #999999;
+        margin-right: 8upx;
+    }
+    .calc-total-val {
+        font-size: 16upx;
+        font-weight: bold;
+        color: #09c567;
+    }
+    .calc-discount-row {
+        display: flex;
+        flex-direction: row;
+        align-items: center;
+        justify-content: center;
+        margin-top: 6upx;
+    }
+    .calc-discount-label {
+        font-size: 11upx;
+        color: #d97706;
+        margin-right: 8upx;
+    }
+    .calc-discount-val {
+        font-size: 15upx;
+        font-weight: bold;
+        color: #d97706;
+    }
+    .calc-btn-row {
+        display: flex;
+        flex-direction: row;
+        margin-top: 10upx;
+        & + .calc-btn-row {
+            margin-top: 8upx;
         }
-        .goods-name{
-            border:1upx solid #EEEEEE;
-            width:150upx;
-            margin:10upx auto 0 auto;
-            height:30upx;
-            font-size:15upx;
-            text-align:center;
+    }
+    .calc-btn-row-single .calc-btn {
+        flex: 1;
+    }
+    .calc-btn {
+        flex: 1;
+        text-align: center;
+        font-size: 12upx;
+        padding: 8upx 4upx;
+        border: 1upx solid #dddddd;
+        border-radius: 4upx;
+        color: #333333;
+        background: #fafafa;
+        & + .calc-btn {
+            margin-left: 8upx;
         }
     }
-    & .flower-open_box {
+    .calc-btn-primary {
+        background: #09c567;
+        color: #ffffff;
+        border-color: #09c567;
+    }
+    .calc-history-popup {
+        width: 45vw;
+        max-width: 300upx;
+        max-height: 82vh;
+        display: flex;
+        flex-direction: column;
+        box-sizing: border-box;
+        border-radius: 8upx;
+        overflow: hidden;
+    }
+    .calc-history-popup-hd {
+        display: flex;
+        flex-direction: row;
+        align-items: center;
+        justify-content: space-between;
+        padding: 12upx 16upx;
+        border-bottom: 1upx solid #eeeeee;
+        flex-shrink: 0;
+    }
+    .calc-history-popup-title {
+        font-size: 15upx;
+        font-weight: bold;
+        color: #333333;
+    }
+    .calc-history-popup-close {
+        font-size: 13upx;
+        color: #09c567;
+        padding: 4upx 8upx;
+    }
+    .calc-history-popup-body {
+        height: 480upx;
+        max-height: 64vh;
+        box-sizing: border-box;
+    }
+    .history-item {
+        display: flex;
+        flex-direction: row;
+        align-items: center;
+        padding: 8upx 6upx;
+        border-bottom: 1upx solid #f0f0f0;
+    }
+    .history-item-body {
+        flex: 1;
+        min-width: 0;
+    }
+    .history-del {
+        flex-shrink: 0;
+        font-size: 11upx;
+        color: #e54545;
+        padding: 4upx 8upx;
+        margin-left: 6upx;
+    }
+    .history-line {
+        display: flex;
+        flex-direction: row;
+        justify-content: space-between;
+        margin-bottom: 4upx;
+    }
+    .history-time {
+        font-size: 11upx;
+        color: #999999;
+    }
+    .history-total {
+        font-size: 11upx;
+        color: #09c567;
+    }
+    .history-detail {
+        font-size: 11upx;
+        color: #666666;
+        word-break: break-all;
+    }
+    .history-empty {
+        text-align: center;
+        font-size: 11upx;
+        color: #cccccc;
+        padding: 16upx 0;
+    }
+    .discount-popup {
+        width: 52vw;
+        max-width: 400upx;
+    }
+    .discount-popup-toolbar {
+        flex-shrink: 0;
+        padding: 10upx 12upx 12upx;
+        border-bottom: 1upx solid #f0f0f0;
+        box-sizing: border-box;
+    }
+    .discount-custom-row {
+        display: flex;
+        flex-direction: row;
+        align-items: center;
+    }
+    .discount-custom-input {
+        flex: 1;
+        min-width: 0;
+        height: 36upx;
+        line-height: 36upx;
+        padding: 0 10upx;
+        font-size: 13upx;
+        border: 1upx solid #dddddd;
+        border-radius: 6upx;
+        background: #ffffff;
+        box-sizing: border-box;
+    }
+    .discount-custom-add-btn {
+        flex-shrink: 0;
+        margin-left: 8upx;
+        padding: 0 14upx;
+        height: 36upx;
+        line-height: 36upx;
+        font-size: 13upx;
+        color: #ffffff;
+        background: #09c567;
+        border-radius: 6upx;
+        text-align: center;
+        box-sizing: border-box;
+    }
+    .discount-toolbar-hint {
+        display: block;
+        font-size: 11upx;
+        color: #09c567;
+        line-height: 1.4;
+        padding: 8upx 4upx 0;
+    }
+    .discount-toolbar-hint--muted {
+        color: #999999;
+    }
+    .discount-popup-body-scroll {
+        box-sizing: border-box;
+        padding: 10upx 12upx 12upx;
+    }
+    .discount-option {
+        display: flex;
+        flex-direction: row;
+        align-items: center;
+        justify-content: space-between;
+        padding: 12upx 10upx;
+        border: 1upx solid #eeeeee;
+        border-radius: 6upx;
+        margin-bottom: 8upx;
+        background: #fafafa;
+        box-sizing: border-box;
+    }
+    .discount-option--active {
+        border-color: #09c567;
+        background: #f0fff4;
+    }
+    .discount-option-text {
+        font-size: 14upx;
+        color: #333333;
+    }
+    .discount-option-check {
+        font-size: 14upx;
+        color: #09c567;
+        font-weight: bold;
+    }
+    .discount-option--with-del {
+        padding-right: 6upx;
+    }
+    .discount-option-main {
+        flex: 1;
+        min-width: 0;
         display: flex;
+        flex-direction: row;
         align-items: center;
-        margin-top:10upx;
+        justify-content: space-between;
+        padding-right: 8upx;
+        box-sizing: border-box;
+    }
+    .discount-option-del {
+        flex-shrink: 0;
+        font-size: 11upx;
+        color: #e54545;
+        padding: 4upx 8upx;
+    }
+    .flower-open_box {
+        display: flex;
+        align-items: center;
+        margin-top: 10upx;
         justify-content: center;
         & > view {
-            width:90upx;
+            width: 100%;
+            box-sizing: border-box;
             & .desc {
                 text-align: center;
                 font-size: 13upx;
                 margin-bottom: 5upx;
-                color:#CCCCCC;
+                color: #cccccc;
             }
             & .flower-unit_box {
                 display: flex;
-                font-size:15upx;
+                font-size: 15upx;
                 align-items: center;
-                justify-content:center;
+                justify-content: center;
                 border-bottom: 1px solid #eee;
-                & > text{
-                    height:25upx;
+                & > text {
+                    height: 25upx;
                 }
-                .selected{
-                    background:blue;
-                    color:white;
+                .selected {
+                    background: blue;
+                    color: white;
                 }
             }
         }
     }
-    .switch-button{
+    .flower-open_box.pay-amount-col {
+        margin-top: 0;
+    }
+    .switch-button {
         display: flex;
         justify-content: center;
         width: 35upx;
         height: 35upx;
         position: absolute;
-        top:70upx;
-        left:14vw;
+        top: 70upx;
+        left: 14vw;
         .change-unit {
             width: 35upx;
             height: 35upx;
         }
     }
-    & .keyboard-body_box {
+    .keyboard-body_box {
         flex: 1;
+        min-height: 0;
+        display: flex;
+        flex-direction: column;
+        padding-top: 0;
+        border-top: none;
         overflow: hidden;
     }
 }
+.keyboard-body_box .numpad {
+    flex: 1;
+    display: flex;
+    flex-direction: column;
+    min-height: 0;
+}
+.numpad {
+    width: 100%;
+    background: #ececec;
+    border-radius: 8upx;
+    padding: 8upx;
+    box-sizing: border-box;
+}
+.numpad-grid-wrap {
+    flex: 1;
+    display: flex;
+    flex-direction: row;
+    align-items: stretch;
+    min-height: 0;
+}
+.numpad-digits {
+    flex: 1;
+    min-width: 0;
+    display: flex;
+    flex-direction: column;
+    min-height: 0;
+}
+.numpad-digits .numpad-row {
+    flex: 1;
+}
+.numpad-op-col {
+    width: 26%;
+    flex-shrink: 0;
+    display: flex;
+    flex-direction: column;
+    margin-left: 8upx;
+}
+.numpad-op-col .numpad-key {
+    margin-left: 0;
+}
+.numpad-op-col .numpad-key-op {
+    flex: 1;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    min-height: 44upx;
+    line-height: normal;
+    font-size: 22upx;
+    font-weight: 600;
+}
+.numpad-op-col .numpad-key-op + .numpad-key-op {
+    margin-top: 8upx;
+}
+.numpad-key-op-muted {
+    font-size: 13upx;
+    font-weight: 600;
+    background: #e8e8e8;
+    color: #333333;
+    box-shadow: 0 2upx 0 #bdbdbd;
+}
+.keyboard-body_box .numpad-op-col .numpad-key-op {
+    min-height: 0;
+}
+.keyboard-body_box .numpad-row {
+    flex: 1;
+    min-height: 40upx;
+}
+.numpad-row {
+    display: flex;
+    flex-direction: row;
+    margin-top: 8upx;
+    &:first-child {
+        margin-top: 0;
+    }
+}
+.keyboard-body_box .numpad-key {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    height: auto;
+    min-height: 44upx;
+    line-height: normal;
+}
+.numpad-key {
+    flex: 1;
+    margin-left: 8upx;
+    height: 44upx;
+    line-height: 44upx;
+    text-align: center;
+    font-size: 18upx;
+    font-weight: 500;
+    color: #222222;
+    background: #ffffff;
+    border-radius: 8upx;
+    box-shadow: 0 2upx 0 #c8c8c8;
+    &:first-child {
+        margin-left: 0;
+    }
+    &:active {
+        background: #e8e8e8;
+        box-shadow: 0 1upx 0 #bbbbbb;
+    }
+}
+.numpad-key-muted {
+    font-size: 22upx;
+    font-weight: 400;
+    color: #555555;
+}
+.numpad-key-text {
+    font-size: 14upx;
+}
+.numpad-row-actions {
+    margin-top: 10upx;
+}
+.numpad-key-action-dual {
+    flex: 1;
+}
+.numpad-key-delete-only {
+    flex: 1;
+    margin-left: 0;
+}
+.numpad-key-wide {
+    flex: 1.2;
+}
+.numpad-key-danger {
+    background: #ffeded;
+    color: #cc4444;
+    box-shadow: 0 2upx 0 #ddb8b8;
+}
+.numpad-key-action {
+    font-size: 14upx;
+    background: #f7f7f7;
+}
+.numpad-key-ok {
+    background: #09c567;
+    color: #ffffff;
+    font-weight: 600;
+    box-shadow: 0 2upx 0 #078a4a;
+}
+.numpad-key-ok:active {
+    background: #08b35d;
+}
 </style>

+ 4 - 4
hdPad/src/pages/home/components/selectPrice.vue

@@ -61,9 +61,9 @@
                                 {{ item.name }}
                             </view>
                         </view>
-                        <view v-if="Number(balance) > 0" class="rcv-customer-line">
+                        <view class="rcv-customer-line">
                             <text class="rcv-customer-line__name">{{ rcvCustomerBalanceLabel }}</text>
-                            <text class="rcv-customer-line__amt"> ¥{{ formatMoney(balance) }}</text>
+                            <text class="rcv-customer-line__amt" v-if="Number(balance) > 0"> ¥{{ formatMoney(balance) }}</text>
                         </view>
                         <view v-if="selHasPay === 1" class="rcv-sub-block">
                             <view class="rcv-pay-title rcv-pay-title-sub">请选择线下收款方式</view>
@@ -1425,8 +1425,8 @@ export default {
         margin-top: 2upx;
     }
     .rcv-customer-line__name {
-        color: #999999;
-        font-weight: normal;
+        color:black;
+        font-weight: bold;
     }
     .rcv-customer-line__amt {
         color: #3385ff;