| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264 |
- <template>
- <view class="calc-page">
- <scroll-view class="calc-scroll" scroll-y :enable-back-to-top="false">
- <view class="calc-sheet">
- <view class="calc-card-wrap">
- <view
- class="calc-section"
- :class="{ 'calc-section-active': keyboardTarget === 'calc' }"
- @tap="focusCalc"
- >
- <view class="calc-actions-top">
- <view class="discount-entry" @tap.stop="openDiscountPopup" hover-class="discount-entry-hover">
- <text class="discount-entry-glyph">折</text>
- </view>
- <text class="calc-section-title">计算器</text>
- <view class="history-entry" @tap.stop="openCalcHistory" hover-class="history-entry-hover">
- <text class="iconfont iconmingxi1 history-entry-icon"></text>
- </view>
- </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>
- <view class="customer-strip" @tap.stop="pickCustomer">
- <text class="customer-strip-label">客户</text>
- <text class="customer-strip-name">{{ customerDisplayName }}</text>
- <text class="customer-strip-action">修改客户</text>
- </view>
- <view v-if="showBalanceRow" class="balance-strip">
- <text class="balance-strip-label">余额</text>
- <text class="balance-strip-val">¥{{ formatMoney(customInfo.balance) }}</text>
- <text v-if="balanceCoversDue && selHasPay === 4" class="balance-strip-hint balance-strip-hint-ok">余额充足</text>
- <text v-else-if="balanceCoversDue && selHasPay !== 4" class="balance-strip-hint balance-strip-hint-tip">可选用「余额」收款</text>
- </view>
- <view class="rcv-pay-block">
- <view class="rcv-pay-title">收款方式</view>
- <view class="rcv-item-row">
- <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 v-if="selHasPay === 0" class="rcv-pay-hint">
- <text class="rcv-pay-hint-text">扫码付款、分享小程序付款</text>
- </view>
- <view v-if="selHasPay === 2" class="rcv-pay-hint">
- <text class="rcv-pay-hint-text">客户赊账欠款请选挂账</text>
- </view>
- <view v-if="selHasPay === 4" class="rcv-pay-hint">
- <text class="rcv-pay-hint-text">使用客户余额扣款</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-row">
- <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 v-if="showCashPayCols" class="pay-cash-panel">
- <view class="pay-cash-item" @tap="checkCurrent(2)">
- <text class="pay-cash-desc">收到现金</text>
- <text
- class="pay-cash-amt"
- :class="{ 'pay-cash-amt-selected': keyboardTarget !== 'calc' && checkType == 2 }"
- >
- {{ formatMoney(cash) }}
- </text>
- </view>
- <view class="pay-cash-item pay-cash-item-change">
- <view class="pay-change-line">
- <text v-if="Number(zl) > 0" class="lbl-ok">找零</text>
- <text v-else-if="Number(zl) < 0" class="lbl-bad">还差</text>
- <text v-else class="lbl-neu">找零</text>
- </view>
- <text v-if="Number(zl) > 0" class="amt-ok">{{ formatChangeAmount(zl) }}</text>
- <text v-else-if="Number(zl) < 0" class="amt-bad">{{ formatChangeAmount(-zl) }}</text>
- <text v-else class="amt-neu">—</text>
- </view>
- </view>
- <view class="pay-submit-row">
- <view class="pay-submit-btn pay-submit-btn-outline" @tap.stop="stashCurrent">暂存</view>
- <view class="pay-submit-btn" @tap.stop="submitCashierPay">提交</view>
- </view>
- <view class="safe-bottom-spacer" />
- </view>
- </view>
- </scroll-view>
- <view class="numpad-dock safe-area-padding">
- <view class="numpad">
- <view class="numpad-grid-wrap">
- <view class="numpad-cell"><view class="numpad-key" data-char="1" @tap="onNumpadTap">1</view></view>
- <view class="numpad-cell"><view class="numpad-key" data-char="2" @tap="onNumpadTap">2</view></view>
- <view class="numpad-cell"><view class="numpad-key" data-char="3" @tap="onNumpadTap">3</view></view>
- <view class="numpad-cell numpad-cell-op numpad-cell-mul">
- <view class="numpad-key numpad-key-op" data-action="calc-mul" @tap="onNumpadTap">×</view>
- </view>
- <view class="numpad-cell"><view class="numpad-key" data-char="4" @tap="onNumpadTap">4</view></view>
- <view class="numpad-cell"><view class="numpad-key" data-char="5" @tap="onNumpadTap">5</view></view>
- <view class="numpad-cell"><view class="numpad-key" data-char="6" @tap="onNumpadTap">6</view></view>
- <view class="numpad-cell numpad-cell-op numpad-cell-add">
- <view class="numpad-key numpad-key-op numpad-key-op-add" data-action="calc-add" @tap="onNumpadTap">
- +
- </view>
- </view>
- <view class="numpad-cell"><view class="numpad-key" data-char="7" @tap="onNumpadTap">7</view></view>
- <view class="numpad-cell"><view class="numpad-key" data-char="8" @tap="onNumpadTap">8</view></view>
- <view class="numpad-cell"><view class="numpad-key" data-char="9" @tap="onNumpadTap">9</view></view>
- <view class="numpad-cell"><view class="numpad-key numpad-key-muted" data-char="." @tap="onNumpadTap">.</view></view>
- <view class="numpad-cell"><view class="numpad-key" data-char="0" @tap="onNumpadTap">0</view></view>
- <view class="numpad-cell"><view class="numpad-key" data-char="00" @tap="onNumpadTap">00</view></view>
- <view class="numpad-cell numpad-cell-op numpad-cell-sub">
- <view class="numpad-key numpad-key-op" data-action="calc-sub" @tap="onNumpadTap">−</view>
- </view>
- <view class="numpad-cell numpad-cell-actions-row">
- <view
- class="numpad-key numpad-key-action-dual numpad-key-action-muted"
- @tap.stop="tapClearCalculator"
- >
- <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 class="numpad-cell numpad-cell-op numpad-cell-div">
- <view class="numpad-key numpad-key-op" data-action="calc-div" @tap="onNumpadTap">÷</view>
- </view>
- </view>
- </view>
- </view>
- <uni-popup ref="calcHistoryPopup" type="center" background-color="#ffffff" @change="onCalcHistoryPopupChange">
- <view class="mob-popup-wrap mob-popup-history">
- <view class="mob-popup-hd">
- <text class="mob-popup-title">暂存记录</text>
- <text class="mob-popup-close" @tap.stop="onHistoryPopupCloseTap">关闭</text>
- </view>
- <scroll-view scroll-y class="mob-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 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="mob-popup-wrap mob-popup-discount">
- <view class="mob-popup-hd">
- <text class="mob-popup-title">折扣</text>
- <text class="mob-popup-close" @tap.stop="onDiscountPopupCloseTap">关闭</text>
- </view>
- <view class="discount-toolbar">
- <view class="discount-custom-row">
- <input
- class="discount-custom-input"
- v-model="customZheInput"
- type="digit"
- placeholder="如 8.8 折"
- confirm-type="done"
- />
- <view class="discount-custom-add-btn" @tap.stop="addCustomDiscountZhe">添加</view>
- </view>
- <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 class="mob-popup-body discount-options-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>
- </uni-popup>
- <uni-popup ref="cashShortPopup" type="center" background-color="#ffffff" @change="onCashShortPopupChange">
- <view class="mob-popup-wrap mob-popup-cash-short">
- <view class="mob-popup-hd">
- <text class="mob-popup-title">现金不足</text>
- </view>
- <text class="cash-short-desc">实收现金不足,还可以再用微信和支付宝付款</text>
- <view class="cash-short-way-row">
- <view
- class="cash-short-way-chip"
- :class="{ 'cash-short-way-chip-on': cashShortPick === 0 }"
- @tap.stop="setCashShortPick(0)"
- >
- 微信
- </view>
- <view
- class="cash-short-way-chip"
- :class="{ 'cash-short-way-chip-on': cashShortPick === 1 }"
- @tap.stop="setCashShortPick(1)"
- >
- 支付宝
- </view>
- </view>
- <view class="cash-short-actions">
- <view class="cash-short-btn cash-short-btn-cancel" @tap.stop="closeCashShortPopupByCancel">取消</view>
- <view class="cash-short-btn cash-short-btn-ok" @tap.stop="confirmCashShortWay">确认并提交</view>
- </view>
- </view>
- </uni-popup>
- </view>
- </template>
- <script>
- import { createOrder } from '@/api/order/index'
- import { getCustomInfo } from '@/api/custom'
- import { mapActions, mapGetters } from 'vuex'
- const CALC_HISTORY_STORAGE_KEY = 'hdApp_calculator_calcHistory'
- const DISCOUNT_CUSTOM_STORAGE_KEY = 'hdApp_calculator_customDiscountZhe'
- const DISCOUNT_SELECTED_STORAGE_KEY = 'hdApp_calculator_discountZhe'
- /** 计算器选客回填:hdApp 零售 selectCustom 使用 style=6(style=3 为花束开单) */
- const CALCULATOR_PICK_CUSTOMER_KEY = 'hdApp_calculator_pick_customer'
- /** 与首页「快捷开单」一致:取自 loginInfo.skCustomId,界面展示名固定 */
- const SHORTCUT_CUSTOM_DISPLAY_NAME = '快捷开单'
- const SETTLE_FORM_DEFAULTS = {
- sendType: 1,
- hasPay: 0,
- payWay: 0,
- needPrint: 2,
- freight: '',
- groupName: '',
- remark: '',
- sendCost: 0,
- customSendCost: 0,
- labourCost: 0,
- modifyPrice: 0,
- getStaffId: 0,
- getStaffName: '',
- shopAdminId: 0,
- shopAdminName: '',
- cash: 0,
- dealPrice: 1,
- fromType: 1,
- reductionRule: 0,
- callErrand: 0,
- weight: 1,
- deliveryRemark: '',
- }
- export default {
- name: 'ToolsCalculator',
- data() {
- return {
- customId: 0,
- customName: '',
- price: 0,
- checkType: 3,
- keyboardTarget: 'calc',
- name: '',
- cash: 0,
- tokens: [],
- calcInput: '',
- calcHistory: [],
- maxHistory: 20,
- _historyPopupCloseByApi: false,
- discountZhe: 10,
- customDiscountZheList: [],
- customZheInput: '',
- _discountPopupCloseByApi: false,
- selHasPay: 0,
- selPayWay: 0,
- optHasPayList: [
- { name: '扫码', id: 0 },
- { name: '余额', id: 4 },
- { name: '线下', id: 1 },
- { name: '挂账', id: 2 },
- ],
- optPayWayList: [
- { name: '微信', id: 0 },
- { name: '支付宝', id: 1 },
- { name: '现金', id: 4 },
- { name: '银行卡', id: 5 },
- ],
- customInfo: { discount: 1, balance: 0 },
- cashShortPick: 0,
- _cashShortPopupCloseByApi: false,
- }
- },
- computed: {
- ...mapGetters({
- loginInfo: 'getLoginInfo',
- getMerchantInfo: 'getMerchantInfo',
- }),
- /** 工作台「快捷开单」客户 id,与 workbench skKd 使用的 loginInfo.skCustomId 一致 */
- shortcutCustomerId() {
- const login = this.loginInfo || {}
- const sid = login.skCustomId
- if (this.$util.isEmpty(sid) || Number(sid) <= 0) return 0
- return Number(sid)
- },
- customerDisplayName() {
- const n = (this.customName || '').trim()
- if (n) return n
- const skid = this.shortcutCustomerId
- if (Number(this.customId) > 0 && skid > 0 && Number(this.customId) === skid) {
- return SHORTCUT_CUSTOM_DISPLAY_NAME
- }
- 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) {
- amount = Number(this.cash) - Number(this.price)
- amount = parseFloat(amount.toFixed(2))
- }
- return Number(amount)
- },
- showCashPayCols() {
- return this.selHasPay === 1 && this.selPayWay === 4
- },
- showBalanceRow() {
- const b = Number(this.customInfo && this.customInfo.balance)
- return Number.isFinite(b) && b > 0 && Number(this.customId) > 0
- },
- balanceCoversDue() {
- const b = Number(this.customInfo && this.customInfo.balance) || 0
- const due =
- this.keyboardTarget === 'calc'
- ? Number(this.stackDiscountedAmount)
- : Number(this.price)
- const p = Number(due)
- if (!Number.isFinite(p) || p <= 0) return false
- return b + 1e-6 >= p
- },
- },
- watch: {
- shortcutCustomerId(n) {
- if (n > 0 && Number(this.customId) <= 0) this.applyDefaultShortcutCustomer()
- },
- stackDiscountedAmount: {
- handler(v) {
- if (this.keyboardTarget === 'calc') {
- const n = parseFloat(Number(v).toFixed(2))
- this.price = isNaN(n) ? 0 : n
- }
- },
- immediate: true,
- },
- },
- onLoad(options) {
- if (options && options.customId) {
- const id = Number(options.customId)
- if (!isNaN(id) && id > 0) this.customId = id
- }
- if (options && options.customName) {
- this.customName = decodeURIComponent(String(options.customName))
- }
- this.applyDefaultShortcutCustomer()
- this.refreshCustomerBalance()
- },
- onShow() {
- this.applyPickCustomerFromStorage()
- this.applyDefaultShortcutCustomer()
- this.refreshCustomerBalance()
- },
- mounted() {
- this.loadCalcHistoryFromStorage()
- this.loadDiscountFromStorage()
- this.initMerchantInfo().catch(() => {})
- },
- methods: {
- ...mapActions(['initMerchantInfo']),
- init(){
- },
- pickCustomer() {
- this.tapVoice()
- this.$util.pageTo({url: '/admin/custom/selectCustom?style=6'})
- },
- applyPickCustomerFromStorage() {
- try {
- const raw = uni.getStorageSync(CALCULATOR_PICK_CUSTOMER_KEY)
- if (!raw) return
- const obj = typeof raw === 'string' ? JSON.parse(raw) : raw
- if (obj && obj.id != null) {
- this.customId = Number(obj.id) || 0
- this.customName = obj.name != null ? String(obj.name) : ''
- }
- uni.removeStorageSync(CALCULATOR_PICK_CUSTOMER_KEY)
- } catch (e) {}
- },
- /** 未选客且无路由参数带入时,默认工作台设置的快捷客户 */
- applyDefaultShortcutCustomer() {
- if (Number(this.customId) > 0) return
- const skid = this.shortcutCustomerId
- if (!skid || skid <= 0) return
- this.customId = skid
- this.customName = SHORTCUT_CUSTOM_DISPLAY_NAME
- },
- /** 对齐 affirm:拉取客户余额;余额大于应收且未手动选收款方式时再自动切换到余额收款 */
- refreshCustomerBalance() {
- const cid = Number(this.customId) || 0
- if (cid <= 0) {
- this.customInfo = { discount: 1, balance: 0 }
- return
- }
- getCustomInfo({ id: cid })
- .then((res) => {
- if (!res || res.code !== 1 || !res.data || !res.data.custom) return
- const prevHasPay = this.selHasPay
- this.customInfo = res.data.custom
- const bal = Number(this.customInfo.balance) || 0
- const due =
- this.keyboardTarget === 'calc'
- ? Number(this.stackDiscountedAmount)
- : Number(this.price)
- const amt = Number(due)
- if (
- prevHasPay !== 4 &&
- Number.isFinite(amt) &&
- amt > 0 &&
- bal > amt + 1e-6
- ) {
- this.selHasPay = 4
- this.cash = 0
- this.selPayWay = 0
- this.blurCashIfNoColumn()
- }
- this.$forceUpdate()
- })
- .catch(() => {})
- },
- 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
- this.calcHistory = list.slice(0, this.maxHistory).map((row, i) => this.normalizeHistoryRow(row, i))
- } catch (e) {
- console.warn('[calculator] 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('[calculator] 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('[calculator] loadDiscountFromStorage', e)
- }
- },
- 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('[calculator] persistDiscountSelected', e)
- }
- },
- persistCustomDiscountList() {
- try {
- uni.setStorageSync(DISCOUNT_CUSTOM_STORAGE_KEY, JSON.stringify(this.customDiscountZheList))
- } catch (e) {
- console.warn('[calculator] 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()
- },
- 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()
- }
- },
- tapVoice() {
- this.$util.hitRemind()
- },
- 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
- },
- /** 金额展示:最多两位小数,不保留小数末尾多余的 0 */
- formatMoney(n) {
- const x = Number(n)
- if (isNaN(x)) return '0'
- return parseFloat(x.toFixed(2)).toString()
- },
- 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.keyboardTarget = n === 1 ? 'order' : 'cash'
- },
- onHasPayChipTap(e) {
- const ds = e.currentTarget.dataset || {}
- const val = ds.hpid !== undefined ? Number(ds.hpid) : NaN
- if (isNaN(val)) return
- this.applySelHasPay(val)
- },
- onPayWayChipTap(e) {
- const ds = e.currentTarget.dataset || {}
- const val = ds.pwid !== undefined ? Number(ds.pwid) : NaN
- 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()
- },
- setCashShortPick(v) {
- this.tapVoice()
- this.cashShortPick = Number(v) === 1 ? 1 : 0
- },
- closeCashShortPopupByCancel() {
- this.tapVoice()
- this._cashShortPopupCloseByApi = true
- this.$refs.cashShortPopup && this.$refs.cashShortPopup.close()
- },
- onCashShortPopupChange(e) {
- if (!e || e.show !== false) return
- if (this._cashShortPopupCloseByApi) {
- this._cashShortPopupCloseByApi = false
- return
- }
- this.tapVoice()
- },
- confirmCashShortWay() {
- this.tapVoice()
- const way = Number(this.cashShortPick) === 1 ? 1 : 0
- this.applySelPayWay(way)
- this._cashShortPopupCloseByApi = true
- this.$refs.cashShortPopup && this.$refs.cashShortPopup.close()
- this.$nextTick(() => {
- this._submitCashierPayCore()
- })
- },
- 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()
- },
- tapClearCalculator() {
- this.tapVoice()
- this.clearStack()
- this.keyboardTarget = 'calc'
- this.checkType = 3
- this.price = 0
- },
- 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
- }
- },
- 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 === '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 (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 (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 (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 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 (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
- }
- }
- }
- this.checkType = 0
- },
- submitCashierPay() {
- this._submitCashierPayCore()
- },
- _resolveXjPayload() {
- try {
- if (Number(this.customId) > 0) {
- return uni.getStorageSync('xj' + this.customId)
- }
- } catch (e) {}
- try {
- return uni.getStorageSync('xj')
- } catch (e2) {}
- return null
- },
- _submitCashierPayCore() {
- this.tapVoice()
- if (this.keyboardTarget === 'calc') {
- const st = this.stackTotal
- if (!Number.isFinite(st) || isNaN(st)) {
- this.$msg('算式无效')
- return
- }
- this.price = this.stackDiscountedAmount
- }
- const p = Number(this.price)
- if (isNaN(p) || p <= 0) {
- this.$msg('金额须大于 0')
- return
- }
- if (this.selHasPay === 2 && (!this.customId || Number(this.customId) <= 0)) {
- this.$msg('请先选择客户')
- return
- }
- if (this.selHasPay === 4) {
- if (!this.customId || Number(this.customId) <= 0) {
- this.$msg('请先选择客户')
- return
- }
- const bal = Number(this.customInfo.balance) || 0
- if (bal + 1e-6 < p) {
- this.$msg('客户余额不足')
- return
- }
- }
- if (this.showCashPayCols) {
- const cx = Number(this.cash) || 0
- if (cx <= 0) {
- this.$msg('请输入收到的现金')
- return
- }
- if (Number(this.zl) < 0) {
- this.cashShortPick = 0
- this.$refs.cashShortPopup && this.$refs.cashShortPopup.open()
- 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,
- })
- const orderName = (this.name != null ? String(this.name) : '').trim()
- const formPayload = {
- ...SETTLE_FORM_DEFAULTS,
- hasPay: emitHasPay,
- payWay: emitPayWay,
- modifyPrice: p,
- cash: cashSubmit,
- orderName,
- gatheringPrice: p,
- }
- const xjRaw = this._resolveXjPayload()
- const payload = {
- ...formPayload,
- isCashier: 1,
- customId: Number(this.customId) || 0,
- xj: JSON.stringify(xjRaw),
- version: 3,
- zjGathering: 1,
- dealPrice: 1,
- getGoods: 1,
- product: '',
- orderType: 2,
- payWay: Number(formPayload.hasPay) === 1 ? (formPayload.payWay != null ? formPayload.payWay : 0) : 0,
- }
- const sendCreate = () => {
- if (!this.getMerchantInfo || !this.getMerchantInfo.id) {
- this.$msg('未获取到店铺信息')
- return
- }
- const shopPayload = {
- ...payload,
- shopId: this.getMerchantInfo.id,
- }
- uni.showLoading({ mask: true })
- createOrder(shopPayload)
- .then((res) => {
- uni.hideLoading()
- if (res.code != 1) {
- return
- }
- if (res.data && res.data.diff && !this.$util.isEmpty(res.data.diff)) {
- this.$msg('存在价差条目,请到开单页处理')
- return
- }
- let navigatedPay = false
- if (res.data && res.data.status == 1) {
- const id = res.data.id
- const orderSn = res.data.orderSn
- const actPrice = res.data.actPrice != null ? res.data.actPrice : p
- if (formPayload.hasPay == 0) {
- navigatedPay = true
- this.$util.pageTo({
- url: '/admin/billing/toPay',
- type: 2,
- query: { orderId: id, orderSn: orderSn, actPrice: actPrice },
- })
- } else {
- navigatedPay = true
- this.$util.pageTo({
- url: '/admin/billing/result',
- type: 2,
- query: { orderId: id, orderSn: orderSn, title: '收款成功' },
- })
- }
- } else if (!navigatedPay) {
- uni.showToast({ title: '操作成功', icon: 'success' })
- }
- this.price = 0
- this.checkType = 3
- this.keyboardTarget = 'calc'
- this.cash = 0
- this.selHasPay = 0
- this.selPayWay = 0
- this.tokens = []
- this.calcInput = ''
- this.resetActiveDiscountToNone()
- this.refreshCustomerBalance()
- })
- .catch(() => {
- uni.hideLoading()
- })
- }
- if (!this.getMerchantInfo || !this.getMerchantInfo.id) {
- this.initMerchantInfo()
- .then(() => {
- sendCreate()
- })
- .catch(() => {
- this.$msg('店铺信息加载失败')
- })
- return
- }
- sendCreate()
- },
- },
- }
- </script>
- <style lang="scss" scoped>
- .calc-page {
- height: 100%;
- background: #f4f6f8;
- box-sizing: border-box;
- display: flex;
- flex-direction: column;
- overflow: hidden;
- }
- /* 上 4/7 内容区,下 3/7 数字键区(随屏高同比伸缩) */
- .calc-scroll {
- flex: 4;
- flex-basis: 0;
- min-height: 0;
- width: 100%;
- }
- .calc-sheet {
- padding: 20upx 24upx 0;
- box-sizing: border-box;
- }
- .calc-card-wrap {
- background: transparent;
- }
- .calc-section {
- position: relative;
- border: 2upx solid #e5e9ef;
- border-radius: 16upx;
- padding: 20upx 20upx 24upx;
- background: #fff;
- box-shadow: 0 4upx 20upx rgba(0, 0, 0, 0.04);
- }
- .calc-section-active {
- border-color: #09c567;
- background: linear-gradient(180deg, #f6fff9 0%, #fff 52%);
- }
- .calc-actions-top {
- display: flex;
- flex-direction: row;
- align-items: center;
- justify-content: space-between;
- margin-bottom: 16upx;
- }
- .calc-section-title {
- font-size: 28upx;
- font-weight: 600;
- color: #1a1a1a;
- }
- .discount-entry {
- width: 64upx;
- height: 64upx;
- border-radius: 12upx;
- background: rgba(255, 251, 245, 0.98);
- border: 2upx solid #f0dcc8;
- display: flex;
- align-items: center;
- justify-content: center;
- }
- .discount-entry-hover {
- opacity: 0.82;
- }
- .discount-entry-glyph {
- font-size: 26upx;
- font-weight: 700;
- color: #d97706;
- }
- .history-entry {
- width: 64upx;
- height: 64upx;
- border-radius: 12upx;
- background: rgba(248, 255, 248, 0.95);
- border: 2upx solid #c8ecd8;
- display: flex;
- align-items: center;
- justify-content: center;
- }
- .history-entry-hover {
- opacity: 0.82;
- }
- .history-entry-icon {
- font-size: 34upx;
- color: #09c567;
- }
- .calc-formula {
- min-height: 88upx;
- font-size: 38upx;
- color: #333;
- text-align: center;
- line-height: 1.45;
- word-break: break-all;
- padding: 0 8upx;
- }
- .calc-formula-inner {
- display: flex;
- flex-direction: row;
- flex-wrap: wrap;
- align-items: center;
- justify-content: center;
- }
- .calc-placeholder {
- font-size: 32upx;
- color: #9aa4b2;
- }
- .calc-num {
- font-size: 42upx;
- font-weight: 600;
- color: #1a1a1a;
- }
- .calc-op {
- font-size: 42upx;
- font-weight: 700;
- color: #09c567;
- }
- .calc-total-row {
- display: flex;
- flex-direction: row;
- align-items: baseline;
- justify-content: center;
- margin-top: 20upx;
- }
- .calc-total-label {
- font-size: 26upx;
- color: #9aa4b2;
- margin-right: 12upx;
- }
- .calc-total-val {
- font-size: 44upx;
- font-weight: 700;
- color: #09c567;
- line-height: 1.2;
- }
- .calc-discount-row {
- display: flex;
- flex-direction: row;
- align-items: center;
- justify-content: center;
- margin-top: 12upx;
- }
- .calc-discount-label {
- font-size: 24upx;
- color: #d97706;
- margin-right: 10upx;
- }
- .calc-discount-val {
- font-size: 34upx;
- font-weight: 700;
- color: #d97706;
- }
- .customer-strip {
- display: flex;
- flex-direction: row;
- align-items: center;
- background: #fff;
- border-radius: 14upx;
- padding: 32upx 28upx;
- min-height: 94upx;
- box-sizing: border-box;
- margin-top: 12upx;
- border: 2upx solid #e5e9ef;
- }
- .customer-strip-label {
- font-size: 34upx;
- color: #9aa4b2;
- flex-shrink: 0;
- }
- .customer-strip-name {
- flex: 1;
- min-width: 0;
- font-size: 34upx;
- font-weight: 600;
- color: #1a1a1a;
- margin-left: 16upx;
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
- }
- .customer-strip-action {
- font-size: 32upx;
- color: #09c567;
- flex-shrink: 0;
- margin-left: 12upx;
- }
- .balance-strip {
- display: flex;
- flex-direction: row;
- align-items: center;
- flex-wrap: wrap;
- background: #fff;
- border-radius: 14upx;
- padding: 24upx 28upx;
- margin-top: 12upx;
- border: 2upx solid #e5e9ef;
- box-sizing: border-box;
- }
- .balance-strip-label {
- font-size: 30upx;
- color: #9aa4b2;
- flex-shrink: 0;
- margin-right: 12upx;
- }
- .balance-strip-val {
- font-size: 34upx;
- font-weight: 700;
- color: #3385ff;
- }
- .balance-strip-hint {
- margin-left: 16upx;
- font-size: 24upx;
- line-height: 1.35;
- flex: 1;
- min-width: 200upx;
- }
- .balance-strip-hint-ok {
- color: #09c567;
- }
- .balance-strip-hint-tip {
- color: #876500;
- }
- .rcv-pay-block {
- margin-top: 12upx;
- padding: 22upx 20upx;
- background: #fff;
- border-radius: 14upx;
- border: 2upx solid #e8edf3;
- box-sizing: border-box;
- }
- .rcv-pay-title {
- font-size: 30upx;
- color: #5c6573;
- text-align: left;
- margin-bottom: 12upx;
- font-weight: 500;
- }
- .rcv-pay-title-sub {
- margin-top: 16upx;
- }
- .rcv-pay-hint {
- margin-bottom: 4upx;
- }
- .rcv-pay-hint-text {
- display: block;
- font-size: 24upx;
- color: #3385ff;
- line-height: 1.35;
- }
- .rcv-sub-block {
- margin-top: 4upx;
- }
- .rcv-item-row {
- display: flex;
- flex-direction: row;
- flex-wrap: wrap;
- align-items: center;
- }
- .rcv-item-chip {
- min-width: 140upx;
- padding: 0 20upx;
- height: 68upx;
- line-height: 68upx;
- text-align: center;
- font-size: 28upx;
- border: 2upx solid #e8edf3;
- color: #3d4654;
- border-radius: 10upx;
- background: #fafbfc;
- box-sizing: border-box;
- display: inline-flex;
- align-items: center;
- justify-content: center;
- }
- .rcv-item-chip {
- margin-right: 18upx;
- margin-bottom: 12upx;
- }
- .rcv-item-chip.active {
- background: #09c567;
- color: #fff;
- border-color: #09c567;
- }
- .pay-cash-panel {
- display: flex;
- flex-direction: row;
- align-items: stretch;
- margin-top: 16upx;
- }
- .pay-cash-item {
- flex: 1;
- min-width: 0;
- background: #fff;
- border: 2upx solid #e8edf3;
- border-radius: 14upx;
- padding: 22upx 16upx;
- box-sizing: border-box;
- text-align: center;
- }
- .pay-cash-item.pay-cash-item-change {
- border-style: dashed;
- }
- .pay-cash-item.pay-cash-item-change {
- margin-left: 12upx;
- }
- .pay-cash-desc {
- display: block;
- font-size: 24upx;
- color: #9aa4b2;
- margin-bottom: 10upx;
- }
- .pay-cash-amt {
- font-size: 32upx;
- font-weight: 600;
- color: #1a1a1a;
- }
- .pay-cash-amt-selected {
- color: #fff;
- background: #09c567;
- padding: 8upx 16upx;
- border-radius: 10upx;
- display: inline-block;
- }
- .pay-change-line {
- display: flex;
- justify-content: center;
- margin-bottom: 8upx;
- }
- .lbl-ok {
- font-size: 24upx;
- color: #09c567;
- }
- .lbl-bad {
- font-size: 24upx;
- color: #e54545;
- }
- .lbl-neu {
- font-size: 24upx;
- color: #c5ccd6;
- }
- .amt-ok {
- font-size: 34upx;
- font-weight: 700;
- color: #09c567;
- display: block;
- }
- .amt-bad {
- font-size: 34upx;
- font-weight: 700;
- color: #e54545;
- display: block;
- }
- .amt-neu {
- font-size: 30upx;
- color: #c5ccd6;
- display: block;
- }
- .pay-submit-row {
- display: flex;
- flex-direction: row;
- margin-top: 20upx;
- margin-bottom: 8upx;
- }
- .pay-submit-btn {
- flex: 1;
- text-align: center;
- font-size: 30upx;
- padding: 30upx 22upx;
- background: #09c567;
- color: #fff;
- border-radius: 12upx;
- font-weight: 600;
- box-sizing: border-box;
- border: 2upx solid #09c567;
- }
- .pay-submit-btn-outline {
- flex: 1;
- background: #fff;
- color: #09c567;
- border: 2upx solid #09c567;
- font-weight: 600;
- border-radius: 12upx;
- text-align: center;
- font-size: 30upx;
- padding: 30upx 22upx;
- box-sizing: border-box;
- margin-right: 12upx;
- }
- .safe-bottom-spacer {
- height: 24upx;
- }
- .numpad-dock {
- flex: 3;
- flex-basis: 0;
- min-height: 0;
- width: 100%;
- display: flex;
- flex-direction: column;
- box-sizing: border-box;
- background: linear-gradient(180deg, transparent 0%, #eceef1 22%, #dcdfe4 100%);
- padding: 12upx 16upx 10upx;
- box-shadow: 0 -4upx 24upx rgba(0, 0, 0, 0.06);
- }
- .safe-area-padding {
- padding-bottom: calc(10upx + constant(safe-area-inset-bottom));
- padding-bottom: calc(10upx + env(safe-area-inset-bottom));
- }
- .numpad {
- width: 100%;
- flex: 1;
- min-height: 0;
- display: flex;
- flex-direction: column;
- background: #e2e5ea;
- border-radius: 14upx;
- padding: 10upx;
- box-sizing: border-box;
- }
- .numpad-grid-wrap {
- flex: 1;
- min-width: 0;
- min-height: 0;
- display: grid;
- grid-template-columns: 1fr 1fr 1fr 25%;
- grid-template-rows: repeat(5, minmax(0, 1fr));
- box-sizing: border-box;
- align-items: stretch;
- justify-items: stretch;
- width: 100%;
- }
- /* 小程序端 grid 子项 margin 常与 100% 宽高组合失效 → 用 cell 的内边距留白,不配 gap,兼容微信与 App */
- .numpad-cell {
- box-sizing: border-box;
- min-width: 0;
- min-height: 0;
- overflow: visible;
- display: flex;
- flex-direction: column;
- align-items: stretch;
- padding-right: 10upx;
- padding-bottom: 8upx;
- }
- .numpad-cell-op {
- padding-right: 0;
- }
- .numpad-cell-mul {
- grid-column: 4;
- grid-row: 1;
- }
- .numpad-cell-add {
- grid-column: 4;
- grid-row: 2 / span 2;
- }
- .numpad-cell-sub {
- grid-column: 4;
- grid-row: 4;
- }
- .numpad-cell-div {
- grid-column: 4;
- grid-row: 5;
- }
- .numpad-cell-actions-row {
- grid-column: 1 / span 3;
- grid-row: 5;
- flex-direction: row;
- align-items: stretch;
- }
- .numpad-grid-wrap .numpad-cell-actions-row > .numpad-key {
- flex: 1;
- width: auto;
- min-width: 0;
- min-height: 0;
- align-self: stretch;
- }
- .numpad-grid-wrap .numpad-cell-actions-row > .numpad-key + .numpad-key {
- margin-left: 10upx;
- }
- .numpad-key {
- min-width: 0;
- min-height: 0;
- display: flex;
- align-items: center;
- justify-content: center;
- text-align: center;
- font-size: 46upx;
- font-weight: 600;
- margin-left: 0;
- color: #1a2430;
- background: linear-gradient(180deg, #fff 0%, #f5f7fa 100%);
- border-radius: 12upx;
- box-shadow: 0 3upx 0 #bdc3c9;
- }
- .numpad-grid-wrap .numpad-cell > .numpad-key {
- flex: 1;
- width: 100%;
- min-width: 0;
- min-height: 0;
- align-self: stretch;
- }
- .numpad-key:active {
- opacity: 0.88;
- transform: translateY(1px);
- box-shadow: 0 1upx 0 #a8afb8;
- }
- .numpad-grid-wrap .numpad-key.numpad-key-op:not(.numpad-key-op-add) {
- font-size: 46upx;
- font-weight: 600;
- background: linear-gradient(180deg, #fff 0%, #f0f2f5 100%);
- color: #1a2430;
- box-shadow: 0 3upx 0 #bdc3c9;
- }
- .numpad-grid-wrap .numpad-key.numpad-key-op-add {
- font-size: 48upx;
- font-weight: 700;
- background: #52b384;
- color: #063d2b;
- box-shadow: 0 4upx 0 #388f65;
- }
- .numpad-key-muted {
- color: #4a5666;
- font-size: 50upx;
- }
- .numpad-key-text {
- font-size: 34upx;
- font-weight: 600;
- }
- .numpad-key-action-muted {
- font-size: 32upx !important;
- color: #c0392b !important;
- background: #eac997;
- box-shadow: 0 3upx 0 #b9c2cf !important;
- }
- .numpad-key-danger {
- background: #eac997;
- color: black !important;
- box-shadow: 0 3upx 0 #e0abab !important;
- }
- .mob-popup-wrap {
- width: 86vw;
- max-width: 640upx;
- max-height: 70vh;
- display: flex;
- flex-direction: column;
- border-radius: 16upx;
- overflow: hidden;
- background: #fff;
- }
- .mob-popup-hd {
- display: flex;
- flex-direction: row;
- align-items: center;
- justify-content: space-between;
- padding: 24upx 28upx;
- border-bottom: 2upx solid #eef1f6;
- flex-shrink: 0;
- }
- .mob-popup-title {
- font-size: 32upx;
- font-weight: bold;
- color: #1a1a1a;
- }
- .mob-popup-close {
- font-size: 28upx;
- color: #09c567;
- padding: 8upx 16upx;
- }
- .mob-popup-body {
- max-height: 46vh;
- box-sizing: border-box;
- padding: 0 12upx 12upx;
- }
- .mob-popup-cash-short {
- max-height: none;
- }
- .cash-short-desc {
- display: block;
- font-size: 26upx;
- color: #3d4654;
- line-height: 1.45;
- padding: 12upx 28upx 8upx;
- box-sizing: border-box;
- }
- .cash-short-way-row {
- display: flex;
- flex-direction: row;
- align-items: center;
- padding: 12upx 28upx 8upx;
- box-sizing: border-box;
- }
- .cash-short-way-chip {
- flex: 1;
- text-align: center;
- font-size: 28upx;
- padding: 20upx 12upx;
- border-radius: 12upx;
- border: 2upx solid #e8edf3;
- background: #fafbfc;
- color: #3d4654;
- box-sizing: border-box;
- margin-right: 16upx;
- font-weight: 500;
- }
- .cash-short-way-chip:last-child {
- margin-right: 0;
- }
- .cash-short-way-chip-on {
- border-color: #09c567;
- background: #effff4;
- color: #09c567;
- }
- .cash-short-actions {
- display: flex;
- flex-direction: row;
- align-items: center;
- padding: 22upx 28upx 26upx;
- border-top: 2upx solid #eef1f6;
- box-sizing: border-box;
- margin-top: 8upx;
- }
- .cash-short-btn {
- flex: 1;
- text-align: center;
- font-size: 30upx;
- padding: 22upx 12upx;
- border-radius: 12upx;
- box-sizing: border-box;
- font-weight: 600;
- }
- .cash-short-btn-cancel {
- margin-right: 16upx;
- background: #f2f4f7;
- color: #3d4654;
- }
- .cash-short-btn-ok {
- background: #09c567;
- color: #fff;
- }
- .discount-toolbar {
- flex-shrink: 0;
- padding: 20upx 24upx 22upx;
- border-bottom: 2upx solid #f0f2f5;
- box-sizing: border-box;
- }
- .discount-custom-row {
- display: flex;
- flex-direction: row;
- align-items: center;
- }
- .discount-custom-input {
- flex: 1;
- min-width: 0;
- height: 72upx;
- line-height: 72upx;
- padding: 0 20upx;
- font-size: 28upx;
- border: 2upx solid #e8edf3;
- border-radius: 10upx;
- background: #fff;
- box-sizing: border-box;
- }
- .discount-custom-add-btn {
- flex-shrink: 0;
- margin-left: 14upx;
- padding: 0 28upx;
- height: 72upx;
- line-height: 72upx;
- font-size: 28upx;
- color: #fff;
- background: #09c567;
- border-radius: 10upx;
- text-align: center;
- box-sizing: border-box;
- font-weight: 500;
- }
- .discount-toolbar-hint {
- display: block;
- font-size: 24upx;
- color: #09c567;
- line-height: 1.45;
- padding: 14upx 6upx 0;
- }
- .discount-toolbar-hint-muted {
- color: #9aa4b2;
- }
- .discount-options-scroll {
- padding: 20upx 20upx 28upx;
- box-sizing: border-box;
- max-height: 44vh;
- }
- .discount-option {
- display: flex;
- flex-direction: row;
- align-items: center;
- justify-content: space-between;
- padding: 24upx 20upx;
- border: 2upx solid #e8edf3;
- border-radius: 12upx;
- margin-bottom: 12upx;
- background: #fafbfc;
- box-sizing: border-box;
- }
- .discount-option:last-child {
- margin-bottom: 0;
- }
- .discount-option-active {
- border-color: #09c567;
- background: #f0fff5;
- }
- .discount-option-text {
- font-size: 30upx;
- color: #1a2430;
- }
- .discount-option-check {
- font-size: 30upx;
- color: #09c567;
- font-weight: bold;
- }
- .discount-option-with-del {
- padding-right: 12upx;
- }
- .discount-option-main {
- flex: 1;
- min-width: 0;
- display: flex;
- flex-direction: row;
- align-items: center;
- justify-content: space-between;
- padding-right: 12upx;
- box-sizing: border-box;
- }
- .discount-option-del {
- flex-shrink: 0;
- font-size: 26upx;
- color: #e54545;
- padding: 8upx 12upx;
- }
- .history-item {
- display: flex;
- flex-direction: row;
- align-items: center;
- padding: 18upx 12upx;
- border-bottom: 2upx solid #f0f3f8;
- }
- .history-item-body {
- flex: 1;
- min-width: 0;
- }
- .history-del {
- flex-shrink: 0;
- font-size: 26upx;
- color: #e54545;
- padding: 10upx 16upx;
- margin-left: 12upx;
- }
- .history-line {
- display: flex;
- flex-direction: row;
- justify-content: space-between;
- margin-bottom: 8upx;
- }
- .history-time {
- font-size: 24upx;
- color: #9aa4b2;
- }
- .history-total {
- font-size: 24upx;
- color: #09c567;
- font-weight: 600;
- }
- .history-detail {
- font-size: 24upx;
- color: #5c6573;
- word-break: break-all;
- line-height: 1.35;
- }
- .history-empty {
- text-align: center;
- font-size: 26upx;
- color: #c5ccd6;
- padding: 60upx 0;
- }
- </style>
|