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:
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>
|
||||||
|
|||||||
@@ -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"),
|
||||||
}
|
}
|
||||||
)}
|
)}
|
||||||
|
|||||||
Reference in New Issue
Block a user