feat: 增加管理端多语言与风控/报表/奖池操作能力

This commit is contained in:
2026-05-18 15:08:34 +08:00
parent afffa4e508
commit 49a4caf01e
31 changed files with 918 additions and 115 deletions

View File

@@ -81,6 +81,8 @@ export function OddsConfigDocScreen() {
const [rollbackOpen, setRollbackOpen] = useState(false);
const [rollbackTarget, setRollbackTarget] = useState<ConfigVersionSummary | null>(null);
const [publishConfirmOpen, setPublishConfirmOpen] = useState(false);
const [activeCompareRows, setActiveCompareRows] = useState<OddsItemRow[]>([]);
const refreshTypes = useCallback(async () => {
setLoadingTypes(true);
@@ -281,6 +283,24 @@ export function OddsConfigDocScreen() {
}
}
async function requestPublishConfirm() {
if (!detail || !isDraft) {
return;
}
const active = list.find((x) => x.status === "active");
if (active && active.id !== detail.id) {
try {
const d = await getOddsVersion(active.id);
setActiveCompareRows(d.items);
} catch {
setActiveCompareRows([]);
}
} else {
setActiveCompareRows([]);
}
setPublishConfirmOpen(true);
}
async function handleNewDraft() {
setSaving(true);
try {
@@ -343,6 +363,25 @@ export function OddsConfigDocScreen() {
setRollbackOpen(true);
}
const publishDiffRows = useMemo(() => {
if (!detail) {
return [];
}
const selectedPlay = resolvedPlayCode;
return PRIZE_SCOPE_ORDER.map((scope) => {
const next = draftRows.find((r) => r.play_code === selectedPlay && r.prize_scope === scope);
const old = activeCompareRows.find((r) => r.play_code === selectedPlay && r.prize_scope === scope);
return {
scope,
label: PRIZE_SCOPE_LABELS[scope],
oldValue: old?.odds_value ?? null,
newValue: next?.odds_value ?? null,
};
});
}, [activeCompareRows, detail, draftRows, resolvedPlayCode]);
const catTabs: { id: CatTab; label: string }[] = [
{ id: "all", label: "全部" },
{ id: "d4", label: "4D" },
@@ -421,7 +460,7 @@ export function OddsConfigDocScreen() {
onRefresh={() => void refreshList()}
onNewDraft={() => void handleNewDraft()}
onSaveDraft={() => void handleSave()}
onPublish={() => void handlePublish()}
onPublish={() => void requestPublishConfirm()}
/>
</div>
</div>
@@ -532,6 +571,48 @@ export function OddsConfigDocScreen() {
</DialogFooter>
</DialogContent>
</Dialog>
<Dialog open={publishConfirmOpen} onOpenChange={setPublishConfirmOpen}>
<DialogContent showCloseButton className="sm:max-w-lg">
<DialogHeader>
<DialogTitle></DialogTitle>
<DialogDescription>
</DialogDescription>
</DialogHeader>
<div className="rounded-lg border">
<div className="grid grid-cols-3 border-b bg-muted/40 px-3 py-2 text-sm font-medium">
<span></span>
<span className="text-right"></span>
<span className="text-right"></span>
</div>
{publishDiffRows.map((row) => (
<div key={row.scope} className="grid grid-cols-3 px-3 py-2 text-sm">
<span>{row.label}</span>
<span className="text-right font-mono tabular-nums">
{row.oldValue === null ? "—" : row.oldValue}
</span>
<span className="text-right font-mono tabular-nums">{row.newValue ?? "—"}</span>
</div>
))}
</div>
<DialogFooter>
<Button type="button" variant="outline" onClick={() => setPublishConfirmOpen(false)}>
</Button>
<Button
type="button"
disabled={saving}
onClick={() => {
setPublishConfirmOpen(false);
void handlePublish();
}}
>
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
</Card>
);
}