| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| import { useState, useEffect, useContext, useCallback, useMemo } from 'react'; |
| import { useNavigate, useLocation } from 'react-router-dom'; |
| import { useTranslation } from 'react-i18next'; |
| import { UserContext } from '../../context/User'; |
| import { StatusContext } from '../../context/Status'; |
| import { useSetTheme, useTheme, useActualTheme } from '../../context/Theme'; |
| import { getLogo, getSystemName, API, showSuccess } from '../../helpers'; |
| import { normalizeLanguage } from '../../i18n/language'; |
| import { useIsMobile } from './useIsMobile'; |
| import { useSidebarCollapsed } from './useSidebarCollapsed'; |
| import { useMinimumLoadingTime } from './useMinimumLoadingTime'; |
|
|
| export const useHeaderBar = ({ onMobileMenuToggle, drawerOpen }) => { |
| const { t, i18n } = useTranslation(); |
| const [userState, userDispatch] = useContext(UserContext); |
| const [statusState] = useContext(StatusContext); |
| const isMobile = useIsMobile(); |
| const [collapsed, toggleCollapsed] = useSidebarCollapsed(); |
| const [logoLoaded, setLogoLoaded] = useState(false); |
| const navigate = useNavigate(); |
| const [currentLang, setCurrentLang] = useState(normalizeLanguage(i18n.language)); |
| const location = useLocation(); |
|
|
| const loading = statusState?.status === undefined; |
| const isLoading = useMinimumLoadingTime(loading, 200); |
|
|
| const systemName = getSystemName(); |
| const logo = getLogo(); |
| const currentDate = new Date(); |
| const isNewYear = currentDate.getMonth() === 0 && currentDate.getDate() === 1; |
|
|
| const isSelfUseMode = statusState?.status?.self_use_mode_enabled || false; |
| const docsLink = statusState?.status?.docs_link || ''; |
| const isDemoSiteMode = statusState?.status?.demo_site_enabled || false; |
|
|
| |
| const headerNavModulesConfig = statusState?.status?.HeaderNavModules; |
|
|
| |
| const headerNavModules = useMemo(() => { |
| if (headerNavModulesConfig) { |
| try { |
| const modules = JSON.parse(headerNavModulesConfig); |
|
|
| |
| if (typeof modules.pricing === 'boolean') { |
| modules.pricing = { |
| enabled: modules.pricing, |
| requireAuth: false, |
| }; |
| } |
|
|
| return modules; |
| } catch (error) { |
| console.error('解析顶栏模块配置失败:', error); |
| return null; |
| } |
| } |
| return null; |
| }, [headerNavModulesConfig]); |
|
|
| |
| const pricingRequireAuth = useMemo(() => { |
| if (headerNavModules?.pricing) { |
| return typeof headerNavModules.pricing === 'object' |
| ? headerNavModules.pricing.requireAuth |
| : false; |
| } |
| return false; |
| }, [headerNavModules]); |
|
|
| const isConsoleRoute = location.pathname.startsWith('/console'); |
|
|
| const theme = useTheme(); |
| const actualTheme = useActualTheme(); |
| const setTheme = useSetTheme(); |
|
|
| |
| useEffect(() => { |
| setLogoLoaded(false); |
| if (!logo) return; |
| const img = new Image(); |
| img.src = logo; |
| img.onload = () => setLogoLoaded(true); |
| }, [logo]); |
|
|
| |
| useEffect(() => { |
| try { |
| const iframe = document.querySelector('iframe'); |
| const cw = iframe && iframe.contentWindow; |
| if (cw) { |
| cw.postMessage({ themeMode: actualTheme }, '*'); |
| } |
| } catch (e) { |
| |
| } |
| }, [actualTheme]); |
|
|
| |
| useEffect(() => { |
| const handleLanguageChanged = (lng) => { |
| const normalizedLang = normalizeLanguage(lng); |
| setCurrentLang(normalizedLang); |
| try { |
| const iframe = document.querySelector('iframe'); |
| const cw = iframe && iframe.contentWindow; |
| if (cw) { |
| cw.postMessage({ lang: normalizedLang }, '*'); |
| } |
| } catch (e) { |
| |
| } |
| }; |
|
|
| i18n.on('languageChanged', handleLanguageChanged); |
| return () => { |
| i18n.off('languageChanged', handleLanguageChanged); |
| }; |
| }, [i18n]); |
|
|
| |
| const logout = useCallback(async () => { |
| await API.get('/api/user/logout'); |
| showSuccess(t('注销成功!')); |
| userDispatch({ type: 'logout' }); |
| localStorage.removeItem('user'); |
| navigate('/login'); |
| }, [navigate, t, userDispatch]); |
|
|
| const handleLanguageChange = useCallback( |
| async (lang) => { |
| |
| const previousLang = normalizeLanguage(i18n.language); |
| i18n.changeLanguage(lang); |
| localStorage.setItem('i18nextLng', lang); |
|
|
| |
| if (userState?.user?.id) { |
| try { |
| const res = await API.put('/api/user/self', { |
| language: lang, |
| }); |
| if (res.data.success) { |
| |
| |
| let settings = {}; |
| if (userState?.user?.setting) { |
| try { |
| settings = JSON.parse(userState.user.setting) || {}; |
| } catch (e) { |
| settings = {}; |
| } |
| } |
|
|
| settings.language = lang; |
| const nextUser = { |
| ...userState.user, |
| setting: JSON.stringify(settings), |
| }; |
|
|
| userDispatch({ |
| type: 'login', |
| payload: nextUser, |
| }); |
| localStorage.setItem('user', JSON.stringify(nextUser)); |
| } |
| } catch (error) { |
| if (previousLang) { |
| i18n.changeLanguage(previousLang); |
| localStorage.setItem('i18nextLng', previousLang); |
| } |
| console.error('Failed to save language preference:', error); |
| } |
| } |
| }, |
| [i18n, userState, userDispatch], |
| ); |
|
|
| const handleThemeToggle = useCallback( |
| (newTheme) => { |
| if ( |
| !newTheme || |
| (newTheme !== 'light' && newTheme !== 'dark' && newTheme !== 'auto') |
| ) { |
| return; |
| } |
| setTheme(newTheme); |
| }, |
| [setTheme], |
| ); |
|
|
| const handleMobileMenuToggle = useCallback(() => { |
| if (isMobile) { |
| onMobileMenuToggle(); |
| } else { |
| toggleCollapsed(); |
| } |
| }, [isMobile, onMobileMenuToggle, toggleCollapsed]); |
|
|
| return { |
| |
| userState, |
| statusState, |
| isMobile, |
| collapsed, |
| logoLoaded, |
| currentLang, |
| location, |
| isLoading, |
| systemName, |
| logo, |
| isNewYear, |
| isSelfUseMode, |
| docsLink, |
| isDemoSiteMode, |
| isConsoleRoute, |
| theme, |
| drawerOpen, |
| headerNavModules, |
| pricingRequireAuth, |
|
|
| |
| logout, |
| handleLanguageChange, |
| handleThemeToggle, |
| handleMobileMenuToggle, |
| navigate, |
| t, |
| }; |
| }; |
|
|