feat: 增加角色管理与奖池配置迁移,优化管理端权限与样式
This commit is contained in:
@@ -26,7 +26,12 @@ 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";
|
||||
import {
|
||||
PRD_DRAW_REOPEN_MANAGE,
|
||||
PRD_DRAW_RESULT_MANAGE,
|
||||
PRD_PAYOUT_MANAGE,
|
||||
PRD_PAYOUT_REVIEW,
|
||||
} from "./draw-prd";
|
||||
|
||||
function Field({ label, children }: { label: string; children: React.ReactNode }) {
|
||||
return (
|
||||
@@ -42,7 +47,11 @@ 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 canReopenDraw = adminHasAnyPermission(profile?.permissions, [PRD_DRAW_REOPEN_MANAGE]);
|
||||
const canRunSettlement = adminHasAnyPermission(profile?.permissions, [
|
||||
PRD_PAYOUT_MANAGE,
|
||||
PRD_PAYOUT_REVIEW,
|
||||
]);
|
||||
const formatDt = useAdminDateTimeFormatter();
|
||||
const [data, setData] = useState<AdminDrawShowData | null>(null);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
@@ -159,17 +168,16 @@ export function DrawDetailConsole({ drawId }: { drawId: string }) {
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle className="text-base">{t("drawActions")}</CardTitle>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{t("drawActionsDesc")}
|
||||
</p>
|
||||
</CardHeader>
|
||||
<CardContent className="flex flex-wrap gap-2">
|
||||
{(canManageDraw || canReopenDraw || canRunSettlement) ? (
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle className="text-base">{t("drawActions")}</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="flex flex-wrap gap-2">
|
||||
<Button
|
||||
type="button"
|
||||
variant="secondary"
|
||||
variant="outline"
|
||||
size="sm"
|
||||
disabled={!canManageDraw || acting !== null || !["pending", "open"].includes(data.status)}
|
||||
onClick={() => void runAction(t("manualClose"), () => postAdminManualCloseDraw(idNum))}
|
||||
>
|
||||
@@ -178,6 +186,7 @@ export function DrawDetailConsole({ drawId }: { drawId: string }) {
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
size="sm"
|
||||
disabled={!canManageDraw || acting !== null || !["pending", "open", "closing", "closed"].includes(data.status)}
|
||||
onClick={() => void runAction(t("cancelDraw"), () => postAdminCancelDraw(idNum))}
|
||||
>
|
||||
@@ -185,15 +194,18 @@ export function DrawDetailConsole({ drawId }: { drawId: string }) {
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
size="sm"
|
||||
disabled={!canManageDraw || acting !== null || data.status !== "closed"}
|
||||
onClick={() => void runAction(t("rngDraw"), () => postAdminRunDrawRng(idNum))}
|
||||
>
|
||||
{acting === t("rngDraw") ? t("generating") : t("rngAutoGenerate")}
|
||||
</Button>
|
||||
{isSuperAdmin ? (
|
||||
{canReopenDraw ? (
|
||||
<Button
|
||||
type="button"
|
||||
variant="destructive"
|
||||
size="sm"
|
||||
disabled={acting !== null || data.status !== "cooldown"}
|
||||
onClick={() => void runAction(t("reopen"), () => postAdminReopenDraw(idNum))}
|
||||
>
|
||||
@@ -203,13 +215,15 @@ export function DrawDetailConsole({ drawId }: { drawId: string }) {
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
disabled={acting !== null || !["settling", "cooldown"].includes(data.status)}
|
||||
size="sm"
|
||||
disabled={!canRunSettlement || acting !== null || data.status !== "settling"}
|
||||
onClick={() => void runAction(t("runSettlement"), () => postAdminRunDrawSettlement(idNum))}
|
||||
>
|
||||
{acting === t("runSettlement") ? t("processing") : t("runSettlement")}
|
||||
</Button>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</CardContent>
|
||||
</Card>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -16,14 +16,23 @@ import {
|
||||
TableHeader,
|
||||
TableRow,
|
||||
} from "@/components/ui/table";
|
||||
import { adminHasAnyPermission } from "@/lib/admin-permissions";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { useAdminProfile } from "@/stores/admin-session";
|
||||
import { LotteryApiBizError } from "@/types/api/errors";
|
||||
import type { AdminDrawFinanceSummaryData } from "@/types/api/admin-draw-finance";
|
||||
import { toast } from "sonner";
|
||||
|
||||
import { PRD_PAYOUT_MANAGE, PRD_PAYOUT_REVIEW } from "./draw-prd";
|
||||
|
||||
export function DrawFinanceConsole({ drawId }: { drawId: string }): React.ReactElement {
|
||||
const { t } = useTranslation(["draws", "common"]);
|
||||
const idNum = Number(drawId);
|
||||
const profile = useAdminProfile();
|
||||
const canRunSettlement = adminHasAnyPermission(profile?.permissions, [
|
||||
PRD_PAYOUT_MANAGE,
|
||||
PRD_PAYOUT_REVIEW,
|
||||
]);
|
||||
const [data, setData] = useState<AdminDrawFinanceSummaryData | null>(null);
|
||||
const [err, setErr] = useState<string | null>(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
@@ -122,7 +131,12 @@ export function DrawFinanceConsole({ drawId }: { drawId: string }): React.ReactE
|
||||
<Button type="button" variant="secondary" size="sm" onClick={() => void load()}>
|
||||
{t("actions.refresh", { ns: "common" })}
|
||||
</Button>
|
||||
<Button type="button" size="sm" disabled={settling} onClick={() => void runSettlement()}>
|
||||
<Button
|
||||
type="button"
|
||||
size="sm"
|
||||
disabled={!canRunSettlement || settling || data.draw_status !== "settling"}
|
||||
onClick={() => void runSettlement()}
|
||||
>
|
||||
{settling ? t("processing") : t("runSettlement")}
|
||||
</Button>
|
||||
<Link
|
||||
|
||||
@@ -1,2 +1,5 @@
|
||||
/** 开奖结果发布权限 slug */
|
||||
export const PRD_DRAW_RESULT_MANAGE = "prd.draw_result.manage" as const;
|
||||
export const PRD_DRAW_REOPEN_MANAGE = "prd.draw_reopen.manage" as const;
|
||||
export const PRD_PAYOUT_MANAGE = "prd.payout.manage" as const;
|
||||
export const PRD_PAYOUT_REVIEW = "prd.payout.review" as const;
|
||||
|
||||
@@ -27,10 +27,13 @@ import {
|
||||
TableRow,
|
||||
} from "@/components/ui/table";
|
||||
import { useAdminDateTimeFormatter } from "@/hooks/use-admin-datetime-formatter";
|
||||
import { adminHasAnyPermission } from "@/lib/admin-permissions";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { useAdminProfile } from "@/stores/admin-session";
|
||||
import { LotteryApiBizError } from "@/types/api/errors";
|
||||
import type { AdminDrawListData, AdminDrawListItem } from "@/types/api/admin-draws";
|
||||
|
||||
import { PRD_DRAW_RESULT_MANAGE } from "./draw-prd";
|
||||
import { DrawStatusBadge } from "./draw-status-badge";
|
||||
|
||||
/** 下拉「不限」;请求时不传 status */
|
||||
@@ -62,6 +65,8 @@ function drawAdminStatusSelectLabel(raw: unknown, t: (key: string) => string): s
|
||||
export function DrawsIndexConsole() {
|
||||
const { t } = useTranslation(["draws", "common"]);
|
||||
const formatDt = useAdminDateTimeFormatter();
|
||||
const profile = useAdminProfile();
|
||||
const canManageDraw = adminHasAnyPermission(profile?.permissions, [PRD_DRAW_RESULT_MANAGE]);
|
||||
const [data, setData] = useState<AdminDrawListData | null>(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
@@ -138,9 +143,11 @@ export function DrawsIndexConsole() {
|
||||
<Card>
|
||||
<CardHeader className="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
|
||||
<CardTitle className="text-lg">{t("statusListTitle")}</CardTitle>
|
||||
<Button type="button" onClick={() => void generatePlan()} disabled={generating}>
|
||||
{generating ? t("generating") : t("generatePlan")}
|
||||
</Button>
|
||||
{canManageDraw ? (
|
||||
<Button type="button" onClick={() => void generatePlan()} disabled={generating}>
|
||||
{generating ? t("generating") : t("generatePlan")}
|
||||
</Button>
|
||||
) : null}
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4">
|
||||
{/* Grid:桌面端标签一行 / 控件一行,避免 flex+items-end 与各列实际高度不一致;移动端单列自上而下 */}
|
||||
|
||||
Reference in New Issue
Block a user