From 8b940229fc7769acb62240ae1fd4f0d5000a0bb3 Mon Sep 17 00:00:00 2001 From: dengqichen Date: Tue, 11 Nov 2025 10:57:42 +0800 Subject: [PATCH] =?UTF-8?q?=E9=87=8D=E6=9E=84=E5=89=8D=E7=AB=AF=E9=80=BB?= =?UTF-8?q?=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/index.html | 2 +- frontend/src/config/env.ts | 2 +- .../Dashboard/components/EnvironmentTabs.tsx | 4 +- .../Dashboard/components/TeamSelector.tsx | 32 ++++---- frontend/src/pages/Dashboard/index.tsx | 13 +++ .../TeamEnvironmentConfigDialog.tsx | 22 +++++- frontend/src/pages/Login/index.tsx | 52 +++++------- .../Role/List/components/AssignTagDialog.tsx | 79 +++++++------------ frontend/src/router/index.tsx | 13 ++- 9 files changed, 113 insertions(+), 106 deletions(-) diff --git a/frontend/index.html b/frontend/index.html index 9d5cb775..133ec398 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -4,7 +4,7 @@ - Deploy Ease Platform + 链宇Deploy Ease平台
diff --git a/frontend/src/config/env.ts b/frontend/src/config/env.ts index 4da296bf..4988c2fd 100644 --- a/frontend/src/config/env.ts +++ b/frontend/src/config/env.ts @@ -16,7 +16,7 @@ export const isProd = MODE === 'production'; export const API_BASE_URL = import.meta.env.VITE_API_BASE_URL || ''; // 应用标题 -export const APP_TITLE = import.meta.env.VITE_APP_TITLE || 'Deploy Ease Platform'; +export const APP_TITLE = import.meta.env.VITE_APP_TITLE || '链宇Deploy Ease平台'; // 是否启用 Mock export const USE_MOCK = import.meta.env.VITE_USE_MOCK === 'true'; diff --git a/frontend/src/pages/Dashboard/components/EnvironmentTabs.tsx b/frontend/src/pages/Dashboard/components/EnvironmentTabs.tsx index 6eb6e2c0..1121189d 100644 --- a/frontend/src/pages/Dashboard/components/EnvironmentTabs.tsx +++ b/frontend/src/pages/Dashboard/components/EnvironmentTabs.tsx @@ -35,7 +35,7 @@ export const EnvironmentTabs: React.FC = React.memo(({

部署环境

