argus/src/web/src/components/NodeTable.jsx
2025-10-17 15:38:24 +08:00

146 lines
5.4 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { useState, useEffect } from "react";
import { Card, Table, Button, Loader, Center, Group, Anchor, Text } from "@mantine/core";
import { Link } from "react-router-dom";
import NodeStatus from "./NodeStatus";
import PaginationControl from "./PaginationControl";
import { apiRequest } from "../config/request";
import { MASTER_API, EXTERNAL_HOST } from "../config/api";
export function NodeTable({
withSearch = false,
withPagination = false,
withActions = false,
clusterData = null, // 直接传入数据Dashboard 用)
fetchDetail, // 详情函数NodePage 用)
title,
viewMoreLink,
}) {
const [nodes, setNodes] = useState([]);
const [page, setPage] = useState(1);
const [pageSize, setPageSize] = useState(5);
const [loading, setLoading] = useState(false);
// 拉取节点数据(仅 NodePage 使用)
const fetchNodes = async (params = {}) => {
if (!withPagination && !withSearch) return; // Dashboard 只用 clusterData
setLoading(true);
try {
const query = new URLSearchParams({
page: params.page || page,
limit: params.pageSize || pageSize,
}).toString();
const result = await apiRequest(`${MASTER_API.LIST}?${query}`);
setNodes(result);
} finally {
setLoading(false);
}
};
// 初始化加载
useEffect(() => {
if (withPagination || withSearch) {
fetchNodes();
} else if (clusterData) {
setNodes(clusterData || []);
}
}, [clusterData]);
// 表格行
const rows = nodes.map((node) => (
<Table.Tr key={node.id}>
<Table.Td>{node.id}</Table.Td>
<Table.Td>{node.name}</Table.Td>
<Table.Td><NodeStatus status={node.status} /></Table.Td>
<Table.Td>{node.type}</Table.Td>
<Table.Td>{node.version}</Table.Td>
{withActions && (
<Table.Td>
<Group spacing="xs">
{/* 查看详情 */}
<Button
size="xs"
variant="light"
onClick={() => fetchDetail && fetchDetail(node.id)}
>
查看详情
</Button>
{/* Grafana Dashboard 链接 */}
<Button
size="xs"
variant="outline"
component="a"
href={`${EXTERNAL_HOST.GRAFANA}/d/node_gpu_metrics/node-and-gpu-metrics?var-hostname=${encodeURIComponent(node.name)}`}
target="_blank"
rel="noopener noreferrer"
>
Grafana
</Button>
</Group>
</Table.Td>
)}
</Table.Tr>
));
return (
<Card shadow="sm" radius="md" p="lg">
{/* 标题 + 查看更多 */}
{(title || viewMoreLink) && (
<Group position="apart" mb="sm">
{title && <Text fw={700} size="lg">{title}</Text>}
{viewMoreLink && (
<Anchor component={Link} to={viewMoreLink} size="sm" underline>
查看更多
</Anchor>
)}
</Group>
)}
{/* 搜索区域 */}
{withSearch && (
<div style={{ display: "flex", gap: 8, marginBottom: 16 }}>
<Button onClick={() => fetchNodes()} variant="outline">刷新列表</Button>
</div>
)}
{loading ? (
<Center h={200}><Loader size="lg" /></Center>
) : (
<>
<Table striped highlightOnHover withTableBorder>
<Table.Thead>
<Table.Tr>
<Table.Th>ID</Table.Th>
<Table.Th>名称</Table.Th>
<Table.Th>状态</Table.Th>
<Table.Th>类型</Table.Th>
<Table.Th>版本</Table.Th>
{withActions && <Table.Th>操作</Table.Th>}
</Table.Tr>
</Table.Thead>
<Table.Tbody>{rows}</Table.Tbody>
</Table>
{withPagination && (
<PaginationControl
page={page}
pageSize={pageSize}
hasPrevPage={page > 1}
hasNextPage={nodes.length === pageSize}
onPageChange={(p) => {
setPage(p);
fetchNodes({ page: p });
}}
onPageSizeChange={(size) => {
setPageSize(size);
setPage(1);
fetchNodes({ page: 1, pageSize: size });
}}
/>
)}
</>
)}
</Card>
);
}