@@ -17,7 +17,12 @@ function formatMoney(amount: number) {
|
||||
return amount.toLocaleString('zh-CN', { minimumFractionDigits: 2, maximumFractionDigits: 2 });
|
||||
}
|
||||
|
||||
/** 核销金额输入:最多两位小数;去掉前导 0;禁止非法字符 */
|
||||
/**
|
||||
* 核销金额输入清洗:
|
||||
* - 只保留数字与一个小数点
|
||||
* - 小数最多 2 位(再输入会被截断)
|
||||
* - 去掉多余前导 0
|
||||
*/
|
||||
function sanitizeRedeemAmountInput(raw: string): string {
|
||||
let next = String(raw ?? '').replace(/[^\d.]/g, '');
|
||||
if (!next) return '';
|
||||
@@ -28,19 +33,18 @@ function sanitizeRedeemAmountInput(raw: string): string {
|
||||
const decRaw = next
|
||||
.slice(firstDot + 1)
|
||||
.replace(/\D/g, '')
|
||||
.replace(/\./g, '')
|
||||
.slice(0, 2);
|
||||
const intPart = intRaw.replace(/^0+(?=\d)/, '') || '0';
|
||||
// 正在输入小数点或小数位时保留点
|
||||
if (decRaw.length > 0 || next.endsWith('.')) {
|
||||
if (next.endsWith('.') && decRaw.length === 0) {
|
||||
return `${intPart}.`;
|
||||
}
|
||||
if (decRaw.length > 0) {
|
||||
return `${intPart}.${decRaw}`;
|
||||
}
|
||||
return intPart;
|
||||
}
|
||||
|
||||
// 纯整数:忽略前导 0(保留单个 0)
|
||||
next = next.replace(/^0+(?=\d)/, '');
|
||||
return next;
|
||||
return next.replace(/^0+(?=\d)/, '');
|
||||
}
|
||||
|
||||
export default function RedeemPage() {
|
||||
@@ -50,6 +54,8 @@ export default function RedeemPage() {
|
||||
const [balance, setBalance] = useState(0);
|
||||
const [couponBalance, setCouponBalance] = useState<number | null>(null);
|
||||
const [amount, setAmount] = useState(() => sanitizeRedeemAmountInput(initialAmount));
|
||||
/** 原生 input 在截断小数后偶发不同步,强制 remount 对齐受控值 */
|
||||
const [inputKey, setInputKey] = useState(0);
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
const redeemableMax = couponId ? (couponBalance ?? 0) : balance;
|
||||
@@ -80,10 +86,16 @@ export default function RedeemPage() {
|
||||
function fillMaxAmount() {
|
||||
if (redeemableMax < MIN_REDEEM_AMOUNT) return;
|
||||
setAmount(sanitizeRedeemAmountInput(redeemableMax.toFixed(2)));
|
||||
setInputKey((k) => k + 1);
|
||||
}
|
||||
|
||||
function onAmountChange(raw: string) {
|
||||
setAmount(sanitizeRedeemAmountInput(raw));
|
||||
const next = sanitizeRedeemAmountInput(raw);
|
||||
setAmount(next);
|
||||
// 用户试图输入超过两位小数 / 非法字符时,强制刷新原生框显示
|
||||
if (next !== raw) {
|
||||
setInputKey((k) => k + 1);
|
||||
}
|
||||
}
|
||||
|
||||
async function submit() {
|
||||
@@ -130,11 +142,13 @@ export default function RedeemPage() {
|
||||
</View>
|
||||
<View className="redeem-input-wrap">
|
||||
<Input
|
||||
key={inputKey}
|
||||
className="redeem-input"
|
||||
type="digit"
|
||||
placeholder="输入核销金额"
|
||||
placeholderClass="redeem-input-placeholder"
|
||||
value={amount}
|
||||
maxlength={12}
|
||||
onInput={(e) => onAmountChange(e.detail.value)}
|
||||
onBlur={(e) => onAmountChange(e.detail.value)}
|
||||
style={{ textAlign: 'center' }}
|
||||
@@ -150,7 +164,7 @@ export default function RedeemPage() {
|
||||
</View>
|
||||
<View className="redeem-tips">
|
||||
<Text className="redeem-tips-text">
|
||||
核销金额最低 0.01 元,且不超过可用权益余额。核销码有效期 3 分钟,请到店出示给收银员扫码。
|
||||
核销金额最低 0.01 元,小数最多两位。不超过可用权益余额。核销码有效期 3 分钟,请到店出示给收银员扫码。
|
||||
</Text>
|
||||
</View>
|
||||
<View
|
||||
|
||||
Reference in New Issue
Block a user