From c3325c379d17c589e37ae8277e43a0528a129009 Mon Sep 17 00:00:00 2001 From: dengqichen Date: Fri, 28 Nov 2025 18:16:45 +0800 Subject: [PATCH] =?UTF-8?q?=E9=87=8D=E6=9E=84=E6=B6=88=E6=81=AF=E9=80=9A?= =?UTF-8?q?=E7=9F=A5=E5=BC=B9=E7=AA=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Dashboard/components/ApplicationCard.tsx | 105 ++++++++------ .../Dashboard/components/EnvironmentTabs.tsx | 137 +++++++++++++----- .../List/components/TeamApplicationDialog.tsx | 131 +++++++++++++---- 3 files changed, 259 insertions(+), 114 deletions(-) diff --git a/frontend/src/pages/Dashboard/components/ApplicationCard.tsx b/frontend/src/pages/Dashboard/components/ApplicationCard.tsx index 7e9df690..3b50849a 100644 --- a/frontend/src/pages/Dashboard/components/ApplicationCard.tsx +++ b/frontend/src/pages/Dashboard/components/ApplicationCard.tsx @@ -48,8 +48,8 @@ export const ApplicationCard: React.FC = ({ return (
- {/* 应用基本信息 */} -
+ {/* 应用基本信息 - 固定高度确保一致性 */} +
{app.applicationName ? ( @@ -64,11 +64,16 @@ export const ApplicationCard: React.FC = ({ ) : ( )} - {app.applicationDesc && ( -

- {app.applicationDesc} -

- )} + {/* 应用描述 - 固定单行显示,超出省略 */} +
+ {app.applicationDesc ? ( +

+ {app.applicationDesc} +

+ ) : ( + + )} +
@@ -208,12 +213,12 @@ export const ApplicationCard: React.FC = ({ const record = app.recentDeployRecords?.[index]; if (record) { - // 显示实际记录 + // 显示实际记录 - 固定高度确保一致性 const { icon: StatusIcon, color } = getStatusIcon(record.status); return ( -
+
{/* 第一行:状态、编号、部署人 */} -
+
{getStatusText(record.status)} @@ -237,52 +242,64 @@ export const ApplicationCard: React.FC = ({
{/* 第二行:时间信息(一行显示) */} - {(record.startTime || record.endTime || record.duration) && ( -
- -
- {record.startTime && ( - {formatTime(record.startTime)} - )} - {record.endTime && ( - <> - - {formatTime(record.endTime)} - - )} - {record.duration && ( - <> - - {formatDuration(record.duration)} - - )} -
-
- )} +
+ {(record.startTime || record.endTime || record.duration) ? ( + <> + +
+ {record.startTime && ( + {formatTime(record.startTime)} + )} + {record.endTime && ( + <> + + {formatTime(record.endTime)} + + )} + {record.duration && ( + <> + + {formatDuration(record.duration)} + + )} +
+ + ) : ( + - + )} +
- {/* 第三行:备注 */} - {record.deployRemark && ( -
- - {record.deployRemark} -
- )} + {/* 第三行:备注(固定高度,超出截断) */} +
+ {record.deployRemark ? ( + <> + + {record.deployRemark} + + ) : ( + - + )} +
); } else { - // 显示骨架屏占位 - 3条线模拟实际记录的3行 + // 显示骨架屏占位 - 固定高度与实际记录一致 return ( -
+
{/* 第一行:状态、编号、部署人 */} -
+
{/* 第二行:时间信息 */} - +
+ +
{/* 第三行:备注 */} - +
+ +
); } diff --git a/frontend/src/pages/Dashboard/components/EnvironmentTabs.tsx b/frontend/src/pages/Dashboard/components/EnvironmentTabs.tsx index 44de46f4..0574aaee 100644 --- a/frontend/src/pages/Dashboard/components/EnvironmentTabs.tsx +++ b/frontend/src/pages/Dashboard/components/EnvironmentTabs.tsx @@ -1,7 +1,9 @@ -import React from 'react'; +import React, { useState, useMemo } from 'react'; import { Card, CardContent } from "@/components/ui/card"; import { Tabs, TabsContent } from "@/components/ui/tabs"; -import { Package, Shield, CheckCircle2 } from "lucide-react"; +import { Input } from "@/components/ui/input"; +import { Package, Shield, CheckCircle2, Search, X } from "lucide-react"; +import { Button } from "@/components/ui/button"; import { ApplicationCard } from './ApplicationCard'; import type { DeployTeam, DeployEnvironment, ApplicationConfig } from '../types'; @@ -24,8 +26,25 @@ export const EnvironmentTabs: React.FC = React.memo(({ onEnvChange, onDeploy }) => { + const [searchValue, setSearchValue] = useState(''); const currentEnv = team.environments.find(e => e.environmentId === currentEnvId); + // 过滤应用列表 + const filteredEnvironments = useMemo(() => { + if (!searchValue.trim()) { + return team.environments; + } + + const searchLower = searchValue.toLowerCase(); + return team.environments.map(env => ({ + ...env, + applications: env.applications.filter(app => + app.applicationCode?.toLowerCase().includes(searchLower) || + app.applicationName?.toLowerCase().includes(searchLower) + ) + })); + }, [team.environments, searchValue]); + return ( onEnvChange(Number(value))}> {/* 现代化 TAB 头部 */} @@ -47,48 +66,74 @@ export const EnvironmentTabs: React.FC = React.memo(({
-
+
- {team.environments.map((env) => ( -
- - ))} + `} + > +
+ {env.requiresApproval ? ( + + ) : ( + + )} + {env.environmentName} + + {displayCount} + +
+ + ); + })} +
+ + {/* 搜索框 */} +
+ + setSearchValue(e.target.value)} + className="pl-9 pr-9 h-9" + /> + {searchValue && ( + + )}
{/* 内容区域 */} - {team.environments.map((env) => ( + {filteredEnvironments.map((env) => (
{env.applications.length === 0 ? ( @@ -97,10 +142,26 @@ export const EnvironmentTabs: React.FC = React.memo(({
-

暂无可部署应用

+

+ {searchValue.trim() ? '未找到匹配的应用' : '暂无可部署应用'} +

- 环境「{env.environmentName}」暂未配置任何应用 + {searchValue.trim() + ? `没有找到包含"${searchValue}"的应用` + : `环境「${env.environmentName}」暂未配置任何应用` + }

+ {searchValue.trim() && ( + + )} ) : ( diff --git a/frontend/src/pages/Deploy/Team/List/components/TeamApplicationDialog.tsx b/frontend/src/pages/Deploy/Team/List/components/TeamApplicationDialog.tsx index 9496d0ff..1ce8f507 100644 --- a/frontend/src/pages/Deploy/Team/List/components/TeamApplicationDialog.tsx +++ b/frontend/src/pages/Deploy/Team/List/components/TeamApplicationDialog.tsx @@ -101,6 +101,8 @@ const TeamApplicationDialog: React.FC = ({ const [loadingRepoProjects, setLoadingRepoProjects] = useState(false); // 搜索和弹窗状态 + const [appSearchValue, setAppSearchValue] = useState(''); + const [appPopoverOpen, setAppPopoverOpen] = useState(false); const [branchSearchValue, setBranchSearchValue] = useState(''); const [branchPopoverOpen, setBranchPopoverOpen] = useState(false); const [projectSearchValue, setProjectSearchValue] = useState(''); @@ -362,39 +364,104 @@ const TeamApplicationDialog: React.FC = ({ - { + const app = applications.find(a => a.id === formData.appId); + if (!app) return ''; + const category = app.applicationCategory?.name || '未分类'; + return `${category}/${app.appCode}(${app.appName})`; + })()} + disabled + /> +

+ 编辑时不可修改应用,如需更换请删除后重新添加 +

+ + ) : ( + // 新建模式:使用 Popover + Command 组件 + + + + + +
+ + setAppSearchValue(e.target.value)} + /> +
+ +
+ {(() => { + // 过滤掉已添加的应用 + const availableApps = applications.filter(app => !existingApplicationIds.includes(app.id)); + // 搜索过滤 + const filteredApps = availableApps.filter(app => + app.appName.toLowerCase().includes(appSearchValue.toLowerCase()) || + app.appCode.toLowerCase().includes(appSearchValue.toLowerCase()) || + (app.applicationCategory?.name || '').toLowerCase().includes(appSearchValue.toLowerCase()) + ); + + if (filteredApps.length === 0) { + return ( +
+ {availableApps.length === 0 ? '暂无可添加的应用' : '未找到应用'} +
+ ); + } + + return filteredApps.map((app) => { + const category = app.applicationCategory?.name || '未分类'; + return ( +
{ + handleAppChange(app.id); + setAppSearchValue(''); + setAppPopoverOpen(false); + }} + > +
+ {category} / + {app.appCode} + ({app.appName}) +
+ {app.id === formData.appId && ( + + )} +
+ ); + }); + })()}
- ) : ( - availableApps.map((app) => ( - - {app.appName}({app.appCode}) - - )) - ); - })()} - - - {mode === 'edit' && ( -

- 编辑时不可修改应用,如需更换请删除后重新添加 -

+
+
+
)}