"use client"; import { Suspense, useEffect, useState } from "react"; import { useQuery } from "@tanstack/react-query"; import { Button, Chip, Modal, Pagination, Spinner, Table } from "@heroui/react"; import Link from "next/link"; import { useSearchParams } from "next/navigation"; import { TrashBin, ArrowRotateRight } from "@gravity-ui/icons"; import { api } from "@/lib/api"; import { useSession } from "@/lib/auth-client"; import dayjs from "@/lib/dayjs"; const LIMIT = 15; const idTagRejectLabelMap: Record = { Blocked: "卡片不可用或余额不足", Expired: "卡片已过期", Invalid: "卡片无效", ConcurrentTx: "该卡已有进行中的订单", }; const stopReasonLabelMap: Record = { EmergencyStop: "紧急停止", EVDisconnected: "车辆断开", HardReset: "硬重启", Local: "本地结束", Other: "其他原因", PowerLoss: "断电结束", Reboot: "重启结束", Remote: "远程结束", SoftReset: "软重启", UnlockCommand: "解锁结束", }; const stopReasonColorMap: Record = { Local: "success", EVDisconnected: "success", Remote: "warning", UnlockCommand: "warning", EmergencyStop: "danger", PowerLoss: "danger", HardReset: "danger", SoftReset: "warning", Reboot: "warning", Other: "default", }; function formatDuration(start: string, stop: string | null): string { if (!stop) return "进行中"; const min = dayjs(stop).diff(dayjs(start), "minute"); if (min < 60) return `${min} 分钟`; const h = Math.floor(min / 60); const m = min % 60; return `${h}h ${m}m`; } function TransactionsPageContent() { const searchParams = useSearchParams(); const { data: sessionData } = useSession(); const isAdmin = sessionData?.user?.role === "admin"; const statusFromQuery = searchParams.get("status"); const initialStatus: "all" | "active" | "completed" = statusFromQuery === "active" || statusFromQuery === "completed" ? statusFromQuery : "all"; const [page, setPage] = useState(1); const [status, setStatus] = useState<"all" | "active" | "completed">(initialStatus); const [stoppingId, setStoppingId] = useState(null); const [deletingId, setDeletingId] = useState(null); useEffect(() => { if (status !== initialStatus) { setStatus(initialStatus); setPage(1); } // We intentionally depend on searchParams-derived value only. // eslint-disable-next-line react-hooks/exhaustive-deps }, [initialStatus]); const { data, isPending: loading, isFetching: refreshing, refetch, } = useQuery({ queryKey: ["transactions", page, status], queryFn: () => api.transactions.list({ page, limit: LIMIT, status: status === "all" ? undefined : status, }), refetchInterval: 3_000, }); const handleStatusChange = (s: typeof status) => { setStatus(s); setPage(1); }; const handleStop = async (id: number) => { setStoppingId(id); try { await api.transactions.stop(id); await refetch(); } finally { setStoppingId(null); } }; const handleDelete = async (id: number) => { setDeletingId(id); try { await api.transactions.delete(id); await refetch(); } finally { setDeletingId(null); } }; const pages = data ? Array.from({ length: data.totalPages }, (_, i) => i + 1) : []; return (

充电记录

共 {data?.total ?? "—"} 条

{(["all", "active", "completed"] as const).map((s) => ( ))}{" "}
{" "}
ID 充电桩 接口 储值卡 状态 开始时间 时长 电量 (kWh) 费用 (元) 停止原因 {""} (
{loading ? "加载中…" : "暂无记录"}
)} > {(data?.data ?? []).map((tx) => ( {tx.id} {tx.chargePointIdentifier ?? "—"} {tx.connectorNumber ?? "—"} {tx.idTag} {tx.stopTimestamp && tx.stopReason === "DeAuthorized" ? ( 已拒绝 ) : tx.stopTimestamp ? ( 已完成 ) : ( 进行中 )} {dayjs(tx.startTimestamp).format("YYYY/M/D HH:mm:ss")} {formatDuration(tx.startTimestamp, tx.stopTimestamp)} {tx.energyWh != null ? ( (tx.energyWh / 1000).toFixed(3) ) : tx.liveEnergyWh != null ? ( {(tx.liveEnergyWh / 1000).toFixed(3)} 预估 ) : ( "—" )} {tx.chargeAmount != null ? ( `¥${(tx.chargeAmount / 100).toFixed(2)}` ) : tx.estimatedCost != null ? ( ¥{(tx.estimatedCost / 100).toFixed(2)} 预估 ) : ( "—" )} {tx.stopReason === "DeAuthorized" ? (

{tx.idTagStatus && tx.idTagStatus !== "Accepted" ? `${idTagRejectLabelMap[tx.idTagStatus] ?? tx.idTagStatus}` : "鉴权失败"}

) : tx.stopReason ? ( {stopReasonLabelMap[tx.stopReason] ?? tx.stopReason} ) : tx.stopTimestamp ? ( 本地结束 ) : ( "—" )}
{!tx.stopTimestamp && ( 确认中止充电

将强制结束交易{" "} #{tx.id} (储值卡:{tx.idTag})。 如果充电桩在线,将发送远程结算指令。

)} {isAdmin && ( 确认删除记录

将永久删除充电记录{" "} #{tx.id} (储值卡:{tx.idTag})。 {!tx.stopTimestamp && "该记录仍进行中,删除同时将重置接口状态。"}

)}
))}
{data && data.totalPages > 1 && ( 第 {(page - 1) * LIMIT + 1}–{Math.min(page * LIMIT, data.total)} 条,共 {data.total}{" "} 条 setPage((p) => Math.max(1, p - 1))} > 上一页 {pages.map((p) => ( setPage(p)}> {p} ))} setPage((p) => Math.min(data.totalPages, p + 1))} > 下一页 )}
); } export default function TransactionsPage() { return ( } > ); }