refactor(mcp): improve server list pagination and form validation

This commit is contained in:
2025-10-23 17:14:59 +08:00
parent b513565f30
commit cdc0a466b4
3 changed files with 51 additions and 36 deletions

View File

@@ -8,6 +8,7 @@ import type { IFactory, IMyLlmModel } from "@/interfaces/database/llm";
import type { LLMFactory } from "@/constants/llm"; import type { LLMFactory } from "@/constants/llm";
import type { IMcpServer, IMcpServerListResponse } from "@/interfaces/database/mcp"; import type { IMcpServer, IMcpServerListResponse } from "@/interfaces/database/mcp";
import type { IImportMcpServersRequestBody, ITestMcpRequestBody, ICreateMcpServerRequestBody } from "@/interfaces/request/mcp"; import type { IImportMcpServersRequestBody, ITestMcpRequestBody, ICreateMcpServerRequestBody } from "@/interfaces/request/mcp";
import type { IPaginationBody } from "@/interfaces/request/base";
/** /**
* 个人中心设置 * 个人中心设置
@@ -141,7 +142,7 @@ export function useTeamSetting() {
// 获取租户用户列表 // 获取租户用户列表
const fetchTenantUsers = useCallback(async () => { const fetchTenantUsers = useCallback(async () => {
if (!tenantInfo?.tenant_id) return; if (!tenantInfo?.tenant_id) return;
try { try {
const response = await userService.listTenantUser(tenantInfo.tenant_id); const response = await userService.listTenantUser(tenantInfo.tenant_id);
if (response.data.code === 0) { if (response.data.code === 0) {
@@ -254,20 +255,23 @@ export function useTeamSetting() {
/** /**
* MCP 设置 * MCP 设置
*/ */
export function useMcpSetting() { export function useMcpSetting({ refreshServer }: { refreshServer: (initial?: boolean) => Promise<void> }) {
const [mcpServers, setMcpServers] = useState<IMcpServer[]>([]); const [mcpServers, setMcpServers] = useState<IMcpServer[]>([]);
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null); const [error, setError] = useState<string | null>(null);
const [total, setTotal] = useState(0); const [total, setTotal] = useState(0);
// 获取 MCP 服务器列表 // 获取 MCP 服务器列表
const fetchMcpServers = useCallback(async () => { const fetchMcpServers = useCallback(async (params?: IPaginationBody & {
keyword?: string;
}) => {
try { try {
setLoading(true); setLoading(true);
setError(null); setError(null);
const res = await userService.listMcpServer({ const res = await userService.listMcpServer({
page: 1, page: params?.page || 1,
size: 10, size: params?.size || 8,
keyword: params?.keyword,
}); });
if (res.data.code === 0) { if (res.data.code === 0) {
const data: IMcpServerListResponse = res.data.data; const data: IMcpServerListResponse = res.data.data;
@@ -292,7 +296,7 @@ export function useMcpSetting() {
setError(null); setError(null);
const res = await userService.importMcpServer(data); const res = await userService.importMcpServer(data);
if (res.data.code === 0) { if (res.data.code === 0) {
await fetchMcpServers(); // 重新获取列表 await refreshServer();
return { success: true }; return { success: true };
} else { } else {
throw new Error(res.data.message || '导入 MCP 服务器失败'); throw new Error(res.data.message || '导入 MCP 服务器失败');
@@ -305,7 +309,7 @@ export function useMcpSetting() {
} finally { } finally {
setLoading(false); setLoading(false);
} }
}, [fetchMcpServers]); }, []);
// 导出 MCP 服务器 // 导出 MCP 服务器
const exportMcpServers = useCallback(async (mcpIds: string[]) => { const exportMcpServers = useCallback(async (mcpIds: string[]) => {
@@ -386,7 +390,7 @@ export function useMcpSetting() {
setError(null); setError(null);
const res = await userService.createMcpServer(data); const res = await userService.createMcpServer(data);
if (res.data.code === 0) { if (res.data.code === 0) {
await fetchMcpServers(); // 重新获取列表 await refreshServer(true); // 重新获取列表
return { success: true, data: res.data.data }; return { success: true, data: res.data.data };
} else { } else {
throw new Error(res.data.message || '创建 MCP 服务器失败'); throw new Error(res.data.message || '创建 MCP 服务器失败');
@@ -399,7 +403,7 @@ export function useMcpSetting() {
} finally { } finally {
setLoading(false); setLoading(false);
} }
}, [fetchMcpServers]); }, []);
// 更新 MCP 服务器 // 更新 MCP 服务器
const updateMcpServer = useCallback(async (data: ICreateMcpServerRequestBody & { mcp_id: string }) => { const updateMcpServer = useCallback(async (data: ICreateMcpServerRequestBody & { mcp_id: string }) => {
@@ -408,7 +412,7 @@ export function useMcpSetting() {
setError(null); setError(null);
const res = await userService.updateMcpServer(data); const res = await userService.updateMcpServer(data);
if (res.data.code === 0) { if (res.data.code === 0) {
await fetchMcpServers(); // 重新获取列表 await refreshServer(); // 刷新列表
return { success: true, data: res.data.data }; return { success: true, data: res.data.data };
} else { } else {
throw new Error(res.data.message || '更新 MCP 服务器失败'); throw new Error(res.data.message || '更新 MCP 服务器失败');
@@ -421,7 +425,7 @@ export function useMcpSetting() {
} finally { } finally {
setLoading(false); setLoading(false);
} }
}, [fetchMcpServers]); }, []);
// 删除 MCP 服务器 // 删除 MCP 服务器
const deleteMcpServer = useCallback(async (mcpId: string) => { const deleteMcpServer = useCallback(async (mcpId: string) => {
@@ -430,7 +434,7 @@ export function useMcpSetting() {
setError(null); setError(null);
const res = await userService.removeMcpServer([mcpId]); const res = await userService.removeMcpServer([mcpId]);
if (res.data.code === 0) { if (res.data.code === 0) {
await fetchMcpServers(); // 重新获取列表 await refreshServer(); // 刷新列表
return { success: true }; return { success: true };
} else { } else {
throw new Error(res.data.message || '删除 MCP 服务器失败'); throw new Error(res.data.message || '删除 MCP 服务器失败');
@@ -443,10 +447,10 @@ export function useMcpSetting() {
} finally { } finally {
setLoading(false); setLoading(false);
} }
}, [fetchMcpServers]); }, []);
// 批量删除 MCP 服务器 // 批量删除 MCP 服务器
const deleteMcpServers = useCallback(async (mcpIds: string[]) => { const batchDeleteMcpServers = useCallback(async (mcpIds: string[]) => {
try { try {
setLoading(true); setLoading(true);
setError(null); setError(null);
@@ -465,7 +469,7 @@ export function useMcpSetting() {
} finally { } finally {
setLoading(false); setLoading(false);
} }
}, [fetchMcpServers]); }, []);
return { return {
// 状态 // 状态
@@ -473,7 +477,7 @@ export function useMcpSetting() {
loading, loading,
error, error,
total, total,
// 方法 // 方法
fetchMcpServers, fetchMcpServers,
importMcpServers, importMcpServers,
@@ -483,7 +487,7 @@ export function useMcpSetting() {
createMcpServer, createMcpServer,
updateMcpServer, updateMcpServer,
deleteMcpServer, deleteMcpServer,
deleteMcpServers, batchDeleteMcpServers,
}; };
} }

View File

@@ -83,8 +83,6 @@ const McpForm: React.FC<McpFormProps> = ({
const [testLoading, setTestLoading] = useState(false); const [testLoading, setTestLoading] = useState(false);
const [testResult, setTestResult] = useState<{ const [testResult, setTestResult] = useState<{
success: boolean; success: boolean;
tools?: McpTool[]; tools?: McpTool[];
@@ -121,7 +119,7 @@ const McpForm: React.FC<McpFormProps> = ({
return t return t
}); });
setTestResult({ setTestResult({
success: true, success: tools.length > 0,
tools, tools,
}); });
} }
@@ -201,7 +199,7 @@ const McpForm: React.FC<McpFormProps> = ({
} }
setTestResult({ setTestResult({
success: true, success: tools.length > 0,
tools, tools,
}); });
} else { } else {
@@ -359,7 +357,7 @@ const McpForm: React.FC<McpFormProps> = ({
<Button <Button
variant="contained" variant="contained"
onClick={handleSubmit} onClick={handleSubmit}
disabled={loading || disabled} disabled={loading || disabled || !testResult?.success}
startIcon={loading ? <CircularProgress size={16} /> : undefined} startIcon={loading ? <CircularProgress size={16} /> : undefined}
> >
{loading ? '保存中...' : '保存'} {loading ? '保存中...' : '保存'}

View File

@@ -69,6 +69,19 @@ const SearchContainer = styled(Box)(({ theme }) => ({
export default function McpSettingPage() { export default function McpSettingPage() {
const handleRefreshServer = async (initial?: boolean) => {
if (initial) {
setCurrentPage(1);
} else {
await fetchMcpServers({
page: currentPage,
size: pageSize,
keyword: searchString,
});
}
}
const { const {
mcpServers, mcpServers,
loading, loading,
@@ -81,8 +94,8 @@ export default function McpSettingPage() {
createMcpServer, createMcpServer,
updateMcpServer, updateMcpServer,
deleteMcpServer, deleteMcpServer,
deleteMcpServers, batchDeleteMcpServers,
} = useMcpSetting(); } = useMcpSetting({ refreshServer: handleRefreshServer });
// 本地状态 // 本地状态
const [searchString, setSearchString] = useState(''); const [searchString, setSearchString] = useState('');
@@ -93,24 +106,22 @@ export default function McpSettingPage() {
const [editingServer, setEditingServer] = useState<Partial<IMcpServer> | undefined | null>(null); const [editingServer, setEditingServer] = useState<Partial<IMcpServer> | undefined | null>(null);
const [importDialogOpen, setImportDialogOpen] = useState(false); const [importDialogOpen, setImportDialogOpen] = useState(false);
const [currentPage, setCurrentPage] = useState(1); const [currentPage, setCurrentPage] = useState(1);
const [pageSize] = useState(6); const [pageSize] = useState(8);
const [importJson, setImportJson] = useState(''); const [importJson, setImportJson] = useState('');
const showMessage = useMessage() const showMessage = useMessage()
// 初始化加载数据 // 初始化加载数据
useEffect(() => { useEffect(() => {
fetchMcpServers(); fetchMcpServers({
}, [fetchMcpServers]); page: currentPage,
size: pageSize,
keyword: searchString,
});
}, [fetchMcpServers, currentPage, pageSize, searchString]);
// 过滤和分页逻辑 const totalPages = Math.ceil(total / pageSize);
const filteredServers = mcpServers.filter(server => const paginatedServers = mcpServers.slice(
server.name.toLowerCase().includes(searchString.toLowerCase()) ||
server.url.toLowerCase().includes(searchString.toLowerCase())
);
const totalPages = Math.ceil(filteredServers.length / pageSize);
const paginatedServers = filteredServers.slice(
(currentPage - 1) * pageSize, (currentPage - 1) * pageSize,
currentPage * pageSize currentPage * pageSize
); );
@@ -185,7 +196,7 @@ export default function McpSettingPage() {
}; };
const handleBulkDelete = async () => { const handleBulkDelete = async () => {
const result = await deleteMcpServers(selectedServers); const result = await batchDeleteMcpServers(selectedServers);
if (result.success) { if (result.success) {
showMessage.success('批量删除成功'); showMessage.success('批量删除成功');
setSelectedServers([]); setSelectedServers([]);
@@ -309,6 +320,7 @@ export default function McpSettingPage() {
</PageHeader> </PageHeader>
<SearchContainer> <SearchContainer>
<Box>
<TextField <TextField
size="small" size="small"
placeholder="Search MCP servers..." placeholder="Search MCP servers..."
@@ -319,6 +331,7 @@ export default function McpSettingPage() {
}} }}
sx={{ width: 300 }} sx={{ width: 300 }}
/> />
</Box>
{selectedServers.length > 0 && ( {selectedServers.length > 0 && (
<Box display="flex" gap={1}> <Box display="flex" gap={1}>
<Button <Button