-
+
{currentEnv && currentEnv.requiresApproval && currentEnv.approvers.length > 0 && (
@@ -90,7 +90,7 @@ export const EnvironmentTabs: React.FC = React.memo(({ {/* 内容区域 */} {team.environments.map((env) => ( -
+
{env.applications.length === 0 ? ( diff --git a/frontend/src/pages/Dashboard/components/TeamSelector.tsx b/frontend/src/pages/Dashboard/components/TeamSelector.tsx index a1f0b7b2..d4874e94 100644 --- a/frontend/src/pages/Dashboard/components/TeamSelector.tsx +++ b/frontend/src/pages/Dashboard/components/TeamSelector.tsx @@ -8,6 +8,7 @@ interface TeamSelectorProps { teams: DeployTeam[]; currentTeamId: number | null; pendingApprovalCount: number; + showApprovalButton: boolean; // 是否显示待审批按钮(基于当前环境) onTeamChange: (teamId: string) => void; onApprovalClick: () => void; } @@ -20,25 +21,28 @@ export const TeamSelector: React.FC = React.memo(({ teams, currentTeamId, pendingApprovalCount, + showApprovalButton, onTeamChange, onApprovalClick }) => { return (
- {/* 待审批按钮 */} - + {/* 待审批按钮 - 只在当前环境需要审批且用户有权限时显示 */} + {showApprovalButton && ( + + )}
diff --git a/frontend/src/pages/Dashboard/index.tsx b/frontend/src/pages/Dashboard/index.tsx index b8aea25f..4bbf4af1 100644 --- a/frontend/src/pages/Dashboard/index.tsx +++ b/frontend/src/pages/Dashboard/index.tsx @@ -60,6 +60,18 @@ const Dashboard: React.FC = () => { // eslint-disable-next-line react-hooks/exhaustive-deps }, [deploymentData.teams.length]); + // 计算当前环境是否应该显示待审批按钮 + // 逻辑:当前环境需要审批 且 用户是该环境的审批人 + const shouldShowApprovalButton = React.useMemo(() => { + if (!deploymentData.currentTeam || !deploymentData.currentEnvId) { + return false; + } + const currentEnv = deploymentData.currentTeam.environments.find( + env => env.environmentId === deploymentData.currentEnvId + ); + return currentEnv?.requiresApproval === true && currentEnv?.canApprove === true; + }, [deploymentData.currentTeam, deploymentData.currentEnvId]); + // 处理部署成功 - 使用 useCallback 避免重复创建 const handleDeploy = useCallback(async (app: ApplicationConfig, remark: string) => { await deploymentData.handleDeploySuccess(); @@ -101,6 +113,7 @@ const Dashboard: React.FC = () => { teams={deploymentData.teams} currentTeamId={deploymentData.currentTeamId} pendingApprovalCount={approvalData.pendingApprovalCount} + showApprovalButton={shouldShowApprovalButton} onTeamChange={deploymentData.handleTeamChange} onApprovalClick={() => approvalData.setApprovalModalOpen(true)} /> diff --git a/frontend/src/pages/Deploy/Team/List/components/TeamEnvironmentConfigDialog.tsx b/frontend/src/pages/Deploy/Team/List/components/TeamEnvironmentConfigDialog.tsx index 650905f0..e56a8478 100644 --- a/frontend/src/pages/Deploy/Team/List/components/TeamEnvironmentConfigDialog.tsx +++ b/frontend/src/pages/Deploy/Team/List/components/TeamEnvironmentConfigDialog.tsx @@ -261,8 +261,10 @@ export const TeamEnvironmentConfigDialog: React.FC< teamId, environmentId: data.environmentId, approvalRequired: data.approvalRequired, - approverUserIds: data.approverUserIds, - notificationChannelId: data.notificationChannelId, + // 如果不需要审批,清空审批人列表 + approverUserIds: data.approvalRequired ? data.approverUserIds : [], + // 如果未启用通知,清空通知渠道 + notificationChannelId: data.notificationEnabled ? data.notificationChannelId : undefined, notificationEnabled: data.notificationEnabled, requireCodeReview: data.requireCodeReview, remark: data.remark, @@ -376,7 +378,13 @@ export const TeamEnvironmentConfigDialog: React.FC< { + field.onChange(checked); + // 取消审批时自动清空审批人列表 + if (!checked) { + form.setValue('approverUserIds', []); + } + }} /> @@ -500,7 +508,13 @@ export const TeamEnvironmentConfigDialog: React.FC< { + field.onChange(checked); + // 取消通知时自动清空通知渠道 + if (!checked) { + form.setValue('notificationChannelId', undefined); + } + }} /> diff --git a/frontend/src/pages/Login/index.tsx b/frontend/src/pages/Login/index.tsx index f3f84227..d37f4137 100644 --- a/frontend/src/pages/Login/index.tsx +++ b/frontend/src/pages/Login/index.tsx @@ -47,7 +47,7 @@ const Login: React.FC = () => { } = useForm({ resolver: zodResolver(loginFormSchema), defaultValues: { - tenantId: '', + tenantId: 'admin', // 默认租户 username: '', password: '', }, @@ -69,14 +69,21 @@ const Login: React.FC = () => { }, []); // 加载菜单数据 - const loadMenuData = async () => { + const loadMenuData = async (): Promise => { try { const menuData = await getCurrentUserMenus(); if (menuData && menuData.length > 0) { dispatch(setMenus(menuData)); + return true; + } else { + // 没有任何菜单权限 + dispatch(setMenus([])); + return false; } } catch (error) { console.error('获取菜单数据失败:', error); + dispatch(setMenus([])); + return false; } }; @@ -97,19 +104,23 @@ const Login: React.FC = () => { ); // 2. 获取菜单数据 - await loadMenuData(); + const hasMenus = await loadMenuData(); // 3. 显示成功提示 toast({ title: '登录成功', - description: '正在进入系统...', + description: hasMenus ? '正在进入系统...' : '正在加载...', }); // 4. 短暂延迟后跳转(让用户看到成功提示) setTimeout(() => { - // ✅ 最稳定方案:刷新页面重新初始化 - // 优点:100% 可靠,使用缓存菜单启动快 - window.location.href = '/'; + if (hasMenus) { + // 有菜单权限,跳转到首页 + window.location.href = '/'; + } else { + // 没有任何菜单权限,跳转到无权限欢迎页 + window.location.href = '/no-permission'; + } }, 300); } catch (error) { console.error('登录失败:', error); @@ -141,34 +152,13 @@ const Login: React.FC = () => {
-

Deploy Ease Platform

+

链宇Deploy Ease平台

输入您的账号密码登录系统

- {/* 租户选择 */} -
- - - {errors.tenantId && ( -

{errors.tenantId.message}

- )} -
- + {/* 租户选择 - 隐藏,默认使用 admin */} + {/* 用户名 */}
diff --git a/frontend/src/pages/System/Role/List/components/AssignTagDialog.tsx b/frontend/src/pages/System/Role/List/components/AssignTagDialog.tsx index da5920cb..0f2162e2 100644 --- a/frontend/src/pages/System/Role/List/components/AssignTagDialog.tsx +++ b/frontend/src/pages/System/Role/List/components/AssignTagDialog.tsx @@ -12,7 +12,7 @@ import { Badge } from '@/components/ui/badge'; import { Checkbox } from '@/components/ui/checkbox'; import { Input } from '@/components/ui/input'; import { useToast } from '@/components/ui/use-toast'; -import { Loader2, Tag as TagIcon, Search, CheckCircle2 } from 'lucide-react'; +import { Loader2, Tag as TagIcon, Search } from 'lucide-react'; import type { RoleTagResponse } from '../types'; import { getAllTags, assignTags } from '../service'; @@ -106,26 +106,19 @@ const AssignTagDialog: React.FC = ({ return ( - - - -
- -
-
-
分配标签
-
- 为角色选择适合的标签分类 -
-
+ + + + + 分配标签 {loading ? ( -
- - 加载标签中... +
+ + 加载中...
) : (
@@ -143,25 +136,20 @@ const AssignTagDialog: React.FC = ({ )} {/* 统计信息 */} -
- - 共 {filteredTags.length} 个标签 - -
- - - 已选 {selectedTagIds.length} 个 - + {allTags.length > 0 && ( +
+ 共 {filteredTags.length} 个标签 + 已选 {selectedTagIds.length} 个
-
+ )} {/* 标签列表 */} -
+
{filteredTags.length > 0 ? ( filteredTags.map(tag => (
handleToggle(tag.id)} > = ({ onCheckedChange={() => handleToggle(tag.id)} onClick={(e) => e.stopPropagation()} /> -
-
- - {tag.name} - -
+
+ + {tag.name} + {tag.description && ( -

+ {tag.description} -

+ )}
)) ) : searchText ? ( -
- +

未找到匹配的标签

-

请尝试其他搜索词

) : ( -
- +

暂无可用标签

-

请先创建标签

)}
@@ -221,9 +198,7 @@ const AssignTagDialog: React.FC = ({ diff --git a/frontend/src/router/index.tsx b/frontend/src/router/index.tsx index d4a32656..6f8f02fb 100644 --- a/frontend/src/router/index.tsx +++ b/frontend/src/router/index.tsx @@ -12,6 +12,7 @@ import ErrorFallback from '@/components/ErrorFallback'; // 错误页面 const Forbidden = lazy(() => import('@/pages/Error/403')); const NotFound = lazy(() => import('@/pages/Error/404')); +const NoPermission = lazy(() => import('@/pages/Error/NoPermission')); // 表单设计器测试页面(写死的路由) const FormDesignerTest = lazy(() => import('../pages/FormDesigner')); @@ -67,6 +68,7 @@ const generateRoutes = (menus: MenuResponse[]): RouteObject[] => { export const createDynamicRouter = () => { const state = store.getState(); const menus = state.user.menus || []; + const hasMenus = menus.length > 0; // 动态生成路由 const dynamicRoutes = generateRoutes(menus); @@ -83,7 +85,7 @@ export const createDynamicRouter = () => { children: [ { path: '', - element: , + element: hasMenus ? : , }, // 写死的测试路由:表单设计器测试页面 { @@ -98,6 +100,15 @@ export const createDynamicRouter = () => { }, // 动态生成的路由 ...dynamicRoutes, + // 无权限欢迎页(用户没有任何菜单权限时显示) + { + path: 'no-permission', + element: ( + }> + + + ), + }, // 403 无权限页面 { path: '403',