"use client"; import { useState } from "react"; import { useQuery } from "@tanstack/react-query"; import { Button, Chip, Modal, Pagination, Spinner, Table } from "@heroui/react"; import { TrashBin } from "@gravity-ui/icons"; import { api, type PaginatedTransactions } from "@/lib/api"; import { useSession } from "@/lib/auth-client"; const LIMIT = 15; function formatDuration(start: string, stop: string | null): string { if (!stop) return "进行中"; const ms = new Date(stop).getTime() - new Date(start).getTime(); const min = Math.floor(ms / 60000); if (min < 60) return `${min} 分钟`; const h = Math.floor(min / 60); const m = min % 60; return `${h}h ${m}m`; } export default function TransactionsPage() { const { data: sessionData } = useSession(); const isAdmin = sessionData?.user?.role === "admin"; const [page, setPage] = useState(1); const [status, setStatus] = useState<"all" | "active" | "completed">("all"); const [stoppingId, setStoppingId] = useState(null); const [deletingId, setDeletingId] = useState(null); const { data, isPending: loading, 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 ? ( 已完成 ) : ( 进行中 )} {new Date(tx.startTimestamp).toLocaleString("zh-CN")} {formatDuration(tx.startTimestamp, tx.stopTimestamp)} {tx.energyWh != null ? (tx.energyWh / 1000).toFixed(3) : "—"} {tx.chargeAmount != null ? `¥${(tx.chargeAmount / 100).toFixed(2)}` : "—"} {tx.stopReason ? ( {tx.stopReason} ) : tx.stopTimestamp ? ( Local ) : ( "—" )}
{!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))} > 下一页 )}
); }