From a682d5718b5da69cc984d74a21333615353f992c Mon Sep 17 00:00:00 2001 From: dengqichen Date: Mon, 10 Nov 2025 16:50:28 +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/src/hooks/useTableData.ts | 29 +- .../Dashboard/components/EnvironmentTabs.tsx | 127 +++++ .../Dashboard/components/TeamSelector.tsx | 66 +++ .../Dashboard/hooks/useDeploymentData.ts | 232 +++++++++ .../Dashboard/hooks/usePendingApproval.ts | 86 ++++ frontend/src/pages/Dashboard/index.tsx | 441 +++--------------- 6 files changed, 597 insertions(+), 384 deletions(-) create mode 100644 frontend/src/pages/Dashboard/components/EnvironmentTabs.tsx create mode 100644 frontend/src/pages/Dashboard/components/TeamSelector.tsx create mode 100644 frontend/src/pages/Dashboard/hooks/useDeploymentData.ts create mode 100644 frontend/src/pages/Dashboard/hooks/usePendingApproval.ts diff --git a/frontend/src/hooks/useTableData.ts b/frontend/src/hooks/useTableData.ts index c42deab2..554def28 100644 --- a/frontend/src/hooks/useTableData.ts +++ b/frontend/src/hooks/useTableData.ts @@ -1,4 +1,4 @@ -import { useState, useCallback, useEffect } from 'react'; +import { useState, useCallback, useEffect, useRef } from 'react'; import { message, Modal } from 'antd'; import type { Page } from '@/types/base/page'; import type { TablePaginationConfig } from 'antd/es/table'; @@ -60,16 +60,30 @@ export function useTableData< loading: false }); - // 加载数据 - const loadData = useCallback(async (params?: Partial) => { + // 使用 ref 存储当前分页参数,避免循环依赖 + const paginationRef = useRef({ + current: 1, + pageSize: config.defaultPageSize || 10 + }); + + // 同步 ref 和 state + useEffect(() => { + paginationRef.current = { + current: state.pagination.current || 1, + pageSize: state.pagination.pageSize || 10 + }; + }, [state.pagination.current, state.pagination.pageSize]); + + // 加载数据 - 修复:使用 ref 避免依赖 state + const loadData = useCallback(async (params?: Partial & { pageNum?: number; pageSize?: number }) => { setState(prev => ({ ...prev, loading: true })); try { const pageData = await service.list({ ...defaultParams, ...params, - pageNum: state.pagination.current, - pageSize: state.pagination.pageSize - }); + pageNum: params?.pageNum ?? paginationRef.current.current, + pageSize: params?.pageSize ?? paginationRef.current.pageSize + } as Q & { pageNum?: number; pageSize?: number }); setState(prev => ({ ...prev, @@ -80,11 +94,12 @@ export function useTableData< }, loading: false })); + return pageData; } catch (error) { setState(prev => ({ ...prev, loading: false })); throw error; } - }, [service, defaultParams, state.pagination]); + }, [service, defaultParams]); // 表格变化处理 const handleTableChange = ( diff --git a/frontend/src/pages/Dashboard/components/EnvironmentTabs.tsx b/frontend/src/pages/Dashboard/components/EnvironmentTabs.tsx new file mode 100644 index 00000000..6eb6e2c0 --- /dev/null +++ b/frontend/src/pages/Dashboard/components/EnvironmentTabs.tsx @@ -0,0 +1,127 @@ +import React from 'react'; +import { Card, CardContent } from "@/components/ui/card"; +import { Tabs, TabsContent } from "@/components/ui/tabs"; +import { Package, Shield, CheckCircle2 } from "lucide-react"; +import { ApplicationCard } from './ApplicationCard'; +import type { DeployTeam, DeployEnvironment, ApplicationConfig } from '../types'; + +interface EnvironmentTabsProps { + team: DeployTeam; + currentEnvId: number | null; + deploying: Set; + onEnvChange: (envId: number) => void; + onDeploy: (app: ApplicationConfig, remark: string) => Promise; +} + +/** + * 环境标签页组件 + * 显示环境切换标签和应用列表 + */ +export const EnvironmentTabs: React.FC = React.memo(({ + team, + currentEnvId, + deploying, + onEnvChange, + onDeploy +}) => { + const currentEnv = team.environments.find(e => e.environmentId === currentEnvId); + + return ( + onEnvChange(Number(value))}> + {/* 现代化 TAB 头部 */} +
+
+
+
+

