feat: 扩展开奖与结算管理,支持手动操作、导出和版本展示
This commit is contained in:
@@ -2,19 +2,30 @@
|
||||
|
||||
import Link from "next/link";
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import { toast } from "sonner";
|
||||
|
||||
import { getAdminDraw } from "@/api/admin-draws";
|
||||
import { buttonVariants } from "@/components/ui/button";
|
||||
import {
|
||||
getAdminDraw,
|
||||
postAdminCancelDraw,
|
||||
postAdminManualCloseDraw,
|
||||
postAdminReopenDraw,
|
||||
postAdminRunDrawRng,
|
||||
} from "@/api/admin-draws";
|
||||
import { postAdminRunDrawSettlement } from "@/api/admin-settlement";
|
||||
import { Button, buttonVariants } from "@/components/ui/button";
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { Separator } from "@/components/ui/separator";
|
||||
import { useAdminDateTimeFormatter } from "@/hooks/use-admin-datetime-formatter";
|
||||
import { LotteryApiBizError } from "@/types/api/errors";
|
||||
import type { AdminDrawShowData } from "@/types/api/admin-draws";
|
||||
import { adminHasAnyPermission } from "@/lib/admin-permissions";
|
||||
import { useAdminProfile } from "@/stores/admin-session";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
import { DrawStatusBadge } from "./draw-status-badge";
|
||||
import { PRD_DRAW_RESULT_MANAGE } from "./draw-prd";
|
||||
|
||||
function Field({ label, children }: { label: string; children: React.ReactNode }) {
|
||||
return (
|
||||
@@ -27,10 +38,14 @@ function Field({ label, children }: { label: string; children: React.ReactNode }
|
||||
|
||||
export function DrawDetailConsole({ drawId }: { drawId: string }) {
|
||||
const idNum = Number(drawId);
|
||||
const profile = useAdminProfile();
|
||||
const canManageDraw = adminHasAnyPermission(profile?.permissions, [PRD_DRAW_RESULT_MANAGE]);
|
||||
const isSuperAdmin = profile?.permissions?.includes("prd.admin_user.manage") ?? false;
|
||||
const formatDt = useAdminDateTimeFormatter();
|
||||
const [data, setData] = useState<AdminDrawShowData | null>(null);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [acting, setActing] = useState<string | null>(null);
|
||||
|
||||
const load = useCallback(async () => {
|
||||
if (!Number.isFinite(idNum)) {
|
||||
@@ -50,6 +65,20 @@ export function DrawDetailConsole({ drawId }: { drawId: string }) {
|
||||
}
|
||||
}, [idNum]);
|
||||
|
||||
async function runAction(name: string, action: () => Promise<unknown>): Promise<void> {
|
||||
if (!Number.isFinite(idNum)) return;
|
||||
setActing(name);
|
||||
try {
|
||||
await action();
|
||||
toast.success(`${name}成功`);
|
||||
await load();
|
||||
} catch (e) {
|
||||
toast.error(e instanceof LotteryApiBizError ? e.message : `${name}失败`);
|
||||
} finally {
|
||||
setActing(null);
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
const timer = window.setTimeout(() => {
|
||||
void load();
|
||||
@@ -124,6 +153,58 @@ export function DrawDetailConsole({ drawId }: { drawId: string }) {
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle className="text-base">期号操作</CardTitle>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
手动封盘 / 取消 / RNG / 重开 / 触发结算均直接调用后台接口。
|
||||
</p>
|
||||
</CardHeader>
|
||||
<CardContent className="flex flex-wrap gap-2">
|
||||
<Button
|
||||
type="button"
|
||||
variant="secondary"
|
||||
disabled={!canManageDraw || acting !== null || !["pending", "open"].includes(data.status)}
|
||||
onClick={() => void runAction("手动封盘", () => postAdminManualCloseDraw(idNum))}
|
||||
>
|
||||
{acting === "手动封盘" ? "处理中…" : "手动封盘"}
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
disabled={!canManageDraw || acting !== null || !["pending", "open", "closing", "closed"].includes(data.status)}
|
||||
onClick={() => void runAction("取消期号", () => postAdminCancelDraw(idNum))}
|
||||
>
|
||||
{acting === "取消期号" ? "处理中…" : "未开奖前取消"}
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
disabled={!canManageDraw || acting !== null || data.status !== "closed"}
|
||||
onClick={() => void runAction("RNG开奖", () => postAdminRunDrawRng(idNum))}
|
||||
>
|
||||
{acting === "RNG开奖" ? "生成中…" : "RNG 自动生成"}
|
||||
</Button>
|
||||
{isSuperAdmin ? (
|
||||
<Button
|
||||
type="button"
|
||||
variant="destructive"
|
||||
disabled={acting !== null || data.status !== "cooldown"}
|
||||
onClick={() => void runAction("重开", () => postAdminReopenDraw(idNum))}
|
||||
>
|
||||
{acting === "重开" ? "处理中…" : "冷静期重开"}
|
||||
</Button>
|
||||
) : null}
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
disabled={acting !== null || !["settling", "cooldown"].includes(data.status)}
|
||||
onClick={() => void runAction("触发结算", () => postAdminRunDrawSettlement(idNum))}
|
||||
>
|
||||
{acting === "触发结算" ? "处理中…" : "触发结算"}
|
||||
</Button>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user