增加团队管理页面

This commit is contained in:
dengqichen 2025-10-30 10:53:39 +08:00
parent e087b6abd8
commit f60bcf704b
6 changed files with 76 additions and 61 deletions

View File

@ -38,7 +38,6 @@ import {
ChevronDown, ChevronDown,
ExternalLink, ExternalLink,
Shield,
Code2, Code2,
Calendar, Calendar,
User, User,
@ -379,26 +378,14 @@ const GitManager: React.FC = () => {
// 选中仓库组(支持反选取消) // 选中仓库组(支持反选取消)
const handleSelectGroup = useCallback((group: RepositoryGroupResponse) => { const handleSelectGroup = useCallback((group: RepositoryGroupResponse) => {
// 如果点击的是已选中的组,则取消选择 // 如果点击的是已选中的组,则取消选择
setSelectedGroup((prev) => { setSelectedGroup((prev) => prev?.id === group.id ? undefined : group);
const newGroup = prev?.id === group.id ? undefined : group; }, []);
setSelectedProject(undefined);
setBranches([]);
// 加载项目列表,如果取消选择则不传 repoGroupId加载所有项目
loadProjects(newGroup?.repoGroupId || newGroup?.id);
return newGroup;
});
}, [loadProjects]);
// 选中项目(支持反选取消) // 选中项目(支持反选取消)
const handleSelectProject = useCallback((project: RepositoryProjectResponse) => { const handleSelectProject = useCallback((project: RepositoryProjectResponse) => {
// 如果点击的是已选中的项目,则取消选择 // 如果点击的是已选中的项目,则取消选择
setSelectedProject((prev) => { setSelectedProject((prev) => prev?.id === project.id ? undefined : project);
const newProject = prev?.id === project.id ? undefined : project; }, []);
// 加载分支列表,如果取消选择则不传 repoProjectId加载所有分支
loadBranches(newProject?.repoProjectId || newProject?.id);
return newProject;
});
}, [loadBranches]);
// 格式化时间 // 格式化时间
const formatTime = useCallback((time?: string) => { const formatTime = useCallback((time?: string) => {
@ -571,6 +558,7 @@ const GitManager: React.FC = () => {
loadInstancesEffect(); loadInstancesEffect();
}, [selectedInstanceId, toast]); }, [selectedInstanceId, toast]);
// 监听实例变化
useEffect(() => { useEffect(() => {
if (selectedInstanceId) { if (selectedInstanceId) {
loadGroupTree(); loadGroupTree();
@ -581,6 +569,26 @@ const GitManager: React.FC = () => {
} }
}, [selectedInstanceId, loadGroupTree]); }, [selectedInstanceId, loadGroupTree]);
// 监听仓库组加载完成 或 仓库组选择变化,自动加载项目列表
useEffect(() => {
// 只有当仓库组不在加载中时才加载项目
if (selectedInstanceId && !loading.groups) {
setSelectedProject(undefined);
setBranches([]);
// 如果选择了组传递repoGroupId否则不传加载所有项目
loadProjects(selectedGroup?.repoGroupId || selectedGroup?.id);
}
}, [loading.groups, selectedInstanceId, selectedGroup, loadProjects]);
// 监听项目加载完成 或 项目选择变化,自动加载分支列表
useEffect(() => {
// 只有当项目不在加载中时才加载分支
if (selectedInstanceId && !loading.projects) {
// 如果选择了项目传递repoProjectId否则不传加载所有分支
loadBranches(selectedProject?.repoProjectId || selectedProject?.id);
}
}, [loading.projects, selectedInstanceId, selectedProject, loadBranches]);
const filteredGroupTree = useMemo(() => filterGroupTree(groupTree), [groupTree, filterGroupTree]); const filteredGroupTree = useMemo(() => filterGroupTree(groupTree), [groupTree, filterGroupTree]);
return ( return (
@ -910,24 +918,11 @@ const GitManager: React.FC = () => {
</TooltipContent> </TooltipContent>
</Tooltip> </Tooltip>
)} )}
{branch.isProtected && (
<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> </div>
{branch.lastCommitMessage && ( {branch.commitMessage && (
<p className="text-xs text-muted-foreground mt-1 line-clamp-2"> <p className="text-xs text-muted-foreground mt-1 line-clamp-2">
<GitCommit className="h-3 w-3 inline mr-1" /> <GitCommit className="h-3 w-3 inline mr-1" />
{branch.lastCommitMessage} {branch.commitMessage}
</p> </p>
)} )}
{(branch.commitAuthor || branch.commitDate) && ( {(branch.commitAuthor || branch.commitDate) && (

View File

@ -71,17 +71,27 @@ export const syncRepositoryProjects = (externalSystemId: number, repoGroupId?: n
// ==================== 仓库分支 ==================== // ==================== 仓库分支 ====================
/** /**
* *
*/ */
export const getRepositoryBranches = (params?: RepositoryBranchQuery) => export const getRepositoryBranches = (params?: RepositoryBranchQuery) =>
request.get<RepositoryBranchResponse[]>(`${BRANCH_URL}/list`, { params }); request.get<RepositoryBranchResponse[]>(`${BRANCH_URL}/list`, {
params: {
...params,
sortField: 'lastUpdateTime',
sortOrder: 'desc'
}
});
/** /**
* ID获取分支列表 * ID获取分支列表
*/ */
export const getBranchesByProjectId = (repoProjectId: number) => export const getBranchesByProjectId = (repoProjectId: number) =>
request.get<RepositoryBranchResponse[]>(`${BRANCH_URL}/list`, { request.get<RepositoryBranchResponse[]>(`${BRANCH_URL}/list`, {
params: { repoProjectId }, params: {
repoProjectId,
sortField: 'lastUpdateTime',
sortOrder: 'desc'
},
}); });
/** /**

View File

@ -74,8 +74,6 @@ export interface RepositoryBranchResponse extends BaseResponse {
name: string; name: string;
/** 是否为默认分支 */ /** 是否为默认分支 */
isDefaultBranch?: boolean; isDefaultBranch?: boolean;
/** 是否受保护 */
isProtected?: boolean;
/** 是否可推送 */ /** 是否可推送 */
canPush?: boolean; canPush?: boolean;
/** 开发者是否可推送 */ /** 开发者是否可推送 */
@ -84,15 +82,19 @@ export interface RepositoryBranchResponse extends BaseResponse {
developersCanMerge?: boolean; developersCanMerge?: boolean;
/** 最后提交ID */ /** 最后提交ID */
lastCommitId?: string; lastCommitId?: string;
/** 最后提交信息 */ /** 提交信息 */
lastCommitMessage?: string; commitMessage?: string;
/** 提交作者 */ /** 提交作者 */
commitAuthor?: string; commitAuthor?: string;
/** 提交日期 */ /** 提交日期 */
commitDate?: string; commitDate?: string;
/** 最后更新时间 */
lastUpdateTime?: string;
/** 网页URL */ /** 网页URL */
webUrl?: string; webUrl?: string;
/** 所属项目ID */ /** 项目ID */
projectId?: number;
/** 所属项目ID外部系统中的项目ID */
repoProjectId: number; repoProjectId: number;
/** 外部系统ID */ /** 外部系统ID */
externalSystemId: number; externalSystemId: number;

View File

@ -309,23 +309,25 @@ const JenkinsManager: React.FC = () => {
} }
}, [selectedInstanceId, loadViews]); }, [selectedInstanceId, loadViews]);
// 监听视图选择变化 // 监听视图加载完成 或 视图选择变化,自动加载任务列表
useEffect(() => { useEffect(() => {
if (selectedInstanceId) { // 只有当视图不在加载中时才加载任务
if (selectedInstanceId && !loading.views) {
setSelectedJob(undefined); setSelectedJob(undefined);
setBuilds([]); setBuilds([]);
// 如果选择了视图传递viewId否则不传加载所有任务 // 如果选择了视图传递viewId否则不传加载所有任务
loadJobs(selectedView?.id); loadJobs(selectedView?.id);
} }
}, [selectedView, selectedInstanceId, loadJobs]); }, [loading.views, selectedInstanceId, selectedView, loadJobs]);
// 监听任务选择变化 // 监听任务加载完成 或 任务选择变化,自动加载构建列表
useEffect(() => { useEffect(() => {
if (selectedInstanceId) { // 只有当任务不在加载中时才加载构建
if (selectedInstanceId && !loading.jobs) {
// 如果选择了任务传递jobId否则不传加载所有构建 // 如果选择了任务传递jobId否则不传加载所有构建
loadBuilds(selectedJob?.id); loadBuilds(selectedJob?.id);
} }
}, [selectedJob, selectedInstanceId, loadBuilds]); }, [loading.jobs, selectedInstanceId, selectedJob, loadBuilds]);
// 过滤视图(使用防抖搜索值) // 过滤视图(使用防抖搜索值)
const filteredViews = useMemo(() => { const filteredViews = useMemo(() => {

View File

@ -36,9 +36,15 @@ export const syncJenkinsJobs = (params: { externalSystemId: number; viewId?: num
// ==================== Jenkins 构建 ==================== // ==================== Jenkins 构建 ====================
// 获取构建列表 // 获取构建列表(按开始时间倒序排序)
export const getJenkinsBuilds = (params: { externalSystemId: number; jobId?: number }) => export const getJenkinsBuilds = (params: { externalSystemId: number; jobId?: number }) =>
request.get<JenkinsBuildDTO[]>(`/api/v1/jenkins-build/list`, { params }); request.get<JenkinsBuildDTO[]>(`/api/v1/jenkins-build/list`, {
params: {
...params,
sortField: 'starttime',
sortOrder: 'desc'
}
});
// 同步构建根据jobId // 同步构建根据jobId
export const syncJenkinsBuilds = (params: { externalSystemId: number; jobId?: number }) => export const syncJenkinsBuilds = (params: { externalSystemId: number; jobId?: number }) =>

View File

@ -282,8 +282,8 @@ const MemberManageDialog: React.FC<MemberManageDialogProps> = ({
</DialogTitle> </DialogTitle>
</DialogHeader> </DialogHeader>
<div className="flex-1 overflow-hidden flex flex-col px-6 pb-6"> <div className="flex-1 overflow-hidden flex flex-col px-6 pb-6">
<div className="space-y-4 flex-1 overflow-y-auto"> <div className="space-y-4 flex-1 overflow-y-auto">
{/* 操作栏 */} {/* 操作栏 */}
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<div className="relative flex-1"> <div className="relative flex-1">
@ -358,8 +358,8 @@ const MemberManageDialog: React.FC<MemberManageDialogProps> = ({
</> </>
) : ( ) : (
<> <>
<Plus className="h-4 w-4 mr-2" /> <Plus className="h-4 w-4 mr-2" />
</> </>
)} )}
</Button> </Button>
@ -451,8 +451,8 @@ const MemberManageDialog: React.FC<MemberManageDialogProps> = ({
</div> </div>
)} )}
</div> </div>
</div>
</div> </div>
</div>
</DialogContent> </DialogContent>
</Dialog> </Dialog>
@ -469,8 +469,8 @@ const MemberManageDialog: React.FC<MemberManageDialogProps> = ({
</div> </div>
<div className="space-y-2"> <div className="space-y-2">
<label className="text-sm font-medium"></label> <label className="text-sm font-medium"></label>
<Input <Input
placeholder="请输入团队角色,如:开发、测试、产品经理等" placeholder="请输入团队角色,如:开发、测试、产品经理等"
value={editRole} value={editRole}
onChange={(e) => setEditRole(e.target.value)} onChange={(e) => setEditRole(e.target.value)}
/> />
@ -478,12 +478,12 @@ const MemberManageDialog: React.FC<MemberManageDialogProps> = ({
</div> </div>
<div className="flex justify-end gap-2"> <div className="flex justify-end gap-2">
<Button variant="outline" onClick={() => setEditDialogOpen(false)}> <Button variant="outline" onClick={() => setEditDialogOpen(false)}>
</Button> </Button>
<Button onClick={handleEditSave}> <Button onClick={handleEditSave}>
</Button> </Button>
</div> </div>
</DialogContent> </DialogContent>
</Dialog> </Dialog>