From d65e82b0e8ad70862252cac96b048ad9c050e4b2 Mon Sep 17 00:00:00 2001 From: dengqichen Date: Thu, 4 Dec 2025 15:26:56 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=9B=BE=E6=A0=87=E5=B1=95?= =?UTF-8?q?=E7=A4=BA=EF=BC=8C=E5=8C=BA=E5=88=86=E6=B8=85=E6=A5=9A=E9=A1=B9?= =?UTF-8?q?=E7=9B=AE=E7=B1=BB=E5=9E=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/components/ui/select.tsx | 32 ++ .../pages/Dashboard/utils/languageIcons.tsx | 57 +++ .../List/components/GitConfigSelector.tsx | 254 +++++++++++++ .../List/components/TeamApplicationDialog.tsx | 358 +++++++----------- .../TeamApplicationManageDialog.tsx | 99 ++++- .../TeamEnvironmentManageDialog.tsx | 3 + .../Deploy/Team/List/components/TeamModal.tsx | 73 ++++ frontend/src/pages/Deploy/Team/List/index.tsx | 24 ++ frontend/src/pages/Deploy/Team/List/schema.ts | 3 + frontend/src/pages/Deploy/Team/List/types.ts | 74 +++- 10 files changed, 735 insertions(+), 242 deletions(-) create mode 100644 frontend/src/pages/Dashboard/utils/languageIcons.tsx create mode 100644 frontend/src/pages/Deploy/Team/List/components/GitConfigSelector.tsx diff --git a/frontend/src/components/ui/select.tsx b/frontend/src/components/ui/select.tsx index bc4fe280..3ea7d8ee 100644 --- a/frontend/src/components/ui/select.tsx +++ b/frontend/src/components/ui/select.tsx @@ -171,6 +171,37 @@ const SelectItem = React.forwardRef< )) SelectItem.displayName = SelectPrimitive.Item.displayName +// 支持副标题的选项组件 +interface SelectItemWithDescriptionProps extends React.ComponentPropsWithoutRef { + label: string; + description?: string; +} + +const SelectItemWithDescription = React.forwardRef< + React.ElementRef, + SelectItemWithDescriptionProps +>(({ className, label, description, ...props }, ref) => ( + + + + + + + {label} + {description && ( + {description} + )} + +)) +SelectItemWithDescription.displayName = "SelectItemWithDescription" + const SelectSeparator = React.forwardRef< React.ElementRef, React.ComponentPropsWithoutRef @@ -225,6 +256,7 @@ export { SelectContent, SelectLabel, SelectItem, + SelectItemWithDescription, SelectSeparator, SelectScrollUpButton, SelectScrollDownButton, diff --git a/frontend/src/pages/Dashboard/utils/languageIcons.tsx b/frontend/src/pages/Dashboard/utils/languageIcons.tsx new file mode 100644 index 00000000..dfb6a6e0 --- /dev/null +++ b/frontend/src/pages/Dashboard/utils/languageIcons.tsx @@ -0,0 +1,57 @@ +import { + CodeOutlined, + JavaOutlined, + NodeIndexOutlined, + PythonOutlined, +} from '@ant-design/icons'; +import { DevelopmentLanguageTypeEnum } from '@/pages/Deploy/Application/List/types'; + +interface LanguageIconConfig { + icon: React.ReactNode; + color: string; + label: string; +} + +/** + * 语言图标配置映射(与 /deploy/applications 保持一致) + */ +export const LANGUAGE_ICONS: Record = { + JAVA: { + icon: , + color: '#E76F00', + label: 'Java' + }, + NODE_JS: { + icon: , + color: '#339933', + label: 'NodeJS' + }, + PYTHON: { + icon: , + color: '#3776AB', + label: 'Python' + }, + GO: { + icon: , + color: '#00ADD8', + label: 'Go' + } +}; + +/** + * 获取语言图标配置 + */ +export const getLanguageIcon = (language?: DevelopmentLanguageTypeEnum): LanguageIconConfig => { + if (!language) { + return { + icon: , + color: '#666666', + label: '未知' + }; + } + return LANGUAGE_ICONS[language] || { + icon: , + color: '#666666', + label: language + }; +}; diff --git a/frontend/src/pages/Deploy/Team/List/components/GitConfigSelector.tsx b/frontend/src/pages/Deploy/Team/List/components/GitConfigSelector.tsx new file mode 100644 index 00000000..cee09893 --- /dev/null +++ b/frontend/src/pages/Deploy/Team/List/components/GitConfigSelector.tsx @@ -0,0 +1,254 @@ +import React, { useState } from 'react'; +import { Label } from '@/components/ui/label'; +import { Input } from '@/components/ui/input'; +import { Button } from '@/components/ui/button'; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from '@/components/ui/select'; +import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'; +import { ScrollArea } from '@/components/ui/scroll-area'; +import { Search, Check, ChevronDown } from 'lucide-react'; +import { cn } from '@/lib/utils'; +import type { RepositoryBranchResponse } from '@/pages/Resource/Git/List/types'; + +interface GitConfigSelectorProps { + label: string; // "源" 或 "目标" + // Git系统 + gitSystems: any[]; + selectedSystemId: number | null; + onSystemChange: (systemId: number) => void; + // 仓库项目 + repoProjects: any[]; + loadingRepoProjects: boolean; + selectedProjectId: number | null; + onProjectChange: (projectId: number) => void; + // 分支 + branches: RepositoryBranchResponse[]; + loadingBranches: boolean; + selectedBranch: string; + onBranchChange: (branch: string) => void; +} + +export const GitConfigSelector: React.FC = ({ + label, + gitSystems, + selectedSystemId, + onSystemChange, + repoProjects, + loadingRepoProjects, + selectedProjectId, + onProjectChange, + branches, + loadingBranches, + selectedBranch, + onBranchChange, +}) => { + const [projectSearchValue, setProjectSearchValue] = useState(''); + const [projectPopoverOpen, setProjectPopoverOpen] = useState(false); + const [branchSearchValue, setBranchSearchValue] = useState(''); + const [branchPopoverOpen, setBranchPopoverOpen] = useState(false); + + // 过滤分支 + const filteredBranches = branches.filter(branch => + branch.name.toLowerCase().includes(branchSearchValue.toLowerCase()) + ); + + return ( +
+ +
+ {/* 代码系统 */} +
+ + +
+ + {/* 仓库项目 */} +
+ + {selectedSystemId ? ( + + + + + +
+ + setProjectSearchValue(e.target.value)} + /> +
+ +
+ {repoProjects + .filter( + (project) => + project.name.toLowerCase().includes(projectSearchValue.toLowerCase()) || + project.repoGroupName?.toLowerCase().includes(projectSearchValue.toLowerCase()) + ) + .map((project) => ( +
{ + onProjectChange(project.repoProjectId); + setProjectSearchValue(''); + setProjectPopoverOpen(false); + }} + > +
+ {project.repoGroupName && ( + {project.repoGroupName} / + )} + {project.name} +
+ {project.repoProjectId === selectedProjectId && } +
+ ))} +
+
+
+
+ ) : ( + + )} +
+ + {/* 分支 */} +
+ + {selectedProjectId ? ( + + + + + +
+ + setBranchSearchValue(e.target.value)} + /> +
+ +
+ {filteredBranches.length === 0 ? ( +
未找到分支
+ ) : ( + filteredBranches.map((branch) => ( +
{ + onBranchChange(branch.name); + setBranchSearchValue(''); + setBranchPopoverOpen(false); + }} + > + + {branch.name} + {branch.isDefaultBranch && ( + (默认) + )} + + {branch.name === selectedBranch && } +
+ )) + )} +
+
+
+
+ ) : ( + + )} +
+
+
+ ); +}; diff --git a/frontend/src/pages/Deploy/Team/List/components/TeamApplicationDialog.tsx b/frontend/src/pages/Deploy/Team/List/components/TeamApplicationDialog.tsx index 1ce8f507..48c14757 100644 --- a/frontend/src/pages/Deploy/Team/List/components/TeamApplicationDialog.tsx +++ b/frontend/src/pages/Deploy/Team/List/components/TeamApplicationDialog.tsx @@ -32,6 +32,7 @@ import type { RepositoryBranchResponse } from '@/pages/Resource/Git/List/types'; import type { WorkflowDefinition } from '@/pages/Workflow/Definition/List/types'; import { getExternalSystemList } from '@/pages/Resource/External/List/service'; import { getRepositoryProjectsList, getRepositoryBranchesList } from '@/pages/Resource/Git/List/service'; +import { GitConfigSelector } from './GitConfigSelector'; interface TeamApplicationDialogProps { open: boolean; @@ -39,6 +40,7 @@ interface TeamApplicationDialogProps { teamId: number; environmentId: number; environmentName: string; + enableGitSyncCheck: boolean; // 🆕 团队是否启用Git同步检测 application?: TeamApplication; // 编辑时传入 applications: Application[]; // 可选择的应用列表 jenkinsSystems: any[]; @@ -55,6 +57,9 @@ interface TeamApplicationDialogProps { workflowDefinitionId: number | null; codeSourceSystemId: number | null; codeSourceProjectId: number | null; + targetGitSystemId: number | null; // 🆕 目标Git系统ID + targetGitProjectId: number | null; // 🆕 目标Git项目ID + targetBranch: string; // 🆕 目标分支 }) => Promise; onLoadBranches: (appId: number, app: Application) => Promise; onLoadJenkinsJobs: (systemId: number) => Promise; @@ -66,6 +71,7 @@ const TeamApplicationDialog: React.FC = ({ teamId, environmentId, environmentName, + enableGitSyncCheck, application, applications, jenkinsSystems, @@ -86,8 +92,11 @@ const TeamApplicationDialog: React.FC = ({ deploySystemId: null as number | null, deployJob: '', workflowDefinitionId: null as number | null, - codeSourceSystemId: null as number | null, - codeSourceProjectId: null as number | null, + codeSourceSystemId: null as number | null, // 源代码系统ID + codeSourceProjectId: null as number | null, // 源仓库项目ID + targetGitSystemId: null as number | null, // 🆕 目标Git系统ID + targetGitProjectId: null as number | null, // 🆕 目标Git项目ID + targetBranch: '', // 🆕 目标分支 }); // 加载状态 @@ -99,14 +108,15 @@ const TeamApplicationDialog: React.FC = ({ const [gitSystems, setGitSystems] = useState([]); const [repoProjects, setRepoProjects] = useState([]); const [loadingRepoProjects, setLoadingRepoProjects] = useState(false); + // 🆕 目标Git相关状态 + const [targetRepoProjects, setTargetRepoProjects] = useState([]); + const [loadingTargetRepoProjects, setLoadingTargetRepoProjects] = useState(false); + const [targetBranches, setTargetBranches] = useState([]); + const [loadingTargetBranches, setLoadingTargetBranches] = useState(false); // 搜索和弹窗状态 const [appSearchValue, setAppSearchValue] = useState(''); const [appPopoverOpen, setAppPopoverOpen] = useState(false); - const [branchSearchValue, setBranchSearchValue] = useState(''); - const [branchPopoverOpen, setBranchPopoverOpen] = useState(false); - const [projectSearchValue, setProjectSearchValue] = useState(''); - const [projectPopoverOpen, setProjectPopoverOpen] = useState(false); const [jobSearchValue, setJobSearchValue] = useState(''); const [jobPopoverOpen, setJobPopoverOpen] = useState(false); @@ -124,14 +134,17 @@ const TeamApplicationDialog: React.FC = ({ workflowDefinitionId: application.workflowDefinitionId || null, codeSourceSystemId: application.codeSourceSystemId || null, codeSourceProjectId: application.codeSourceProjectId || null, + targetGitSystemId: application.targetGitSystemId || null, + targetGitProjectId: application.targetGitProjectId || null, + targetBranch: application.targetBranch || '', }); - // 加载仓库项目 + // 加载源仓库项目 if (application.codeSourceSystemId) { loadRepoProjects(application.codeSourceSystemId); } - // 加载分支(优先使用代码源信息,向后兼容旧数据) + // 加载源分支(优先使用代码源信息,向后兼容旧数据) if (application.codeSourceSystemId && application.codeSourceProjectId) { // 使用代码源信息加载分支 loadBranchesFromCodeSource(application.codeSourceSystemId, application.codeSourceProjectId); @@ -143,6 +156,16 @@ const TeamApplicationDialog: React.FC = ({ } } + // 🆕 加载目标仓库项目(如果有) + if (application.targetGitSystemId) { + loadTargetRepoProjects(application.targetGitSystemId); + } + + // 🆕 加载目标分支(如果有) + if (application.targetGitSystemId && application.targetGitProjectId) { + loadTargetBranches(application.targetGitSystemId, application.targetGitProjectId); + } + // 加载Jenkins Jobs if (application.deploySystemId) { loadJenkinsJobs(application.deploySystemId); @@ -158,6 +181,9 @@ const TeamApplicationDialog: React.FC = ({ workflowDefinitionId: null, codeSourceSystemId: null, codeSourceProjectId: null, + targetGitSystemId: null, + targetGitProjectId: null, + targetBranch: '', }); setBranches([]); setJenkinsJobs([]); @@ -204,20 +230,34 @@ const TeamApplicationDialog: React.FC = ({ } }; - // 加载仓库项目列表 + // 加载源仓库项目列表 const loadRepoProjects = async (externalSystemId: number) => { setLoadingRepoProjects(true); try { const projects = await getRepositoryProjectsList({ externalSystemId }); setRepoProjects(projects || []); } catch (error) { - console.error('加载仓库项目失败:', error); + console.error('加载源仓库项目失败:', error); setRepoProjects([]); } finally { setLoadingRepoProjects(false); } }; + // 🆕 加载目标仓库项目列表 + const loadTargetRepoProjects = async (externalSystemId: number) => { + setLoadingTargetRepoProjects(true); + try { + const projects = await getRepositoryProjectsList({ externalSystemId }); + setTargetRepoProjects(projects || []); + } catch (error) { + console.error('加载目标仓库项目失败:', error); + setTargetRepoProjects([]); + } finally { + setLoadingTargetRepoProjects(false); + } + }; + // 初始化加载Git系统列表 useEffect(() => { if (open) { @@ -236,6 +276,9 @@ const TeamApplicationDialog: React.FC = ({ workflowDefinitionId: null, codeSourceSystemId: null, codeSourceProjectId: null, + targetGitSystemId: null, + targetGitProjectId: null, + targetBranch: '', }); // 清空分支列表(分支现在基于代码源,不基于应用) setBranches([]); @@ -263,21 +306,35 @@ const TeamApplicationDialog: React.FC = ({ loadJenkinsJobs(systemId); }; - // 加载基于代码源的分支列表 + // 加载基于源代码的分支列表 const loadBranchesFromCodeSource = async (externalSystemId: number, repoProjectId: number) => { setLoadingBranches(true); try { const branchList = await getRepositoryBranchesList({ externalSystemId, repoProjectId }); setBranches(branchList || []); } catch (error) { - console.error('加载分支失败:', error); + console.error('加载源分支失败:', error); setBranches([]); } finally { setLoadingBranches(false); } }; - // 处理代码源系统选择 + // 🆕 加载目标分支列表 + const loadTargetBranches = async (externalSystemId: number, repoProjectId: number) => { + setLoadingTargetBranches(true); + try { + const branchList = await getRepositoryBranchesList({ externalSystemId, repoProjectId }); + setTargetBranches(branchList || []); + } catch (error) { + console.error('加载目标分支失败:', error); + setTargetBranches([]); + } finally { + setLoadingTargetBranches(false); + } + }; + + // 处理源代码系统选择 const handleCodeSourceSystemChange = (systemId: number) => { setFormData({ ...formData, @@ -289,7 +346,7 @@ const TeamApplicationDialog: React.FC = ({ loadRepoProjects(systemId); }; - // 处理仓库项目选择 + // 处理源仓库项目选择 const handleCodeSourceProjectChange = (projectId: number) => { setFormData({ ...formData, @@ -302,6 +359,31 @@ const TeamApplicationDialog: React.FC = ({ } }; + // 🆕 处理目标Git系统选择 + const handleTargetGitSystemChange = (systemId: number) => { + setFormData({ + ...formData, + targetGitSystemId: systemId, + targetGitProjectId: null, + targetBranch: '', // 清空分支 + }); + setTargetBranches([]); // 清空分支列表 + loadTargetRepoProjects(systemId); + }; + + // 🆕 处理目标仓库项目选择 + const handleTargetGitProjectChange = (projectId: number) => { + setFormData({ + ...formData, + targetGitProjectId: projectId, + targetBranch: '', // 清空分支 + }); + // 加载该项目的分支 + if (formData.targetGitSystemId) { + loadTargetBranches(formData.targetGitSystemId, projectId); + } + }; + // 保存 const handleSave = async () => { // 表单验证 @@ -325,6 +407,9 @@ const TeamApplicationDialog: React.FC = ({ workflowDefinitionId: formData.workflowDefinitionId, codeSourceSystemId: formData.codeSourceSystemId, codeSourceProjectId: formData.codeSourceProjectId, + targetGitSystemId: formData.targetGitSystemId, + targetGitProjectId: formData.targetGitProjectId, + targetBranch: formData.targetBranch, }); toast({ @@ -338,11 +423,6 @@ const TeamApplicationDialog: React.FC = ({ } }; - // 分支过滤 - const filteredBranches = branches.filter(branch => - branch.name.toLowerCase().includes(branchSearchValue.toLowerCase()) - ); - // Job 过滤 const filteredJobs = jenkinsJobs.filter(job => job.jobName.toLowerCase().includes(jobSearchValue.toLowerCase()) @@ -599,215 +679,39 @@ const TeamApplicationDialog: React.FC = ({ )} - {/* 代码源选择 */} -
- - -
+ {/* 源Git配置 */} + setFormData({ ...formData, branch })} + /> - {/* 仓库项目选择 */} -
- - {formData.codeSourceSystemId ? ( - - - - - -
- - setProjectSearchValue(e.target.value)} - /> -
- -
- {repoProjects - .filter((project) => - project.name.toLowerCase().includes(projectSearchValue.toLowerCase()) || - (project.repoGroupName?.toLowerCase().includes(projectSearchValue.toLowerCase())) - ) - .map((project) => ( -
{ - handleCodeSourceProjectChange(project.repoProjectId); - setProjectSearchValue(''); - setProjectPopoverOpen(false); - }} - > -
- {project.repoGroupName && ( - {project.repoGroupName} / - )} - {project.name} -
- {project.repoProjectId === formData.codeSourceProjectId && ( - - )} -
- ))} -
-
-
-
- ) : ( - - )} -
- - {/* 分支选择 */} -
- - {formData.codeSourceProjectId ? ( - - - - - -
- - setBranchSearchValue(e.target.value)} - /> -
- -
- {filteredBranches.length === 0 ? ( -
- 未找到分支 -
- ) : ( - filteredBranches.map((branch) => ( -
{ - setFormData({ ...formData, branch: branch.name }); - setBranchSearchValue(''); - setBranchPopoverOpen(false); - }} - > - - {branch.name} - {branch.isDefaultBranch && ( - - (默认) - - )} - - {branch.name === formData.branch && ( - - )} -
- )) - )} -
-
-
-
- ) : ( - - )} -
+ {/* 🆕 目标Git配置(仅当启用Git同步检测时显示) */} + {enableGitSyncCheck && ( + setFormData({ ...formData, targetBranch: branch })} + /> + )} {/* 工作流定义 */}
diff --git a/frontend/src/pages/Deploy/Team/List/components/TeamApplicationManageDialog.tsx b/frontend/src/pages/Deploy/Team/List/components/TeamApplicationManageDialog.tsx index 73b39c7c..3213e37b 100644 --- a/frontend/src/pages/Deploy/Team/List/components/TeamApplicationManageDialog.tsx +++ b/frontend/src/pages/Deploy/Team/List/components/TeamApplicationManageDialog.tsx @@ -10,8 +10,9 @@ import { import { ConfirmDialog } from '@/components/ui/confirm-dialog'; import { Button } from '@/components/ui/button'; import { useToast } from '@/components/ui/use-toast'; +import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip'; import { PaginatedTable, type ColumnDef, type PaginatedTableRef } from '@/components/ui/paginated-table'; -import { Plus, Edit, Trash2 } from 'lucide-react'; +import { Plus, Edit, Trash2, GitBranch } from 'lucide-react'; import type { Environment } from '@/pages/Deploy/Environment/List/types'; import type { TeamApplication, Application } from '../types'; import type { WorkflowDefinition } from '@/pages/Workflow/Definition/List/types'; @@ -32,6 +33,7 @@ interface TeamApplicationManageDialogProps { open: boolean; onOpenChange: (open: boolean) => void; teamId: number; + enableGitSyncCheck: boolean; // 🆕 团队是否启用Git同步检测 environmentId?: number; // 可选:如果指定,则只管理该环境的应用 environments: Environment[]; onSuccess?: () => void; @@ -39,7 +41,7 @@ interface TeamApplicationManageDialogProps { export const TeamApplicationManageDialog: React.FC< TeamApplicationManageDialogProps -> = ({ open, onOpenChange, teamId, environmentId, environments, onSuccess }) => { +> = ({ open, onOpenChange, teamId, enableGitSyncCheck, environmentId, environments, onSuccess }) => { const { toast } = useToast(); const tableRef = useRef>(null); const [teamApplications, setTeamApplications] = useState([]); @@ -127,6 +129,9 @@ export const TeamApplicationManageDialog: React.FC< workflowDefinitionId: number | null; codeSourceSystemId: number | null; codeSourceProjectId: number | null; + targetGitSystemId: number | null; // 🆕 目标Git系统ID + targetGitProjectId: number | null; // 🆕 目标Git项目ID + targetBranch: string; // 🆕 目标分支 }) => { if (!editingEnvironment) return; @@ -142,6 +147,10 @@ export const TeamApplicationManageDialog: React.FC< workflowDefinitionId: data.workflowDefinitionId || undefined, codeSourceSystemId: data.codeSourceSystemId || undefined, codeSourceProjectId: data.codeSourceProjectId || undefined, + // 🆕 目标Git相关字段 + targetGitSystemId: data.targetGitSystemId || undefined, + targetGitProjectId: data.targetGitProjectId || undefined, + targetBranch: data.targetBranch || undefined, }; if (appDialogMode === 'edit' && data.id) { @@ -207,6 +216,84 @@ export const TeamApplicationManageDialog: React.FC< width: '100px', render: (_, app) => getEnvironmentName(app.environmentId), }, + { + key: 'gitConfig', + title: 'Git配置', + width: '350px', + render: (_, app) => { + const hasTargetGit = app.targetGitSystemId && app.targetGitProjectId; + + if (!hasTargetGit) { + // 未配置目标:单行显示 + return ( +
+ + {app.codeSourceSystemName || '-'} / {app.codeSourceProjectName || '-'} + + {app.branch && ( + ({app.branch}) + )} +
+ ); + } + + // 配置了目标:对称双行显示 + return ( + + + +
+ {/* 源Git */} +
+ 源: + + {app.codeSourceSystemName || '-'} / {app.codeSourceProjectName || '-'} + + {app.branch && ( + ({app.branch}) + )} +
+ {/* 同步标识 - 居中 */} +
+ ⇅ 同步 +
+ {/* 目标Git */} +
+ 目: + + {app.targetGitSystemName || '-'} / {app.targetGitProjectName || '-'} + + {app.targetBranch && ( + ({app.targetBranch}) + )} +
+
+
+ +
+
+
源Git
+
+
系统: {app.codeSourceSystemName}
+
项目: {app.codeSourceProjectName}
+
分支: {app.branch || '-'}
+
+
+
+
目标Git
+
+
系统: {app.targetGitSystemName}
+
项目: {app.targetGitProjectName}
+
分支: {app.targetBranch || '-'}
+
+
+
+
+
+
+ ); + }, + }, { key: 'buildType', title: '构建类型', @@ -214,13 +301,6 @@ export const TeamApplicationManageDialog: React.FC< render: (_, app) => app.buildType === 'JENKINS' ? 'Jenkins构建' : app.buildType === 'NATIVE' ? '脚本部署' : '-', }, - { - key: 'branch', - title: '分支', - dataIndex: 'branch', - width: '100px', - render: (value) => value || '-', - }, { key: 'deploySystemName', title: 'Jenkins系统', @@ -315,6 +395,7 @@ export const TeamApplicationManageDialog: React.FC< open={appDialogOpen} onOpenChange={setAppDialogOpen} mode={appDialogMode} + enableGitSyncCheck={enableGitSyncCheck} teamId={teamId} environmentId={editingEnvironment.id} environmentName={editingEnvironment.envName} diff --git a/frontend/src/pages/Deploy/Team/List/components/TeamEnvironmentManageDialog.tsx b/frontend/src/pages/Deploy/Team/List/components/TeamEnvironmentManageDialog.tsx index 633d139d..72fa43c7 100644 --- a/frontend/src/pages/Deploy/Team/List/components/TeamEnvironmentManageDialog.tsx +++ b/frontend/src/pages/Deploy/Team/List/components/TeamEnvironmentManageDialog.tsx @@ -44,6 +44,7 @@ interface TeamEnvironmentManageDialogProps { onOpenChange: (open: boolean) => void; teamId: number; teamName: string; + enableGitSyncCheck: boolean; // 🆕 团队是否启用Git同步检测 environments: Environment[]; users: User[]; onSuccess?: () => void; @@ -56,6 +57,7 @@ export const TeamEnvironmentManageDialog: React.FC< onOpenChange, teamId, teamName, + enableGitSyncCheck, environments, users, onSuccess, @@ -301,6 +303,7 @@ export const TeamEnvironmentManageDialog: React.FC< open={appManageDialogOpen} onOpenChange={setAppManageDialogOpen} teamId={teamId} + enableGitSyncCheck={enableGitSyncCheck} environmentId={selectedEnvironmentId} environments={environments} onSuccess={handleConfigSuccess} diff --git a/frontend/src/pages/Deploy/Team/List/components/TeamModal.tsx b/frontend/src/pages/Deploy/Team/List/components/TeamModal.tsx index 112d9e98..ac230bca 100644 --- a/frontend/src/pages/Deploy/Team/List/components/TeamModal.tsx +++ b/frontend/src/pages/Deploy/Team/List/components/TeamModal.tsx @@ -1,5 +1,6 @@ import React, { useEffect, useState } from 'react'; import type { TeamResponse } from '../types'; +import { DevelopmentModeEnum, DEVELOPMENT_MODE_OPTIONS } from '../types'; import { createTeam, updateTeam } from '../service'; import { getUserList } from '@/pages/System/User/List/service'; import type { UserResponse } from '@/pages/System/User/List/types'; @@ -25,6 +26,7 @@ import { Select, SelectContent, SelectItem, + SelectItemWithDescription, SelectTrigger, SelectValue, } from "@/components/ui/select"; @@ -62,9 +64,21 @@ const TeamModal: React.FC = ({ ownerName: "", enabled: true, sort: 0, + developmentMode: DevelopmentModeEnum.STANDARD, + enableGitSyncCheck: false, }, }); + // 监听开发模式变化,自动调整Git同步检测选项 + const developmentMode = form.watch('developmentMode'); + useEffect(() => { + const modeOption = DEVELOPMENT_MODE_OPTIONS.find(opt => opt.value === developmentMode); + if (modeOption && !modeOption.enablesGitSyncCheck) { + // 如果当前模式不支持Git同步检测,自动禁用 + form.setValue('enableGitSyncCheck', false); + } + }, [developmentMode, form]); + // 加载用户列表 const loadUsers = async () => { try { @@ -89,6 +103,8 @@ const TeamModal: React.FC = ({ ownerName: initialValues.ownerName || "", enabled: initialValues.enabled, sort: initialValues.sort, + developmentMode: initialValues.developmentMode || DevelopmentModeEnum.STANDARD, + enableGitSyncCheck: initialValues.enableGitSyncCheck || false, }); } else { form.reset({ @@ -99,6 +115,8 @@ const TeamModal: React.FC = ({ ownerName: "", enabled: true, sort: 0, + developmentMode: DevelopmentModeEnum.STANDARD, + enableGitSyncCheck: false, }); } } @@ -236,6 +254,61 @@ const TeamModal: React.FC = ({ )} /> + ( + + 开发模式 + + + + )} + /> + + {developmentMode === 'SYNC_MODE' && ( + ( + + Git同步检测 + +
+ + + {field.value ? '启用' : '禁用'} + +
+
+ +
+ )} + /> + )} +
{ width: '140px', render: (_, record) => record.ownerName || '-', }, + { + key: 'developmentMode', + title: '开发模式', + width: '160px', + render: (_, record) => { + const modeOption = DEVELOPMENT_MODE_OPTIONS.find(opt => opt.value === record.developmentMode); + return ( +
+ + {modeOption?.label || record.developmentMode} + + {record.enableGitSyncCheck && ( +
+ + Git同步检测 + +
+ )} +
+ ); + }, + }, { key: 'memberCount', title: '成员数量', @@ -332,6 +355,7 @@ const TeamList: React.FC = () => { onOpenChange={setEnvManageDialogOpen} teamId={currentTeam.id} teamName={currentTeam.teamName} + enableGitSyncCheck={currentTeam.enableGitSyncCheck} environments={environments} users={users} onSuccess={handleSuccess} diff --git a/frontend/src/pages/Deploy/Team/List/schema.ts b/frontend/src/pages/Deploy/Team/List/schema.ts index 2e882b0d..3b49e19e 100644 --- a/frontend/src/pages/Deploy/Team/List/schema.ts +++ b/frontend/src/pages/Deploy/Team/List/schema.ts @@ -1,4 +1,5 @@ import { z } from 'zod'; +import { DevelopmentModeEnum } from './types'; /** * 搜索表单校验 @@ -20,6 +21,8 @@ export const teamFormSchema = z.object({ ownerName: z.string().optional(), enabled: z.boolean().default(true), sort: z.number().min(0).default(0), + developmentMode: z.nativeEnum(DevelopmentModeEnum).default(DevelopmentModeEnum.STANDARD), + enableGitSyncCheck: z.boolean().default(false), }); export type SearchFormValues = z.infer; diff --git a/frontend/src/pages/Deploy/Team/List/types.ts b/frontend/src/pages/Deploy/Team/List/types.ts index d758b287..744a9cd6 100644 --- a/frontend/src/pages/Deploy/Team/List/types.ts +++ b/frontend/src/pages/Deploy/Team/List/types.ts @@ -5,6 +5,56 @@ import type { Application } from '@/pages/Deploy/Application/List/types'; // 导出环境和应用类型供使用 export type { Environment, Application }; +/** + * 开发模式枚举 + */ +export enum DevelopmentModeEnum { + /** 标准模式 - 单仓库直接开发部署 */ + STANDARD = 'STANDARD', + /** 同步模式 - 源码内外网同步 */ + SYNC_MODE = 'SYNC_MODE', + /** 制品交付模式 - 仅交付编译制品,无源码 */ + ARTIFACT_DELIVERY = 'ARTIFACT_DELIVERY' +} + +/** + * 开发模式选项配置 + */ +export interface DevelopmentModeOption { + value: DevelopmentModeEnum; + label: string; + description: string; + requiresExternalRepo: boolean; + enablesGitSyncCheck: boolean; +} + +/** + * 开发模式选项列表 + */ +export const DEVELOPMENT_MODE_OPTIONS: DevelopmentModeOption[] = [ + { + value: DevelopmentModeEnum.STANDARD, + label: '标准模式', + description: '单仓库直接开发部署', + requiresExternalRepo: false, + enablesGitSyncCheck: false + }, + { + value: DevelopmentModeEnum.SYNC_MODE, + label: '同步模式', + description: '源码内外网同步', + requiresExternalRepo: true, + enablesGitSyncCheck: true + }, + { + value: DevelopmentModeEnum.ARTIFACT_DELIVERY, + label: '制品交付模式', + description: '仅交付编译制品,无源码', + requiresExternalRepo: true, + enablesGitSyncCheck: false + } +]; + /** * 团队查询参数 */ @@ -25,6 +75,8 @@ export interface TeamResponse extends BaseResponse { ownerName?: string; enabled: boolean; sort: number; + developmentMode: DevelopmentModeEnum; // 开发模式 + enableGitSyncCheck: boolean; // 是否启用Git同步检测 memberCount?: number; environmentCount?: number; applicationCount?: number; @@ -41,6 +93,8 @@ export interface TeamRequest { ownerName?: string; enabled?: boolean; sort?: number; + developmentMode?: DevelopmentModeEnum; // 开发模式 + enableGitSyncCheck?: boolean; // 是否启用Git同步检测 } // ==================== 团队环境配置相关 ==================== @@ -130,16 +184,21 @@ export interface TeamApplication extends BaseResponse { deploySystemId?: number; deployJob?: string; workflowDefinitionId?: number; - codeSourceSystemId?: number; // 代码源系统ID - codeSourceProjectId?: number; // 代码源项目ID + codeSourceSystemId?: number; // 源代码系统ID + codeSourceProjectId?: number; // 源仓库项目ID + targetGitSystemId?: number; // 目标Git系统ID(仅SYNC_MODE) + targetGitProjectId?: number; // 目标Git项目ID + targetBranch?: string; // 目标分支名称 teamName?: string; applicationName?: string; applicationCode?: string; environmentName?: string; deploySystemName?: string; workflowDefinitionName?: string; - codeSourceSystemName?: string; // 代码源系统名称 - codeSourceProjectName?: string; // 代码源项目名称 + codeSourceSystemName?: string; // 源代码系统名称 + codeSourceProjectName?: string; // 源仓库项目名称 + targetGitSystemName?: string; // 目标Git系统名称 + targetGitProjectName?: string; // 目标Git项目名称 } /** @@ -154,7 +213,10 @@ export interface TeamApplicationRequest { deploySystemId?: number; deployJob?: string; workflowDefinitionId?: number; - codeSourceSystemId?: number; // 代码源系统ID - codeSourceProjectId?: number; // 代码源项目ID + codeSourceSystemId?: number; // 源代码系统ID + codeSourceProjectId?: number; // 源仓库项目ID + targetGitSystemId?: number; // 目标Git系统ID(仅SYNC_MODE) + targetGitProjectId?: number; // 目标Git项目ID + targetBranch?: string; // 目标分支名称 }