From 43670b8142c3d1022d965603f05f58a007badac5 Mon Sep 17 00:00:00 2001 From: asp_ly Date: Sun, 29 Dec 2024 19:32:05 +0800 Subject: [PATCH] 1 --- .../Deploy/JenkinsManager/List/index.tsx | 109 +++++++++++------- .../Deploy/JenkinsManager/List/service.ts | 53 +-------- .../pages/Deploy/JenkinsManager/List/types.ts | 45 +++++--- 3 files changed, 95 insertions(+), 112 deletions(-) diff --git a/frontend/src/pages/Deploy/JenkinsManager/List/index.tsx b/frontend/src/pages/Deploy/JenkinsManager/List/index.tsx index 6ce2e070..ff00b616 100644 --- a/frontend/src/pages/Deploy/JenkinsManager/List/index.tsx +++ b/frontend/src/pages/Deploy/JenkinsManager/List/index.tsx @@ -16,20 +16,21 @@ import { import { Badge } from "@/components/ui/badge"; import { useToast } from "@/components/ui/use-toast"; import { Separator } from "@/components/ui/separator"; -import type { JenkinsInstance, JenkinsView, SyncType } from './types'; -import { getJenkinsInstances, getJenkinsViews, syncViews, syncJobs, syncBuilds } from './service'; +import type { JenkinsInstance, JenkinsInstanceDTO } from './types'; +import { getJenkinsInstances, getJenkinsInstance, syncViews, syncJobs, syncBuilds } from './service'; const JenkinsManagerList: React.FC = () => { const [jenkinsList, setJenkinsList] = useState([]); const [currentJenkinsId, setCurrentJenkinsId] = useState(); const [currentJenkins, setCurrentJenkins] = useState(); - const [views, setViews] = useState([]); + const [instanceDetails, setInstanceDetails] = useState(); const [loading, setLoading] = useState(false); - const [syncing, setSyncing] = useState>({ + const [syncing, setSyncing] = useState>({ views: false, jobs: false, builds: false }); + const [expandedViews, setExpandedViews] = useState>({}); const { toast } = useToast(); // 获取 Jenkins 实例列表 @@ -51,17 +52,17 @@ const JenkinsManagerList: React.FC = () => { } }; - // 获取视图列表 - const loadViews = async () => { + // 获取 Jenkins 实例详情 + const loadInstanceDetails = async () => { if (!currentJenkinsId) return; setLoading(true); try { - const data = await getJenkinsViews(currentJenkinsId); - setViews(data); + const data = await getJenkinsInstance(currentJenkinsId); + setInstanceDetails(data); } catch (error) { toast({ variant: "destructive", - title: "获取视图列表失败", + title: "获取实例详情失败", duration: 3000, }); } finally { @@ -74,18 +75,17 @@ const JenkinsManagerList: React.FC = () => { setCurrentJenkinsId(id); const jenkins = jenkinsList.find(j => String(j.id) === id); setCurrentJenkins(jenkins); - setViews([]); + setInstanceDetails(undefined); }; // 同步数据 - const handleSync = async (type: SyncType) => { + const handleSync = async (type: 'views' | 'jobs' | 'builds') => { if (!currentJenkinsId) return; setSyncing(prev => ({ ...prev, [type]: true })); try { switch (type) { case 'views': await syncViews(currentJenkinsId); - await loadViews(); // 重新加载视图数据 break; case 'jobs': await syncJobs(currentJenkinsId); @@ -94,7 +94,7 @@ const JenkinsManagerList: React.FC = () => { await syncBuilds(currentJenkinsId); break; } - await loadJenkinsList(); // 重新加载实例��据以更新同步时间 + await loadInstanceDetails(); // 重新加载实例详情 toast({ title: "同步成功", duration: 3000, @@ -110,13 +110,21 @@ const JenkinsManagerList: React.FC = () => { } }; + // 添加切换展开/收起的处理函数 + const toggleView = (viewId: number) => { + setExpandedViews(prev => ({ + ...prev, + [viewId]: !prev[viewId] + })); + }; + useEffect(() => { loadJenkinsList(); }, []); useEffect(() => { if (currentJenkinsId) { - loadViews(); + loadInstanceDetails(); } }, [currentJenkinsId]); @@ -125,13 +133,6 @@ const JenkinsManagerList: React.FC = () => { return time; }; - // 模拟统计数据 - const mockStats = { - views: 5, - jobs: 20, - builds: 100 - }; - return (
@@ -166,7 +167,7 @@ const JenkinsManagerList: React.FC = () => {
Views - {mockStats.views} + {instanceDetails?.totalViews || 0}
Last sync: {formatTime(currentJenkins.lastSyncTime)} @@ -185,7 +186,7 @@ const JenkinsManagerList: React.FC = () => {
Jobs - {mockStats.jobs} + {instanceDetails?.totalJobs || 0}
Last sync: {formatTime(currentJenkins.lastSyncTime)} @@ -204,7 +205,7 @@ const JenkinsManagerList: React.FC = () => {
Builds - {mockStats.builds} + {instanceDetails?.totalBuilds || 0}
Last sync: {formatTime(currentJenkins.lastSyncTime)} @@ -233,31 +234,51 @@ const JenkinsManagerList: React.FC = () => {
- ) : views.map((view, index) => ( + ) : instanceDetails?.jenkinsViewList.map((view, index) => (
{index > 0 && }
-
-

{view.name}

- +
toggleView(view.id)} + > +
+

{view.viewName}

+ {view.description && ( +

{view.description}

+ )} +
+
- {view.jobs.length > 0 && ( -
- {view.jobs.map(job => ( -
- {job.name} - {job.lastBuild && ( -
- - #{job.lastBuild.number} - {job.lastBuild.result} - - {job.lastBuild.timestamp} + {expandedViews[view.id] && ( +
+ {instanceDetails.jenkinsJobList + .filter(job => job.viewId === view.id) + .map(job => ( +
+
+ + {job.jobName} + + {job.description && ( +

{job.description}

+ )}
- )} -
- ))} +
+ + #{job.lastBuildNumber} - {job.lastBuildStatus} + + {formatTime(job.lastBuildTime)} +
+
+ ))}
)}
diff --git a/frontend/src/pages/Deploy/JenkinsManager/List/service.ts b/frontend/src/pages/Deploy/JenkinsManager/List/service.ts index e4a1636c..4e5c54c0 100644 --- a/frontend/src/pages/Deploy/JenkinsManager/List/service.ts +++ b/frontend/src/pages/Deploy/JenkinsManager/List/service.ts @@ -1,5 +1,5 @@ import request from '@/utils/request'; -import type { JenkinsInstance, JenkinsView } from './types'; +import type { JenkinsInstance, JenkinsInstanceDTO } from './types'; import { getExternalSystems } from '@/pages/Deploy/External/service'; import { SystemType } from '@/pages/Deploy/External/types'; @@ -10,54 +10,9 @@ export const getJenkinsInstances = () => enabled: true }).then(response => response.content); -// 获取 Jenkins 视图列表 -export const getJenkinsViews = (jenkinsId: string) => - // 模拟数据 - Promise.resolve([ - { - id: '1', - name: 'All', - url: 'https://jenkins.prod.example.com/view/all', - jobs: [] - }, - { - id: '2', - name: 'Frontend', - url: 'https://jenkins.prod.example.com/view/frontend', - jobs: [ - { - id: '1', - name: 'Build Frontend', - url: 'https://jenkins.prod.example.com/job/build-frontend', - lastBuild: { - id: '42', - number: 42, - result: 'SUCCESS', - timestamp: '2024/12/28 20:28:43', - url: 'https://jenkins.prod.example.com/job/build-frontend/42' - } - }, - { - id: '2', - name: 'Test Backend', - url: 'https://jenkins.prod.example.com/job/test-backend', - lastBuild: { - id: '41', - number: 41, - result: 'FAILURE', - timestamp: '2024/12/28 19:28:43', - url: 'https://jenkins.prod.example.com/job/test-backend/41' - } - } - ] - }, - { - id: '3', - name: 'Backend', - url: 'https://jenkins.prod.example.com/view/backend', - jobs: [] - } - ]); +// 获取 Jenkins 实例详情 +export const getJenkinsInstance = (externalSystemId: string) => + request.get(`/api/v1/jenkins-manager/${externalSystemId}/instance`); // 同步视图 export const syncViews = (externalSystemId: string) => diff --git a/frontend/src/pages/Deploy/JenkinsManager/List/types.ts b/frontend/src/pages/Deploy/JenkinsManager/List/types.ts index da3c4e2a..48c85aa1 100644 --- a/frontend/src/pages/Deploy/JenkinsManager/List/types.ts +++ b/frontend/src/pages/Deploy/JenkinsManager/List/types.ts @@ -4,29 +4,36 @@ import type { ExternalSystemResponse } from '@/pages/Deploy/External/types'; // 使用外部系统响应作为 Jenkins 实例 export type JenkinsInstance = ExternalSystemResponse; -// Jenkins 视图类型 -export interface JenkinsView { - id: string; - name: string; - url: string; - jobs: JenkinsJob[]; +// Jenkins 实例详情 +export interface JenkinsInstanceDTO extends BaseResponse { + totalViews: number; + totalJobs: number; + totalBuilds: number; + jenkinsViewList: JenkinsViewDTO[]; + jenkinsJobList: JenkinsJobDTO[]; } -// Jenkins Job 类型 -export interface JenkinsJob { - id: string; - name: string; - url: string; - lastBuild?: JenkinsBuild; +// Jenkins 视图 +export interface JenkinsViewDTO extends BaseResponse { + description: string; + externalSystemId: number; + viewName: string; + viewUrl: string; } -// Jenkins 构建类型 -export interface JenkinsBuild { - id: string; - number: number; - result: 'SUCCESS' | 'FAILURE' | 'RUNNING' | 'ABORTED'; - timestamp: string; - url: string; +// Jenkins 任务 +export interface JenkinsJobDTO extends BaseResponse { + buildable: boolean; + description: string; + jobName: string; + jobUrl: string; + nextBuildNumber: number; + lastBuildNumber: number; + lastBuildStatus: string; + healthReportScore: number; + lastBuildTime: string; + externalSystemId: number; + viewId: number; } // 同步类型