From f6917d63d38f0ca8117d54ebba748aabedbfaac1 Mon Sep 17 00:00:00 2001 From: WangDL Date: Sun, 24 May 2026 10:18:22 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20M1-01~03=20admin=20pages=20=E2=80=94=20?= =?UTF-8?q?AI=20Gateway,=20Vector,=20Events=20deepening?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit M1-01 AI Gateway: - Tab-based layout: Overview, Routes CRUD, Provider toggle, Fallback logs M1-02 Vector: - New VectorAdmin page: collection stats, info panel, reindex trigger - Route /vector + menu entry under 系统运维 M1-03 Events: - Tab-based layout: Queue overview (with batch retry), Task statistics - Worker status panel, 16 task type configs table Co-Authored-By: Claude Opus 4.7 --- src/App.tsx | 5 + src/config/menu.tsx | 1 + src/pages/AiGateway.tsx | 194 ++++++++++++++++++++++++++++++-------- src/pages/Events.tsx | 115 ++++++++++++++++++---- src/pages/VectorAdmin.tsx | 82 ++++++++++++++++ 5 files changed, 340 insertions(+), 57 deletions(-) create mode 100644 src/pages/VectorAdmin.tsx diff --git a/src/App.tsx b/src/App.tsx index 708344a..e550018 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -23,6 +23,7 @@ const AiGatewayPage = lazy(() => import("./pages/AiGateway")) const CSPage = lazy(() => import("./pages/ContentSafety")) const MetricsPage = lazy(() => import("./pages/Metrics")) const EventsPage = lazy(() => import("./pages/Events")) +const VectorAdminPage = lazy(() => import("./pages/VectorAdmin")) const ServersPage = lazy(() => import("./pages/Servers")) const AuditLogPage = lazy(() => import("./pages/AuditLog")) const Dashboard = lazy(() => import('./pages/Dashboard')) @@ -141,6 +142,10 @@ function App() { path="events" element={}>} /> + }>} + /> => api.get('/admin-api/ai-gateway/status'), staleTime: 30_000 }) + const { message } = App.useApp() + const [activeTab, setActiveTab] = useState('overview') + const [routeModal, setRouteModal] = useState<{ open: boolean; editing?: any }>({ open: false }) + const [form] = Form.useForm() - const tierCols = [ - { title: '级别', dataIndex: 'name', width: 80 }, - { title: '主模型', dataIndex: 'provider', width: 120 }, - { title: '模型', dataIndex: 'model', width: 180 }, - { title: '备用', dataIndex: 'fallback', width: 180, render: (v: string) => v || '-' }, + const { data: status } = useQuery({ + queryKey: ['ai-gateway', 'status'], + queryFn: (): Promise => api.get('/admin-api/ai-gateway/status'), + staleTime: 30_000, + }) + + const { data: routes, refetch: refetchRoutes } = useQuery({ + queryKey: ['ai-gateway', 'routes'], + queryFn: (): Promise => api.get('/admin-api/ai-gateway/routes'), + enabled: activeTab === 'routes', + }) + + const { data: providers, refetch: refetchProviders } = useQuery({ + queryKey: ['ai-gateway', 'providers'], + queryFn: (): Promise => api.get('/admin-api/ai-gateway/providers'), + enabled: activeTab === 'providers', + }) + + const { data: fallbackEvents } = useQuery({ + queryKey: ['ai-gateway', 'fallback-events'], + queryFn: (): Promise => api.get('/admin-api/ai-gateway/fallback-events'), + enabled: activeTab === 'fallback', + refetchInterval: 30_000, + }) + + const createRoute = useMutation({ + mutationFn: (values: any) => api.post('/admin-api/ai-gateway/routes', values), + onSuccess: () => { message.success('路由已创建'); refetchRoutes(); setRouteModal({ open: false }); form.resetFields() }, + }) + + const deleteRoute = useMutation({ + mutationFn: (id: string) => api.delete(`/admin-api/ai-gateway/routes/${id}`), + onSuccess: () => { message.success('路由已删除'); refetchRoutes() }, + }) + + const toggleProvider = useMutation({ + mutationFn: ({ name, enabled }: { name: string; enabled: boolean }) => api.put(`/admin-api/ai-gateway/providers/${name}`, { enabled }), + onSuccess: () => { message.success('Provider 已更新'); refetchProviders() }, + }) + + const routeColumns = [ + { title: '级别', dataIndex: 'tier', width: 80, render: (v: string) => {v} }, + { title: '任务类型', dataIndex: 'taskType', width: 100 }, + { title: '首选 Provider', dataIndex: 'preferredProvider', width: 120, render: (v: string) => {v} }, + { title: '首选模型', dataIndex: 'preferredModel', width: 180, ellipsis: true }, + { title: '备用 Provider', dataIndex: 'fallbackProvider', width: 120, render: (v: string) => {v} }, + { title: '备用模型', dataIndex: 'fallbackModel', width: 180, ellipsis: true }, + { title: '重试', dataIndex: 'maxRetries', width: 60, align: 'center' as const }, + { title: '状态', dataIndex: 'isActive', width: 70, render: (v: boolean) => v ? 启用 : 禁用 }, + { title: '操作', width: 80, render: (_: any, r: any) => ( +