增加团队管理页面
This commit is contained in:
parent
714ced899f
commit
1886b51d18
@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@ -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 },
|
||||
});
|
||||
|
||||
/**
|
||||
* 同步仓库组数据
|
||||
*/
|
||||
|
||||
Loading…
Reference in New Issue
Block a user