1
This commit is contained in:
parent
43670b8142
commit
426297ebbc
@ -1,6 +1,6 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import React, { useState, useEffect, useRef } from 'react';
|
||||
import { PageContainer } from '@/components/ui/page-container';
|
||||
import { RefreshCw, ChevronRight } from 'lucide-react';
|
||||
import { RefreshCw, ChevronLeft, ChevronRight } from 'lucide-react';
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
@ -15,7 +15,12 @@ import {
|
||||
} from "@/components/ui/select";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { useToast } from "@/components/ui/use-toast";
|
||||
import { Separator } from "@/components/ui/separator";
|
||||
import {
|
||||
Tabs,
|
||||
TabsContent,
|
||||
TabsList,
|
||||
TabsTrigger,
|
||||
} from "@/components/ui/tabs";
|
||||
import type { JenkinsInstance, JenkinsInstanceDTO } from './types';
|
||||
import { getJenkinsInstances, getJenkinsInstance, syncViews, syncJobs, syncBuilds } from './service';
|
||||
|
||||
@ -25,12 +30,13 @@ const JenkinsManagerList: React.FC = () => {
|
||||
const [currentJenkins, setCurrentJenkins] = useState<JenkinsInstance>();
|
||||
const [instanceDetails, setInstanceDetails] = useState<JenkinsInstanceDTO>();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [currentView, setCurrentView] = useState<string>();
|
||||
const [syncing, setSyncing] = useState<Record<string, boolean>>({
|
||||
views: false,
|
||||
jobs: false,
|
||||
builds: false
|
||||
});
|
||||
const [expandedViews, setExpandedViews] = useState<Record<number, boolean>>({});
|
||||
const tabsRef = useRef<HTMLDivElement>(null);
|
||||
const { toast } = useToast();
|
||||
|
||||
// 获取 Jenkins 实例列表
|
||||
@ -110,13 +116,12 @@ const JenkinsManagerList: React.FC = () => {
|
||||
}
|
||||
};
|
||||
|
||||
// 添加切换展开/收起的处理函数
|
||||
const toggleView = (viewId: number) => {
|
||||
setExpandedViews(prev => ({
|
||||
...prev,
|
||||
[viewId]: !prev[viewId]
|
||||
}));
|
||||
};
|
||||
// 在获取实例详情后设置默认视图
|
||||
useEffect(() => {
|
||||
if (instanceDetails?.jenkinsViewList.length > 0) {
|
||||
setCurrentView(String(instanceDetails.jenkinsViewList[0].id));
|
||||
}
|
||||
}, [instanceDetails]);
|
||||
|
||||
useEffect(() => {
|
||||
loadJenkinsList();
|
||||
@ -133,6 +138,19 @@ const JenkinsManagerList: React.FC = () => {
|
||||
return time;
|
||||
};
|
||||
|
||||
const handleScroll = (direction: 'left' | 'right') => {
|
||||
if (tabsRef.current) {
|
||||
const scrollAmount = 200; // 每次滚动的距离
|
||||
const newScrollLeft = direction === 'left'
|
||||
? tabsRef.current.scrollLeft - scrollAmount
|
||||
: tabsRef.current.scrollLeft + scrollAmount;
|
||||
tabsRef.current.scrollTo({
|
||||
left: newScrollLeft,
|
||||
behavior: 'smooth'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<PageContainer>
|
||||
<div className="flex items-center justify-between mb-6">
|
||||
@ -226,34 +244,65 @@ const JenkinsManagerList: React.FC = () => {
|
||||
</Card>
|
||||
)}
|
||||
|
||||
<Card>
|
||||
<CardContent className="pt-6">
|
||||
<h3 className="text-lg font-semibold mb-4">Views</h3>
|
||||
<div className="space-y-4">
|
||||
{loading ? (
|
||||
<div className="flex items-center justify-center py-8">
|
||||
<Card>
|
||||
<CardContent className="flex items-center justify-center py-8">
|
||||
<RefreshCw className="h-6 w-6 animate-spin" />
|
||||
</div>
|
||||
) : instanceDetails?.jenkinsViewList.map((view, index) => (
|
||||
<div key={view.id}>
|
||||
{index > 0 && <Separator className="my-4" />}
|
||||
<div className="space-y-4">
|
||||
<div
|
||||
className="flex items-center justify-between cursor-pointer"
|
||||
onClick={() => toggleView(view.id)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
) : instanceDetails && (
|
||||
<Tabs value={currentView} onValueChange={setCurrentView}>
|
||||
<div className="relative mb-6">
|
||||
<div className="absolute left-0 top-0 bottom-0 flex items-center">
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="h-8 w-8 rounded-full bg-background/80 backdrop-blur-sm"
|
||||
onClick={() => handleScroll('left')}
|
||||
>
|
||||
<div>
|
||||
<h4 className="text-base font-medium">{view.viewName}</h4>
|
||||
<ChevronLeft className="h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div className="overflow-x-auto scrollbar-hide mx-10" ref={tabsRef}>
|
||||
<TabsList className="w-max">
|
||||
{instanceDetails.jenkinsViewList.map(view => (
|
||||
<TabsTrigger
|
||||
key={view.id}
|
||||
value={String(view.id)}
|
||||
className="min-w-[120px] max-w-[200px]"
|
||||
title={`${view.viewName}${view.description ? ` - ${view.description}` : ''}`}
|
||||
>
|
||||
<div className="truncate">
|
||||
<span className="font-medium">{view.viewName}</span>
|
||||
{view.description && (
|
||||
<p className="text-sm text-muted-foreground">{view.description}</p>
|
||||
<span className="ml-1 text-xs text-muted-foreground">
|
||||
({view.description})
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<ChevronRight
|
||||
className={`h-4 w-4 transition-transform ${expandedViews[view.id] ? 'rotate-90' : ''}`}
|
||||
/>
|
||||
</TabsTrigger>
|
||||
))}
|
||||
</TabsList>
|
||||
</div>
|
||||
{expandedViews[view.id] && (
|
||||
<div className="pl-4 space-y-2 border-l">
|
||||
|
||||
<div className="absolute right-0 top-0 bottom-0 flex items-center">
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="h-8 w-8 rounded-full bg-background/80 backdrop-blur-sm"
|
||||
onClick={() => handleScroll('right')}
|
||||
>
|
||||
<ChevronRight className="h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{instanceDetails.jenkinsViewList.map(view => (
|
||||
<TabsContent key={view.id} value={String(view.id)}>
|
||||
<Card>
|
||||
<CardContent className="pt-6">
|
||||
<div className="space-y-4">
|
||||
{instanceDetails.jenkinsJobList
|
||||
.filter(job => job.viewId === view.id)
|
||||
.map(job => (
|
||||
@ -280,13 +329,12 @@ const JenkinsManagerList: React.FC = () => {
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</TabsContent>
|
||||
))}
|
||||
</Tabs>
|
||||
)}
|
||||
</PageContainer>
|
||||
);
|
||||
};
|
||||
|
||||
Loading…
Reference in New Issue
Block a user