feat: 增加管理端多语言与多模块界面国际化支持
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
export const reportsModuleMeta = {
|
||||
segment: "reports",
|
||||
title: "报表",
|
||||
title: "Reports",
|
||||
description: "",
|
||||
} as const;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { toast } from "sonner";
|
||||
|
||||
import {
|
||||
@@ -34,20 +35,21 @@ import { LotteryApiBizError } from "@/types/api/errors";
|
||||
import type { AdminReportJobListData } from "@/types/api/admin-reports";
|
||||
|
||||
const REPORT_TYPES = [
|
||||
{ value: "draw_profit_summary", label: "期号盈亏" },
|
||||
{ value: "daily_profit_summary", label: "每日盈亏汇总" },
|
||||
{ value: "player_win_loss", label: "玩家输赢报表" },
|
||||
{ value: "wallet_transfer_report", label: "玩家转入转出报表" },
|
||||
{ value: "hot_number_risk_report", label: "热门号码风险报表" },
|
||||
{ value: "play_dimension_report", label: "玩法维度报表" },
|
||||
{ value: "sold_out_number_report", label: "售罄号码报表" },
|
||||
{ value: "rebate_commission_report", label: "佣金回水报表" },
|
||||
{ value: "audit_operation_report", label: "后台操作审计报表" },
|
||||
{ value: "wallet_txns_daily", label: "钱包流水日报" },
|
||||
{ value: "transfer_orders_daily", label: "转账单日报" },
|
||||
{ value: "draw_profit_summary" },
|
||||
{ value: "daily_profit_summary" },
|
||||
{ value: "player_win_loss" },
|
||||
{ value: "wallet_transfer_report" },
|
||||
{ value: "hot_number_risk_report" },
|
||||
{ value: "play_dimension_report" },
|
||||
{ value: "sold_out_number_report" },
|
||||
{ value: "rebate_commission_report" },
|
||||
{ value: "audit_operation_report" },
|
||||
{ value: "wallet_txns_daily" },
|
||||
{ value: "transfer_orders_daily" },
|
||||
] as const;
|
||||
|
||||
export function ReportsConsole(): React.ReactElement {
|
||||
const { t } = useTranslation(["reports", "common"]);
|
||||
const formatTs = useAdminDateTimeFormatter();
|
||||
const [data, setData] = useState<AdminReportJobListData | null>(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
@@ -67,12 +69,12 @@ export function ReportsConsole(): React.ReactElement {
|
||||
const d = await getAdminReportJobs({ page, per_page: perPage });
|
||||
setData(d);
|
||||
} catch (e) {
|
||||
setErr(e instanceof LotteryApiBizError ? e.message : "加载失败");
|
||||
setErr(e instanceof LotteryApiBizError ? e.message : t("errors.loadFailed", { ns: "common" }));
|
||||
setData(null);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, [page, perPage]);
|
||||
}, [page, perPage, t]);
|
||||
|
||||
useEffect(() => {
|
||||
queueMicrotask(() => {
|
||||
@@ -87,7 +89,7 @@ export function ReportsConsole(): React.ReactElement {
|
||||
try {
|
||||
filter_json = JSON.parse(trimmed) as Record<string, unknown>;
|
||||
} catch {
|
||||
toast.error("筛选 JSON 无法解析");
|
||||
toast.error(t("parseFilterFailed"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -99,11 +101,11 @@ export function ReportsConsole(): React.ReactElement {
|
||||
parameters: filter_json,
|
||||
filter_json,
|
||||
});
|
||||
toast.success("已创建导出任务");
|
||||
toast.success(t("createSuccess"));
|
||||
setPage(1);
|
||||
await load();
|
||||
} catch (e) {
|
||||
toast.error(e instanceof LotteryApiBizError ? e.message : "创建失败");
|
||||
toast.error(e instanceof LotteryApiBizError ? e.message : t("createFailed"));
|
||||
} finally {
|
||||
setSubmitting(false);
|
||||
}
|
||||
@@ -119,7 +121,7 @@ export function ReportsConsole(): React.ReactElement {
|
||||
a.click();
|
||||
URL.revokeObjectURL(url);
|
||||
} catch {
|
||||
toast.error("下载失败");
|
||||
toast.error(t("downloadFailed"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -132,11 +134,11 @@ export function ReportsConsole(): React.ReactElement {
|
||||
<div className="flex w-full max-w-none flex-col gap-8">
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>新建导出</CardTitle>
|
||||
<CardTitle>{t("createExport")}</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
|
||||
<div className="grid gap-1.5">
|
||||
<Label>报表类型</Label>
|
||||
<Label>{t("reportType")}</Label>
|
||||
<Select
|
||||
modal={false}
|
||||
value={reportType}
|
||||
@@ -152,14 +154,14 @@ export function ReportsConsole(): React.ReactElement {
|
||||
<SelectContent>
|
||||
{REPORT_TYPES.map((o) => (
|
||||
<SelectItem key={o.value} value={o.value}>
|
||||
{o.label}
|
||||
{t(`reportTypes.${o.value}`)}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<div className="grid gap-1.5">
|
||||
<Label>导出格式</Label>
|
||||
<Label>{t("exportFormat")}</Label>
|
||||
<Select
|
||||
modal={false}
|
||||
value={exportFormat}
|
||||
@@ -179,7 +181,7 @@ export function ReportsConsole(): React.ReactElement {
|
||||
</Select>
|
||||
</div>
|
||||
<div className="sm:col-span-2 lg:col-span-3 grid gap-1.5">
|
||||
<Label htmlFor="report-filter-json">filter_json(可选)</Label>
|
||||
<Label htmlFor="report-filter-json">{t("filterJson")}</Label>
|
||||
<Textarea
|
||||
id="report-filter-json"
|
||||
value={filterJsonText}
|
||||
@@ -190,7 +192,7 @@ export function ReportsConsole(): React.ReactElement {
|
||||
</div>
|
||||
<div className="sm:col-span-2 lg:col-span-3">
|
||||
<Button type="button" onClick={() => void onCreate()} disabled={submitting}>
|
||||
{submitting ? "提交中…" : "创建任务"}
|
||||
{submitting ? t("actions.submitting", { ns: "common" }) : t("actions.createTask", { ns: "common" })}
|
||||
</Button>
|
||||
</div>
|
||||
</CardContent>
|
||||
@@ -199,16 +201,16 @@ export function ReportsConsole(): React.ReactElement {
|
||||
<Card>
|
||||
<CardHeader className="flex flex-row flex-wrap items-end justify-between gap-4">
|
||||
<div>
|
||||
<CardTitle>任务列表</CardTitle>
|
||||
<CardTitle>{t("taskList")}</CardTitle>
|
||||
</div>
|
||||
<Button type="button" variant="secondary" size="sm" onClick={() => void load()}>
|
||||
刷新
|
||||
{t("actions.refresh", { ns: "common" })}
|
||||
</Button>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4">
|
||||
{err ? <p className="text-sm text-red-600 dark:text-red-400">{err}</p> : null}
|
||||
{loading && !data ? (
|
||||
<p className="text-muted-foreground text-sm">加载中…</p>
|
||||
<p className="text-muted-foreground text-sm">{t("states.loading", { ns: "common" })}</p>
|
||||
) : null}
|
||||
{data ? (
|
||||
<>
|
||||
@@ -216,21 +218,21 @@ export function ReportsConsole(): React.ReactElement {
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead className="w-24">ID</TableHead>
|
||||
<TableHead>任务号</TableHead>
|
||||
<TableHead>类型</TableHead>
|
||||
<TableHead>格式</TableHead>
|
||||
<TableHead>状态</TableHead>
|
||||
<TableHead>输出</TableHead>
|
||||
<TableHead>下载</TableHead>
|
||||
<TableHead>创建时间</TableHead>
|
||||
<TableHead className="w-24">{t("id")}</TableHead>
|
||||
<TableHead>{t("jobId")}</TableHead>
|
||||
<TableHead>{t("type")}</TableHead>
|
||||
<TableHead>{t("format")}</TableHead>
|
||||
<TableHead>{t("status")}</TableHead>
|
||||
<TableHead>{t("output")}</TableHead>
|
||||
<TableHead>{t("download")}</TableHead>
|
||||
<TableHead>{t("createdAt")}</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{data.items.length === 0 ? (
|
||||
<TableRow>
|
||||
<TableCell colSpan={8} className="text-muted-foreground">
|
||||
无数据
|
||||
{t("empty")}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
) : (
|
||||
@@ -238,7 +240,11 @@ export function ReportsConsole(): React.ReactElement {
|
||||
<TableRow key={row.id}>
|
||||
<TableCell className="tabular-nums">{row.id}</TableCell>
|
||||
<TableCell className="font-mono text-xs">{row.job_no}</TableCell>
|
||||
<TableCell className="text-sm">{row.report_type}</TableCell>
|
||||
<TableCell className="text-sm">
|
||||
{t(`reportTypes.${row.report_type}`, {
|
||||
defaultValue: row.report_type,
|
||||
})}
|
||||
</TableCell>
|
||||
<TableCell>{row.export_format}</TableCell>
|
||||
<TableCell>
|
||||
<Badge variant="secondary">{row.status}</Badge>
|
||||
@@ -253,7 +259,7 @@ export function ReportsConsole(): React.ReactElement {
|
||||
size="sm"
|
||||
onClick={() => void onDownload(row.id)}
|
||||
>
|
||||
下载
|
||||
{t("actions.download", { ns: "common" })}
|
||||
</Button>
|
||||
</TableCell>
|
||||
<TableCell className="whitespace-nowrap font-mono text-[11px] text-muted-foreground">
|
||||
|
||||
Reference in New Issue
Block a user