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) }; }