This commit is contained in:
asp_ly 2024-12-29 19:43:18 +08:00
parent 43670b8142
commit 426297ebbc

View File

@ -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>
);
};