refactor: 使用管理员日期时间格式化器更新请求和完成时间的显示

This commit is contained in:
2026-05-09 15:32:04 +08:00
parent 4ace3151e6
commit f19cdb48ad
3 changed files with 76 additions and 13 deletions

View File

@@ -0,0 +1,21 @@
"use client";
import { useCallback } from "react";
import { formatAdminInstant } from "@/lib/admin-datetime";
import { getAdminRequestLocale } from "@/lib/admin-locale";
/**
* 按当前界面语言 + **浏览器本地时区** 格式化时间。
*/
export function useAdminDateTimeFormatter(): (
iso: string | null | undefined,
) => string {
return useCallback(
(iso) =>
formatAdminInstant(iso, {
locale: getAdminRequestLocale(),
}),
[],
);
}

36
src/lib/admin-datetime.ts Normal file
View File

@@ -0,0 +1,36 @@
import type { AdminApiLocale } from "@/lib/admin-locale";
function pad2(n: number): string {
return String(n).padStart(2, "0");
}
/**
* 将接口返回的 ISO 时间串格式化为 **浏览器本地时区** 下的
* `YYYY-MM-DD HH:mm:ss`与《01-界面文档》时间展示习惯一致,且列宽稳定)。
*
* 不使用 `Intl` 拼接 `GMT+8` 等时区后缀,避免在表格里挤出过长、重叠的字符串。
*
* `@param options.locale` 与调用方保持一致,便于日后按界面语言微调格式。
*/
export function formatAdminInstant(
iso: string | null | undefined,
_options: {
locale: AdminApiLocale;
},
): string {
if (iso == null || iso === "") {
return "—";
}
const ms = Date.parse(iso);
if (Number.isNaN(ms)) {
return "—";
}
const date = new Date(ms);
const y = date.getFullYear();
const m = pad2(date.getMonth() + 1);
const d = pad2(date.getDate());
const h = pad2(date.getHours());
const min = pad2(date.getMinutes());
const s = pad2(date.getSeconds());
return `${y}-${m}-${d} ${h}:${min}:${s}`;
}

View File

