refactor: 使用管理员日期时间格式化器更新请求和完成时间的显示
This commit is contained in:
21
src/hooks/use-admin-datetime-formatter.ts
Normal file
21
src/hooks/use-admin-datetime-formatter.ts
Normal 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
36
src/lib/admin-datetime.ts
Normal 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}`;
|
||||||
|
}
|
||||||
@@ -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>
|
||||||
|
|||||||
Reference in New Issue
Block a user