import { swrFetcher } from "@/api/api" import { blockUser } from "@/api/online-user" import { BlockButtonGroup } from "@/components/action-button-group" import { HeaderBlockButtonGroup } from "@/components/header-button-group" import { SettingsTab } from "@/components/settings-tab" import { Checkbox } from "@/components/ui/checkbox" import { Pagination, PaginationContent, PaginationEllipsis, PaginationItem, PaginationLink, PaginationNext, PaginationPrevious, } from "@/components/ui/pagination" import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from "@/components/ui/table" import { useAuth } from "@/hooks/useAuth" import { ModelOnlineUser, ModelOnlineUserApi } from "@/types" import { ColumnDef, flexRender, getCoreRowModel, useReactTable } from "@tanstack/react-table" import { useEffect, useMemo } from "react" import { useTranslation } from "react-i18next" import { useSearchParams } from "react-router-dom" import { toast } from "sonner" import useSWR from "swr" export default function OnlineUserPage() { const { t } = useTranslation() const { profile } = useAuth() const [searchParams, setSearchParams] = useSearchParams() const page = Number(searchParams.get("page")) || 1 const pageSize = Number(searchParams.get("pageSize")) || 10 // 计算 offset const offset = (page - 1) * pageSize const { data, mutate, error, isLoading } = useSWR( `/api/v1/online-user?offset=${offset}&limit=${pageSize}`, swrFetcher, ) const isAdmin = profile?.role === 0 useEffect(() => { if (error) toast(t("Error"), { description: t(`Error fetching resource: ${error.message}.`), }) // eslint-disable-next-line react-hooks/exhaustive-deps }, [error]) let columns: ColumnDef[] = [ { id: "select", header: ({ table }) => ( table.toggleAllPageRowsSelected(!!value)} aria-label="Select all" /> ), cell: ({ row }) => ( row.toggleSelected(!!value)} aria-label="Select row" /> ), enableSorting: false, enableHiding: false, }, { header: "IP", accessorKey: "ip", accessorFn: (row) => row.ip ?? "", }, { header: t("UserId"), accessorKey: "user_id", accessorFn: (row) => row.user_id || "", }, { header: t("ConnectedAt"), accessorKey: "connected_at", accessorFn: (row) => row.connected_at, cell: ({ row }) => { const s = row.original const date = new Date(s.connected_at) return {date.toISOString()} }, }, { id: "actions", header: "Actions", cell: ({ row }) => { const s = row.original return ( <> ) }, }, ] if (!isAdmin) { // 非管理员隐藏操作列 columns = columns.filter((c) => c.id !== "actions") } const dataCache = useMemo(() => { return data?.value ?? [] }, [data]) const table = useReactTable({ data: dataCache, columns, getCoreRowModel: getCoreRowModel(), }) const selectedRows = table.getSelectedRowModel().rows const renderPagination = () => { if (!data?.pagination) return null const { total } = data.pagination const totalPages = Math.ceil(total / pageSize) const handlePageChange = (newPage: number) => { if (newPage < 1 || newPage > totalPages) return setSearchParams({ page: newPage.toString(), pageSize: pageSize.toString() }) } // 计算要显示的页码范围 const getPageNumbers = () => { const pages: number[] = [] const maxVisiblePages = 5 if (totalPages <= maxVisiblePages) { return Array.from({ length: totalPages }, (_, i) => i + 1) } // 始终显示第一页 pages.push(1) let startPage = Math.max(2, page - 1) let endPage = Math.min(totalPages - 1, page + 1) if (page <= 3) { endPage = Math.min(maxVisiblePages - 1, totalPages - 1) } else if (page >= totalPages - 2) { startPage = Math.max(2, totalPages - (maxVisiblePages - 2)) } if (startPage > 2) { pages.push(-1) // 表示省略号 } for (let i = startPage; i <= endPage; i++) { pages.push(i) } if (endPage < totalPages - 1) { pages.push(-1) // 表示省略号 } // 始终显示最后一页 if (totalPages > 1) { pages.push(totalPages) } return pages } return (
{t("Total")}: {total}
handlePageChange(page - 1)} className={ page <= 1 ? "pointer-events-none opacity-50" : "cursor-pointer" } /> {getPageNumbers().map((pageNum, idx) => pageNum === -1 ? ( ) : ( handlePageChange(pageNum)} isActive={pageNum === page} > {pageNum} ), )} handlePageChange(page + 1)} className={ page >= totalPages ? "pointer-events-none opacity-50" : "cursor-pointer" } />
) } return (
{isAdmin && ( r.original.ip ?? ""), mutate: mutate, }} > <> )}
{table.getHeaderGroups().map((headerGroup) => ( {headerGroup.headers.map((header) => { return ( {header.isPlaceholder ? null : flexRender( header.column.columnDef.header, header.getContext(), )} ) })} ))} {isLoading ? ( {t("Loading")}... ) : table.getRowModel().rows?.length ? ( table.getRowModel().rows.map((row) => ( {row.getVisibleCells().map((cell) => ( {flexRender(cell.column.columnDef.cell, cell.getContext())} ))} )) ) : ( {t("NoResults")} )}
{renderPagination()}
) }