import AddOutlined from "@mui/icons-material/AddOutlined"; import DeleteOutlineOutlined from "@mui/icons-material/DeleteOutlineOutlined"; import SaveOutlined from "@mui/icons-material/SaveOutlined"; import Drawer from "@mui/material/Drawer"; import IconButton from "@mui/material/IconButton"; import MenuItem from "@mui/material/MenuItem"; import TextField from "@mui/material/TextField"; import { AdminSwitch } from "@/shared/ui/AdminSwitch.jsx"; import { Button } from "@/shared/ui/Button.jsx"; import { FieldLabel } from "@/features/lucky-gift/components/FieldLabel.jsx"; import { atmosphereLevelOptions, controlStrengthOptions, jackpotStrategyOptions, noviceStrengthOptions, poolModeOptions, riskLevelOptions, } from "@/features/lucky-gift/constants.js"; import styles from "@/features/lucky-gift/lucky-gift.module.css"; const helpText = { enabled: "控制全站幸运礼物抽奖是否生效,关闭后用户仍可送礼但不触发幸运抽奖。", poolId: "奖池 ID 是请求接口传入的隔离键,不同奖池的 RTP、预算、风控和用户阶段互不影响。", targetRtpPercent: "基础返奖成本比例,95 表示长期每消耗 100 金币,基础奖池目标返还 95 金币。", controlStrength: "控制 RTP 收敛窗口大小,越精准越快贴近目标,越自然短期波动越大。", poolMode: "决定平台池、房间池、整体幸运礼物池分别承担多少基础返奖责任。", noviceStrength: "控制新手池持续抽数,越强表示用户前期更长时间处于低波动体验。", multiplierValues: "配置用户可能命中的倍率,例如 0、0.5、1、2、10、100;每个倍率的概率由后端按 RTP 窗口自动生成。", jackpotStrategy: "控制高倍奖档开放强度,保守需要更高水位,关闭则不出高倍大奖。", riskLevel: "控制单用户、设备、房间和主播关联的奖励上限,越严格越防套利。", atmosphereLevel: "控制房间气氛池入账强度,用于房间共享爆点,不计入基础 RTP。", activitySubsidyEnabled: "活动补贴是独立预算,开启后会提高用户可见奖励,但不污染基础 RTP。", activityBudget: "活动期最多可额外补贴的金币总额。", activityDailyLimit: "活动补贴每天最多释放的金币额度。", }; export function LuckyGiftConfigDrawer({ abilities, configLoading, configSaving, form, onClose, onSubmit, open, setForm, }) { const disabled = !abilities.canUpdate || configLoading || configSaving; return (

幸运礼物配置

基础控制
} value={form.poolId} /> } type="number" value={form.targetRtpPercent} onChange={(event) => updateForm(setForm, "targetRtpPercent", event.target.value)} /> updateForm(setForm, "controlStrength", value)} /> updateForm(setForm, "enabled", event.target.checked)} />
体验策略
updateForm(setForm, "poolMode", value)} /> updateForm(setForm, "noviceStrength", value)} /> updateForm(setForm, "jackpotStrategy", value)} />
保护和补贴
updateForm(setForm, "riskLevel", value)} /> updateForm(setForm, "atmosphereLevel", value)} />
updateForm(setForm, "activitySubsidyEnabled", event.target.checked)} /> {form.activitySubsidyEnabled ? (
} type="number" value={form.activityBudget} onChange={(event) => updateForm(setForm, "activityBudget", event.target.value)} /> } type="number" value={form.activityDailyLimit} onChange={(event) => updateForm(setForm, "activityDailyLimit", event.target.value)} />
) : null}
); } function MultiplierField({ disabled, help, setForm, values }) { const list = values.length > 0 ? values : ["0"]; return (
{list.map((value, index) => (
updateMultiplier(setForm, index, event.target.value)} /> removeMultiplier(setForm, index)} >
))}
); } function SelectField({ disabled, help, label, onChange, options, value }) { return ( } value={value} onChange={(event) => onChange(event.target.value)} > {options.map(([optionValue, optionLabel]) => ( {optionLabel} ))} ); } function SwitchField({ checked, className, disabled, help, label, onChange }) { return (
); } function updateForm(setForm, key, value) { setForm((current) => ({ ...current, [key]: value })); } function updateMultiplier(setForm, index, value) { setForm((current) => { const nextValues = [...(current.multiplierValues || [])]; nextValues[index] = value; return { ...current, multiplierValues: nextValues }; }); } function addMultiplier(setForm) { setForm((current) => ({ ...current, multiplierValues: [...(current.multiplierValues || []), ""], })); } function removeMultiplier(setForm, index) { setForm((current) => ({ ...current, multiplierValues: (current.multiplierValues || []).filter((_, itemIndex) => itemIndex !== index), })); }