@@ -24,6 +24,7 @@ import {
TableRow, TableRow,
} from "@/components/ui/table"; } from "@/components/ui/table";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { useAdminDateTimeFormatter } from "@/hooks/use-admin-datetime-formatter";
import { LotteryApiBizError } from "@/types/api/errors"; import { LotteryApiBizError } from "@/types/api/errors";
import type { import type {
AdminPlayerWalletsData, AdminPlayerWalletsData,
@@ -36,11 +37,6 @@ function formatMinorUnits(minor: number, currencyCode: string): string {
return `${major.toFixed(2)} ${currencyCode}`; return `${major.toFixed(2)} ${currencyCode}`;
} }
function formatTs(iso: string | null | undefined): string {
if (!iso) return "—";
return iso.replace("T", " ").slice(0, 19);
}
/** 长单号/流水号:单行截断;点击复制全文,悬停可看全文 */ /** 长单号/流水号:单行截断;点击复制全文,悬停可看全文 */
function CellMonoId({ function CellMonoId({
value, value,
@@ -164,6 +160,7 @@ export function WalletConsole(): React.ReactElement {
} }
function TransferOrdersPanel(): React.ReactElement { function TransferOrdersPanel(): React.ReactElement {
const formatTs = useAdminDateTimeFormatter();
const [data, setData] = useState<AdminTransferOrderListData | null>(null); const [data, setData] = useState<AdminTransferOrderListData | null>(null);
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
const [err, setErr] = useState<string | null>(null); const [err, setErr] = useState<string | null>(null);
@@ -324,8 +321,12 @@ function TransferOrdersPanel(): React.ReactElement {
<TableHead className="w-14"></TableHead> <TableHead className="w-14"></TableHead>
<TableHead className="whitespace-nowrap"></TableHead> <TableHead className="whitespace-nowrap"></TableHead>
<TableHead className="whitespace-nowrap"></TableHead> <TableHead className="whitespace-nowrap"></TableHead>
<TableHead className="whitespace-nowrap"></TableHead> <TableHead className="min-w-0 whitespace-normal leading-tight">
<TableHead className="whitespace-nowrap"></TableHead>
</TableHead>
<TableHead className="min-w-0 whitespace-normal leading-tight">
</TableHead>
<TableHead className="w-24"></TableHead> <TableHead className="w-24"></TableHead>
</TableRow> </TableRow>
</TableHeader> </TableHeader>
@@ -359,10 +360,10 @@ function TransferOrdersPanel(): React.ReactElement {
<TableCell> <TableCell>
<Badge variant={statusBadgeVariant(row.status)}>{row.status}</Badge> <Badge variant={statusBadgeVariant(row.status)}>{row.status}</Badge>
</TableCell> </TableCell>
<TableCell className="whitespace-nowrap text-xs text-muted-foreground"> <TableCell className="min-w-0 whitespace-normal font-mono text-[11px] leading-snug text-muted-foreground">
{formatTs(row.created_at)} {formatTs(row.created_at)}
</TableCell> </TableCell>
<TableCell className="whitespace-nowrap text-xs text-muted-foreground"> <TableCell className="min-w-0 whitespace-normal font-mono text-[11px] leading-snug text-muted-foreground">
{formatTs(row.finished_at)} {formatTs(row.finished_at)}
</TableCell> </TableCell>
<TableCell className="text-xs text-muted-foreground"></TableCell> <TableCell className="text-xs text-muted-foreground"></TableCell>
@@ -406,6 +407,7 @@ function TransferOrdersPanel(): React.ReactElement {
} }
function WalletTxnsPanel(): React.ReactElement { function WalletTxnsPanel(): React.ReactElement {
const formatTs = useAdminDateTimeFormatter();
const [data, setData] = useState<AdminWalletTxnListData | null>(null); const [data, setData] = useState<AdminWalletTxnListData | null>(null);
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
const [err, setErr] = useState<string | null>(null); const [err, setErr] = useState<string | null>(null);
@@ -576,8 +578,12 @@ function WalletTxnsPanel(): React.ReactElement {
<TableHead className="whitespace-nowrap"></TableHead> <TableHead className="whitespace-nowrap"></TableHead>
<TableHead className="whitespace-nowrap"></TableHead> <TableHead className="whitespace-nowrap"></TableHead>
<TableHead className="whitespace-nowrap"></TableHead> <TableHead className="whitespace-nowrap"></TableHead>
<TableHead className="whitespace-nowrap"></TableHead> <TableHead className="min-w-0 whitespace-normal leading-tight">
<TableHead className="whitespace-nowrap"></TableHead>
</TableHead>
<TableHead className="min-w-0 whitespace-normal leading-tight">
</TableHead>
<TableHead className="w-24"></TableHead> <TableHead className="w-24"></TableHead>
</TableRow> </TableRow>
</TableHeader> </TableHeader>
@@ -611,10 +617,10 @@ function WalletTxnsPanel(): React.ReactElement {
<TableCell> <TableCell>
<Badge variant={statusBadgeVariant(row.status)}>{row.status}</Badge> <Badge variant={statusBadgeVariant(row.status)}>{row.status}</Badge>
</TableCell> </TableCell>
<TableCell className="whitespace-nowrap text-xs text-muted-foreground"> <TableCell className="min-w-0 whitespace-normal font-mono text-[11px] leading-snug text-muted-foreground">
{formatTs(row.created_at)} {formatTs(row.created_at)}
</TableCell> </TableCell>
<TableCell className="whitespace-nowrap text-xs text-muted-foreground"> <TableCell className="min-w-0 whitespace-normal font-mono text-[11px] leading-snug text-muted-foreground">
{formatTs(row.updated_at)} {formatTs(row.updated_at)}
</TableCell> </TableCell>
<TableCell className="text-xs text-muted-foreground"></TableCell> <TableCell className="text-xs text-muted-foreground"></TableCell>