部署环境

+
+
+ {currentEnv && currentEnv.requiresApproval && currentEnv.approvers.length > 0 && ( +
+ + + 需审批: {currentEnv.approvers.map((a) => a.realName).join('、')} + +
+ )} +
+
+ +
+
+ {team.environments.map((env) => ( + + ))} +
+
+
+ + {/* 内容区域 */} + {team.environments.map((env) => ( + +
+ {env.applications.length === 0 ? ( + + +
+ +
+

暂无可部署应用

+

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

+
+
+ ) : ( +
+ {env.applications.map((app) => ( + + ))} +
+ )} +
+
+ ))} + + ); +}); + +EnvironmentTabs.displayName = 'EnvironmentTabs'; diff --git a/frontend/src/pages/Dashboard/components/TeamSelector.tsx b/frontend/src/pages/Dashboard/components/TeamSelector.tsx new file mode 100644 index 00000000..a1f0b7b2 --- /dev/null +++ b/frontend/src/pages/Dashboard/components/TeamSelector.tsx @@ -0,0 +1,66 @@ +import React from 'react'; +import { Button } from "@/components/ui/button"; +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; +import { Users, ClipboardCheck } from "lucide-react"; +import type { DeployTeam } from '../types'; + +interface TeamSelectorProps { + teams: DeployTeam[]; + currentTeamId: number | null; + pendingApprovalCount: number; + onTeamChange: (teamId: string) => void; + onApprovalClick: () => void; +} + +/** + * 团队选择器组件 + * 负责显示团队选择下拉框和待审批按钮 + */ +export const TeamSelector: React.FC = React.memo(({ + teams, + currentTeamId, + pendingApprovalCount, + onTeamChange, + onApprovalClick +}) => { + return ( +
+ {/* 待审批按钮 */} + + +
+ +
+ + 当前团队: +
+ + +
+ ); +}); + +TeamSelector.displayName = 'TeamSelector'; diff --git a/frontend/src/pages/Dashboard/hooks/useDeploymentData.ts b/frontend/src/pages/Dashboard/hooks/useDeploymentData.ts new file mode 100644 index 00000000..1f85b194 --- /dev/null +++ b/frontend/src/pages/Dashboard/hooks/useDeploymentData.ts @@ -0,0 +1,232 @@ +import { useState, useCallback, useEffect, useRef } from 'react'; +import { useToast } from '@/components/ui/use-toast'; +import { getDeployEnvironments } from '../service'; +import type { DeployTeam } from '../types'; + +interface UseDeploymentDataOptions { + onInitialLoadComplete?: () => void; +} + +/** + * 部署数据管理 Hook + * 优化点: + * 1. 智能轮询 - 根据部署状态动态调整轮询频率 + * 2. 页面可见性检测 - 页面隐藏时暂停轮询 + * 3. 状态持久化 - 刷新时保持选中的团队和环境 + */ +export function useDeploymentData(options: UseDeploymentDataOptions = {}) { + const { toast } = useToast(); + const [loading, setLoading] = useState(true); + const [teams, setTeams] = useState([]); + const [currentTeamId, setCurrentTeamId] = useState(null); + const [currentEnvId, setCurrentEnvId] = useState(null); + const [deploying, setDeploying] = useState>(new Set()); + const [isInitialLoad, setIsInitialLoad] = useState(true); + + const intervalRef = useRef(null); + const optionsRef = useRef(options); + const toastRef = useRef(toast); + + // 同步 options 和 toast 到 ref + useEffect(() => { + optionsRef.current = options; + toastRef.current = toast; + }, [options, toast]); + + // 使用 ref 存储当前选中的团队和环境,避免 loadData 依赖变化 + const currentSelectionRef = useRef({ teamId: currentTeamId, envId: currentEnvId }); + + useEffect(() => { + currentSelectionRef.current = { teamId: currentTeamId, envId: currentEnvId }; + }, [currentTeamId, currentEnvId]); + + // 加载部署环境数据 - 移除状态依赖,使用 ref + const loadData = useCallback(async (showLoading = false) => { + try { + if (showLoading) { + setLoading(true); + } + + const response = await getDeployEnvironments(); + + if (response && response.length > 0) { + const prevTeamId = currentSelectionRef.current.teamId; + const prevEnvId = currentSelectionRef.current.envId; + + setTeams(response); + + setIsInitialLoad(prev => { + if (prev) { + // 首次加载:默认选中第一个团队和第一个环境 + if (response.length > 0) { + setCurrentTeamId(response[0].teamId); + + if (response[0].environments.length > 0) { + setCurrentEnvId(response[0].environments[0].environmentId); + } + } + optionsRef.current.onInitialLoadComplete?.(); + return false; + } else { + // 后续刷新:保持当前选中的团队和环境 + if (prevTeamId !== null) { + const teamExists = response.some(t => t.teamId === prevTeamId); + if (teamExists) { + setCurrentTeamId(prevTeamId); + + if (prevEnvId !== null) { + const team = response.find(t => t.teamId === prevTeamId); + const envExists = team?.environments.some(e => e.environmentId === prevEnvId); + if (envExists) { + setCurrentEnvId(prevEnvId); + } else if (team && team.environments.length > 0) { + setCurrentEnvId(team.environments[0].environmentId); + } + } + } else if (response.length > 0) { + setCurrentTeamId(response[0].teamId); + if (response[0].environments.length > 0) { + setCurrentEnvId(response[0].environments[0].environmentId); + } + } + } + + // 检查部署状态,如果应用不再是 RUNNING 状态,从 deploying 状态中移除 + setDeploying((prevDeploying) => { + const newDeploying = new Set(prevDeploying); + let hasChanges = false; + + response.forEach(team => { + team.environments.forEach(env => { + env.applications.forEach(app => { + if (newDeploying.has(app.teamApplicationId)) { + const latestStatus = app.deployStatistics?.latestStatus; + if (latestStatus && latestStatus !== 'RUNNING') { + newDeploying.delete(app.teamApplicationId); + hasChanges = true; + } + } + }); + }); + }); + + return hasChanges ? newDeploying : prevDeploying; + }); + } + return prev; + }); + } + } catch (error) { + console.error('加载数据失败:', error); + setIsInitialLoad(prev => { + if (prev) { + toastRef.current({ + variant: 'destructive', + title: '加载失败', + description: '无法加载部署环境数据,请稍后重试', + }); + } + return prev; + }); + } finally { + if (showLoading) { + setLoading(false); + } + } + }, []); + + // 首次加载数据 + useEffect(() => { + const initData = async () => { + await loadData(false); + setLoading(false); + }; + + initData(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + // 定时刷新数据(智能轮询) - 简化版 + useEffect(() => { + // 初始加载时不启动轮询 + if (isInitialLoad) return; + + // 清除旧的定时器 + if (intervalRef.current) { + clearInterval(intervalRef.current); + } + + // 页面可见性检测 + const handleVisibilityChange = () => { + if (document.hidden) { + // 页面隐藏时停止轮询 + if (intervalRef.current) { + clearInterval(intervalRef.current); + intervalRef.current = null; + } + } else { + // 页面显示时恢复轮询 + if (intervalRef.current) { + clearInterval(intervalRef.current); + } + // 智能间隔:有部署时5秒,无部署时30秒 + const interval = deploying.size > 0 ? 5000 : 30000; + intervalRef.current = setInterval(() => { + loadData(false); + }, interval); + } + }; + + // 启动轮询 - 智能间隔 + const interval = deploying.size > 0 ? 5000 : 30000; + intervalRef.current = setInterval(() => { + loadData(false); + }, interval); + + // 监听页面可见性变化 + document.addEventListener('visibilitychange', handleVisibilityChange); + + return () => { + if (intervalRef.current) { + clearInterval(intervalRef.current); + } + document.removeEventListener('visibilitychange', handleVisibilityChange); + }; + }, [isInitialLoad, deploying.size, loadData]); + + // 切换团队 + const handleTeamChange = useCallback((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); + } + }, [teams]); + + // 处理部署成功 + const handleDeploySuccess = useCallback(async () => { + await loadData(false); + }, [loadData]); + + // 获取当前团队和环境 + const currentTeam = teams.find(t => t.teamId === currentTeamId); + const currentEnv = currentTeam?.environments.find(e => e.environmentId === currentEnvId); + + return { + loading, + teams, + currentTeamId, + currentEnvId, + deploying, + currentTeam, + currentEnv, + setCurrentEnvId, + handleTeamChange, + handleDeploySuccess, + refreshData: () => loadData(false) + }; +} diff --git a/frontend/src/pages/Dashboard/hooks/usePendingApproval.ts b/frontend/src/pages/Dashboard/hooks/usePendingApproval.ts new file mode 100644 index 00000000..2be0c69e --- /dev/null +++ b/frontend/src/pages/Dashboard/hooks/usePendingApproval.ts @@ -0,0 +1,86 @@ +import { useState, useCallback, useMemo, useEffect, useRef } from 'react'; +import { getMyApprovalTasks } from '../service'; +import type { DeployTeam } from '../types'; + +interface UsePendingApprovalOptions { + teams: DeployTeam[]; + pollingEnabled?: boolean; + pollingInterval?: number; // 轮询间隔,默认30秒 +} + +/** + * 待审批数据管理 Hook + * 负责管理待审批任务的加载和刷新 + * 优化:添加智能轮询,与部署数据同步刷新 + */ +export function usePendingApproval({ + teams, + pollingEnabled = true, + pollingInterval = 30000 // 默认30秒 +}: UsePendingApprovalOptions) { + const [approvalModalOpen, setApprovalModalOpen] = useState(false); + const [pendingApprovalCount, setPendingApprovalCount] = useState(0); + const intervalRef = useRef(null); + + // 提取所有工作流定义键(去重)- 使用 useMemo 避免重复计算 + const workflowDefinitionKeys = useMemo(() => { + const workflowKeys = teams.flatMap(team => + team.environments.flatMap(env => + env.applications + .map(app => app.workflowDefinitionKey) + .filter((key): key is string => !!key) + ) + ); + return Array.from(new Set(workflowKeys)); + }, [teams]); + + // 加载待审批数量 - 使用 useCallback 避免重复创建 + const loadPendingApprovalCount = useCallback(async () => { + if (!pollingEnabled || workflowDefinitionKeys.length === 0) { + return; + } + + try { + const response = await getMyApprovalTasks(workflowDefinitionKeys); + if (response) { + setPendingApprovalCount(response.length || 0); + } + } catch (error) { + // 静默失败,不影响主页面 + console.error('Failed to load pending approval count:', error); + } + }, [workflowDefinitionKeys, pollingEnabled]); + + // 轮询待审批数量 + useEffect(() => { + if (!pollingEnabled || workflowDefinitionKeys.length === 0) { + return; + } + + // 清除旧的定时器 + if (intervalRef.current) { + clearInterval(intervalRef.current); + } + + // 立即加载一次 + loadPendingApprovalCount(); + + // 设置定时轮询 + intervalRef.current = setInterval(loadPendingApprovalCount, pollingInterval); + + return () => { + if (intervalRef.current) { + clearInterval(intervalRef.current); + } + }; + }, [loadPendingApprovalCount, pollingEnabled, workflowDefinitionKeys.length, pollingInterval]); + + return { + approvalModalOpen, + setApprovalModalOpen, + pendingApprovalCount, + workflowDefinitionKeys, + loadPendingApprovalCount, + refreshApprovalCount: loadPendingApprovalCount + }; +} diff --git a/frontend/src/pages/Dashboard/index.tsx b/frontend/src/pages/Dashboard/index.tsx index 3cdbd3a0..b8aea25f 100644 --- a/frontend/src/pages/Dashboard/index.tsx +++ b/frontend/src/pages/Dashboard/index.tsx @@ -1,24 +1,12 @@ -import React, { useState, useEffect, useRef } from 'react'; +import React, { useEffect, useCallback } from 'react'; import { Card, CardContent } from "@/components/ui/card"; -import { Button } from "@/components/ui/button"; -import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; -import { Tabs, TabsContent } from "@/components/ui/tabs"; -import { - Package, - Shield, - Loader2, - Users, - Server, - CheckCircle2, - ClipboardCheck, -} from "lucide-react"; -import { useToast } from '@/components/ui/use-toast'; -import { useSelector } from 'react-redux'; -import type { RootState } from '@/store'; -import { getDeployEnvironments, getMyApprovalTasks } from './service'; -import { ApplicationCard } from './components/ApplicationCard'; +import { Package, Server } from "lucide-react"; +import { TeamSelector } from './components/TeamSelector'; +import { EnvironmentTabs } from './components/EnvironmentTabs'; import { PendingApprovalModal } from './components/PendingApprovalModal'; -import type { DeployTeam, ApplicationConfig } from './types'; +import { useDeploymentData } from './hooks/useDeploymentData'; +import { usePendingApproval } from './hooks/usePendingApproval'; +import type { ApplicationConfig } from './types'; // ✅ 优化:使用骨架屏替代 loading,体验更好 const LoadingState = () => ( @@ -41,236 +29,50 @@ const LoadingState = () => (
); +/** + * Dashboard 组件 - 优化版 + * + * 优化点: + * 1. 拆分为更小的子组件(TeamSelector, EnvironmentTabs) + * 2. 使用自定义 hooks 管理状态(useDeploymentData, usePendingApproval) + * 3. 使用 React.memo 和 useCallback 优化性能 + * 4. 智能轮询 - 根据部署状态动态调整轮询频率 + */ const Dashboard: React.FC = () => { - const { toast } = useToast(); - const [loading, setLoading] = useState(true); - const [teams, setTeams] = useState([]); - const [currentTeamId, setCurrentTeamId] = useState(null); - const [currentEnvId, setCurrentEnvId] = useState(null); - const [deploying, setDeploying] = useState>(new Set()); - const [isInitialLoad, setIsInitialLoad] = useState(true); - const [approvalModalOpen, setApprovalModalOpen] = useState(false); - const [pendingApprovalCount, setPendingApprovalCount] = useState(0); - const intervalRef = useRef(null); - - // 从 Redux store 中获取当前登录用户信息 - const currentUserId = useSelector((state: RootState) => state.user.userInfo?.id); - - // 提取所有工作流定义键(去重) - const workflowDefinitionKeys = React.useMemo(() => { - const workflowKeys = teams.flatMap(team => - team.environments.flatMap(env => - env.applications - .map(app => app.workflowDefinitionKey) - .filter((key): key is string => !!key) - ) - ); - return Array.from(new Set(workflowKeys)); - }, [teams]); - - // 加载待审批数量 - const loadPendingApprovalCount = React.useCallback(async () => { - try { - const response = await getMyApprovalTasks(workflowDefinitionKeys); - if (response) { - setPendingApprovalCount(response.length || 0); - } - } catch (error) { - // 静默失败,不影响主页面 - console.error('Failed to load pending approval count:', error); + // 使用自定义 hooks 管理状态 + const deploymentData = useDeploymentData({ + onInitialLoadComplete: () => { + // 初始加载完成后,加载待审批数据 + approvalData.loadPendingApprovalCount(); } - }, [workflowDefinitionKeys]); + }); - // 加载部署环境数据 - const loadData = React.useCallback(async (showLoading = false) => { - try { - if (showLoading) { - setLoading(true); - } - - const response = await getDeployEnvironments(); - - // ✅ 新接口直接返回 teams 数组 - if (response && response.length > 0) { - const prevTeamId = currentTeamId; - const prevEnvId = currentEnvId; - - setTeams(response); - - if (isInitialLoad) { - // 首次加载:默认选中第一个团队和第一个环境 - if (response.length > 0) { - setCurrentTeamId(response[0].teamId); - - if (response[0].environments.length > 0) { - setCurrentEnvId(response[0].environments[0].environmentId); - } - } - setIsInitialLoad(false); - } else { - // 后续刷新:保持当前选中的团队和环境 - // 如果之前选中的团队/环境仍然存在,保持选中 - if (prevTeamId !== null) { - const teamExists = response.some(t => t.teamId === prevTeamId); - if (teamExists) { - setCurrentTeamId(prevTeamId); - - if (prevEnvId !== null) { - const team = response.find(t => t.teamId === prevTeamId); - const envExists = team?.environments.some(e => e.environmentId === prevEnvId); - if (envExists) { - setCurrentEnvId(prevEnvId); - } else if (team && team.environments.length > 0) { - // 如果之前的环境不存在了,选择第一个环境 - setCurrentEnvId(team.environments[0].environmentId); - } - } - } else if (response.length > 0) { - // 如果之前的团队不存在了,选择第一个团队 - setCurrentTeamId(response[0].teamId); - if (response[0].environments.length > 0) { - setCurrentEnvId(response[0].environments[0].environmentId); - } - } - } - - // 检查部署状态,如果应用不再是 RUNNING 状态,从 deploying 状态中移除 - setDeploying((prevDeploying) => { - const newDeploying = new Set(prevDeploying); - let hasChanges = false; - - // 遍历所有团队、环境、应用,检查部署状态 - response.forEach(team => { - team.environments.forEach(env => { - env.applications.forEach(app => { - // 如果应用在 deploying 状态中,但最新状态不是 RUNNING,则移除 - if (newDeploying.has(app.teamApplicationId)) { - const latestStatus = app.deployStatistics?.latestStatus; - if (latestStatus && latestStatus !== 'RUNNING') { - newDeploying.delete(app.teamApplicationId); - hasChanges = true; - } - } - }); - }); - }); - - return hasChanges ? newDeploying : prevDeploying; - }); - } - } - } catch (error) { - console.error('加载数据失败:', error); - // 只在首次加载失败时显示错误提示 - if (isInitialLoad) { - toast({ - variant: 'destructive', - title: '加载失败', - description: '无法加载部署环境数据,请稍后重试', - }); - } - } finally { - if (showLoading) { - setLoading(false); - } - } - }, [currentTeamId, currentEnvId, isInitialLoad, toast]); + const approvalData = usePendingApproval({ + teams: deploymentData.teams, + pollingEnabled: !deploymentData.loading + }); - // 首次加载数据 + // 监听团队数据变化,自动刷新待审批数量 useEffect(() => { - // ✅ 优化:异步加载数据,不阻塞页面渲染 - // 先渲染骨架,然后后台加载数据 - const initData = async () => { - await Promise.all([ - loadData(false), // 不显示 loading,直接显示内容 - loadPendingApprovalCount() - ]); - setLoading(false); - }; - - initData(); + if (deploymentData.teams.length > 0) { + approvalData.loadPendingApprovalCount(); + } // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); + }, [deploymentData.teams.length]); - // 定时刷新数据(每5秒) - useEffect(() => { - // 只在非首次加载后开始轮询 - if (isInitialLoad) return; + // 处理部署成功 - 使用 useCallback 避免重复创建 + const handleDeploy = useCallback(async (app: ApplicationConfig, remark: string) => { + await deploymentData.handleDeploySuccess(); + await approvalData.refreshApprovalCount(); + }, [deploymentData.handleDeploySuccess, approvalData.refreshApprovalCount]); - // 启动定时器的函数 - const startPolling = () => { - // 清除旧的定时器 - if (intervalRef.current) { - clearInterval(intervalRef.current); - } - // 立即刷新一次 - loadData(false); - loadPendingApprovalCount(); - // 设置新的定时器 - intervalRef.current = setInterval(() => { - loadData(false); - loadPendingApprovalCount(); - }, 5000); - }; - - // 停止定时器的函数 - const stopPolling = () => { - if (intervalRef.current) { - clearInterval(intervalRef.current); - intervalRef.current = null; - } - }; - - // 页面可见性检测:当页面隐藏时暂停轮询,显示时恢复 - const handleVisibilityChange = () => { - if (document.hidden) { - stopPolling(); - } else { - startPolling(); - } - }; - - // 启动轮询 - startPolling(); - - // 监听页面可见性变化 - document.addEventListener('visibilitychange', handleVisibilityChange); - - return () => { - stopPolling(); - document.removeEventListener('visibilitychange', handleVisibilityChange); - }; - }, [isInitialLoad, loadData]); - - // 切换团队时,自动选中第一个环境 - 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); - } - }; - - // 处理部署成功后的回调(刷新数据) - // 注意:实际的部署提交已在 DeploymentFormModal 中完成 - const handleDeploy = async (app: ApplicationConfig, remark: string) => { - // 部署成功后,刷新数据以获取最新状态 - await loadData(false); - }; - - // 获取当前团队和环境 - const currentTeam = teams.find(t => t.teamId === currentTeamId); - const currentEnv = currentTeam?.environments.find(e => e.environmentId === currentEnvId); - - if (loading) { + // 加载状态 + if (deploymentData.loading) { return ; } - if (teams.length === 0) { + // 无团队状态 + if (deploymentData.teams.length === 0) { return (

部署管理

@@ -295,171 +97,56 @@ const Dashboard: React.FC = () => {

-
- {/* 待审批按钮 */} - - -
- -
- - 当前团队: -
- -
+ approvalData.setApprovalModalOpen(true)} + />
{/* 当前团队信息 */} - {currentTeam && ( + {deploymentData.currentTeam && (
- {currentTeam.teamName} - {currentTeam.description && ( + {deploymentData.currentTeam.teamName} + {deploymentData.currentTeam.description && ( <> · - {currentTeam.description} + {deploymentData.currentTeam.description} )}
)} {/* 环境切换和应用列表 */} - {currentTeam && ( - currentTeam.environments.length === 0 ? ( + {deploymentData.currentTeam && ( + deploymentData.currentTeam.environments.length === 0 ? (

暂无部署环境

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

) : ( - setCurrentEnvId(Number(value))}> - {/* 现代化 TAB 头部 - 独立于 Card */} -
-
-
-
-

部署环境

-
-
- {currentEnv && currentEnv.requiresApproval && currentEnv.approvers.length > 0 && ( -
- - - 需审批: {currentEnv.approvers.map((a) => a.realName).join('、')} - -
- )} -
-
-
-
- {currentTeam.environments.map((env) => ( - - ))} -
-
-
- - {/* 内容区域 */} - {currentTeam.environments.map((env) => ( - -
- {/* 应用列表 */} - {env.applications.length === 0 ? ( - - -
- -
-

暂无可部署应用

-

- 环境「{env.environmentName}」暂未配置任何应用 -

-
-
- ) : ( -
- {env.applications.map((app) => ( - - ))} -
- )} -
-
- ))} - + ) )} {/* 待审批列表弹窗 */}
);