269 lines
11 KiB
TypeScript
269 lines
11 KiB
TypeScript
'use client'
|
||
import { useEffect, useState, Suspense } from 'react'
|
||
import { useSearchParams } from 'next/navigation'
|
||
import { Table, TableHeader, TableBody, TableHead, TableRow, TableCell } from '@/components/ui/table'
|
||
|
||
interface GatewayConfig {
|
||
id: number
|
||
city: string
|
||
edge: string
|
||
user: string
|
||
public: string
|
||
inner_ip: string
|
||
ischange: number
|
||
isonline: number
|
||
}
|
||
|
||
function GatewayConfigContent() {
|
||
const [data, setData] = useState<GatewayConfig[]>([])
|
||
const [loading, setLoading] = useState(false)
|
||
const [macAddress, setMacAddress] = useState('')
|
||
const [error, setError] = useState('')
|
||
const [success, setSuccess] = useState('')
|
||
const searchParams = useSearchParams()
|
||
|
||
// 监听URL的mac参数变化:同步到输入框并触发查询
|
||
useEffect(() => {
|
||
const urlMac = searchParams.get('mac')
|
||
if (urlMac) {
|
||
setMacAddress(urlMac)
|
||
fetchData(urlMac)
|
||
} else {
|
||
// 如果没有mac参数,显示所有网关配置
|
||
setMacAddress('')
|
||
fetchData('')
|
||
}
|
||
}, [searchParams])
|
||
|
||
const fetchData = async (mac: string) => {
|
||
setLoading(true)
|
||
setError('')
|
||
setSuccess('')
|
||
|
||
try {
|
||
// 构建API URL - 如果有MAC地址则添加参数,否则获取全部
|
||
const apiUrl = mac.trim()
|
||
? `/api/stats?type=gateway_config&mac=${encodeURIComponent(mac)}`
|
||
: `/api/stats?type=gateway_config`
|
||
|
||
const response = await fetch(apiUrl)
|
||
const result = await response.json()
|
||
|
||
if (!response.ok) {
|
||
throw new Error(result.error || '查询失败')
|
||
}
|
||
|
||
// 检查返回的数据是否有效
|
||
if (!result || result.length === 0) {
|
||
if (mac.trim()) {
|
||
setError(`未找到MAC地址为 ${mac} 的网关配置信息`)
|
||
} else {
|
||
setError('未找到任何网关配置信息')
|
||
}
|
||
setData([])
|
||
return
|
||
}
|
||
|
||
const validatedData = result.map((item: {
|
||
city: string
|
||
edge: string
|
||
user: string
|
||
public: string
|
||
inner_ip: string
|
||
ischange: number
|
||
isonline: number
|
||
}) => ({
|
||
city: item.city,
|
||
edge: item.edge,
|
||
user: item.user,
|
||
public: item.public,
|
||
inner_ip: item.inner_ip,
|
||
ischange: item.ischange,
|
||
isonline: item.isonline,
|
||
}))
|
||
|
||
setData(validatedData)
|
||
} catch (error) {
|
||
console.error('Failed to fetch gateway config:', error)
|
||
setError(error instanceof Error ? error.message : '获取网关配置失败')
|
||
setData([])
|
||
} finally {
|
||
setLoading(false)
|
||
}
|
||
}
|
||
|
||
const handleSubmit = (e: React.FormEvent) => {
|
||
e.preventDefault()
|
||
fetchData(macAddress)
|
||
}
|
||
|
||
const getStatusBadge = (value: number, trueText: string = '是', falseText: string = '否') => {
|
||
// 0是正常1是更新,正常(绿)+ 更新(红)
|
||
return (
|
||
<span className={`inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium ${
|
||
value === 0 ? 'bg-green-100 text-green-800' : 'bg-red-100 text-red-800'}`}>
|
||
{value === 0 ? trueText : falseText}
|
||
</span>
|
||
)
|
||
}
|
||
|
||
const getOnlineStatus = (isonline: number) => {
|
||
// 0是空闲1是在用,在用(红)+ 空闲(绿)
|
||
return (
|
||
<div className="flex items-center">
|
||
<div className={`${isonline === 0 ? 'bg-green-500' : 'bg-red-500'}`} />
|
||
{getStatusBadge(isonline, '空闲', '在用')}
|
||
</div>
|
||
)
|
||
}
|
||
|
||
return (
|
||
<div className="flex flex-col bg-white shadow rounded-lg p-6 overflow-hidden">
|
||
<div className="flex justify-between items-start mb-6">
|
||
<div>
|
||
<h2 className="text-xl font-semibold text-gray-800">网关配置状态</h2>
|
||
<p className="text-sm text-gray-600 mt-1">查询和管理网关设备的配置信息</p>
|
||
</div>
|
||
</div>
|
||
|
||
{/* 查询表单 */}
|
||
<form onSubmit={handleSubmit} className="mb-6">
|
||
<div className="flex items-center">
|
||
<input
|
||
type="text"
|
||
value={macAddress}
|
||
onChange={(e) => setMacAddress(e.target.value)}
|
||
placeholder="输入MAC地址查询"
|
||
className="px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
||
/>
|
||
<button
|
||
type="submit"
|
||
className="ml-2 px-4 py-2 bg-blue-500 text-white rounded-md hover:bg-blue-600 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2"
|
||
>
|
||
查询
|
||
</button>
|
||
</div>
|
||
|
||
{error && (
|
||
<div className="mt-4 p-3 bg-red-50 border border-red-200 rounded-md">
|
||
<div className="flex items-center text-red-800">
|
||
<svg className="w-5 h-5 mr-2" fill="currentColor" viewBox="0 0 20 20">
|
||
<path fillRule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clipRule="evenodd" />
|
||
</svg>
|
||
{error}
|
||
</div>
|
||
</div>
|
||
)}
|
||
|
||
{success && !error && (
|
||
<div className="mt-4 p-3 bg-green-50 border border-green-200 rounded-md">
|
||
<div className="flex items-center text-green-800">
|
||
<svg className="w-5 h-5 mr-2" fill="currentColor" viewBox="0 0 20 20">
|
||
<path fillRule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clipRule="evenodd" />
|
||
</svg>
|
||
{success}
|
||
</div>
|
||
</div>
|
||
)}
|
||
</form>
|
||
|
||
{loading ? (
|
||
<div className="text-center py-12">
|
||
<div className="text-gray-400 text-4xl mb-4">⏳</div>
|
||
<p className="text-gray-600">正在查询网关配置信息...</p>
|
||
</div>
|
||
) : data.length > 0 ? (
|
||
<>
|
||
{/* 详细表格 */}
|
||
<div className='flex gap-6 overflow-hidden'>
|
||
<div className="flex-3 w-full flex">
|
||
<Table>
|
||
<TableHeader>
|
||
<TableRow className="bg-gray-50 TableRow ">
|
||
<TableHead className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">MAC地址</TableHead>
|
||
<TableHead className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">城市</TableHead>
|
||
<TableHead className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">内部账号</TableHead>
|
||
<TableHead className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">IP地址</TableHead>
|
||
<TableHead className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">内网入口</TableHead>
|
||
<TableHead className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">配置更新</TableHead>
|
||
<TableHead className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">在用状态</TableHead>
|
||
</TableRow>
|
||
</TableHeader>
|
||
<TableBody>
|
||
{data.map((item, index) => (
|
||
<TableRow
|
||
key={index}
|
||
className={index % 2 === 0 ? 'bg-white' : 'bg-gray-50 '}
|
||
>
|
||
<TableCell className="hover:bg-gray-50 transition-colors">
|
||
<div className="font-mono text-sm text-blue-600 font-medium">{item.edge}</div>
|
||
</TableCell>
|
||
<TableCell className="px-6 whitespace-nowrap">
|
||
<span className="px-2 py-1 bg-gray-100 text-gray-700 rounded-full text-xs">
|
||
{item.city}
|
||
</span></TableCell>
|
||
<TableCell className="px-6 whitespace-nowrap text-sm text-gray-900">{item.user}</TableCell>
|
||
<TableCell className="px-6 whitespace-nowrap">
|
||
<div className="font-mono text-sm text-green-600">{item.public}</div>
|
||
</TableCell>
|
||
<TableCell className="px-6 whitespace-nowrap">
|
||
<div className="font-mono text-sm text-purple-600">{item.inner_ip}</div>
|
||
</TableCell>
|
||
<TableCell className="px-6 whitespace-nowrap">
|
||
<div className="font-mono text-sm text-green-600">{getStatusBadge(item.ischange, '正常', '更新')}</div>
|
||
</TableCell>
|
||
<TableCell className="px-6 whitespace-nowrap">
|
||
<div className="font-mono text-sm text-purple-600">{getOnlineStatus(item.isonline)}</div>
|
||
</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 border border-blue-100">
|
||
<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 border border-green-100">
|
||
<div className="text-2xl font-bold text-green-600">
|
||
{data.filter(item => item.isonline === 1).length}
|
||
</div>
|
||
<div className="text-sm text-green-800">在线网关</div>
|
||
</div>
|
||
<div className="bg-orange-50 p-4 rounded-lg border border-orange-100">
|
||
<div className="text-2xl font-bold text-orange-600">
|
||
{data.filter(item => item.ischange === 1).length}
|
||
</div>
|
||
<div className="text-sm text-orange-800">已更新配置</div>
|
||
</div>
|
||
<div className="bg-purple-50 p-4 rounded-lg border border-purple-100">
|
||
<div className="text-2xl font-bold text-purple-600">
|
||
{new Set(data.map(item => item.city)).size}
|
||
</div>
|
||
<div className="text-sm text-purple-800">覆盖城市</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</>
|
||
) : (
|
||
<></>
|
||
)}
|
||
</div>
|
||
)
|
||
}
|
||
|
||
export default function GatewayConfig() {
|
||
return (
|
||
<Suspense fallback={
|
||
<div className="bg-white shadow rounded-lg p-6">
|
||
<div className="text-center py-12">
|
||
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-500 mx-auto"></div>
|
||
<p className="mt-4 text-gray-600">加载搜索参数...</p>
|
||
</div>
|
||
</div>
|
||
}>
|
||
<GatewayConfigContent />
|
||
</Suspense>
|
||
)
|
||
} |