diff --git a/frontend/package.json b/frontend/package.json index 42fd007a..abdeeed9 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -21,6 +21,7 @@ "@radix-ui/react-alert-dialog": "^1.1.15", "@radix-ui/react-avatar": "^1.1.2", "@radix-ui/react-checkbox": "^1.3.3", + "@radix-ui/react-collapsible": "^1.1.12", "@radix-ui/react-dialog": "^1.1.15", "@radix-ui/react-dropdown-menu": "^2.1.4", "@radix-ui/react-label": "^2.1.1", diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml index 225bc91f..e9840e51 100644 --- a/frontend/pnpm-lock.yaml +++ b/frontend/pnpm-lock.yaml @@ -41,6 +41,9 @@ importers: '@radix-ui/react-checkbox': specifier: ^1.3.3 version: 1.3.3(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-collapsible': + specifier: ^1.1.12 + version: 1.1.12(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@radix-ui/react-dialog': specifier: ^1.1.15 version: 1.1.15(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) diff --git a/frontend/src/components/ui/collapsible.tsx b/frontend/src/components/ui/collapsible.tsx new file mode 100644 index 00000000..c69ecb0d --- /dev/null +++ b/frontend/src/components/ui/collapsible.tsx @@ -0,0 +1,11 @@ +import * as React from "react" +import * as CollapsiblePrimitive from "@radix-ui/react-collapsible" + +const Collapsible = CollapsiblePrimitive.Root + +const CollapsibleTrigger = CollapsiblePrimitive.CollapsibleTrigger + +const CollapsibleContent = CollapsiblePrimitive.CollapsibleContent + +export { Collapsible, CollapsibleTrigger, CollapsibleContent } + diff --git a/frontend/src/pages/Dashboard/index.tsx b/frontend/src/pages/Dashboard/index.tsx index eafd2d8a..a25bb8fc 100644 --- a/frontend/src/pages/Dashboard/index.tsx +++ b/frontend/src/pages/Dashboard/index.tsx @@ -1,67 +1,21 @@ -import React, {useState, useEffect} from 'react'; -import {Card, CardContent, CardHeader, CardTitle} from "@/components/ui/card"; -import {Badge} from "@/components/ui/badge"; -import {Progress} from "@/components/ui/progress"; -import {Button} from "@/components/ui/button"; -import {Tabs, TabsContent, TabsList, TabsTrigger} from "@/components/ui/tabs"; -import {Input} from "@/components/ui/input"; +import React, { useState, useEffect } from 'react'; +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; +import { Button } from "@/components/ui/button"; +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; +import { cn } from "@/lib/utils"; import { - Select, - SelectContent, - SelectItem, - SelectTrigger, - SelectValue, -} from "@/components/ui/select"; -import {cn} from "@/lib/utils"; -import { - Clock, - Search, - Edit, Package, - ChevronRight, - CheckCircle, - AlertTriangle, - XCircle, + Shield, Loader2, - ServerCrash, - Server + Rocket, + GitBranch, + Users, + Server, + CheckCircle2 } from "lucide-react"; -import {getEnvironmentList} from '@/pages/Deploy/Environment/List/service'; -import {getDevelopmentLanguages} from '@/pages/Deploy/Application/List/service'; -import {getDeploymentConfigPage} from '@/pages/Deploy/Deployment/List/service'; -import type {Environment} from '@/pages/Deploy/Environment/List/types'; -import type {DevelopmentLanguageType} from '@/pages/Deploy/Application/List/types'; -import type {DeploymentConfig} from '@/pages/Deploy/Deployment/List/types'; -import type {Page} from '@/types/base'; -import type { JsonNode } from '@/types/common'; -import DeploymentFormModal from './components/DeploymentFormModal'; -import {message} from "antd"; - -type EnvironmentStatus = 'success' | 'warning' | 'error'; - -// 扩展环境类型,添加监控数据 -interface EnhancedEnvironment extends Environment { - projectCount: number; - status: EnvironmentStatus; - lastDeployment: string; - cpu: number; - memory: number; - storage: number; -} - -const EmptyState: React.FC<{ title: string; description: string; icon: React.ReactNode }> = ({ - title, - description, - icon - }) => ( -
-
- {icon} -
-

{title}

-

{description}

-
-); +import { useToast } from '@/components/ui/use-toast'; +import { getDeployEnvironments, startDeployment } from './service'; +import type { DeployTeam, ApplicationConfig } from './types'; const LoadingState = () => (
@@ -73,404 +27,291 @@ const LoadingState = () => ( ); const Dashboard: React.FC = () => { - const [projectType, setProjectType] = useState("ALL"); - const [status, setStatus] = useState(""); - const [environments, setEnvironments] = useState([]); + const { toast } = useToast(); const [loading, setLoading] = useState(true); - const [languages, setLanguages] = useState([]); - const [deployConfigs, setDeployConfigs] = useState([]); - const [searchText, setSearchText] = useState(""); - const [currentEnvId, setCurrentEnvId] = useState(); - const [deployModalOpen, setDeployModalOpen] = useState(false); - const [selectedConfig, setSelectedConfig] = useState(null); + const [teams, setTeams] = useState([]); + const [currentTeamId, setCurrentTeamId] = useState(null); + const [currentEnvId, setCurrentEnvId] = useState(null); + const [deploying, setDeploying] = useState>(new Set()); - // 获取环境和语言数据 + // 加载部署环境数据 useEffect(() => { - const fetchData = async () => { + const loadData = async () => { try { setLoading(true); - const [envResponse, langResponse] = await Promise.all([ - getEnvironmentList(), - getDevelopmentLanguages() - ]); - - if (envResponse) { - const enrichedEnvironments = envResponse.map(env => { - const randomStatus: EnvironmentStatus = Math.random() > 0.7 ? 'warning' : 'success'; - return { - ...env, - projectCount: 0, // 初始化为0,后续更新 - status: randomStatus, - lastDeployment: '暂无部署', - cpu: Math.floor(Math.random() * 40) + 40, - memory: Math.floor(Math.random() * 30) + 50, - storage: Math.floor(Math.random() * 40) + 30, - }; - }); - setEnvironments(enrichedEnvironments); - if (enrichedEnvironments.length > 0) { - setCurrentEnvId(enrichedEnvironments[0].id); + const response = await getDeployEnvironments(); + + if (response && response.teams) { + setTeams(response.teams); + + // 默认选中第一个团队和第一个环境 + if (response.teams.length > 0) { + setCurrentTeamId(response.teams[0].teamId); + + if (response.teams[0].environments.length > 0) { + setCurrentEnvId(response.teams[0].environments[0].environmentId); + } } } - - if (langResponse) { - setLanguages(langResponse); - } } catch (error) { - console.error('Failed to fetch data:', error); + console.error('加载数据失败:', error); + toast({ + variant: 'destructive', + title: '加载失败', + description: '无法加载部署环境数据,请稍后重试', + }); } finally { setLoading(false); } }; - fetchData(); - }, []); + loadData(); + }, [toast]); - // 获取部署配置数据 - useEffect(() => { - const fetchDeployConfigs = async () => { - if (!currentEnvId) return; - - try { - const response = await getDeploymentConfigPage({ - pageSize: 100, - pageNum: 1, - environmentId: currentEnvId, - workflowDefinitionId: 0, - enabled: undefined - }); - - if (response) { - setDeployConfigs(response.content); - // 更新环境的项目数量 - setEnvironments(prevEnvs => - prevEnvs.map(env => - env.id === currentEnvId - ? {...env, projectCount: response.totalElements} - : env - ) - ); - } - } catch (error) { - console.error('Failed to fetch deployment configs:', error); - } - }; - - fetchDeployConfigs(); - }, [currentEnvId]); - - const handleSearch = async () => { - if (!currentEnvId) return; - - try { - const params: any = { - pageSize: 100, - pageNum: 1, - environmentId: currentEnvId, - enabled: status === 'active' ? true : status === 'paused' ? false : undefined - }; - - // 如果选择了具体语言(不是"所有"),添加到查询参数 - if (projectType !== 'ALL') { - params.languageType = projectType; - } - - const response = await getDeploymentConfigPage(params); - - if (response) { - setDeployConfigs(response.content); - } - } catch (error) { - console.error('Failed to search deployment configs:', error); + // 切换团队时,自动选中第一个环境 + const handleTeamChange = (teamId: string) => { + const newTeamId = Number(teamId); + setCurrentTeamId(newTeamId); + + const team = teams.find(t => t.teamId === newTeamId); + if (team && team.environments.length > 0) { + setCurrentEnvId(team.environments[0].environmentId); + } else { + setCurrentEnvId(null); } }; - const handleReset = () => { - setSearchText(""); - setProjectType("ALL"); - setStatus(""); - if (currentEnvId) { - getDeploymentConfigPage({ - pageSize: 100, - pageNum: 1, - environmentId: currentEnvId, - workflowDefinitionId: 0, - enabled: undefined - }).then(response => { - if (response) { - setDeployConfigs(response.content); - } + // 处理部署 + const handleDeploy = async (app: ApplicationConfig, requiresApproval: boolean) => { + try { + setDeploying((prev) => new Set(prev).add(app.teamApplicationId)); + + await startDeployment(app.teamApplicationId); + + toast({ + title: requiresApproval ? '部署申请已提交' : '部署任务已创建', + description: requiresApproval + ? '您的部署申请已提交,等待审批人审核' + : '部署任务已成功创建并开始执行', + }); + } catch (error: any) { + toast({ + variant: 'destructive', + title: '操作失败', + description: error.response?.data?.message || '部署失败,请稍后重试', + }); + } finally { + setDeploying((prev) => { + const newSet = new Set(prev); + newSet.delete(app.teamApplicationId); + return newSet; }); } }; - const handleTabChange = (envCode: string) => { - const env = environments.find(e => e.envCode === envCode); - if (env) { - setCurrentEnvId(env.id); - } - }; - - const getStatusIcon = (status: string) => { - switch (status) { - case 'success': - return ; - case 'warning': - return ; - case 'error': - return ; - default: - return ; - } - }; - - const getBuildStatusBadge = (enabled: boolean) => { - const config = enabled - ? {className: "bg-green-100 text-green-800", text: "活跃"} - : {className: "bg-red-100 text-red-800", text: "暂停"}; - - return ( - - {config.text} - - ); - }; - - const handleDeploy = (config: DeploymentConfig) => { - if (!config?.formVariablesSchema) { - message.error('工作流配置有误,请检查工作流定义'); - return; - } - setSelectedConfig(config); - setDeployModalOpen(true); - }; + // 获取当前团队和环境 + const currentTeam = teams.find(t => t.teamId === currentTeamId); + const currentEnv = currentTeam?.environments.find(e => e.environmentId === currentEnvId); + const currentApps = currentEnv?.applications || []; if (loading) { return ; } - const hasEnvironments = environments.length > 0; - const hasProjects = deployConfigs.length > 0; - - return ( -
-

部署环境概览

- - {!hasEnvironments ? ( + if (teams.length === 0) { + return ( +
+

部署管理

- - } - title="暂无环境数据" - description="当前系统中还没有配置任何环境。请先添加部署环境,然后开始使用部署功能。" - /> + + +

您还没有加入任何团队

- ) : ( - <> +
+ ); + } -
- {environments.map((env) => ( - - - {env.envName} - {getStatusIcon(env.status)} - - -
{env.projectCount}
-

个项目

-
-
- - 最近部署: {env.lastDeployment} -
-
-
- CPU - {env.cpu}% -
- -
-
-
- 内存 - {env.memory}% -
- -
-
-
- 存储 - {env.storage}% -
- -
-
-
-
- ))} + return ( +
+ {/* 页面标题和团队选择器 */} +
+
+

部署管理

+

+ 管理和执行应用部署任务 +

+
+ +
+
+ + 当前团队:
- -
- -
- - {environments.map((env) => ( - - {env.envName} - - ))} - -
- - {environments.map((env) => ( - -
-
- setSearchText(e.target.value)} - /> - - -
- - -
-
-
- - {!hasProjects ? ( - - - } - title="暂无项目数据" - description="当前环境中还没有配置任何项目。请先创建项目,然后开始部署。" - /> - - - ) : ( -
- {deployConfigs.map((config) => ( - - -
-
-

{config.application.appName}

-

{config.application.appCode}

-
- {getBuildStatusBadge(config.enabled)} -
-
-
-

构建类型

-

{config.buildType}

-
-
-

开发语言

-

{config.languageType}

-
-
-

工作流

-

{config.publishedWorkflowDefinition?.name || '未配置'}

-
-
-

最后部署时间

-

{config.lastBuildStartTime ? new Date(config.lastBuildStartTime).toLocaleString() : '暂无部署'}

-
-
-
-
-
-

构建状态

-

- {config.lastBuildStatus === 'COMPLETED' ? '构建成功' : - config.lastBuildStatus === 'FAILED' ? '构建失败' : - config.lastBuildStatus === 'RUNNING' ? '构建中' : '未构建'} -

-
-
-

构建时长

-

- {config.lastBuildStartTime && config.lastBuildEndTime ? ( - `${Math.round((new Date(config.lastBuildEndTime).getTime() - new Date(config.lastBuildStartTime).getTime()) / 1000 / 60)} 分钟` - ) : '暂无数据'} -

-
-
-
-
- - -
-
-
- ))} -
- )} -
+ +
+
+ {/* 当前团队信息 */} + {currentTeam && ( +
+ {currentTeam.teamName} + {currentTeam.teamRole && ( + <> + · + {currentTeam.teamRole} + + )} + {currentTeam.description && ( + <> + · + {currentTeam.description} + + )} +
)} - {selectedConfig && selectedConfig.formVariablesSchema && ( - { - setDeployModalOpen(false); - setSelectedConfig(null); - }} - formSchema={selectedConfig.formVariablesSchema} - deployConfig={selectedConfig} - /> + + {/* 环境切换和应用列表 */} + {currentTeam && ( + currentTeam.environments.length === 0 ? ( + + + +

暂无部署环境

+

+ 当前团队「{currentTeam.teamName}」还没有配置任何部署环境 +

+
+
+ ) : ( + + +
+
+ 选择部署环境 + +
+
+ {currentEnv && currentEnv.requiresApproval && currentEnv.approvers.length > 0 ? ( + <> + + 审批人: {currentEnv.approvers.map((a) => a.realName).join('、')} + + ) : ( + 占位 + )} +
+
+
+ + + {/* 应用列表 */} + {currentApps.length === 0 ? ( +
+ +

暂无可部署应用

+

+ 环境「{currentEnv?.environmentName}」暂未配置任何应用 +

+
+ ) : ( +
+ {currentApps.map((app) => { + const isDeploying = deploying.has(app.teamApplicationId); + + return ( +
+
+ +
+

{app.applicationName}

+ + {app.applicationCode} + +
+
+ +
+
+ + {app.branch} +
+ +
+ + {app.workflowDefinitionName || '未配置'} +
+ + {app.deploySystemName && ( +
+ + {app.deploySystemName} +
+ )} +
+ + +
+ ); + })} +
+ )} +
+
+ ) )}
); diff --git a/frontend/src/pages/Dashboard/service.ts b/frontend/src/pages/Dashboard/service.ts index 5cd934d3..6f9aeb7b 100644 --- a/frontend/src/pages/Dashboard/service.ts +++ b/frontend/src/pages/Dashboard/service.ts @@ -1,9 +1,16 @@ -import { DeployAppBuildDTO } from './types'; -import request from "@/utils/request.ts"; +import request from '@/utils/request'; +import type { DeployEnvironmentsResponse, StartDeploymentResponse } from './types'; + +const DEPLOY_URL = '/api/v1/deploy'; /** - * 部署应用 - * @param data 部署参数 + * 获取用户的部署环境列表 */ -export const deployApp = (data: DeployAppBuildDTO) => - request.post('/api/v1/deploy-app-config/deploy', data); \ No newline at end of file +export const getDeployEnvironments = () => + request.get(`${DEPLOY_URL}/environments`); + +/** + * 发起部署 + */ +export const startDeployment = (teamApplicationId: number) => + request.post(`${DEPLOY_URL}/execute`, { teamApplicationId }); diff --git a/frontend/src/pages/Dashboard/types.ts b/frontend/src/pages/Dashboard/types.ts index bd51acf8..ca977ef8 100644 --- a/frontend/src/pages/Dashboard/types.ts +++ b/frontend/src/pages/Dashboard/types.ts @@ -1,38 +1,64 @@ -import { JsonNode } from '@/types/common'; +import type { BaseResponse } from '@/types/base'; -export interface DeployAppBuildDTO { - /** - * 构建类型 - */ - buildType: string; +export interface Approver { + userId: number; + username: string; + realName: string; +} - /** - * 应用语言 - */ - languageType: string; +export interface ApplicationConfig { + teamApplicationId: number; + applicationId: number; + applicationCode: string; + applicationName: string; + applicationDesc?: string; + branch: string; + deploySystemId?: number; + deploySystemName?: string; + deployJob?: string; + workflowDefinitionId?: number; + workflowDefinitionName?: string; + workflowDefinitionKey?: string; +} - /** - * 表单配置 - */ - formVariables?: JsonNode; +export interface DeployEnvironment { + environmentId: number; + environmentCode: string; + environmentName: string; + environmentDesc?: string; + enabled: boolean; + sort: number; + requiresApproval: boolean; + approvers: Approver[]; + applications: ApplicationConfig[]; +} - /** - * 构建配置 - */ - buildVariables: JsonNode; +export interface DeployTeam { + teamId: number; + teamCode: string; + teamName: string; + teamRole: string; + description?: string; + environments: DeployEnvironment[]; +} - /** - * 环境ID - */ - environmentId: number; +export interface DeployEnvironmentsResponse { + userId: number; + username: string; + realName: string; + teams: DeployTeam[]; +} - /** - * 应用ID - */ - applicationId: number; +export interface StartDeploymentRequest { + teamApplicationId: number; + workflowDefinitionKey: string; + formData?: Record; +} - /** - * 已发布的流程定义ID - */ - workflowDefinitionId: number; -} \ No newline at end of file +export interface StartDeploymentResponse { + instanceId: string; + processDefinitionId: string; + businessKey: string; + processKey: string; + startTime?: string; +} diff --git a/frontend/src/pages/Deploy/Application/List/components/ApplicationModal.tsx b/frontend/src/pages/Deploy/Application/List/components/ApplicationModal.tsx index d3969aa2..8905f14c 100644 --- a/frontend/src/pages/Deploy/Application/List/components/ApplicationModal.tsx +++ b/frontend/src/pages/Deploy/Application/List/components/ApplicationModal.tsx @@ -97,7 +97,7 @@ const ApplicationModal: React.FC = ({ } }; - // 加载外部系统列表 + // 加载Gitlab列表 const loadExternalSystems = async () => { try { const response = await getExternalSystems({ @@ -110,7 +110,7 @@ const ApplicationModal: React.FC = ({ } catch (error) { toast({ variant: "destructive", - title: "加载外部系统失败", + title: "加载Gitlab失败", description: error instanceof Error ? error.message : undefined, duration: 3000, }); @@ -137,14 +137,14 @@ const ApplicationModal: React.FC = ({ repoProjectId: initialValues.repoProjectId }); - // 如果有外部系统ID,加载仓库项目 + // 如果有Gitlab ID,加载仓库项目 if (initialValues.externalSystemId) { fetchRepositoryProjects(initialValues.externalSystemId); } } }, [initialValues]); - // 当选择外部系统时,获取对应的仓库项目列表 + // 当选择Gitlab时,获取对应的仓库项目列表 const handleExternalSystemChange = (externalSystemId: number | undefined) => { form.setValue('repoProjectId', undefined); setRepositoryProjects([]); @@ -162,8 +162,8 @@ const ApplicationModal: React.FC = ({ const handleSubmit = async (values: ApplicationFormValues) => { console.log('Form submitted with values:', values); try { - // 去掉 externalSystemId 字段,不传给后端 - const { externalSystemId, ...submitData } = values; + // 保留 externalSystemId 字段,传给后端 + const submitData = values; if (isEdit) { await updateApplication({ @@ -311,7 +311,7 @@ const ApplicationModal: React.FC = ({ name="externalSystemId" render={({field}) => ( - 外部系统 + Gitlab { + const appId = Number(value); + const app = applications.find(a => a.id === appId); + setAddForms({ ...addForms, [currentConfigEnvId]: { - ...addForms[currentConfigEnvId], - appId: Number(value), + appId: appId, + branch: '', + deploySystemId: null, + deployJob: '', + workflowDefinitionId: null, }, }); + + // 加载该应用的分支列表 + if (app) { + loadBranches(appId, app); + } }} > @@ -769,46 +954,340 @@ const TeamConfigDialog: React.FC = ({ ))} - - - { +
+ + {/* 分支选择 */} +
+ + {addForms[currentConfigEnvId]?.appId ? ( + (() => { + const appId = addForms[currentConfigEnvId].appId!; + const branches = branchesMap[appId] || []; + const isLoading = loadingBranches[appId]; + const searchValue = branchSearchValues[currentConfigEnvId] || ""; + const open = branchPopoverOpen[currentConfigEnvId] || false; + const filteredBranches = branches.filter(branch => + branch.name.toLowerCase().includes(searchValue.toLowerCase()) + ); + + return ( + { + setBranchPopoverOpen({ + ...branchPopoverOpen, + [currentConfigEnvId]: isOpen, + }); + }} + > + + + + +
+ + { + setBranchSearchValues({ + ...branchSearchValues, + [currentConfigEnvId]: e.target.value, + }); + }} + /> +
+
+ +
+ {filteredBranches.length === 0 ? ( +
+ 未找到分支 +
+ ) : ( + filteredBranches.map((branch) => ( +
{ + setAddForms({ + ...addForms, + [currentConfigEnvId]: { + ...addForms[currentConfigEnvId], + branch: branch.name, + }, + }); + setBranchSearchValues({ + ...branchSearchValues, + [currentConfigEnvId]: "", + }); + setBranchPopoverOpen({ + ...branchPopoverOpen, + [currentConfigEnvId]: false, + }); + }} + > + + {branch.name} + {branch.isDefaultBranch && ( + (默认) + )} + + {branch.name === addForms[currentConfigEnvId]?.branch && ( + + )} +
+ )) + )} +
+
+
+
+
+ ); + })() + ) : ( + + )} +
+ + {/* Jenkins配置 - 网格布局 */} +
+ {/* Jenkins 系统 */} +
+ + +
+ + {/* Jenkins Job */} +
+ + {addForms[currentConfigEnvId]?.deploySystemId ? ( + (() => { + const systemId = addForms[currentConfigEnvId].deploySystemId!; + const jobs = jenkinsJobsMap[systemId] || []; + const isLoading = loadingJobs[systemId]; + const searchValue = jobSearchValues[currentConfigEnvId] || ""; + const open = jobPopoverOpen[currentConfigEnvId] || false; + const filteredJobs = jobs.filter(job => + job.jobName.toLowerCase().includes(searchValue.toLowerCase()) + ); + + return ( + { + setJobPopoverOpen({ + ...jobPopoverOpen, + [currentConfigEnvId]: isOpen, + }); + }} + > + + + + +
+ + { + setJobSearchValues({ + ...jobSearchValues, + [currentConfigEnvId]: e.target.value, + }); + }} + /> +
+
+ +
+ {filteredJobs.length === 0 ? ( +
+ 未找到Job +
+ ) : ( + filteredJobs.map((job) => ( +
{ + setAddForms({ + ...addForms, + [currentConfigEnvId]: { + ...addForms[currentConfigEnvId], + deployJob: job.jobName, + }, + }); + setJobSearchValues({ + ...jobSearchValues, + [currentConfigEnvId]: "", + }); + setJobPopoverOpen({ + ...jobPopoverOpen, + [currentConfigEnvId]: false, + }); + }} + > + + {job.jobName} + + {job.jobName === addForms[currentConfigEnvId]?.deployJob && ( + + )} +
+ )) + )} +
+
+
+
+
+ ); + })() + ) : ( + + )} +
+
+ + {/* 工作流定义 */} +
+ + +
+ + {/* 添加按钮 */} +
- - - )} +
+
+
+ )} - {envApps(currentConfigEnvId).length === 0 && getAvailableApplications(currentConfigEnvId).length === 0 && ( - - - 所有应用已添加 - - - )} - - + {/* 空状态提示 */} + {envApps(currentConfigEnvId).length === 0 && getAvailableApplications(currentConfigEnvId).length === 0 && ( +
+

所有应用已添加

+
+ )} +
)} @@ -841,7 +1320,6 @@ const TeamConfigDialog: React.FC = ({
- {getEnvIcon(env.envCode)} {env.envName}
+ 取消 + -
+ {submitting ? '保存中...' : '确定'} + + ); diff --git a/frontend/src/pages/Workflow/Design/nodes/JenkinsBuildNode.tsx b/frontend/src/pages/Workflow/Design/nodes/JenkinsBuildNode.tsx index bbb8eb29..c2cc33bc 100644 --- a/frontend/src/pages/Workflow/Design/nodes/JenkinsBuildNode.tsx +++ b/frontend/src/pages/Workflow/Design/nodes/JenkinsBuildNode.tsx @@ -1,5 +1,4 @@ import {ConfigurableNodeDefinition, NodeType, NodeCategory, defineNodeOutputs} from './types'; -import { DataSourceType } from '@/domain/dataSource'; /** * Jenkins构建节点定义(纯配置) @@ -44,20 +43,20 @@ export const JenkinsBuildNodeDefinition: ConfigurableNodeDefinition = { title: "输入", description: "当前节点所需数据配置", properties: { - jenkinsServerId: { - type: "number", + serverId: { + type: "string", title: "服务器", - description: "选择要使用的服务器", - 'x-dataSource': DataSourceType.JENKINS_SERVERS + description: "输入要使用的服务器", + default: "${jenkins.serverId}" }, - project: { + jobName: { type: "string", title: "项目", description: "要触发构建的项目", - default: "" + default: "${jenkins.jobName}" }, }, - required: ["jenkinsServerId"] + required: ["serverId", "jobName"] }, outputs: defineNodeOutputs( {