pay.vue 21 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000
  1. <template>
  2. <view class="pay-container">
  3. <!-- 支付金额区域 -->
  4. <view class="amount-section">
  5. <view class="amount-label">支付金额</view>
  6. <view class="amount-display">
  7. <text class="currency">¥</text>
  8. <text class="amount">{{ displayAmount }}</text>
  9. </view>
  10. <view class="amount-tip">请输入金额</view>
  11. </view>
  12. <!-- 客户信息卡片 -->
  13. <view class="customer-info-card">
  14. <view class="card-header">
  15. <view class="customer-avatar">
  16. <text class="avatar-text">{{ customInfo.name ? customInfo.name.charAt(0) : '客' }}</text>
  17. </view>
  18. <view class="customer-details">
  19. <text class="customer-name">{{ customInfo.name || '客户' }}</text>
  20. <text class="customer-status" :class="{ 'has-debt': Number(customInfo.remainDebtAmount) > 0 }">
  21. {{ Number(customInfo.remainDebtAmount) > 0 ? '待结款' : '账单已结清' }}
  22. </text>
  23. </view>
  24. </view>
  25. <view class="debt-info" v-if="Number(customInfo.remainDebtAmount) > 0">
  26. <view class="debt-amount">
  27. <text class="debt-label">待结金额</text>
  28. <text class="debt-value">¥{{ customInfo.remainDebtAmount }}</text>
  29. </view>
  30. </view>
  31. </view>
  32. <!-- 数字键盘 - 使用直接事件绑定,确保兼容性 -->
  33. <view class="keyboard-section">
  34. <view class="keyboard">
  35. <view class="keyboard-row" v-for="(row, rowIndex) in keyboardKeys" :key="rowIndex">
  36. <view
  37. class="key-item"
  38. v-for="(key, keyIndex) in row"
  39. :key="`${rowIndex}-${keyIndex}`"
  40. :class="{
  41. 'key-number': key.type === 'number',
  42. 'key-delete': key.type === 'delete',
  43. 'key-dot': key.type === 'dot',
  44. 'key-confirm': key.type === 'confirm'
  45. }"
  46. @click="handleKeyPress(key)"
  47. @touchstart="handleTouchStart"
  48. @touchend="handleTouchEnd"
  49. >
  50. <text v-if="key.type === 'delete'" class="iconfont icon-delete"></text>
  51. <text v-else-if="key.type === 'confirm'">确认</text>
  52. <text v-else>{{ key.value }}</text>
  53. </view>
  54. </view>
  55. </view>
  56. </view>
  57. <!-- 支付按钮 -->
  58. <view class="pay-button-section">
  59. <button
  60. class="pay-button"
  61. :class="{ disabled: !canPay }"
  62. :disabled="!canPay"
  63. @click="showConfirmDialog"
  64. >
  65. <text class="button-text">支付宝支付</text>
  66. </button>
  67. </view>
  68. <!-- 确认支付弹框 -->
  69. <view class="confirm-dialog" v-if="showConfirm" @click="hideConfirmDialog">
  70. <view class="dialog-content" @click.stop>
  71. <view class="dialog-header">
  72. <text class="dialog-title">提示</text>
  73. <view class="close-btn" @click="hideConfirmDialog">
  74. <text class="iconfont icon-close">×</text>
  75. </view>
  76. </view>
  77. <view class="dialog-body">
  78. <view class="confirm-amount-section">
  79. <text class="confirm-label">支付金额</text>
  80. <view class="confirm-amount-display">
  81. <text class="confirm-currency">¥</text>
  82. <text class="confirm-amount">{{ displayAmount }}</text>
  83. </view>
  84. </view>
  85. <view class="confirm-tips">
  86. <text class="tip-text">确认金额无误</text>
  87. </view>
  88. </view>
  89. <view class="dialog-footer">
  90. <button class="cancel-btn" @click="hideConfirmDialog">取消</button>
  91. <button class="confirm-btn" @click="handlePay">确认</button>
  92. </view>
  93. </view>
  94. </view>
  95. </view>
  96. </template>
  97. <script>
  98. import { getGhsData } from '@/api/ghs';
  99. import { rechargeFn } from '@/api/custom-recharge'
  100. // 优化:使用常量避免重复创建
  101. const KEY_TYPES = {
  102. NUMBER: 'number',
  103. DELETE: 'delete',
  104. DOT: 'dot',
  105. CONFIRM: 'confirm'
  106. };
  107. const MAX_INTEGER_LENGTH = 8;
  108. const MAX_DECIMAL_LENGTH = 2;
  109. export default {
  110. name: 'pay',
  111. data() {
  112. return {
  113. currentAmount: '0',
  114. displayAmount: '0',
  115. // 优化:使用静态数据,避免重复创建
  116. keyboardKeys: [
  117. [
  118. { type: KEY_TYPES.NUMBER, value: '1' },
  119. { type: KEY_TYPES.NUMBER, value: '2' },
  120. { type: KEY_TYPES.NUMBER, value: '3' }
  121. ],
  122. [
  123. { type: KEY_TYPES.NUMBER, value: '4' },
  124. { type: KEY_TYPES.NUMBER, value: '5' },
  125. { type: KEY_TYPES.NUMBER, value: '6' }
  126. ],
  127. [
  128. { type: KEY_TYPES.NUMBER, value: '7' },
  129. { type: KEY_TYPES.NUMBER, value: '8' },
  130. { type: KEY_TYPES.NUMBER, value: '9' }
  131. ],
  132. [
  133. { type: KEY_TYPES.DOT, value: '.' },
  134. { type: KEY_TYPES.NUMBER, value: '0' },
  135. { type: KEY_TYPES.DELETE, value: '' }
  136. ]
  137. ],
  138. // 优化:使用更高效的状态管理
  139. amountBuffer: '0',
  140. updateScheduled: false,
  141. updateTimer: null,
  142. // 弹框状态
  143. showConfirm: false,
  144. id:0,
  145. salt:'',
  146. ghsInfo:[],
  147. customInfo:{},
  148. payWay:1
  149. }
  150. },
  151. computed: {
  152. // 简化支付按钮判断逻辑:只要大于0就能支付
  153. canPay() {
  154. const amount = parseFloat(this.currentAmount);
  155. return amount > 0;
  156. }
  157. },
  158. onLoad(options) {
  159. if (options.id) {
  160. this.id = options.id;
  161. }
  162. if (options.salt) {
  163. this.salt = options.salt;
  164. }
  165. let that = this
  166. if (this.id && this.salt) {
  167. getGhsData({id:this.id,salt:this.salt}).then(res => {
  168. if (res.data && res.data.ghs){
  169. that.ghsInfo = res.data.ghs
  170. that.customInfo = res.data.custom?res.data.custom:[]
  171. if(that.customInfo.remainDebtAmount && Number(that.customInfo.remainDebtAmount)>0){
  172. that.currentAmount = parseFloat(that.customInfo.remainDebtAmount).toString()
  173. that.updateDisplay()
  174. }
  175. }
  176. })
  177. }
  178. },
  179. methods: {
  180. // 优化:添加触摸反馈
  181. handleTouchStart(e) {
  182. const target = e.currentTarget;
  183. if (target && target.style) {
  184. target.style.transform = 'scale(0.95)';
  185. }
  186. },
  187. handleTouchEnd(e) {
  188. const target = e.currentTarget;
  189. if (target && target.style) {
  190. setTimeout(() => {
  191. target.style.transform = 'scale(1)';
  192. }, 100);
  193. }
  194. },
  195. // 优化:简化的键盘处理逻辑
  196. handleKeyPress(key) {
  197. // 防抖处理
  198. if (this.updateTimer) {
  199. clearTimeout(this.updateTimer);
  200. }
  201. if (key.type === KEY_TYPES.NUMBER) {
  202. this.addNumber(key.value);
  203. } else if (key.type === KEY_TYPES.DELETE) {
  204. this.deleteNumber();
  205. } else if (key.type === KEY_TYPES.DOT) {
  206. this.addDecimal();
  207. }
  208. // 使用 requestAnimationFrame 优化更新
  209. this.scheduleUpdate();
  210. },
  211. // 优化:使用 requestAnimationFrame 调度更新
  212. scheduleUpdate() {
  213. if (!this.updateScheduled) {
  214. this.updateScheduled = true;
  215. requestAnimationFrame(() => {
  216. this.updateDisplay();
  217. this.updateScheduled = false;
  218. });
  219. }
  220. },
  221. // 优化:更高效的数字添加逻辑
  222. addNumber(num) {
  223. const buffer = this.amountBuffer;
  224. // 快速路径:如果当前是0且输入的不是小数点,直接替换
  225. if (buffer === '0' && num !== '.') {
  226. this.amountBuffer = num;
  227. this.currentAmount = num;
  228. return;
  229. }
  230. // 检查长度限制
  231. const parts = buffer.split('.');
  232. const integerPart = parts[0];
  233. const decimalPart = parts[1];
  234. // 整数部分限制
  235. if (!decimalPart && integerPart.length >= MAX_INTEGER_LENGTH) {
  236. uni.showToast({
  237. title: '金额不能超过8位数',
  238. icon: 'none',
  239. duration: 1500
  240. })
  241. return;
  242. }
  243. // 小数部分限制
  244. if (decimalPart && decimalPart.length >= MAX_DECIMAL_LENGTH) {
  245. uni.showToast({
  246. title: '最多支持2位小数',
  247. icon: 'none',
  248. duration: 1500
  249. })
  250. return;
  251. }
  252. // 添加数字
  253. this.amountBuffer = buffer + num;
  254. this.currentAmount = this.amountBuffer;
  255. },
  256. // 优化:更高效的小数点添加逻辑
  257. addDecimal() {
  258. if (!this.amountBuffer.includes('.')) {
  259. this.amountBuffer += '.';
  260. this.currentAmount = this.amountBuffer;
  261. }
  262. },
  263. // 优化:更高效的删除逻辑
  264. deleteNumber() {
  265. const buffer = this.amountBuffer;
  266. if (buffer.length > 1) {
  267. this.amountBuffer = buffer.slice(0, -1);
  268. } else {
  269. this.amountBuffer = '0';
  270. }
  271. this.currentAmount = this.amountBuffer;
  272. },
  273. // 优化:更高效的显示更新逻辑
  274. updateDisplay() {
  275. let amount = this.currentAmount;
  276. // 确保 amount 是字符串类型
  277. if (typeof amount !== 'string') {
  278. amount = amount.toString();
  279. }
  280. // 快速路径:如果是0,直接显示
  281. if (amount === '0') {
  282. this.displayAmount = '0';
  283. return;
  284. }
  285. // 检查是否包含小数点
  286. if (amount.includes('.')) {
  287. const numAmount = parseFloat(amount) || 0;
  288. this.displayAmount = numAmount.toFixed(2);
  289. } else {
  290. // 整数显示
  291. this.displayAmount = amount;
  292. }
  293. },
  294. // 优化:更高效的金额设置
  295. setAmount(amount) {
  296. const numAmount = parseFloat(amount);
  297. if (!isNaN(numAmount) && numAmount > 0) {
  298. const amountStr = numAmount.toString();
  299. this.amountBuffer = amountStr;
  300. this.currentAmount = amountStr;
  301. this.updateDisplay();
  302. }
  303. },
  304. // 显示确认弹框
  305. showConfirmDialog() {
  306. if (!this.canPay) return;
  307. this.showConfirm = true;
  308. },
  309. // 隐藏确认弹框
  310. hideConfirmDialog() {
  311. this.showConfirm = false;
  312. },
  313. // 处理支付
  314. handlePay() {
  315. if (!this.canPay) return;
  316. const amount = parseFloat(this.currentAmount);
  317. // 隐藏弹框
  318. this.hideConfirmDialog();
  319. // 简化验证:只要大于0就能支付
  320. if (amount <= 0) {
  321. uni.showToast({
  322. title: '请输入金额!',
  323. icon: 'none',
  324. duration: 2000
  325. })
  326. return false
  327. }
  328. let params = {}
  329. params = {
  330. actPrice: amount,
  331. payWay: this.payWay,
  332. }
  333. params.ghsId = this.id
  334. params.salt = this.salt
  335. rechargeFn(params).then(res => {
  336. //错误返回判断
  337. if(res.code == 0){
  338. uni.hideLoading()
  339. uni.showToast({
  340. title: res.msg,
  341. icon: 'none',
  342. duration: 2000
  343. })
  344. return false
  345. }
  346. // 隐藏加载提示
  347. uni.hideLoading()
  348. // 显示成功提示
  349. uni.showToast({
  350. title: '正在跳转支付...',
  351. icon: 'success',
  352. duration: 1500
  353. })
  354. // 延迟跳转到支付页面
  355. setTimeout(() => {
  356. window.location.href = res.data.url;
  357. }, 300)
  358. }).catch(err => {
  359. // 隐藏加载提示
  360. uni.hideLoading()
  361. // 显示错误提示
  362. uni.showToast({
  363. title: '请求失败,请重试',
  364. icon: 'none',
  365. duration: 2000
  366. })
  367. })
  368. },
  369. // 返回上一页
  370. goBack() {
  371. uni.navigateBack({
  372. delta: 1
  373. });
  374. },
  375. }
  376. }
  377. </script>
  378. <style lang="scss" scoped>
  379. .pay-container {
  380. min-height: 100vh;
  381. background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  382. position: relative;
  383. }
  384. .nav-bar {
  385. display: flex;
  386. align-items: center;
  387. justify-content: center; /* Changed to center the title */
  388. padding: 20upx 30upx;
  389. background: rgba(255, 255, 255, 0.1);
  390. backdrop-filter: blur(10px);
  391. .nav-back {
  392. width: 80upx;
  393. height: 80upx;
  394. display: flex;
  395. align-items: center;
  396. justify-content: center;
  397. border-radius: 50%;
  398. background: rgba(255, 255, 255, 0.2);
  399. cursor: pointer;
  400. transition: all 0.2s ease;
  401. &:active {
  402. background: rgba(255, 255, 255, 0.3);
  403. transform: scale(0.9);
  404. }
  405. .iconfont {
  406. font-size: 40upx;
  407. color: #fff;
  408. font-weight: bold;
  409. }
  410. }
  411. .nav-title {
  412. color: #fff;
  413. font-size: 36upx;
  414. font-weight: 600;
  415. }
  416. .nav-placeholder {
  417. width: 80upx; /* Placeholder for back button */
  418. }
  419. }
  420. .amount-section {
  421. padding: 60upx 40upx;
  422. text-align: center;
  423. .amount-label {
  424. color: rgba(255, 255, 255, 0.8);
  425. font-size: 28upx;
  426. margin-bottom: 20upx;
  427. }
  428. .amount-display {
  429. display: flex;
  430. align-items: baseline;
  431. justify-content: center;
  432. margin-bottom: 20upx;
  433. .currency {
  434. color: #fff;
  435. font-size: 48upx;
  436. font-weight: 600;
  437. margin-right: 10upx;
  438. }
  439. .amount {
  440. color: #fff;
  441. font-size: 80upx;
  442. font-weight: 700;
  443. font-family: 'Arial', sans-serif;
  444. }
  445. }
  446. .amount-tip {
  447. color: rgba(255, 255, 255, 0.6);
  448. font-size: 24upx;
  449. }
  450. }
  451. .customer-info-card {
  452. background: #fff; /* Changed to white */
  453. margin: 0 30upx 20upx;
  454. border-radius: 20upx;
  455. padding: 30upx;
  456. box-shadow: 0 8upx 30upx rgba(0, 0, 0, 0.08);
  457. position: relative;
  458. overflow: hidden;
  459. &::before {
  460. content: '';
  461. position: absolute;
  462. top: 0;
  463. left: 0;
  464. right: 0;
  465. height: 8upx;
  466. background: #fff; /* Changed to white */
  467. box-shadow: 0 2upx 8upx rgba(0, 0, 0, 0.1);
  468. }
  469. .card-header {
  470. display: flex;
  471. align-items: center;
  472. margin-bottom: 20upx;
  473. margin-top: 8upx;
  474. .customer-avatar {
  475. width: 90upx;
  476. height: 90upx;
  477. border-radius: 50%;
  478. background: linear-gradient(135deg, #1677ff 0%, #4096ff 100%);
  479. display: flex;
  480. align-items: center;
  481. justify-content: center;
  482. margin-right: 25upx;
  483. box-shadow: 0 6upx 20upx rgba(22, 119, 255, 0.3);
  484. position: relative;
  485. &::after {
  486. content: '';
  487. position: absolute;
  488. top: -2upx;
  489. left: -2upx;
  490. right: -2upx;
  491. bottom: -2upx;
  492. border-radius: 50%;
  493. background: linear-gradient(135deg, #1677ff 0%, #4096ff 100%);
  494. z-index: -1;
  495. opacity: 0.3;
  496. }
  497. .avatar-text {
  498. color: #fff;
  499. font-size: 44upx;
  500. font-weight: 600;
  501. }
  502. }
  503. .customer-details {
  504. flex: 1;
  505. display: flex;
  506. align-items: center;
  507. justify-content: space-between;
  508. .customer-name {
  509. display: block;
  510. font-size: 34upx;
  511. font-weight: 600;
  512. color: #333;
  513. }
  514. .customer-status {
  515. display: inline-block;
  516. font-size: 24upx;
  517. color: #999;
  518. padding: 4upx 12upx;
  519. border-radius: 20upx;
  520. background: #f8f9fa;
  521. border: 1upx solid #e9ecef;
  522. &.has-debt {
  523. color: #fff;
  524. background: linear-gradient(135deg, #ff6b6b 0%, #ff5252 100%);
  525. border-color: #ff6b6b;
  526. box-shadow: 0 2upx 10upx rgba(255, 107, 107, 0.3);
  527. }
  528. }
  529. }
  530. }
  531. .debt-info {
  532. background: linear-gradient(135deg, #fff5f5 0%, #fef2f2 100%);
  533. border-radius: 15upx;
  534. padding: 20upx 25upx;
  535. margin-top: 20upx;
  536. border: 1upx solid rgba(255, 107, 107, 0.2);
  537. position: relative;
  538. &::before {
  539. content: '';
  540. position: absolute;
  541. top: 0;
  542. left: 0;
  543. right: 0;
  544. bottom: 0;
  545. background: linear-gradient(135deg, rgba(255, 107, 107, 0.05) 0%, rgba(255, 107, 107, 0.02) 100%);
  546. border-radius: 15upx;
  547. }
  548. .debt-amount {
  549. display: flex;
  550. align-items: baseline;
  551. justify-content: space-between;
  552. margin-bottom: 10upx;
  553. position: relative;
  554. z-index: 1;
  555. .debt-label {
  556. font-size: 28upx;
  557. color: #666;
  558. font-weight: 500;
  559. }
  560. .debt-value {
  561. font-size: 40upx;
  562. font-weight: 700;
  563. color: #ff6b6b;
  564. font-family: 'Arial', sans-serif;
  565. text-shadow: 0 2upx 10upx rgba(255, 107, 107, 0.2);
  566. }
  567. }
  568. .debt-tip {
  569. font-size: 22upx;
  570. color: #999;
  571. text-align: right;
  572. position: relative;
  573. z-index: 1;
  574. font-style: italic;
  575. }
  576. }
  577. }
  578. .keyboard-section {
  579. position: fixed;
  580. bottom: 160upx;
  581. left: 0;
  582. right: 0;
  583. background: rgba(255, 255, 255, 0.98);
  584. backdrop-filter: blur(20px);
  585. padding: 30upx;
  586. border-top: 1upx solid rgba(0, 0, 0, 0.05);
  587. .keyboard {
  588. .keyboard-row {
  589. display: flex;
  590. margin-bottom: 20upx;
  591. &:last-child {
  592. margin-bottom: 0;
  593. }
  594. .key-item {
  595. flex: 1;
  596. height: 120upx;
  597. margin: 0 10upx;
  598. border-radius: 20upx;
  599. display: flex;
  600. align-items: center;
  601. justify-content: center;
  602. font-size: 40upx;
  603. font-weight: 600;
  604. transition: all 0.15s ease;
  605. user-select: none;
  606. -webkit-user-select: none;
  607. cursor: pointer;
  608. will-change: transform;
  609. position: relative;
  610. overflow: hidden;
  611. &::before {
  612. content: '';
  613. position: absolute;
  614. top: 0;
  615. left: 0;
  616. right: 0;
  617. bottom: 0;
  618. background: linear-gradient(135deg, rgba(255, 255, 255, 0.1) 0%, rgba(255, 255, 255, 0.05) 100%);
  619. opacity: 0;
  620. transition: opacity 0.15s ease;
  621. }
  622. &:active::before {
  623. opacity: 1;
  624. }
  625. &.key-number {
  626. background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
  627. color: #333;
  628. box-shadow: 0 4upx 20upx rgba(0, 0, 0, 0.08);
  629. border: 1upx solid rgba(0, 0, 0, 0.05);
  630. }
  631. &.key-delete {
  632. background: linear-gradient(135deg, #ff6b6b 0%, #ff5252 100%);
  633. color: #fff;
  634. box-shadow: 0 4upx 20upx rgba(255, 107, 107, 0.3);
  635. }
  636. &.key-dot {
  637. background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
  638. color: #333;
  639. box-shadow: 0 4upx 20upx rgba(0, 0, 0, 0.08);
  640. border: 1upx solid rgba(0, 0, 0, 0.05);
  641. }
  642. &.key-confirm {
  643. background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  644. color: #fff;
  645. box-shadow: 0 4upx 20upx rgba(102, 126, 234, 0.3);
  646. }
  647. }
  648. }
  649. }
  650. }
  651. .pay-button-section {
  652. position: fixed;
  653. bottom: 0;
  654. left: 0;
  655. right: 0;
  656. padding: 30upx;
  657. background: rgba(255, 255, 255, 0.98);
  658. backdrop-filter: blur(20px);
  659. border-top: 1upx solid rgba(0, 0, 0, 0.05);
  660. .pay-button {
  661. width: 100%;
  662. height: 100upx;
  663. background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  664. color: #fff;
  665. font-size: 36upx;
  666. font-weight: 600;
  667. border-radius: 50upx;
  668. border: none;
  669. transition: all 0.3s ease;
  670. position: relative;
  671. overflow: hidden;
  672. &::before {
  673. content: '';
  674. position: absolute;
  675. top: 0;
  676. left: 0;
  677. right: 0;
  678. bottom: 0;
  679. background: linear-gradient(135deg, rgba(255, 255, 255, 0.1) 0%, rgba(255, 255, 255, 0.05) 100%);
  680. opacity: 0;
  681. transition: opacity 0.3s ease;
  682. }
  683. &:active::before {
  684. opacity: 1;
  685. }
  686. &.disabled {
  687. background: linear-gradient(135deg, #e9ecef 0%, #dee2e6 100%);
  688. color: #adb5bd;
  689. box-shadow: none;
  690. }
  691. &:not(.disabled):active {
  692. transform: scale(0.98);
  693. }
  694. .button-text {
  695. position: absolute;
  696. left: 50%;
  697. transform: translateX(-50%);
  698. font-size: 36upx;
  699. font-weight: 600;
  700. color: #fff;
  701. }
  702. .button-amount {
  703. position: absolute;
  704. right: 30upx;
  705. font-size: 36upx;
  706. font-weight: 700;
  707. color: #fff;
  708. font-family: 'Arial', sans-serif;
  709. }
  710. }
  711. }
  712. // 确认支付弹框样式
  713. .confirm-dialog {
  714. position: fixed;
  715. top: 0;
  716. left: 0;
  717. right: 0;
  718. bottom: 0;
  719. background: rgba(0, 0, 0, 0.6);
  720. display: flex;
  721. align-items: center;
  722. justify-content: center;
  723. z-index: 9999;
  724. animation: fadeIn 0.3s ease;
  725. .dialog-content {
  726. width: 85%;
  727. max-width: 700upx;
  728. background: #fff;
  729. border-radius: 25upx;
  730. overflow: hidden;
  731. animation: slideUp 0.3s ease;
  732. box-shadow: 0 20upx 60upx rgba(0, 0, 0, 0.2);
  733. .dialog-header {
  734. display: flex;
  735. align-items: center;
  736. justify-content: space-between;
  737. padding: 40upx 40upx 30upx;
  738. border-bottom: 1upx solid #f0f0f0;
  739. background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
  740. .dialog-title {
  741. font-size: 36upx;
  742. font-weight: 600;
  743. color: #333;
  744. }
  745. .close-btn {
  746. width: 60upx;
  747. height: 60upx;
  748. display: flex;
  749. align-items: center;
  750. justify-content: center;
  751. border-radius: 50%;
  752. background: rgba(0, 0, 0, 0.1);
  753. cursor: pointer;
  754. transition: all 0.2s ease;
  755. &:active {
  756. background: rgba(0, 0, 0, 0.2);
  757. transform: scale(0.95);
  758. }
  759. .iconfont {
  760. font-size: 32upx;
  761. color: #666;
  762. font-weight: bold;
  763. }
  764. }
  765. }
  766. .dialog-body {
  767. padding: 50upx 40upx;
  768. .confirm-amount-section {
  769. text-align: center;
  770. margin-bottom: 40upx;
  771. .confirm-label {
  772. display: block;
  773. font-size: 28upx;
  774. color: #666;
  775. margin-bottom: 25upx;
  776. font-weight: 500;
  777. }
  778. .confirm-amount-display {
  779. display: flex;
  780. align-items: baseline;
  781. justify-content: center;
  782. .confirm-currency {
  783. font-size: 60upx;
  784. font-weight: 600;
  785. color: #ff6b6b;
  786. margin-right: 10upx;
  787. }
  788. .confirm-amount {
  789. font-size: 100upx;
  790. font-weight: 700;
  791. color: #ff6b6b;
  792. font-family: 'Arial', sans-serif;
  793. text-shadow: 0 2upx 10upx rgba(255, 107, 107, 0.3);
  794. }
  795. }
  796. }
  797. .confirm-tips {
  798. text-align: center;
  799. padding: 20upx 30upx;
  800. background: rgba(255, 107, 107, 0.1);
  801. border-radius: 15upx;
  802. border-left: 4upx solid #ff6b6b;
  803. .tip-text {
  804. font-size: 26upx;
  805. color: #666;
  806. line-height: 1.5;
  807. }
  808. }
  809. }
  810. .dialog-footer {
  811. display: flex;
  812. border-top: 1upx solid #f0f0f0;
  813. padding: 30upx 40upx;
  814. gap: 20upx;
  815. .cancel-btn, .confirm-btn {
  816. flex: 1;
  817. height: 100upx;
  818. border: none;
  819. font-size: 32upx;
  820. font-weight: 600;
  821. transition: all 0.2s ease;
  822. border-radius: 50upx;
  823. display: flex;
  824. align-items: center;
  825. justify-content: center;
  826. position: relative;
  827. overflow: hidden;
  828. &::before {
  829. content: '';
  830. position: absolute;
  831. top: 0;
  832. left: 0;
  833. right: 0;
  834. bottom: 0;
  835. background: linear-gradient(135deg, rgba(255, 255, 255, 0.1) 0%, rgba(255, 255, 255, 0.05) 100%);
  836. opacity: 0;
  837. transition: opacity 0.2s ease;
  838. }
  839. &:active::before {
  840. opacity: 1;
  841. }
  842. &:active {
  843. transform: scale(0.98);
  844. }
  845. }
  846. .cancel-btn {
  847. background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
  848. color: #666;
  849. border: 2upx solid #dee2e6;
  850. box-shadow: 0 2upx 8upx rgba(0, 0, 0, 0.1);
  851. &:active {
  852. background: linear-gradient(135deg, #e9ecef 0%, #dee2e6 100%);
  853. border-color: #ced4da;
  854. box-shadow: 0 1upx 4upx rgba(0, 0, 0, 0.15);
  855. }
  856. }
  857. .confirm-btn {
  858. background: linear-gradient(135deg, #ff6b6b 0%, #ff5252 100%);
  859. color: #fff;
  860. box-shadow: 0 4upx 20upx rgba(255, 107, 107, 0.3);
  861. &:active {
  862. background: linear-gradient(135deg, #ff5252 0%, #ff4444 100%);
  863. box-shadow: 0 2upx 10upx rgba(255, 107, 107, 0.4);
  864. }
  865. }
  866. }
  867. }
  868. }
  869. // 弹框动画
  870. @keyframes fadeIn {
  871. from {
  872. opacity: 0;
  873. }
  874. to {
  875. opacity: 1;
  876. }
  877. }
  878. @keyframes slideUp {
  879. from {
  880. opacity: 0;
  881. transform: translateY(50upx) scale(0.9);
  882. }
  883. to {
  884. opacity: 1;
  885. transform: translateY(0) scale(1);
  886. }
  887. }
  888. </style>