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

View File

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

View File

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