fix: 限制自定义充值最低金额

This commit is contained in:
hh
2026-07-15 13:47:32 +08:00
parent e81d7170ed
commit fdd3bbfad2
3 changed files with 44 additions and 6 deletions
+38 -6
View File
@@ -59,6 +59,7 @@ const RECHARGE: Array<{ amt: number; gift: string; bonus: boolean; bonusAmt: num
{ amt: 1000, gift: "+800 积分赠送", bonus: true, bonusAmt: 800 },
{ amt: 3000, gift: "+3000 积分赠送", bonus: true, bonusAmt: 3000 }
];
const MIN_RECHARGE_AMOUNT = 50;
const STAGES: Array<{ k: string; color: string; bucket: keyof BillingTrend["by_stage"] }> = [
{ k: "视频片段(Seedance)", color: "var(--heat)", bucket: "video" },
@@ -113,12 +114,13 @@ function TopupModal({ open, channel, amount, bonus, rate, close, onDone }: {
);
}
export function AccountPage({ billing, projects, team, onRecharge }: {
export function AccountPage({ billing, projects, team, onRecharge, onNotify }: {
billing: BillingSummary | null;
projects: Project[];
// team prop 用于读取团队级月限额(与团队管理页保持同一来源 · #14)
team?: Team | null;
onRecharge: (amount: number, bonus: number) => void | Promise<unknown>;
onNotify: (type: "success" | "error" | "info", text: string) => void;
}) {
const [tab, setTab] = useState<Tab>("overview");
// 积分汇率(积分/¥):预览换算与后端 recharge 同源,汇率改配后不至于「预览 ×10 实到 ×N」
@@ -175,11 +177,29 @@ export function AccountPage({ billing, projects, team, onRecharge }: {
const [billJump, setBillJump] = useState("");
const selectedCard = RECHARGE.find((item) => item.amt === recharge);
const effectiveAmount = Number(customAmt) > 0 ? Number(customAmt) : recharge;
const effectiveBonus = Number(customAmt) > 0 ? 0 : selectedCard?.bonusAmt || 0;
const customAmount = Number(customAmt);
const hasCustomAmount = customAmt.length > 0;
const customAmountIsValid = !hasCustomAmount || (Number.isFinite(customAmount) && customAmount >= MIN_RECHARGE_AMOUNT);
const effectiveAmount = hasCustomAmount ? customAmount : recharge;
const effectiveBonus = hasCustomAmount ? 0 : selectedCard?.bonusAmt || 0;
const setCustomIntegerAmount = (value: string) => setCustomAmt(value.replace(/\D/g, "").replace(/^0+/, ""));
function validateCustomAmount() {
if (customAmountIsValid) return true;
onNotify("error", `最小充值金额为 ${MIN_RECHARGE_AMOUNT}`);
return false;
}
function openTopup(channel: "wechat" | "alipay") {
if (!validateCustomAmount()) return;
setTopupChannel(channel);
}
async function submitRecharge() {
if (!validateCustomAmount()) {
setTopupChannel(null);
return;
}
if (effectiveAmount <= 0) return;
await onRecharge(effectiveAmount, effectiveBonus); // 父级 action 成功后弹全局「充值成功」toast
setTopupChannel(null);
@@ -319,13 +339,25 @@ export function AccountPage({ billing, projects, team, onRecharge }: {
</div>
<div className="pay-row">
<div className="pay-title"></div>
<input className="input" placeholder="最低 ¥50,可输入任意金额" type="text" inputMode="numeric" pattern="[0-9]*" value={customAmt} onChange={(event) => setCustomIntegerAmount(event.target.value)} />
<input
className={`input${!customAmountIsValid ? " is-invalid" : ""}`}
placeholder="最低 ¥50,可输入任意金额"
type="number"
min={MIN_RECHARGE_AMOUNT}
step="1"
inputMode="numeric"
value={customAmt}
aria-invalid={!customAmountIsValid}
aria-describedby="custom-recharge-error"
onChange={(event) => setCustomIntegerAmount(event.target.value)}
/>
{!customAmountIsValid && <div className="field-hint is-error" id="custom-recharge-error" role="alert"> {MIN_RECHARGE_AMOUNT} </div>}
<div className="pay-btn-row">
<button className="btn pay-method-btn pay-wechat" type="button" aria-label="微信支付" onClick={() => setTopupChannel("wechat")}>
<button className="btn pay-method-btn pay-wechat" type="button" aria-label="微信支付" onClick={() => openTopup("wechat")}>
<span className="pay-logo" aria-hidden="true"><img src="/assets/pay-wechat.png" alt="" /></span>
</button>
<button className="btn pay-method-btn pay-alipay" type="button" aria-label="支付宝" onClick={() => setTopupChannel("alipay")}>
<button className="btn pay-method-btn pay-alipay" type="button" aria-label="支付宝" onClick={() => openTopup("alipay")}>
<span className="pay-logo" aria-hidden="true"><img src="/assets/pay-alipay.png" alt="" /></span>
</button>