import AddOutlined from "@mui/icons-material/AddOutlined"; import Checkbox from "@mui/material/Checkbox"; import Dialog from "@mui/material/Dialog"; import DialogActions from "@mui/material/DialogActions"; import DialogContent from "@mui/material/DialogContent"; import DialogTitle from "@mui/material/DialogTitle"; import FormControlLabel from "@mui/material/FormControlLabel"; import MenuItem from "@mui/material/MenuItem"; import TextField from "@mui/material/TextField"; import { useMemo, useState } from "react"; import { AdminSwitch } from "@/shared/ui/AdminSwitch.jsx"; import { Button } from "@/shared/ui/Button.jsx"; import { formatDecimal } from "@/shared/utils/rtpProbability.js"; import { configToForm, correctAllStageProbabilities, formToConfig, nextStageMultiplier, stageSummary } from "../configForm.js"; import { formatNumber, formatPercent } from "../format.js"; import { OpsStatusBadge } from "./OpsStatusBadge.jsx"; export function LuckyGiftConfigDialog({ appOptions = [], config, mode = "edit", onClose, onSubmit, saving }) { const [form, setForm] = useState(() => configToForm(config)); const [activeStage, setActiveStage] = useState("novice"); const stageSummaries = useMemo(() => form.stages.map((stage) => stageSummary(stage, form)), [form]); const activeStageKey = form.stages.some((stage) => stage.stage === activeStage) ? activeStage : form.stages[0]?.stage; const activeStageConfig = form.stages.find((stage) => stage.stage === activeStageKey); const activeSummary = stageSummaries.find((summary) => summary.stageKey === activeStageKey); const updateField = (field, value) => { setForm((current) => { const next = { ...current, [field]: value }; // 目标 RTP 决定各奖档概率的闭合解,改动后全部阶段重算,避免展示的概率和将要发布的值不一致。 return field === "target_rtp_percent" ? correctAllStageProbabilities(next) : next; }); }; const updateStageTier = (stageKey, tierIndex, patch) => { setForm((current) => correctAllStageProbabilities({ ...current, stages: current.stages.map((stage) => stage.stage === stageKey ? { ...stage, tiers: stage.tiers.map((tier, index) => (index === tierIndex ? { ...tier, ...patch } : tier)) } : stage ) }) ); }; const addStageTier = (stageKey) => { setForm((current) => correctAllStageProbabilities({ ...current, stages: current.stages.map((stage) => stage.stage === stageKey ? { ...stage, tiers: [ ...stage.tiers, { enabled: true, // 10 倍以上属于高倍档,默认只在奖池高水位时开放,防止新档位直接击穿保底线。 highWaterOnly: nextStageMultiplier(stage.tiers) >= 10, multiplier: String(nextStageMultiplier(stage.tiers)), probabilityPercent: "0", tierId: "" } ] } : stage ) }) ); }; const removeStageTier = (stageKey, tierIndex) => { setForm((current) => correctAllStageProbabilities({ ...current, stages: current.stages.map((stage) => stage.stage === stageKey ? { ...stage, tiers: stage.tiers.filter((_, index) => index !== tierIndex) } : stage ) }) ); }; const handleSubmit = (event) => { event.preventDefault(); onSubmit(formToConfig(config, form)); }; const isCreate = mode === "create"; const title = isCreate ? "添加幸运礼物配置" : config.is_default ? "发布幸运礼物配置" : "新增幸运礼物版本"; return (
{title} {form.app_code || "-"} / {form.pool_id || "default"} /{" "} {config.is_default || isCreate ? "默认草稿" : `当前版本 v${config.rule_version ?? "-"}`}
{stageSummaries.map((summary) => ( ))}
{activeStageConfig ? (

{activeSummary?.stageLabel}

{activeStageConfig.tiers.map((tier, index) => (
updateStageTier(activeStageConfig.stage, index, { // 10 倍及以上强制只走高水位,输入时同步勾选并锁死,防止高倍档在低水位放开。 highWaterOnly: Number(event.target.value) >= 10 || tier.highWaterOnly, multiplier: event.target.value }) } /> {/* 概率由目标 RTP 和倍率统一校正,保持只读可以避免运营手填后绕过服务端概率闭合校验。 */} = 10} size="small" slotProps={{ input: { "aria-label": "高水位" } }} onChange={(event) => updateStageTier(activeStageConfig.stage, index, { highWaterOnly: event.target.checked })} /> updateStageTier(activeStageConfig.stage, index, { enabled: event.target.checked })} />
))}
) : null}
); } function ConfigMetric({ label, value }) { return (
{label} {value}
); } function NumberField({ form, label, name, onChange, step = "1" }) { return ( onChange(name, event.target.value)} /> ); }