feat(config): 重构配置模块导航与版本切换,新增版本删除能力

This commit is contained in:
2026-05-15 15:30:52 +08:00
parent 000295ae2b
commit 8bd7cc3d73
20 changed files with 669 additions and 377 deletions

View File

@@ -4,6 +4,7 @@ import { useCallback, useEffect, useMemo, useState } from "react";
import { toast } from "sonner";
import {
deleteOddsVersion,
getAdminPlayTypes,
getOddsVersion,
getOddsVersions,
@@ -23,21 +24,7 @@ import {
} from "@/components/ui/dialog";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import {
Sheet,
SheetContent,
SheetDescription,
SheetHeader,
SheetTitle,
} from "@/components/ui/sheet";
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table";
import { ConfigVersionSwitcher } from "@/modules/config/config-version-switcher";
import { cn } from "@/lib/utils";
import { useAdminDateTimeFormatter } from "@/hooks/use-admin-datetime-formatter";
import { LotteryApiBizError } from "@/types/api/errors";
@@ -91,7 +78,6 @@ export function OddsConfigDocScreen() {
const [rollbackOpen, setRollbackOpen] = useState(false);
const [rollbackTarget, setRollbackTarget] = useState<ConfigVersionSummary | null>(null);
const [historyOpen, setHistoryOpen] = useState(false);
const refreshTypes = useCallback(async () => {
setLoadingTypes(true);
@@ -332,6 +318,22 @@ export function OddsConfigDocScreen() {
const activeHead = list.find((x) => x.status === "active");
async function handleDeleteVersion(row: ConfigVersionSummary) {
try {
await deleteOddsVersion(row.id);
toast.success("已删除该版本");
await refreshList();
} catch (e) {
toast.error(e instanceof LotteryApiBizError ? e.message : "删除失败");
throw e;
}
}
function requestRollback(row: ConfigVersionSummary) {
setRollbackTarget(row);
setRollbackOpen(true);
}
const catTabs: { id: CatTab; label: string }[] = [
{ id: "all", label: "全部" },
{ id: "d4", label: "4D" },
@@ -340,8 +342,6 @@ export function OddsConfigDocScreen() {
{ id: "jackpot", label: "Jackpot" },
];
const sortedHistory = useMemo(() => [...list].sort((a, b) => b.id - a.id), [list]);
return (
<Card>
<CardHeader className="space-y-1">
@@ -354,7 +354,6 @@ export function OddsConfigDocScreen() {
<Button
key={t.id}
type="button"
size="sm"
variant={catTab === t.id ? "default" : "outline"}
className={cn(catTab === t.id && "shadow-sm")}
onClick={() => {
@@ -377,7 +376,6 @@ export function OddsConfigDocScreen() {
<Button
key={t.play_code}
type="button"
size="sm"
variant={resolvedPlayCode === t.play_code ? "secondary" : "outline"}
onClick={() => setPlayCode(t.play_code)}
>
@@ -388,17 +386,28 @@ export function OddsConfigDocScreen() {
</div>
</div>
<ConfigVersionSwitcher
versions={list}
selectedId={selectedId}
onSelectedIdChange={setSelectedId}
loading={loadingList}
sheetTitle="赔率配置版本"
sheetDescription="选择版本在本页查看;非草稿版本可回滚为新建草稿。"
onDeleteVersion={handleDeleteVersion}
onRollbackVersion={requestRollback}
rollbackBusy={saving}
/>
<div className="flex flex-wrap items-center gap-2">
<Button
type="button"
variant="secondary"
size="sm"
disabled={loadingList}
onClick={() => void refreshList()}
>
</Button>
<Button type="button" size="sm" onClick={() => void handleNewDraft()} disabled={saving}>
<Button type="button" onClick={() => void handleNewDraft()} disabled={saving}>
稿
</Button>
</div>
@@ -483,59 +492,6 @@ export function OddsConfigDocScreen() {
) : null}
<div className="flex flex-wrap gap-2 pt-2">
<Button type="button" variant="outline" size="sm" onClick={() => setHistoryOpen(true)}>
</Button>
<Sheet open={historyOpen} onOpenChange={setHistoryOpen}>
<SheetContent side="right" className="sm:max-w-lg flex flex-col">
<SheetHeader>
<SheetTitle></SheetTitle>
<SheetDescription>稿</SheetDescription>
</SheetHeader>
<div className="flex-1 overflow-auto rounded-md border mt-4">
<Table>
<TableHeader>
<TableRow>
<TableHead></TableHead>
<TableHead></TableHead>
<TableHead></TableHead>
<TableHead className="w-[100px]"></TableHead>
</TableRow>
</TableHeader>
<TableBody>
{sortedHistory.map((v) => (
<TableRow key={v.id}>
<TableCell className="font-mono text-xs">v{v.version_no}</TableCell>
<TableCell className="text-xs">
{v.status === "active" ? "生效" : v.status === "draft" ? "草稿" : "归档"}
</TableCell>
<TableCell className="text-xs text-muted-foreground whitespace-nowrap">
{v.updated_at ? formatDt(v.updated_at) : "—"}
</TableCell>
<TableCell>
<Button
type="button"
variant="ghost"
size="sm"
className="text-xs"
disabled={saving || v.status === "draft"}
onClick={() => {
setRollbackTarget(v);
setRollbackOpen(true);
setHistoryOpen(false);
}}
>
</Button>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</div>
</SheetContent>
</Sheet>
<Button type="button" onClick={() => void handleSave()} disabled={!isDraft || saving || loadingDetail}>
</Button>