增加团队管理页面

This commit is contained in:
dengqichen 2025-10-29 14:20:50 +08:00
parent 714ced899f
commit 1886b51d18
2 changed files with 147 additions and 44 deletions

View File

@ -17,6 +17,12 @@ import {
} from '@/components/ui/select';
import { ScrollArea } from '@/components/ui/scroll-area';
import { useToast } from '@/components/ui/use-toast';
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from '@/components/ui/tooltip';
import {
GitBranch,
FolderGit2,
@ -43,7 +49,7 @@ import type {
} from './types';
import {
getGitInstances,
getRepositoryGroupTree,
getRepositoryGroups,
getRepositoryProjects,
getRepositoryBranches,
syncAllGitData,
@ -125,14 +131,56 @@ const GitManager: React.FC = () => {
}
};
// 加载仓库组树
// 构建树形结构
const buildTree = (groups: RepositoryGroupResponse[]): RepositoryGroupResponse[] => {
// 使用 repoGroupId 作为 key因为 parentId 对应的是 Git 系统中的 repoGroupId
const map = new Map<number, RepositoryGroupResponse>();
const roots: RepositoryGroupResponse[] = [];
// 首先将所有节点放入 map使用 repoGroupId 作为 key
groups.forEach((group) => {
const key = group.repoGroupId || group.id;
map.set(key, { ...group, children: [] });
});
// 然后构建树形结构
groups.forEach((group) => {
const key = group.repoGroupId || group.id;
const node = map.get(key)!;
if (group.parentId) {
// parentId 对应的是父节点的 repoGroupId
const parent = map.get(group.parentId);
if (parent) {
if (!parent.children) {
parent.children = [];
}
parent.children.push(node);
} else {
// 如果找不到父节点,作为根节点
roots.push(node);
}
} else {
// parentId 为 null作为根节点
roots.push(node);
}
});
return roots;
};
// 加载仓库组列表并构建树
const loadGroupTree = async () => {
if (!selectedInstanceId) return;
setLoading((prev) => ({ ...prev, groups: true }));
try {
const data = await getRepositoryGroupTree(selectedInstanceId);
setGroupTree(data || []);
const data = await getRepositoryGroups({
externalSystemId: selectedInstanceId,
});
// 构建树形结构
const tree = buildTree(data || []);
setGroupTree(tree);
} catch (error) {
toast({
variant: 'destructive',
@ -346,13 +394,24 @@ const GitManager: React.FC = () => {
return dayjs(time).fromNow();
};
// 渲染可见性图标
// 渲染可见性图标(带 Tooltip
const renderVisibilityIcon = (visibility: string) => {
const config =
VISIBILITY_CONFIG[visibility.toLowerCase() as keyof typeof VISIBILITY_CONFIG] ||
VISIBILITY_CONFIG.public;
const Icon = config.icon;
return <Icon className={`h-3.5 w-3.5 ${config.color}`} />;
return (
<Tooltip>
<TooltipTrigger asChild>
<div className="cursor-help">
<Icon className={`h-3.5 w-3.5 ${config.color}`} />
</div>
</TooltipTrigger>
<TooltipContent>
<p>{config.label}</p>
</TooltipContent>
</Tooltip>
);
};
// 过滤仓库组树
@ -386,7 +445,7 @@ const GitManager: React.FC = () => {
return (
<React.Fragment key={group.id}>
<div
className={`flex items-center gap-2 px-3 py-2 cursor-pointer hover:bg-accent rounded-md transition-colors ${
className={`group flex items-center gap-2 px-3 py-2 cursor-pointer hover:bg-accent rounded-md transition-colors ${
selectedGroup?.id === group.id ? 'bg-accent border-l-2 border-primary' : ''
}`}
style={{ paddingLeft: `${level * 16 + 12}px` }}
@ -416,6 +475,27 @@ const GitManager: React.FC = () => {
<span className="flex-1 truncate text-sm font-medium">
{group.name}
</span>
{group.webUrl && (
<Tooltip>
<TooltipTrigger asChild>
<a
href={group.webUrl}
target="_blank"
rel="noopener noreferrer"
onClick={(e) => e.stopPropagation()}
className="flex-shrink-0 opacity-0 group-hover:opacity-100 transition-opacity"
>
<Button variant="ghost" size="icon" className="h-5 w-5">
<ExternalLink className="h-3 w-3" />
</Button>
</a>
</TooltipTrigger>
<TooltipContent>
<p> GitLab </p>
</TooltipContent>
</Tooltip>
)}
</div>
{hasChildren && isExpanded && (
@ -466,6 +546,7 @@ const GitManager: React.FC = () => {
const filteredGroupTree = useMemo(() => filterGroupTree(groupTree), [groupTree, groupSearch]);
return (
<TooltipProvider>
<div className="flex flex-col h-full p-6 space-y-4">
{/* 顶部标题栏 */}
<div className="flex items-center justify-between">
@ -672,17 +753,24 @@ const GitManager: React.FC = () => {
</div>
</div>
{project.webUrl && (
<Tooltip>
<TooltipTrigger asChild>
<a
href={project.webUrl}
target="_blank"
rel="noopener noreferrer"
onClick={(e) => e.stopPropagation()}
className="flex-shrink-0"
>
<Button variant="ghost" size="icon" className="h-6 w-6">
<ExternalLink className="h-3 w-3" />
</Button>
</a>
onClick={(e) => e.stopPropagation()}
className="flex-shrink-0"
>
<Button variant="ghost" size="icon" className="h-6 w-6">
<ExternalLink className="h-3 w-3" />
</Button>
</a>
</TooltipTrigger>
<TooltipContent>
<p> GitLab </p>
</TooltipContent>
</Tooltip>
)}
</div>
</div>
@ -740,7 +828,7 @@ const GitManager: React.FC = () => {
<div className="divide-y">
{filteredBranches.map((branch) => (
<div key={branch.id} className="p-3 hover:bg-accent transition-colors">
<div className="flex items-start gap-2 mb-2">
<div className="flex items-start gap-2">
<GitBranch className="h-4 w-4 mt-0.5 flex-shrink-0 text-muted-foreground" />
<div className="flex-1 min-w-0">
<div className="flex items-center gap-2 flex-wrap">
@ -748,15 +836,29 @@ const GitManager: React.FC = () => {
{branch.name}
</span>
{branch.isDefaultBranch && (
<Badge variant="default" className="text-xs">
</Badge>
<Tooltip>
<TooltipTrigger asChild>
<Badge variant="default" className="text-xs cursor-help">
</Badge>
</TooltipTrigger>
<TooltipContent>
<p></p>
</TooltipContent>
</Tooltip>
)}
{branch.isProtected && (
<Badge variant="secondary" className="text-xs flex items-center gap-1">
<Shield className="h-3 w-3" />
</Badge>
<Tooltip>
<TooltipTrigger asChild>
<Badge variant="secondary" className="text-xs flex items-center gap-1 cursor-help">
<Shield className="h-3 w-3" />
</Badge>
</TooltipTrigger>
<TooltipContent>
<p></p>
</TooltipContent>
</Tooltip>
)}
</div>
{branch.lastCommitMessage && (
@ -782,26 +884,35 @@ const GitManager: React.FC = () => {
</div>
)}
</div>
</div>
{branch.webUrl && (
<a
href={branch.webUrl}
target="_blank"
rel="noopener noreferrer"
className="text-xs text-primary hover:underline inline-flex items-center gap-1"
>
<ExternalLink className="h-3 w-3" />
Git
</a>
)}
{branch.webUrl && (
<Tooltip>
<TooltipTrigger asChild>
<a
href={branch.webUrl}
target="_blank"
rel="noopener noreferrer"
className="flex-shrink-0"
>
<Button variant="ghost" size="icon" className="h-6 w-6">
<ExternalLink className="h-3 w-3" />
</Button>
</a>
</TooltipTrigger>
<TooltipContent>
<p> GitLab </p>
</TooltipContent>
</Tooltip>
)}
</div>
</div>
))}
))}
</div>
)}
)}
</div>
</Card>
</div>
</div>
</TooltipProvider>
);
};

View File

@ -35,14 +35,6 @@ export const getGitInstances = () =>
export const getRepositoryGroups = (params?: RepositoryGroupQuery) =>
request.get<RepositoryGroupResponse[]>(`${GROUP_URL}/list`, { params });
/**
*
*/
export const getRepositoryGroupTree = (externalSystemId: number) =>
request.get<RepositoryGroupResponse[]>(`${GROUP_URL}/tree`, {
params: { externalSystemId },
});
/**
*
*/