From cd6b82abd86f381a71748936bfb7aa03a4116989 Mon Sep 17 00:00:00 2001 From: jacy <18049821889@163.com> Date: Thu, 23 Jul 2026 00:32:09 +0800 Subject: [PATCH] =?UTF-8?q?=E5=B0=8F=E7=A8=8B=E5=BA=8F=E7=AB=AF=E6=A0=B8?= =?UTF-8?q?=E9=94=80=E6=9D=83=E7=9B=8A=E8=BE=93=E5=85=A5=E6=A1=86=E9=99=90?= =?UTF-8?q?=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/mini-user/src/pages/benefit/index.tsx | 35 +++++++++++++++------- apps/mini-user/src/pages/redeem/index.tsx | 32 ++++++++++++++------ 2 files changed, 48 insertions(+), 19 deletions(-) diff --git a/apps/mini-user/src/pages/benefit/index.tsx b/apps/mini-user/src/pages/benefit/index.tsx index 7ffada2..9a71070 100644 --- a/apps/mini-user/src/pages/benefit/index.tsx +++ b/apps/mini-user/src/pages/benefit/index.tsx @@ -1,4 +1,4 @@ -import { useCallback, useEffect, useState } from 'react'; +import { useCallback, useState } from 'react'; import { View, Text } from '@tarojs/components'; import Taro, { useDidShow, usePullDownRefresh } from '@tarojs/taro'; import PageShell from '../../components/PageShell'; @@ -35,17 +35,18 @@ function usagePercent(coupon: CouponItem) { export default function BenefitPage() { const metrics = useNavBarMetrics(); - const loggedIn = isLoggedIn(); + const [loggedIn, setLoggedIn] = useState(() => isLoggedIn()); const [summary, setSummary] = useState(null); const [coupons, setCoupons] = useState([]); const [tab, setTab] = useState<'available' | 'history'>('available'); - useDidShow(() => { - syncTabBarSelected(2); - }); + const resetGuestState = useCallback(() => { + setSummary(null); + setCoupons([]); + }, []); const loadBenefit = useCallback(() => { - if (!loggedIn) return Promise.resolve(); + if (!isLoggedIn()) return Promise.resolve(); return Promise.all([ request('/benefit/summary'), request('/benefit/coupons'), @@ -55,13 +56,27 @@ export default function BenefitPage() { setCoupons(Array.isArray(list) ? list : []); }) .catch((e) => toast(e instanceof Error ? e.message : '加载失败')); - }, [loggedIn]); + }, []); - useEffect(() => { - void loadBenefit(); - }, [loadBenefit]); + useDidShow(() => { + syncTabBarSelected(2); + const loggedInNow = isLoggedIn(); + setLoggedIn(loggedInNow); + if (loggedInNow) { + void loadBenefit(); + } else { + resetGuestState(); + } + }); usePullDownRefresh(() => { + const loggedInNow = isLoggedIn(); + setLoggedIn(loggedInNow); + if (!loggedInNow) { + resetGuestState(); + Taro.stopPullDownRefresh(); + return; + } void loadBenefit().finally(() => Taro.stopPullDownRefresh()); }); diff --git a/apps/mini-user/src/pages/redeem/index.tsx b/apps/mini-user/src/pages/redeem/index.tsx index b6469d5..86206dc 100644 --- a/apps/mini-user/src/pages/redeem/index.tsx +++ b/apps/mini-user/src/pages/redeem/index.tsx @@ -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(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() { onAmountChange(e.detail.value)} onBlur={(e) => onAmountChange(e.detail.value)} style={{ textAlign: 'center' }} @@ -150,7 +164,7 @@ export default function RedeemPage() { - 核销金额最低 0.01 元,且不超过可用权益余额。核销码有效期 3 分钟,请到店出示给收银员扫码。 + 核销金额最低 0.01 元,小数最多两位。不超过可用权益余额。核销码有效期 3 分钟,请到店出示给收银员扫码。