'use client'; import { UserDetail } from '@/app/dashboard/user/user-detail'; 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 { Button } from '@workspace/ui/components/button'; 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'; interface NodeDetailDialogProps { node: API.Server; children?: React.ReactNode; trigger?: React.ReactNode; } // 统一的用户订阅信息组件 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 NodeDetailDialog({ node, children, trigger }: NodeDetailDialogProps) { const t = useTranslations('server.node'); const [open, setOpen] = useState(false); const { status } = node; const { online, cpu, mem, disk, updated_at } = status || { online: {}, cpu: 0, mem: 0, disk: 0, updated_at: 0, }; const isOnline = updated_at > 0; 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 ( {trigger || ( )} {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 ? ( {ip} ) : ( {ip} )}
))}
); }, }, { 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, })} />
)}
); }