fix(抽奖): 概率按 weight/1000 显示与录入(支持小数%,总权重100000=100%)

- prize-grid: 概率预览/合计/卡片改用 weightToPercent(weight/1000),
  合计校验对齐 FULL_TOTAL_WEIGHT=100000;修正原来把原始 weight 当 % 显示(如 49901%)
- prize-form: 概率输入按百分比(0-100,step 0.001),提交 ×1000 存 weight,回填 ÷1000

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-16 21:03:18 -07:00
parent a30d83505f
commit 6e1e424eb2
2 changed files with 25 additions and 14 deletions
@@ -43,6 +43,8 @@ import { toast } from "sonner";
import { z } from "zod";
const CENTS_PER_YUAN = 100;
// 概率单位约定:weight 1000 = 1%(支持 3 位小数概率,总权重 100000 = 100%)。
const WEIGHT_PER_PERCENT = 1000;
type TranslateFn = (key: string, defaultValue: string) => string;
@@ -102,7 +104,6 @@ const getPrizeSchema = (t: TranslateFn) =>
.optional(),
weight: z
.number()
.int(t("prize.weightInteger", "Probability must be an integer"))
.min(0, t("prize.weightMin", "Probability must be at least 0"))
.max(100, t("prize.weightMax", "Probability cannot exceed 100")),
unlimited_stock: z.boolean(),
@@ -221,7 +222,7 @@ function toFormValues(prize?: API.LotteryPrize): PrizeFormValues {
type: (prize?.type as PrizeFormValues["type"]) ?? "vpn_duration",
name: prize?.name ?? "",
icon_url: prize?.icon_url ?? "",
weight: prize?.weight ?? 1,
weight: (prize?.weight ?? 0) / WEIGHT_PER_PERCENT,
unlimited_stock: prize ? prize.total_stock === null : true,
total_stock: prize?.total_stock ?? undefined,
is_fallback: prize?.is_fallback ?? false,
@@ -325,7 +326,9 @@ export default function PrizeForm({
name: values.name.trim(),
icon_url: values.icon_url?.trim() ?? "",
config: toConfig(values),
weight: values.is_fallback ? 0 : values.weight,
weight: values.is_fallback
? 0
: Math.round(values.weight * WEIGHT_PER_PERCENT),
total_stock: values.unlimited_stock
? null
: (values.total_stock ?? null),
@@ -643,7 +646,7 @@ export default function PrizeForm({
type: "number",
min: 0,
max: 100,
step: 1,
step: 0.001,
suffix: "%",
disabled: isFallback,
},