feat(抽奖): 奖品类型精简为 免费时长+虚拟货币(人工发放)+谢谢参与;重建 dist.zip
- prize-form 只保留 vpn_duration / crypto / none(去掉佣金/实物/人工其他) - crypto 标签改「虚拟货币」 - 重新 vite build 并打包 apps/admin/dist.zip(部署产物,含全部抽奖新功能) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Binary file not shown.
@@ -119,7 +119,7 @@
|
|||||||
"type": "奖品类型",
|
"type": "奖品类型",
|
||||||
"typeCommission": "佣金",
|
"typeCommission": "佣金",
|
||||||
"typeCoupon": "优惠券",
|
"typeCoupon": "优惠券",
|
||||||
"typeCrypto": "加密货币",
|
"typeCrypto": "虚拟货币",
|
||||||
"typeManualOther": "人工发放(其他)",
|
"typeManualOther": "人工发放(其他)",
|
||||||
"typeNone": "谢谢参与",
|
"typeNone": "谢谢参与",
|
||||||
"typePhysical": "实物奖品",
|
"typePhysical": "实物奖品",
|
||||||
|
|||||||
@@ -59,43 +59,21 @@ const PRIZE_TYPE_OPTIONS: {
|
|||||||
labelDefault: "VPN Duration",
|
labelDefault: "VPN Duration",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
value: "commission",
|
value: "crypto",
|
||||||
labelKey: "prize.typeCommission",
|
labelKey: "prize.typeCrypto",
|
||||||
labelDefault: "Commission",
|
labelDefault: "Crypto",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
value: "none",
|
value: "none",
|
||||||
labelKey: "prize.typeNone",
|
labelKey: "prize.typeNone",
|
||||||
labelDefault: "No Prize (Thanks)",
|
labelDefault: "No Prize (Thanks)",
|
||||||
},
|
},
|
||||||
{
|
|
||||||
value: "crypto",
|
|
||||||
labelKey: "prize.typeCrypto",
|
|
||||||
labelDefault: "Crypto",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: "physical",
|
|
||||||
labelKey: "prize.typePhysical",
|
|
||||||
labelDefault: "Physical",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: "manual_other",
|
|
||||||
labelKey: "prize.typeManualOther",
|
|
||||||
labelDefault: "Manual (Other)",
|
|
||||||
},
|
|
||||||
];
|
];
|
||||||
|
|
||||||
const getPrizeSchema = (t: TranslateFn) =>
|
const getPrizeSchema = (t: TranslateFn) =>
|
||||||
z
|
z
|
||||||
.object({
|
.object({
|
||||||
type: z.enum([
|
type: z.enum(["vpn_duration", "crypto", "none"]),
|
||||||
"vpn_duration",
|
|
||||||
"commission",
|
|
||||||
"none",
|
|
||||||
"crypto",
|
|
||||||
"physical",
|
|
||||||
"manual_other",
|
|
||||||
]),
|
|
||||||
name: z.string().min(1, t("prize.nameRequired", "Name is required")),
|
name: z.string().min(1, t("prize.nameRequired", "Name is required")),
|
||||||
icon_url: z
|
icon_url: z
|
||||||
.string()
|
.string()
|
||||||
@@ -134,24 +112,6 @@ const getPrizeSchema = (t: TranslateFn) =>
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (data.type === "commission") {
|
|
||||||
const yuan = data.amount_yuan ?? 0;
|
|
||||||
const cents = yuan * CENTS_PER_YUAN;
|
|
||||||
if (yuan <= 0) {
|
|
||||||
issue(
|
|
||||||
"amount_yuan",
|
|
||||||
t("prize.amountRequired", "Amount must be greater than 0")
|
|
||||||
);
|
|
||||||
} else if (Math.abs(Math.round(cents) - cents) > 1e-6) {
|
|
||||||
issue(
|
|
||||||
"amount_yuan",
|
|
||||||
t(
|
|
||||||
"prize.amountPrecision",
|
|
||||||
"Amount supports at most 2 decimal places"
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (data.type === "crypto") {
|
if (data.type === "crypto") {
|
||||||
if (!data.crypto_amount?.trim()) {
|
if (!data.crypto_amount?.trim()) {
|
||||||
issue(
|
issue(
|
||||||
@@ -256,11 +216,6 @@ function toConfig(values: PrizeFormValues): Record<string, any> {
|
|||||||
}
|
}
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
if (values.type === "commission") {
|
|
||||||
return {
|
|
||||||
amount_cents: Math.round((values.amount_yuan ?? 0) * CENTS_PER_YUAN),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
if (values.type === "crypto") {
|
if (values.type === "crypto") {
|
||||||
return withTtl({
|
return withTtl({
|
||||||
amount: values.crypto_amount?.trim() ?? "",
|
amount: values.crypto_amount?.trim() ?? "",
|
||||||
@@ -271,13 +226,6 @@ function toConfig(values: PrizeFormValues): Record<string, any> {
|
|||||||
.filter(Boolean),
|
.filter(Boolean),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (values.type === "physical" || values.type === "manual_other") {
|
|
||||||
const config: Record<string, any> = {};
|
|
||||||
if (values.sku_name?.trim()) {
|
|
||||||
config.sku_name = values.sku_name.trim();
|
|
||||||
}
|
|
||||||
return withTtl(config);
|
|
||||||
}
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -548,23 +496,6 @@ export default function PrizeForm({
|
|||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{type === "commission" &&
|
|
||||||
renderInput(
|
|
||||||
"amount_yuan",
|
|
||||||
t("prize.amountYuan", "Commission Amount (CNY)"),
|
|
||||||
{
|
|
||||||
type: "number",
|
|
||||||
min: 0,
|
|
||||||
step: 0.01,
|
|
||||||
placeholder: "0.00",
|
|
||||||
prefix: "¥",
|
|
||||||
suffix: t("prize.yuanSuffix", "CNY"),
|
|
||||||
},
|
|
||||||
t(
|
|
||||||
"prize.amountUnitHint",
|
|
||||||
"Entered in CNY (yuan), stored as cents; up to 2 decimal places"
|
|
||||||
)
|
|
||||||
)}
|
|
||||||
{type === "none" && (
|
{type === "none" && (
|
||||||
<p className="rounded-md bg-muted/50 p-3 text-muted-foreground text-xs">
|
<p className="rounded-md bg-muted/50 p-3 text-muted-foreground text-xs">
|
||||||
{t(
|
{t(
|
||||||
@@ -601,20 +532,7 @@ export default function PrizeForm({
|
|||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{(type === "physical" || type === "manual_other") &&
|
{type === "crypto" && (
|
||||||
renderInput(
|
|
||||||
"sku_name",
|
|
||||||
t("prize.skuName", "Item name (optional)"),
|
|
||||||
{
|
|
||||||
placeholder: t(
|
|
||||||
"prize.skuNamePlaceholder",
|
|
||||||
"e.g. Limited T-shirt"
|
|
||||||
),
|
|
||||||
}
|
|
||||||
)}
|
|
||||||
{(type === "crypto" ||
|
|
||||||
type === "physical" ||
|
|
||||||
type === "manual_other") && (
|
|
||||||
<>
|
<>
|
||||||
{renderInput(
|
{renderInput(
|
||||||
"claim_ttl_hours",
|
"claim_ttl_hours",
|
||||||
|
|||||||
Reference in New Issue
Block a user