Files
jh-monitor/src/app/dashboard/components/gatewayinfo.tsx
2025-09-24 19:15:22 +08:00

215 lines
7.2 KiB
TypeScript

'use client'
import { useEffect, useState } from 'react'
import { useRouter } from 'next/navigation'
import { Table, TableHeader, TableBody, TableHead, TableRow, TableCell } from '@/components/ui/table'
import { Form, FormField } from '@/components/ui/form'
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'
import { z } from 'zod'
import { useForm } from 'react-hook-form'
import { zodResolver } from '@hookform/resolvers/zod'
import { getGatewayInfo, type GatewayInfo } from '@/actions/stats'
const filterSchema = z.object({
status: z.string(),
})
type FilterSchema = z.infer<typeof filterSchema>
// IP地址排序函数
const sortByIpAddress = (a: string, b: string): number => {
const ipToNumber = (ip: string): number => {
const parts = ip.split('.').map(part => parseInt(part, 10))
return (parts[0] << 24) + (parts[1] << 16) + (parts[2] << 8) + parts[3]
}
return ipToNumber(a) - ipToNumber(b)
}
export default function Gatewayinfo() {
const [data, setData] = useState<GatewayInfo[]>([])
const [filteredData, setFilteredData] = useState<GatewayInfo[]>([])
const [loading, setLoading] = useState(true)
const [error, setError] = useState('')
const router = useRouter()
const form = useForm<FilterSchema>({
resolver: zodResolver(filterSchema),
defaultValues: {
status: '1',
},
})
const { watch } = form
const statusFilter = watch('status')
useEffect(() => {
fetchData()
}, [])
useEffect(() => {
if (!data.length) return
if (statusFilter === 'all') {
setFilteredData(data)
}
else {
const enableValue = parseInt(statusFilter)
// 添加 NaN 检查
if (isNaN(enableValue)) {
setFilteredData(data)
}
else {
setFilteredData(data.filter(item => item.enable === enableValue))
}
}
}, [data, statusFilter])
const fetchData = async () => {
try {
setLoading(true)
setError('')
const result = await getGatewayInfo()
const sortedData = result.data.sort((a: GatewayInfo, b: GatewayInfo) =>
sortByIpAddress(a.inner_ip, b.inner_ip),
)
setData(sortedData)
setFilteredData(sortedData) // 初始化时设置filteredData
}
catch (error) {
console.error('Failed to fetch gateway info:', error)
setError(error instanceof Error ? error.message : '获取网关信息失败')
}
finally {
setLoading(false)
}
}
const getStatusText = (enable: number) => {
return enable === 1 ? '启用' : '禁用'
}
const getStatusClass = (enable: number) => {
return enable === 1
? 'bg-green-100 text-green-800'
: 'bg-red-100 text-red-800'
}
if (loading) {
return (
<div className="bg-white shadow rounded-lg p-6 overflow-hidden">
<h2 className="text-lg font-semibold mb-4"></h2>
<div className="text-center py-8">...</div>
</div>
)
}
if (error) {
return (
<div className="bg-white shadow rounded-lg p-6">
<h2 className="text-lg font-semibold mb-4"></h2>
<div className="text-center py-8 text-red-600">{error}</div>
</div>
)
}
return (
<div className="flex flex-col bg-white shadow rounded-lg p-6 overflow-hidden">
<div className="flex gap-6">
<div className="flex flex-3 justify-between ">
<span className="text-lg pt-2 font-semibold mb-4"></span>
<Form {...form}>
<form className="flex items-center gap-4">
<FormField
control={form.control}
name="status"
render={({ field }) => (
<div className="flex items-center">
<span className="text-sm mr-2">:</span>
<Select
value={field.value}
onValueChange={field.onChange}
defaultValue="1"
>
<SelectTrigger className="h-9 w-36">
<SelectValue placeholder="选择状态" />
</SelectTrigger>
<SelectContent>
<SelectItem value="all"></SelectItem>
<SelectItem value="1"></SelectItem>
<SelectItem value="0"></SelectItem>
</SelectContent>
</Select>
</div>
)}
/>
</form>
</Form>
</div>
<div className="flex flex-1"></div>
</div>
<div className="flex gap-6 overflow-hidden">
<div className="flex-3 w-full flex">
<Table>
<TableHeader>
<TableRow className="bg-gray-50">
<TableHead className="px-4 py-2 text-left">MAC地址</TableHead>
<TableHead className="px-4 py-2 text-left">IP</TableHead>
<TableHead className="px-4 py-2 text-left"></TableHead>
<TableHead className="px-4 py-2 text-left"></TableHead>
</TableRow>
</TableHeader>
<TableBody>
{filteredData.map((item, index) => (
<TableRow
key={index}
className={index % 2 === 0 ? 'bg-white' : 'bg-gray-50'}
>
<TableCell className="px-4 py-2">
<button
onClick={() => {
router.push(`/dashboard?tab=gateway&mac=${item.macaddr}`)
}}
className="font-mono text-blue-600 hover:text-blue-800 hover:underline cursor-pointer"
>
{item.macaddr}
</button>
</TableCell>
<TableCell className="px-4 py-2">{item.inner_ip}</TableCell>
<TableCell className="px-4 py-2">{item.setid}</TableCell>
<TableCell className="px-4 py-2">
<span className={`px-2 py-1 rounded-full text-xs font-medium ${getStatusClass(item.enable)}`}>
{getStatusText(item.enable)}
</span>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</div>
<div className="flex flex-1 flex-col gap-4 mb-6">
<div className="bg-blue-50 p-4 rounded-lg">
<div className="text-2xl font-bold text-blue-600">{data.length}</div>
<div className="text-sm text-blue-800"></div>
</div>
<div className="bg-green-50 p-4 rounded-lg">
<div className="text-2xl font-bold text-green-600">
{data.filter(item => item.enable === 1).length}
</div>
<div className="text-sm text-green-800"></div>
</div>
<div className="bg-red-50 p-4 rounded-lg">
<div className="text-2xl font-bold text-red-600">
{data.filter(item => item.enable === 0).length}
</div>
<div className="text-sm text-red-800"></div>
</div>
</div>
</div>
</div>
)
}