fix(抽奖): 数字输入改 text 类型(时长/概率/库存/领奖窗口/发放次数)+ 重建 dist.zip

避开 EnhancedInput 在 type=number 下 min/max 实时钳制导致的"输入0/改数字跳变",
用 formatOutput 转 number,后端仍收 int。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-17 06:26:14 -07:00
parent 0feb907954
commit 813d491f97
3 changed files with 40 additions and 17 deletions
Binary file not shown.
@@ -31,6 +31,16 @@ const grantSchema = z.object({
source_ref: z.string().trim().min(1), source_ref: z.string().trim().min(1),
}); });
// 数字输入统一用 type="text" + 该 formatOutput,避开 number 类型 input 的实时钳制问题。
const numberOutput = (v: string | number): number | undefined => {
const s = String(v ?? "").trim();
if (s === "") {
return;
}
const n = Number(s);
return Number.isNaN(n) ? undefined : n;
};
type GrantFormValues = z.infer<typeof grantSchema>; type GrantFormValues = z.infer<typeof grantSchema>;
const SOURCE_REF_RANDOM_LENGTH = 6; const SOURCE_REF_RANDOM_LENGTH = 6;
@@ -123,10 +133,11 @@ export default function GrantDialog({
<FormLabel>{t("grant.userId", "User ID")}</FormLabel> <FormLabel>{t("grant.userId", "User ID")}</FormLabel>
<FormControl> <FormControl>
<EnhancedInput <EnhancedInput
min={1} formatOutput={numberOutput}
inputMode="numeric"
onValueChange={(value) => field.onChange(value)} onValueChange={(value) => field.onChange(value)}
placeholder={t("grant.userIdPlaceholder", "e.g. 87433")} placeholder={t("grant.userIdPlaceholder", "e.g. 87433")}
type="number" type="text"
value={field.value} value={field.value}
/> />
</FormControl> </FormControl>
@@ -148,10 +159,11 @@ export default function GrantDialog({
<FormLabel>{t("grant.amount", "Amount")}</FormLabel> <FormLabel>{t("grant.amount", "Amount")}</FormLabel>
<FormControl> <FormControl>
<EnhancedInput <EnhancedInput
min={1} formatOutput={numberOutput}
inputMode="numeric"
onValueChange={(value) => field.onChange(value)} onValueChange={(value) => field.onChange(value)}
placeholder="1" placeholder="1"
type="number" type="text"
value={field.value} value={field.value}
/> />
</FormControl> </FormControl>
+24 -13
View File
@@ -46,6 +46,18 @@ const CENTS_PER_YUAN = 100;
// 概率单位约定:weight 1000 = 1%(支持 3 位小数概率,总权重 100000 = 100%)。 // 概率单位约定:weight 1000 = 1%(支持 3 位小数概率,总权重 100000 = 100%)。
const WEIGHT_PER_PERCENT = 1000; const WEIGHT_PER_PERCENT = 1000;
// numberOutput 把 text 输入转成数字(空/非法→undefined)。数字输入统一用 type="text"
// + 该 formatOutput,避开 EnhancedInput 在 type="number" 下 min/max 实时钳制导致的
// “输入 0 / 改中间位跳变”问题;提交仍是数字,后端收 int。
const numberOutput = (v: string | number): number | undefined => {
const s = String(v ?? "").trim();
if (s === "") {
return;
}
const n = Number(s);
return Number.isNaN(n) ? undefined : n;
};
type TranslateFn = (key: string, defaultValue: string) => string; type TranslateFn = (key: string, defaultValue: string) => string;
const PRIZE_TYPE_OPTIONS: { const PRIZE_TYPE_OPTIONS: {
@@ -442,9 +454,9 @@ export default function PrizeForm({
"duration_days", "duration_days",
t("prize.durationDays", "Duration (days)"), t("prize.durationDays", "Duration (days)"),
{ {
type: "number", type: "text",
min: 1, inputMode: "numeric",
step: 1, formatOutput: numberOutput,
placeholder: t("prize.durationDaysPlaceholder", "e.g. 1"), placeholder: t("prize.durationDaysPlaceholder", "e.g. 1"),
suffix: t("prize.daysSuffix", "days"), suffix: t("prize.daysSuffix", "days"),
} }
@@ -538,9 +550,9 @@ export default function PrizeForm({
"claim_ttl_hours", "claim_ttl_hours",
t("prize.claimTtlHours", "Claim window (hours)"), t("prize.claimTtlHours", "Claim window (hours)"),
{ {
type: "number", type: "text",
min: 1, inputMode: "numeric",
step: 1, formatOutput: numberOutput,
placeholder: "168", placeholder: "168",
suffix: t("prize.hoursSuffix", "hours"), suffix: t("prize.hoursSuffix", "hours"),
}, },
@@ -561,10 +573,9 @@ export default function PrizeForm({
"weight", "weight",
t("prize.weight", "Win probability (%)"), t("prize.weight", "Win probability (%)"),
{ {
type: "number", type: "text",
min: 0, inputMode: "decimal",
max: 100, formatOutput: numberOutput,
step: 0.001,
suffix: "%", suffix: "%",
disabled: isFallback, disabled: isFallback,
}, },
@@ -587,9 +598,9 @@ export default function PrizeForm({
"total_stock", "total_stock",
t("prize.totalStock", "Total Stock"), t("prize.totalStock", "Total Stock"),
{ {
type: "number", type: "text",
min: 1, inputMode: "numeric",
step: 1, formatOutput: numberOutput,
placeholder: t("prize.totalStockPlaceholder", "e.g. 100"), placeholder: t("prize.totalStockPlaceholder", "e.g. 100"),
} }
)} )}