"use client"; import { use, useState } from "react"; import Link from "next/link"; import { useRouter } from "next/navigation"; import { useQuery } from "@tanstack/react-query"; import { Alert, Button, Chip, Modal, Spinner } from "@heroui/react"; import { ArrowLeft, ArrowRotateRight, TrashBin } from "@gravity-ui/icons"; import { APIError, api } from "@/lib/api"; import { useSession } from "@/lib/auth-client"; import dayjs from "@/lib/dayjs"; import InfoSection from "@/components/info-section"; import MetricIndicator from "@/components/metric-indicator"; import { BanknoteArrowUp, Clock, EvCharger } from "lucide-react"; const stopReasonLabelMap: Record = { EmergencyStop: "紧急停止", EVDisconnected: "车辆断开", HardReset: "硬重启", Local: "本地结束", Other: "其他原因", PowerLoss: "断电结束", Reboot: "重启结束", Remote: "远程结束", SoftReset: "软重启", UnlockCommand: "解锁结束", DeAuthorized: "鉴权拒绝", }; const idTagRejectLabelMap: Record = { Blocked: "卡片不可用或余额不足", Expired: "卡片已过期", Invalid: "卡片无效", ConcurrentTx: "该卡已有进行中的订单", }; 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 formatDateTime(iso: string | null | undefined): string { if (!iso) return "—"; return dayjs(iso).format("YYYY/M/D HH:mm:ss"); } function formatEnergy(wh: number | null | undefined): string { if (wh == null) return "—"; return `${(wh / 1000).toFixed(3)} kWh`; } function formatAmount(fen: number | null | undefined): string { if (fen == null) return "—"; return `¥${(fen / 100).toFixed(2)}`; } export default function TransactionDetailPage({ params }: { params: Promise<{ id: string }> }) { const { id } = use(params); const router = useRouter(); const txId = Number(id); const isValidId = Number.isInteger(txId) && txId > 0; const [stopping, setStopping] = useState(false); const [deleting, setDeleting] = useState(false); const { data: sessionData } = useSession(); const isAdmin = sessionData?.user?.role === "admin"; const { data: tx, isPending, isFetching, isError, error, refetch, } = useQuery({ queryKey: ["transaction", txId], queryFn: () => api.transactions.get(txId), enabled: isValidId, refetchInterval: 3_000, retry: false, }); const handleStop = async () => { if (!tx) return; setStopping(true); try { await api.transactions.stop(tx.id); await refetch(); } finally { setStopping(false); } }; const handleDelete = async () => { if (!tx) return; setDeleting(true); try { await api.transactions.delete(tx.id); router.push("/dashboard/transactions"); } finally { setDeleting(false); } }; if (!isValidId) { return (
充电记录

无效的交易编号。

); } if (isPending) { return (
); } if (isError || !tx) { const notFound = error instanceof APIError && error.status === 404; return (
充电记录

{notFound ? "交易记录不存在。" : "加载交易记录失败。"}

); } const energyWh = tx.energyWh ?? tx.liveEnergyWh; const amountFen = tx.chargeAmount ?? tx.estimatedCost; const isEstimatedEnergy = tx.energyWh == null && tx.liveEnergyWh != null; const isEstimatedAmount = tx.chargeAmount == null && tx.estimatedCost != null; const isRejected = tx.stopReason === "DeAuthorized"; const stopReasonLabel = tx.stopReason ? (stopReasonLabelMap[tx.stopReason] ?? tx.stopReason) : "—"; const rejectReason = tx.idTagStatus && tx.idTagStatus !== "Accepted" ? (idTagRejectLabelMap[tx.idTagStatus] ?? tx.idTagStatus) : "鉴权失败"; return (
充电记录

订单 #{tx.id}

{isRejected ? ( 已拒绝 ) : tx.stopTimestamp ? ( 已完成 ) : ( 进行中 )} {isEstimatedAmount && ( 费用预估 )} {tx.stopReason && !isRejected && ( {stopReasonLabel} )}

开始于 {formatDateTime(tx.startTimestamp)} · {tx.stopTimestamp ? "时长 " : ""} {formatDuration(tx.startTimestamp, tx.stopTimestamp)}

{!tx.stopTimestamp && ( 确认中止充电

将远程中止充电交易{" "} #{tx.id}

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

将永久删除交易 #{tx.id}

)}
{isRejected ? "交易已被系统拒绝" : tx.stopTimestamp ? "本次交易已结束" : "充电进行中"} {isRejected ? rejectReason : tx.stopTimestamp ? `结束原因:${stopReasonLabel}` : "充电进行中,实际充电量和费用以结束时系统计算为准。"}
} value={ {formatEnergy(energyWh)} {isEstimatedEnergy && ( 预估 )} } /> } value={ {formatAmount(amountFen)} {isEstimatedAmount && ( 预估 )} } /> } color={ isRejected ? "border-danger" : tx.stopTimestamp ? "border-success" : "border-warning" } value={isRejected ? "已拒绝" : tx.stopTimestamp ? "已完成" : "进行中"} />
交易编号
#{tx.id}
储值卡
{tx.idTag}
桩编号
{tx.chargePointIdentifier ?? "—"}
连接器
{tx.connectorNumber ?? "—"}
开始时间
{formatDateTime(tx.startTimestamp)}
结束时间
{formatDateTime(tx.stopTimestamp)}
持续时长
{formatDuration(tx.startTimestamp, tx.stopTimestamp)}
起始表计
{tx.startMeterValue != null ? `${tx.startMeterValue} Wh` : "—"}
结束表计
{tx.stopMeterValue != null ? `${tx.stopMeterValue} Wh` : "—"}
消耗电量
{formatEnergy(energyWh)} {isEstimatedEnergy && ( 预估 )}
电费
{formatAmount(tx.electricityFee)}
服务费
{formatAmount(tx.serviceFee)}
总费用
{formatAmount(amountFen)} {isEstimatedAmount && ( 预估 )}
); }