215 lines
7.2 KiB
TypeScript
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>
|
|
)
|
|
}
|