|
|
@@ -30,14 +30,14 @@
|
|
|
</view>
|
|
|
</view>
|
|
|
|
|
|
- <!-- 数字键盘 -->
|
|
|
+ <!-- 数字键盘 - 使用直接事件绑定,确保兼容性 -->
|
|
|
<view class="keyboard-section">
|
|
|
<view class="keyboard">
|
|
|
<view class="keyboard-row" v-for="(row, rowIndex) in keyboardKeys" :key="rowIndex">
|
|
|
<view
|
|
|
class="key-item"
|
|
|
v-for="(key, keyIndex) in row"
|
|
|
- :key="keyIndex"
|
|
|
+ :key="`${rowIndex}-${keyIndex}`"
|
|
|
:class="{
|
|
|
'key-number': key.type === 'number',
|
|
|
'key-delete': key.type === 'delete',
|
|
|
@@ -45,6 +45,8 @@
|
|
|
'key-confirm': key.type === 'confirm'
|
|
|
}"
|
|
|
@click="handleKeyPress(key)"
|
|
|
+ @touchstart="handleTouchStart"
|
|
|
+ @touchend="handleTouchEnd"
|
|
|
>
|
|
|
<text v-if="key.type === 'delete'" class="iconfont icon-delete"></text>
|
|
|
<text v-else-if="key.type === 'confirm'">确认</text>
|
|
|
@@ -73,42 +75,67 @@
|
|
|
|
|
|
<script>
|
|
|
import { } from '@/api/payment';
|
|
|
+
|
|
|
+// 优化:使用常量避免重复创建
|
|
|
+const KEY_TYPES = {
|
|
|
+ NUMBER: 'number',
|
|
|
+ DELETE: 'delete',
|
|
|
+ DOT: 'dot',
|
|
|
+ CONFIRM: 'confirm'
|
|
|
+};
|
|
|
+
|
|
|
+const MAX_INTEGER_LENGTH = 8;
|
|
|
+const MAX_DECIMAL_LENGTH = 2;
|
|
|
+
|
|
|
export default {
|
|
|
name: 'pay',
|
|
|
data() {
|
|
|
return {
|
|
|
currentAmount: '0',
|
|
|
displayAmount: '0',
|
|
|
+ // 优化:使用静态数据,避免重复创建
|
|
|
keyboardKeys: [
|
|
|
[
|
|
|
- { type: 'number', value: '1' },
|
|
|
- { type: 'number', value: '2' },
|
|
|
- { type: 'number', value: '3' }
|
|
|
+ { type: KEY_TYPES.NUMBER, value: '1' },
|
|
|
+ { type: KEY_TYPES.NUMBER, value: '2' },
|
|
|
+ { type: KEY_TYPES.NUMBER, value: '3' }
|
|
|
],
|
|
|
[
|
|
|
- { type: 'number', value: '4' },
|
|
|
- { type: 'number', value: '5' },
|
|
|
- { type: 'number', value: '6' }
|
|
|
+ { type: KEY_TYPES.NUMBER, value: '4' },
|
|
|
+ { type: KEY_TYPES.NUMBER, value: '5' },
|
|
|
+ { type: KEY_TYPES.NUMBER, value: '6' }
|
|
|
],
|
|
|
[
|
|
|
- { type: 'number', value: '7' },
|
|
|
- { type: 'number', value: '8' },
|
|
|
- { type: 'number', value: '9' }
|
|
|
+ { type: KEY_TYPES.NUMBER, value: '7' },
|
|
|
+ { type: KEY_TYPES.NUMBER, value: '8' },
|
|
|
+ { type: KEY_TYPES.NUMBER, value: '9' }
|
|
|
],
|
|
|
[
|
|
|
- { type: 'dot', value: '.' },
|
|
|
- { type: 'number', value: '0' },
|
|
|
- { type: 'delete', value: '' }
|
|
|
+ { type: KEY_TYPES.DOT, value: '.' },
|
|
|
+ { type: KEY_TYPES.NUMBER, value: '0' },
|
|
|
+ { type: KEY_TYPES.DELETE, value: '' }
|
|
|
]
|
|
|
],
|
|
|
- hasDecimal: false,
|
|
|
- decimalPlaces: 0
|
|
|
+ // 优化:使用更高效的状态管理
|
|
|
+ amountBuffer: '0',
|
|
|
+ updateScheduled: false,
|
|
|
+ updateTimer: null,
|
|
|
+ orderId: null,
|
|
|
+ // 优化:缓存计算结果
|
|
|
+ _cachedCanPay: false,
|
|
|
+ _lastAmount: '0'
|
|
|
}
|
|
|
},
|
|
|
computed: {
|
|
|
+ // 优化:使用缓存的计算属性
|
|
|
canPay() {
|
|
|
- const amount = parseFloat(this.currentAmount);
|
|
|
- return amount > 0 && amount <= 99999999.99;
|
|
|
+ // 只有当金额变化时才重新计算
|
|
|
+ if (this.currentAmount !== this._lastAmount) {
|
|
|
+ const amount = parseFloat(this.currentAmount);
|
|
|
+ this._cachedCanPay = amount > 0 && amount <= 99999999.99;
|
|
|
+ this._lastAmount = this.currentAmount;
|
|
|
+ }
|
|
|
+ return this._cachedCanPay;
|
|
|
}
|
|
|
},
|
|
|
onLoad(options) {
|
|
|
@@ -122,103 +149,145 @@ export default {
|
|
|
}
|
|
|
},
|
|
|
methods: {
|
|
|
- // 处理键盘按键
|
|
|
+ // 优化:添加触摸反馈
|
|
|
+ handleTouchStart(e) {
|
|
|
+ const target = e.currentTarget;
|
|
|
+ if (target && target.style) {
|
|
|
+ target.style.transform = 'scale(0.95)';
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ handleTouchEnd(e) {
|
|
|
+ const target = e.currentTarget;
|
|
|
+ if (target && target.style) {
|
|
|
+ setTimeout(() => {
|
|
|
+ target.style.transform = 'scale(1)';
|
|
|
+ }, 100);
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ // 优化:简化的键盘处理逻辑
|
|
|
handleKeyPress(key) {
|
|
|
- if (key.type === 'number') {
|
|
|
+ // 防抖处理
|
|
|
+ if (this.updateTimer) {
|
|
|
+ clearTimeout(this.updateTimer);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (key.type === KEY_TYPES.NUMBER) {
|
|
|
this.addNumber(key.value);
|
|
|
- } else if (key.type === 'delete') {
|
|
|
+ } else if (key.type === KEY_TYPES.DELETE) {
|
|
|
this.deleteNumber();
|
|
|
- } else if (key.type === 'dot') {
|
|
|
+ } else if (key.type === KEY_TYPES.DOT) {
|
|
|
this.addDecimal();
|
|
|
}
|
|
|
+
|
|
|
+ // 使用 requestAnimationFrame 优化更新
|
|
|
+ this.scheduleUpdate();
|
|
|
+ },
|
|
|
+
|
|
|
+ // 优化:使用 requestAnimationFrame 调度更新
|
|
|
+ scheduleUpdate() {
|
|
|
+ if (!this.updateScheduled) {
|
|
|
+ this.updateScheduled = true;
|
|
|
+ requestAnimationFrame(() => {
|
|
|
+ this.updateDisplay();
|
|
|
+ this.updateScheduled = false;
|
|
|
+ });
|
|
|
+ }
|
|
|
},
|
|
|
- // 添加数字
|
|
|
+
|
|
|
+ // 优化:更高效的数字添加逻辑
|
|
|
addNumber(num) {
|
|
|
- if (this.currentAmount === '0' || this.currentAmount === '0.00') {
|
|
|
+ const buffer = this.amountBuffer;
|
|
|
+
|
|
|
+ // 快速路径:如果当前是0,直接替换
|
|
|
+ if (buffer === '0') {
|
|
|
+ this.amountBuffer = num;
|
|
|
this.currentAmount = num;
|
|
|
- this.hasDecimal = false;
|
|
|
- this.decimalPlaces = 0;
|
|
|
- } else {
|
|
|
- if (this.hasDecimal) {
|
|
|
- if (this.decimalPlaces < 2) {
|
|
|
- this.currentAmount += num;
|
|
|
- this.decimalPlaces++;
|
|
|
- }
|
|
|
- } else {
|
|
|
- if (this.currentAmount.length < 8) {
|
|
|
- this.currentAmount += num;
|
|
|
- }
|
|
|
- }
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 检查长度限制
|
|
|
+ const parts = buffer.split('.');
|
|
|
+ const integerPart = parts[0];
|
|
|
+ const decimalPart = parts[1];
|
|
|
+
|
|
|
+ // 整数部分限制
|
|
|
+ if (!decimalPart && integerPart.length >= MAX_INTEGER_LENGTH) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 小数部分限制
|
|
|
+ if (decimalPart && decimalPart.length >= MAX_DECIMAL_LENGTH) {
|
|
|
+ return;
|
|
|
}
|
|
|
- this.updateDisplay();
|
|
|
+
|
|
|
+ // 添加数字
|
|
|
+ this.amountBuffer = buffer + num;
|
|
|
+ this.currentAmount = this.amountBuffer;
|
|
|
},
|
|
|
- // 添加小数点
|
|
|
+
|
|
|
+ // 优化:更高效的小数点添加逻辑
|
|
|
addDecimal() {
|
|
|
- if (!this.hasDecimal) {
|
|
|
- this.hasDecimal = true;
|
|
|
- this.currentAmount += '.';
|
|
|
- this.updateDisplay();
|
|
|
+ if (!this.amountBuffer.includes('.')) {
|
|
|
+ this.amountBuffer += '.';
|
|
|
+ this.currentAmount = this.amountBuffer;
|
|
|
}
|
|
|
},
|
|
|
- // 删除数字
|
|
|
+
|
|
|
+ // 优化:更高效的删除逻辑
|
|
|
deleteNumber() {
|
|
|
- if (this.currentAmount.length > 1) {
|
|
|
- // 如果当前字符是小数点,删除小数点并重置状态
|
|
|
- if (this.currentAmount.endsWith('.')) {
|
|
|
- this.hasDecimal = false;
|
|
|
- this.currentAmount = this.currentAmount.slice(0, -1);
|
|
|
- } else {
|
|
|
- // 删除普通数字
|
|
|
- if (this.hasDecimal && this.currentAmount.split('.')[1]) {
|
|
|
- this.decimalPlaces--;
|
|
|
- // 如果删除后没有小数位了,重置状态
|
|
|
- if (this.decimalPlaces === 0) {
|
|
|
- this.hasDecimal = false;
|
|
|
- }
|
|
|
- }
|
|
|
- this.currentAmount = this.currentAmount.slice(0, -1);
|
|
|
- }
|
|
|
+ const buffer = this.amountBuffer;
|
|
|
+ if (buffer.length > 1) {
|
|
|
+ this.amountBuffer = buffer.slice(0, -1);
|
|
|
} else {
|
|
|
- this.currentAmount = '0';
|
|
|
- this.hasDecimal = false;
|
|
|
- this.decimalPlaces = 0;
|
|
|
+ this.amountBuffer = '0';
|
|
|
}
|
|
|
- this.updateDisplay();
|
|
|
+ this.currentAmount = this.amountBuffer;
|
|
|
},
|
|
|
- // 更新显示金额
|
|
|
+
|
|
|
+ // 优化:更高效的显示更新逻辑
|
|
|
updateDisplay() {
|
|
|
- let amount = parseFloat(this.currentAmount) || 0;
|
|
|
- // 如果没有输入小数点,不显示小数部分
|
|
|
- if (!this.hasDecimal) {
|
|
|
- this.displayAmount = Math.floor(amount).toString();
|
|
|
+ const amount = this.currentAmount;
|
|
|
+
|
|
|
+ // 快速路径:如果是0,直接显示
|
|
|
+ if (amount === '0') {
|
|
|
+ this.displayAmount = '0';
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 检查是否包含小数点
|
|
|
+ if (amount.includes('.')) {
|
|
|
+ const numAmount = parseFloat(amount) || 0;
|
|
|
+ this.displayAmount = numAmount.toFixed(2);
|
|
|
} else {
|
|
|
- // 如果有小数点,显示两位小数
|
|
|
- this.displayAmount = amount.toFixed(2);
|
|
|
+ // 整数显示
|
|
|
+ this.displayAmount = amount;
|
|
|
}
|
|
|
},
|
|
|
- // 设置金额
|
|
|
+
|
|
|
+ // 优化:更高效的金额设置
|
|
|
setAmount(amount) {
|
|
|
const numAmount = parseFloat(amount);
|
|
|
if (!isNaN(numAmount) && numAmount > 0) {
|
|
|
- this.currentAmount = numAmount.toString();
|
|
|
- this.hasDecimal = this.currentAmount.includes('.');
|
|
|
- if (this.hasDecimal) {
|
|
|
- this.decimalPlaces = this.currentAmount.split('.')[1].length;
|
|
|
- } else {
|
|
|
- this.decimalPlaces = 0;
|
|
|
- }
|
|
|
+ const amountStr = numAmount.toString();
|
|
|
+ this.amountBuffer = amountStr;
|
|
|
+ this.currentAmount = amountStr;
|
|
|
this.updateDisplay();
|
|
|
}
|
|
|
},
|
|
|
+
|
|
|
// 处理支付
|
|
|
async handlePay() {
|
|
|
if (!this.canPay) return;
|
|
|
const amount = parseFloat(this.currentAmount);
|
|
|
},
|
|
|
+
|
|
|
// 请求支付
|
|
|
async requestPayment(amount) {
|
|
|
|
|
|
},
|
|
|
+
|
|
|
// 生成订单ID
|
|
|
generateOrderId() {
|
|
|
const timestamp = Date.now();
|
|
|
@@ -391,48 +460,32 @@ export default {
|
|
|
justify-content: center;
|
|
|
font-size: 40rpx;
|
|
|
font-weight: 600;
|
|
|
- transition: all 0.2s ease;
|
|
|
+ transition: transform 0.08s ease;
|
|
|
+ user-select: none;
|
|
|
+ -webkit-user-select: none;
|
|
|
+ cursor: pointer;
|
|
|
+ will-change: transform;
|
|
|
|
|
|
&.key-number {
|
|
|
background: #fff;
|
|
|
color: #333;
|
|
|
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.1);
|
|
|
-
|
|
|
- &:active {
|
|
|
- background: #f0f0f0;
|
|
|
- transform: scale(0.95);
|
|
|
- }
|
|
|
}
|
|
|
|
|
|
&.key-delete {
|
|
|
background: #ff6b6b;
|
|
|
color: #fff;
|
|
|
-
|
|
|
- &:active {
|
|
|
- background: #ff5252;
|
|
|
- transform: scale(0.95);
|
|
|
- }
|
|
|
}
|
|
|
|
|
|
&.key-dot {
|
|
|
background: #fff;
|
|
|
color: #333;
|
|
|
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.1);
|
|
|
-
|
|
|
- &:active {
|
|
|
- background: #f0f0f0;
|
|
|
- transform: scale(0.95);
|
|
|
- }
|
|
|
}
|
|
|
|
|
|
&.key-confirm {
|
|
|
background: #667eea;
|
|
|
color: #fff;
|
|
|
-
|
|
|
- &:active {
|
|
|
- background: #5a6fd8;
|
|
|
- transform: scale(0.95);
|
|
|
- }
|
|
|
}
|
|
|
}
|
|
|
}
|