feat(admin): 完善玩家管理与角色配置

This commit is contained in:
wchino
2026-07-21 21:11:07 +08:00
parent 44fd0633d6
commit 33d887d252
22 changed files with 659 additions and 82 deletions

View File

@@ -86,12 +86,6 @@ type PlayBatchSwitchGroup = {
match: (row: PlayConfigItemRow) => boolean;
};
const TRADITIONAL_PLAY_CODES = new Set([
"big", "small", "pos_4a", "pos_4b", "pos_4c", "pos_4d", "pos_4e", "four_any", "four_top", "four_lower",
"pos_3a", "pos_3lower", "pos_3b", "pos_3c", "pos_3d", "pos_3e",
"pos_2a", "pos_2b", "pos_2c", "pos_2d", "pos_2e", "pos_2any",
]);
const PLAY_BATCH_SWITCH_GROUPS: PlayBatchSwitchGroup[] = [
{
key: "d2",
@@ -118,8 +112,8 @@ const PLAY_BATCH_SWITCH_GROUPS: PlayBatchSwitchGroup[] = [
match: (row) => row.category === "box",
},
{
key: "jackpot",
match: (row) => row.category === "jackpot" || row.play_code.includes("jackpot"),
key: "attribute",
match: (row) => row.category === "attribute",
},
];
@@ -273,7 +267,7 @@ export function PlayConfigDocScreen() {
const orderedRows = useMemo(
() =>
draftRows.filter((row) => TRADITIONAL_PLAY_CODES.has(row.play_code)).sort(
[...draftRows].sort(
(a, b) => a.display_order - b.display_order || a.play_code.localeCompare(b.play_code),
),
[draftRows],
@@ -342,6 +336,7 @@ export function PlayConfigDocScreen() {
setDraftRows((prev) =>
prev.map((row) => (group.match(row) ? { ...row, is_enabled: enabled } : row)),
);
toast.success(t("play.batchSwitchSavedLocal", { ns: "config" }));
}
const batchSwitchStates = useMemo(
@@ -582,47 +577,59 @@ export function PlayConfigDocScreen() {
) : null}
</div>
{isDraft ? (
<ConfigChipGroup label={t("play.batchSwitchesTitle", { ns: "config" })}>
{batchSwitchStates.map((group) => {
const groupOn = group.allEnabled;
const isPartial =
group.total > 0 && group.enabledCount > 0 && group.enabledCount < group.total;
return (
<label
key={group.key}
className="inline-flex cursor-pointer items-center gap-2 rounded-md border border-border/60 px-2.5 py-1.5 text-sm"
>
<span>{group.label}</span>
<Checkbox
checked={groupOn}
indeterminate={isPartial}
disabled={saving || group.total === 0 || confirmBusy}
aria-label={t("play.aria.batchGroupSwitch", {
ns: "config",
group: group.label,
})}
onCheckedChange={(checked) => {
const enable = checked === true;
const action = enable
? t("play.batchSwitchEnable", { ns: "config" })
: t("play.batchSwitchDisable", { ns: "config" });
requestConfirm({
title: t("play.batchSwitchConfirmTitle", { ns: "config", action }),
description: t("play.batchSwitchConfirmDescription", {
ns: "config",
action,
group: group.label,
count: group.total,
}),
confirmVariant: enable ? "default" : "destructive",
onConfirm: () => applyBatchSwitch(group, enable),
});
}}
/>
</label>
);
})}
</ConfigChipGroup>
<div className="space-y-1.5">
<ConfigChipGroup label={t("play.batchSwitchesTitle", { ns: "config" })}>
{batchSwitchStates.map((group) => {
const groupOn = group.allEnabled;
const isPartial =
group.total > 0 && group.enabledCount > 0 && group.enabledCount < group.total;
return (
<label
key={group.key}
className="inline-flex cursor-pointer items-center gap-2 rounded-md border border-border/60 px-2.5 py-1.5 text-sm"
>
<span>{group.label}</span>
<span className="text-xs tabular-nums text-muted-foreground">
{t("play.batchEnabledCount", {
ns: "config",
enabledCount: group.enabledCount,
total: group.total,
})}
</span>
<Checkbox
checked={groupOn}
indeterminate={isPartial}
disabled={saving || group.total === 0 || confirmBusy}
aria-label={t("play.aria.batchGroupSwitch", {
ns: "config",
group: group.label,
})}
onCheckedChange={(checked) => {
const enable = checked === true;
const action = enable
? t("play.batchSwitchEnable", { ns: "config" })
: t("play.batchSwitchDisable", { ns: "config" });
requestConfirm({
title: t("play.batchSwitchConfirmTitle", { ns: "config", action }),
description: t("play.batchSwitchConfirmDescription", {
ns: "config",
action,
group: group.label,
count: group.total,
}),
confirmVariant: enable ? "default" : "destructive",
onConfirm: () => applyBatchSwitch(group, enable),
});
}}
/>
</label>
);
})}
</ConfigChipGroup>
<p className="text-xs text-muted-foreground">
{t("play.batchSwitchesDesc", { ns: "config" })}
</p>
</div>
) : null}
</div>
) : null}