'use client'; import { UserDetail } from '@/app/dashboard/user/user-detail'; import { IpLink } from '@/components/ip-link'; import { ProTable } from '@/components/pro-table'; import { getUserSubscribeById } from '@/services/admin/user'; import { useQuery } from '@tanstack/react-query'; import { Badge } from '@workspace/ui/components/badge'; import { Progress } from '@workspace/ui/components/progress'; import { Sheet, SheetContent, SheetHeader, SheetTitle, SheetTrigger, } from '@workspace/ui/components/sheet'; import { formatBytes, formatDate } from '@workspace/ui/utils'; import { useTranslations } from 'next-intl'; import { useState } from 'react'; export function formatPercentage(value: number): string { return `${value.toFixed(1)}%`; } // 统一的用户订阅信息组件 function UserSubscribeInfo({ userId, type, }: { userId: number; type: 'account' | 'subscribeName' | 'subscribeId' | 'trafficUsage' | 'expireTime'; }) { const { data } = useQuery({ enabled: userId !== 0, queryKey: ['getUserSubscribeById', userId], queryFn: async () => { const { data } = await getUserSubscribeById({ id: userId }); return data.data; }, }); if (!data) return --; switch (type) { case 'account': if (!data.user_id) return --; return ; case 'subscribeName': if (!data.subscribe?.name) return --; return {data.subscribe.name}; case 'subscribeId': if (!data.id) return --; return {data.id}; case 'trafficUsage': { const usedTraffic = data.upload + data.download; const totalTraffic = data.traffic || 0; return (
{formatBytes(usedTraffic)} / {totalTraffic > 0 ? formatBytes(totalTraffic) : '无限制'}
); } case 'expireTime': { if (!data.expire_time) return --; const isExpired = data.expire_time < Date.now() / 1000; return (
{formatDate(data.expire_time)} {isExpired && ( 过期 )}
); } default: return --; } } export function NodeStatusCell({ status, node }: { status: API.NodeStatus; node?: API.Server }) { const t = useTranslations('server.node'); const [open, setOpen] = useState(false); const { online, cpu, mem, disk, updated_at } = status || { online: {}, cpu: 0, mem: 0, disk: 0, updated_at: 0, }; const isOnline = updated_at > 0; const badgeVariant = isOnline ? 'default' : 'destructive'; const badgeText = isOnline ? t('normal') : t('abnormal'); const onlineCount = (online && Object.keys(online).length) || 0; // 转换在线用户数据为ProTable需要的格式 const onlineUsersData = Object.entries(online || {}).map(([uid, ips]) => ({ uid, ips: ips as string[], primaryIp: ips[0] || '', allIps: (ips as string[]).join(', '), })); return ( <> {node && ( {t('nodeDetail')} - {node.name}

{t('nodeStatus')}

{isOnline ? t('normal') : t('abnormal')} {t('onlineCount')}: {onlineCount} {isOnline && ( {t('lastUpdated')}: {formatDate(updated_at)} )}
{isOnline && (
CPU {cpu?.toFixed(1)}%
{t('memory')} {mem?.toFixed(1)}%
{t('disk')} {disk?.toFixed(1)}%
)}
{isOnline && onlineCount > 0 && (

{t('onlineUsers')}

{ const ips = row.original.ips; return (
{ips.map((ip: string, index: number) => (
{index === 0 ? ( ) : ( )}
))}
); }, }, { accessorKey: 'user', header: t('user'), cell: ({ row }) => ( ), }, { accessorKey: 'subscribeName', header: t('subscribeName'), cell: ({ row }) => ( ), }, { accessorKey: 'subscribeId', header: t('subscribeId'), cell: ({ row }) => ( ), }, { accessorKey: 'trafficUsage', header: t('trafficUsage'), cell: ({ row }) => ( ), }, { accessorKey: 'expireTime', header: t('expireTime'), cell: ({ row }) => ( ), }, ]} request={async () => ({ list: onlineUsersData, total: onlineUsersData.length, })} />
)}
)}
); }