import { useState, useCallback, useEffect } from 'react'; import { Toaster } from 'sonner'; import { useLocation, useNavigate } from 'react-router'; import { useQuery } from '@tanstack/react-query'; import Sidebar from '@/components/Sidebar'; import TopBar from '@/components/TopBar'; import PipelineFlowEditor from '@/components/flow/PipelineFlowEditor'; import BasicInfoPanel from '@/components/BasicInfoPanel'; import TriggerPanel from '@/components/TriggerPanel'; import VariablesPanel from '@/components/VariablesPanel'; import ReleasePanel from '@/components/ReleasePanel'; import PipelineList from '@/pages/PipelineList'; import EnvironmentManager from '@/pages/EnvironmentManager'; import CredentialManager from '@/pages/CredentialManager'; import BuildNodeManager from '@/pages/BuildNodeManager'; import ToolConfig from '@/pages/ToolConfig'; import JenkinsImport from '@/pages/JenkinsImport'; import BatchRelease from '@/pages/BatchRelease'; import ReleaseRecords from '@/pages/ReleaseRecords'; import WebhookConfig from '@/pages/WebhookConfig'; import GitOpsDeploy from '@/pages/GitOpsDeploy'; import PluginManager from '@/pages/PluginManager'; import SystemSettings from '@/pages/SystemSettings'; import AiAssistant from '@/components/ai/AiAssistant'; import { apiClient } from '@/lib/api'; export type ModuleType = 'pipelines' | 'gitops' | 'batch-release' | 'release-records' | 'environments' | 'credentials' | 'build-nodes' | 'tools' | 'jenkins-import' | 'webhooks' | 'plugins' | 'settings'; export type EditorTab = '基本信息' | '流程配置' | '触发设置' | '变量和缓存' | '发布'; // 路由 ↔ 模块映射(用户可直接访问 /release、/pipelines/1 等) const ROUTE_TO_MODULE: Record = { '/': 'pipelines', '/pipelines': 'pipelines', '/gitops': 'gitops', '/release': 'batch-release', '/records': 'release-records', '/environments': 'environments', '/credentials': 'credentials', '/build-nodes': 'build-nodes', '/tools': 'tools', '/jenkins-import': 'jenkins-import', '/webhooks': 'webhooks', '/plugins': 'plugins', '/settings': 'settings', }; const MODULE_TO_ROUTE: Record = { pipelines: '/pipelines', gitops: '/gitops', 'batch-release': '/release', 'release-records': '/records', environments: '/environments', credentials: '/credentials', 'build-nodes': '/build-nodes', tools: '/tools', 'jenkins-import': '/jenkins-import', webhooks: '/webhooks', plugins: '/plugins', settings: '/settings', }; export default function App() { const location = useLocation(); const navigate = useNavigate(); // Module navigation const [activeModule, setActiveModule] = useState('pipelines'); const [sidebarCollapsed, setSidebarCollapsed] = useState(false); // Pipeline list/editor view toggle const [pipelineView, setPipelineView] = useState<'list' | 'editor'>('list'); const [currentPipelineId, setCurrentPipelineId] = useState(1); const [activeTab, setActiveTab] = useState('流程配置'); const [saveTick, setSaveTick] = useState(0); const [runTick, setRunTick] = useState(0); // URL → 页面状态(直接输入/刷新/分享链接时恢复) useEffect(() => { const path = location.pathname.replace(/\/+$/, '') || '/'; const editorMatch = path.match(/^\/pipelines\/(\d+)/); if (editorMatch) { setActiveModule('pipelines'); setCurrentPipelineId(Number(editorMatch[1]) || 1); setPipelineView('editor'); return; } const mod = ROUTE_TO_MODULE[path]; if (mod) { setActiveModule(mod); if (mod !== 'pipelines') { setPipelineView('list'); } else if (path === '/pipelines' || path === '/') { setPipelineView('list'); } return; } // 未知路径回首页 navigate('/pipelines', { replace: true }); }, [location.pathname, navigate]); const goToModule = useCallback((m: ModuleType) => { setActiveModule(m); navigate(MODULE_TO_ROUTE[m]); }, [navigate]); const { data: pipelines = [] } = useQuery({ queryKey: ['pipelines'], queryFn: apiClient.listPipelines, }); // 当前流水线最近一次运行状态(TopBar 徽章) const { data: currentRuns = [] } = useQuery({ queryKey: ['runs', { pipelineId: currentPipelineId }], queryFn: () => apiClient.listRuns({ pipelineId: currentPipelineId }), refetchInterval: (query) => query.state.data?.[0]?.status === 'running' || query.state.data?.[0]?.status === 'pending' ? 2000 : false, }); const currentPipeline = pipelines.find((p: any) => p.id === currentPipelineId); const latestStatus = currentRuns[0]?.status as string | undefined; const isPipelineRunning = latestStatus === 'running' || latestStatus === 'pending'; const handleSelectPipeline = useCallback((id: string) => { setCurrentPipelineId(Number(id) || 1); setPipelineView('editor'); setActiveTab('流程配置'); navigate(`/pipelines/${Number(id) || 1}`); }, [navigate]); const handlePipelineSwitch = useCallback((id: number) => { setCurrentPipelineId(id); setActiveTab('流程配置'); navigate(`/pipelines/${id}`); }, [navigate]); const handleOnlySave = useCallback(() => { if (activeTab !== '流程配置') setActiveTab('流程配置'); setSaveTick((t) => t + 1); }, [activeTab]); const handleSaveAndRun = useCallback(() => { setActiveTab('流程配置'); setRunTick((t) => t + 1); }, []); // Render content based on active module const renderContent = () => { switch (activeModule) { case 'pipelines': if (pipelineView === 'list') { return ; } // Pipeline editor mode return (
({ id: p.id, name: p.name }))} currentPipelineId={currentPipelineId} onPipelineChange={handlePipelineSwitch} activeTab={activeTab} onTabChange={setActiveTab} onOnlySave={handleOnlySave} onSaveAndRun={handleSaveAndRun} isRunning={isPipelineRunning} executionStatus={(latestStatus as 'running' | 'success' | 'failed' | 'cancelled') || 'idle'} onBack={() => { setPipelineView('list'); navigate('/pipelines'); }} />
{activeTab === '流程配置' && (
)} {activeTab === '基本信息' && (
)} {activeTab === '触发设置' && (
)} {activeTab === '变量和缓存' && (
)} {activeTab === '发布' && (
)}
); case 'environments': return ; case 'credentials': return ; case 'build-nodes': return ; case 'tools': return ; case 'jenkins-import': return ; case 'batch-release': return goToModule('release-records')} />; case 'release-records': return ; case 'webhooks': return ; case 'gitops': return ; case 'plugins': return ; case 'settings': return ; default: return ; } }; return (
setSidebarCollapsed(!sidebarCollapsed)} />
{renderContent()}
goToModule(m as ModuleType)} />
); }