网关配置的表格添加内网IP的inner_ip字段的筛选条件

This commit is contained in:
wmp
2025-10-20 17:15:10 +08:00
parent 8c05d0b332
commit 9fff300a22
3 changed files with 145 additions and 141 deletions

View File

@@ -25,7 +25,10 @@ function GatewayConfigContent() {
const [initialConfigData, setInitialConfigData] = useState<{ [mac: string]: GatewayConfig[] }>({}) const [initialConfigData, setInitialConfigData] = useState<{ [mac: string]: GatewayConfig[] }>({})
// 初始数据是否已加载完成 // 初始数据是否已加载完成
const [isInitialDataLoaded, setIsInitialDataLoaded] = useState(false) const [isInitialDataLoaded, setIsInitialDataLoaded] = useState(false)
// 定义表单验证规则 // MAC 地址 - 内网 IP
const [gatewayPairs, setGatewayPairs] = useState<{ macaddr: string, inner_ip: string }[]>([])
// 表单验证规则
const filterSchema = z.object({ const filterSchema = z.object({
macaddr: z.string().optional(), macaddr: z.string().optional(),
public: z.string().optional(), public: z.string().optional(),
@@ -33,10 +36,7 @@ function GatewayConfigContent() {
inner_ip: z.string().optional(), inner_ip: z.string().optional(),
user: z.string().optional(), user: z.string().optional(),
}) })
type FilterFormValues = z.infer<typeof filterSchema> type FilterFormValues = z.infer<typeof filterSchema>
// 初始化表单
const form = useForm<FilterFormValues>({ const form = useForm<FilterFormValues>({
resolver: zodResolver(filterSchema), resolver: zodResolver(filterSchema),
defaultValues: { defaultValues: {
@@ -62,6 +62,8 @@ function GatewayConfigContent() {
return processedItem return processedItem
}) })
} }
// 获取IP最后一段用于排序
const getLastOctet = (ip: string | undefined): number => { const getLastOctet = (ip: string | undefined): number => {
if (!ip) return 0 if (!ip) return 0
const parts = ip.split('.') const parts = ip.split('.')
@@ -70,14 +72,14 @@ function GatewayConfigContent() {
return isNaN(last) ? 0 : last return isNaN(last) ? 0 : last
} }
// 端口排序函数 // 按内网IP排序配置
const sortByInnerIp = (a: GatewayConfig, b: GatewayConfig): number => { const sortByInnerIp = (a: GatewayConfig, b: GatewayConfig): number => {
const lastOctetA = getLastOctet(a.inner_ip) const lastOctetA = getLastOctet(a.inner_ip)
const lastOctetB = getLastOctet(b.inner_ip) const lastOctetB = getLastOctet(b.inner_ip)
return lastOctetA - lastOctetB return lastOctetA - lastOctetB
} }
// 为个MAC地址获取配置数据的函数 // 为个MAC获取配置数据
const fetchConfigForMac = async (mac: string, filters: { const fetchConfigForMac = async (mac: string, filters: {
mac?: string | undefined mac?: string | undefined
public?: string | undefined public?: string | undefined
@@ -90,16 +92,17 @@ function GatewayConfigContent() {
const result = await getGatewayConfig(1, queryParams) const result = await getGatewayConfig(1, queryParams)
if (result.success) { if (result.success) {
const processedData = processCityNames(result.data.items) const processedData = processCityNames(result.data.items)
const sortedData = [...processedData].sort(sortByInnerIp) return [...processedData].sort(sortByInnerIp)
return sortedData
} }
return [] return []
} }
catch (error) { catch (error) {
console.error(`获取MAC地址 ${mac} 配置失败:`, error) console.error(`获取MAC ${mac} 配置失败:`, error)
return [] return []
} }
} }
// 本地筛选数据
const filterLocalData = (filters: { const filterLocalData = (filters: {
mac?: string mac?: string
public?: string public?: string
@@ -108,18 +111,26 @@ function GatewayConfigContent() {
inner_ip?: string inner_ip?: string
}) => { }) => {
const { mac, public: publicIp, city, user, inner_ip } = filters const { mac, public: publicIp, city, user, inner_ip } = filters
// 筛选MAC地址 let filteredPairs = [...gatewayPairs]
let filteredMacs = initialMacAddresses
// 按MAC筛选
if (mac) { if (mac) {
filteredMacs = filteredMacs.filter(macAddr => filteredPairs = filteredPairs.filter(pair =>
macAddr.toLowerCase().includes(mac.toLowerCase()), pair.macaddr.toLowerCase().includes(mac.toLowerCase()),
) )
} }
// 筛选每个MAC地址对应的配置数据
const filteredConfigs: { [mac: string]: GatewayConfig[] } = {}
filteredMacs.forEach((macAddr) => {
const configs = initialConfigData[macAddr] || []
// 按内网IP筛选
if (inner_ip) {
filteredPairs = filteredPairs.filter(pair =>
pair.inner_ip.includes(inner_ip),
)
}
// 筛选配置数据
const filteredConfigs: { [mac: string]: GatewayConfig[] } = {}
filteredPairs.forEach((pair) => {
const configs = initialConfigData[pair.macaddr] || []
const filtered = configs.filter((config) => { const filtered = configs.filter((config) => {
// 按各个字段筛选 // 按各个字段筛选
const matchPublic = !publicIp || (config.public && config.public.includes(publicIp)) const matchPublic = !publicIp || (config.public && config.public.includes(publicIp))
@@ -128,24 +139,35 @@ function GatewayConfigContent() {
const matchInnerIp = !inner_ip || (config.inner_ip && config.inner_ip.includes(inner_ip)) const matchInnerIp = !inner_ip || (config.inner_ip && config.inner_ip.includes(inner_ip))
return matchPublic && matchCity && matchUser && matchInnerIp return matchPublic && matchCity && matchUser && matchInnerIp
}) })
if (filtered.length > 0) { if (filtered.length > 0) {
filteredConfigs[macAddr] = filtered filteredConfigs[pair.macaddr] = filtered
} }
}) })
return { filteredMacs: Object.keys(filteredConfigs), filteredConfigs }
// 筛选后按内网IP重新排序确保表头与列顺序一致
const sortedFilteredPairs = filteredPairs
.filter(pair => filteredConfigs[pair.macaddr]?.length > 0)
.sort((a, b) => {
const lastOctetA = getLastOctet(a.inner_ip)
const lastOctetB = getLastOctet(b.inner_ip)
return lastOctetA - lastOctetB
})
return { filteredPairs: sortedFilteredPairs, filteredConfigs }
} }
// 矩阵数据构建函数 // 构建矩阵数据
const buildMatrixData = async ( const buildMatrixData = async (
macList: string[], gatewayPairsList: { macaddr: string, inner_ip: string }[],
configData: { [mac: string]: GatewayConfig[] } = {}, configData: { [mac: string]: GatewayConfig[] } = {},
useLocalData: boolean = false, useLocalData: boolean = false,
) => { ) => {
setLoading(true) setLoading(true)
try { try {
let macConfigMap: { [mac: string]: GatewayConfig[] } = {} let macConfigMap: { [mac: string]: GatewayConfig[] } = {}
const macList = gatewayPairsList.map(pair => pair.macaddr)
// 加载配置数据
if (useLocalData) { if (useLocalData) {
// 使用本地数据 // 使用本地数据
macConfigMap = configData macConfigMap = configData
@@ -161,96 +183,91 @@ function GatewayConfigContent() {
}) })
} }
// 获取所有端口并排序
const allPortLines = Array.from( const allPortLines = Array.from(
new Set( new Set(
Object.values(macConfigMap) Object.values(macConfigMap)
.flat() .flat()
.map(item => item.inner_ip)// 获取端口和线路 .map(item => item.inner_ip)
.filter(Boolean), .filter(Boolean),
), ),
) ).sort((a, b) => {
const portLineConfigMap: { [key: string]: GatewayConfig } = {}
Object.values(macConfigMap).forEach((configs) => {
configs.forEach((config) => {
const key = config.inner_ip
if (!portLineConfigMap[key]) {
portLineConfigMap[key] = config
}
})
})
// 按端口号排序
const sortedPortLines = allPortLines.sort((a, b) => {
const portA = getLastOctet(a.split('|')[0]) const portA = getLastOctet(a.split('|')[0])
const portB = getLastOctet(b.split('|')[0]) const portB = getLastOctet(b.split('|')[0])
return portA - portB return portA - portB
}) })
// 构建矩阵数据 // 构建矩阵
const matrix = sortedPortLines.map((portLine) => { const matrix = allPortLines.map((portLine) => {
const [inner_ip] = portLine.split('|') const [inner_ip] = portLine.split('|')
const config = portLineConfigMap[portLine] const row = { inner_ip: inner_ip || '', devices: {} as { [macaddr: string]: GatewayConfig[] } }
const row = { // 遍历排序后的网关对,双重校验配置归属
inner_ip: inner_ip || '', gatewayPairsList.forEach((pair) => {
devices: {} as { [macaddr: string]: GatewayConfig[] }, const configsForPortLine = macConfigMap[pair.macaddr]
} .filter(item => item.inner_ip === inner_ip) // 匹配当前行端口
// 为每个MAC地址填充该城市的配置数据
macList.forEach((mac) => {
const configsForPortLine = macConfigMap[mac]
.filter(item => item.inner_ip === inner_ip)
.sort(sortByInnerIp) .sort(sortByInnerIp)
row.devices[mac] = configsForPortLine row.devices[pair.macaddr] = configsForPortLine
}) })
return row return row
}) })
// 更新状态
setMatrixData(matrix) setMatrixData(matrix)
setMacAddresses(macList) setGatewayPairs(gatewayPairsList)
setCurrentTotal(matrix.length) setCurrentTotal(matrix.length)
setTotalCount(Object.values(macConfigMap).reduce((sum, configs) => sum + configs.length, 0))
// 计算总记录数 console.log('矩阵构建完成:', { 表头列数: gatewayPairsList.length, 矩阵行数: matrix.length })
const total = Object.values(macConfigMap).reduce((sum, configs) => sum + configs.length, 0)
setTotalCount(total)
} }
catch (error) { catch (error) {
toast.error('构建矩阵数据失败') toast.error('构建矩阵数据失败')
console.error('构建矩阵数据错误:', error) console.error('矩阵构建错误:', error)
} }
finally { finally {
setLoading(false) setLoading(false)
} }
} }
// 初始化调用
// 初始化数据
useEffect(() => { useEffect(() => {
const initData = async () => { const initData = async () => {
setLoading(true) setLoading(true)
try { try {
// 获取网关基本信息 // 获取网关基本信息
const infoResult = await getGatewayInfo() const infoResult = await getGatewayInfo()
if (!infoResult.success) throw new Error(infoResult.error || '查询网关信息失败')
if (!infoResult.success) {
throw new Error(infoResult.error || '查询网关信息失败')
}
setInfoData(infoResult.data) setInfoData(infoResult.data)
// 获取所有MAC地址
const allMacAddresses = Array.from(
new Set(infoResult.data.map(item => item.macaddr).filter(Boolean)),
).sort() as string[]
setInitialMacAddresses(allMacAddresses)
setMacAddresses(allMacAddresses)
const configPromises = allMacAddresses.map(mac => fetchConfigForMac(mac, {}))
const configResults = await Promise.all(configPromises)
// 构建排序后的网关对MAC+内网IP
const sortedGatewayPairs = infoResult.data
.filter(item => item.macaddr && item.inner_ip)
.sort((a, b) => {
const lastOctetA = getLastOctet(a.inner_ip)
const lastOctetB = getLastOctet(b.inner_ip)
return lastOctetA - lastOctetB
})
.map(item => ({ macaddr: item.macaddr!, inner_ip: item.inner_ip! }))
// 加载初始配置
setGatewayPairs(sortedGatewayPairs)// 表头数据
const sortedMacAddresses = sortedGatewayPairs.map(pair => pair.macaddr)
setInitialMacAddresses(sortedMacAddresses)
setMacAddresses(sortedMacAddresses)
const configPromises = sortedMacAddresses.map(mac => fetchConfigForMac(mac, {}))
const configResults = await Promise.all(configPromises)
const initialConfig: { [mac: string]: GatewayConfig[] } = {} const initialConfig: { [mac: string]: GatewayConfig[] } = {}
allMacAddresses.forEach((mac, index) => { sortedMacAddresses.forEach((mac, index) => {
initialConfig[mac] = configResults[index] || [] initialConfig[mac] = configResults[index] || []
}) })
setInitialConfigData(initialConfig) setInitialConfigData(initialConfig)
setIsInitialDataLoaded(true) setIsInitialDataLoaded(true)
// 每个MAC地址调用接口
await buildMatrixData(allMacAddresses, initialConfig, true) // 构建初始矩阵
await buildMatrixData(sortedGatewayPairs, initialConfig, true)
} }
catch (error) { catch (error) {
toast.error((error as Error).message || '获取数据失败') toast.error((error as Error).message || '获取数据失败')
@@ -259,11 +276,10 @@ function GatewayConfigContent() {
setLoading(false) setLoading(false)
} }
} }
initData() initData()
}, []) }, [])
// 网关配置数据查询函数(用于表单查询 // 查询数据表单查询
const fetchData = async (filters: { const fetchData = async (filters: {
mac?: string mac?: string
public?: string public?: string
@@ -275,34 +291,26 @@ function GatewayConfigContent() {
try { try {
// 如果有初始数据,就本地筛选 // 如果有初始数据,就本地筛选
if (isInitialDataLoaded) { if (isInitialDataLoaded) {
const { filteredMacs, filteredConfigs } = filterLocalData(filters) const { filteredPairs, filteredConfigs } = filterLocalData(filters)
setData(Object.values(filteredConfigs).flat())
// 更新表格数据(扁平化所有配置数据) setTotalCount(Object.values(filteredConfigs).flat().length)
const allFilteredData = Object.values(filteredConfigs).flat() await buildMatrixData(filteredPairs, filteredConfigs, true)
setData(allFilteredData)
setTotalCount(allFilteredData.length)
// 构建矩阵数据
await buildMatrixData(filteredMacs, filteredConfigs, true)
} }
else { else {
// 如果没有初始数据,就接口查询 // 如果没有初始数据,就接口查询
const result = await getGatewayConfig(page, filters) const result = await getGatewayConfig(page, filters)
if (!result.success) throw new Error(result.error || '查询网关配置失败')
if (!result.success) {
throw new Error(result.error || '查询网关配置失败')
}
const processedData = processCityNames(result.data.items) const processedData = processCityNames(result.data.items)
const sortedData = [...processedData].sort(sortByInnerIp) const sortedData = [...processedData].sort(sortByInnerIp)
setData(sortedData) setData(sortedData)
setTotalCount(result.data.total) setTotalCount(result.data.total)
const filteredMacs = Array.from( const filteredMacs = Array.from(new Set(sortedData.map(item => item.edge).filter(Boolean))).sort() as string[]
new Set(sortedData.map(item => item.edge).filter(Boolean)), const temporaryGatewayPairs = filteredMacs
).sort() as string[] .map(mac => ({ macaddr: mac, inner_ip: '' }))
.sort((a, b) => a.macaddr.localeCompare(b.macaddr))
await buildMatrixData(filteredMacs, {}, false) await buildMatrixData(temporaryGatewayPairs, {}, false)
} }
} }
catch (error) { catch (error) {
@@ -313,6 +321,7 @@ function GatewayConfigContent() {
} }
} }
// 提交查询
const onSubmit = async (formData: FilterFormValues) => { const onSubmit = async (formData: FilterFormValues) => {
const filters = { const filters = {
mac: formData.macaddr || '', mac: formData.macaddr || '',
@@ -382,6 +391,7 @@ function GatewayConfigContent() {
</Button> </Button>
</form> </form>
</Form> </Form>
<div className="flex gap-6 p-2 items-center justify-between text-sm"> <div className="flex gap-6 p-2 items-center justify-between text-sm">
<div className="flex gap-2"> <div className="flex gap-2">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
@@ -414,15 +424,17 @@ function GatewayConfigContent() {
</div> </div>
</div> </div>
</div> </div>
<Table> <Table>
<TableHeader> <TableHeader>
<TableRow> <TableRow>
<TableHead> <TableHead className="border-r sticky left-0 bg-white z-10"></TableHead>
{gatewayPairs.map((pair, index) => (
</TableHead>
{macAddresses.map((macaddr, index) => (
<TableHead key={index} className="border-r"> <TableHead key={index} className="border-r">
{macaddr} <div className="flex flex-col items-center">
<div className="font-medium">{pair.macaddr}</div>
<div className="text-xs text-gray-500 mt-1">{pair.inner_ip}</div>
</div>
</TableHead> </TableHead>
))} ))}
</TableRow> </TableRow>
@@ -430,33 +442,28 @@ function GatewayConfigContent() {
<TableBody> <TableBody>
{matrixData.map((row, rowIndex) => ( {matrixData.map((row, rowIndex) => (
<TableRow key={rowIndex}> <TableRow key={rowIndex}>
<TableCell className="border-r">{row.inner_ip}</TableCell> <TableCell className="border-r sticky left-0 bg-white z-10">{row.inner_ip}</TableCell>
{macAddresses.map((macaddr, colIndex) => { {gatewayPairs.map((pair, colIndex) => {
const configs = row.devices[macaddr] || [] const configs = row.devices[pair.macaddr] || []
return ( return (
<TableCell key={`${rowIndex}-${colIndex}`} className="border-r"> <TableCell key={`${rowIndex}-${colIndex}`} className="border-r">
{ configs.length === 0 ? ( {configs.length === 0 ? (
<div className="text-center">-</div> <div className="text-center">-</div>
) : ( ) : (
<div className="space-y-2"> <div className="space-y-2">
{configs.map((item, itemIndex) => { {configs.map((item, itemIndex) => {
const statusConfig = { const statusConfig = {
ischange: item.ischange === 0 ischange: item.ischange === 0
? { bg: 'bg-green-100', border: 'border-green-200', text: 'text-green-800', label: '正常' } ? { bg: 'bg-green-100', text: 'text-green-800', label: '正常' }
: { bg: 'bg-yellow-100', border: 'border-yellow-200', text: 'text-yellow-800', label: '更新' }, : { bg: 'bg-yellow-100', text: 'text-yellow-800', label: '更新' },
isonline: item.isonline === 0 isonline: item.isonline === 0
? { bg: 'bg-green-100', border: 'border-green-200', text: 'text-green-800', label: '空闲' } ? { bg: 'bg-green-100', text: 'text-green-800', label: '空闲' }
: { bg: 'bg-blue-100', border: 'border-blue-200', text: 'text-blue-800', label: '在用' }, : { bg: 'bg-blue-100', text: 'text-blue-800', label: '在用' },
} }
return ( return (
<div key={itemIndex} className="flex flex-col gap-1 transition-shadow"> <div key={itemIndex} className="flex flex-col gap-1">
<div className="flex justify-between items-center"> <div className="text-sm font-medium">{item.public || 'N/A'}</div>
<span className="text-sm font-medium">{item.public || 'N/A'}</span> <div className="text-xs font-medium">{item.city || 'N/A'}</div>
</div>
<div className="flex justify-between items-center">
<span className="text-xs font-medium">{item.city || 'N/A'}</span>
</div>
<div className="flex justify-between items-center"> <div className="flex justify-between items-center">
<span className={`px-2 py-0.5 rounded text-xs font-medium ${statusConfig.ischange.bg} ${statusConfig.ischange.text}`}> <span className={`px-2 py-0.5 rounded text-xs font-medium ${statusConfig.ischange.bg} ${statusConfig.ischange.text}`}>
{statusConfig.ischange.label} {statusConfig.ischange.label}

View File

@@ -120,7 +120,6 @@ export default function Gatewayinfo() {
setLoading(true) setLoading(true)
setError('') setError('')
const result = await getGatewayInfo() const result = await getGatewayInfo()
console.log(result.data)
setData(result.data) setData(result.data)
setFilteredData(result.data) // 初始化时设置filteredData setFilteredData(result.data) // 初始化时设置filteredData

View File

@@ -261,36 +261,34 @@ export default function Settings() {
/> />
</div> </div>
<div className="border rounded-lg"> <DataTable
<DataTable data={newData}
data={newData} columns={[
columns={[ {
{ label: '账号',
label: '账号', props: 'account',
props: 'account', },
{
label: '创建时间',
props: 'createdAt',
},
{
label: '操作',
render: (val) => {
return (
<Button
variant="outline"
size="sm"
className="h-5 border-0 hover:bg-transparent"
onClick={() => handleDeleteUser(Number(val.id))}
>
<Trash2Icon className="h-4 w-4" />
</Button>
)
}, },
{ },
label: '创建时间', ]}
props: 'createdAt', />
},
{
label: '操作',
render: (val) => {
return (
<Button
variant="outline"
size="sm"
className="h-5 border-0 hover:bg-transparent"
onClick={() => handleDeleteUser(Number(val.id))}
>
<Trash2Icon className="h-4 w-4" />
</Button>
)
},
},
]}
/>
</div>
</div> </div>
<Toaster richColors /> <Toaster richColors />
</Page> </Page>