import React, { useState, useEffect } from 'react';
import { BrowserRouter as Router, Routes, Route, Navigate, Outlet } from 'react-router-dom';
import { ConfigProvider, theme, App as AntdApp } from 'antd';
import zhCN from 'antd/locale/zh_CN';
import apiClient from './utils/apiClient';
import { buildApiUrl, API_ENDPOINTS } from './config/api';
import HomePage from './pages/HomePage';
import Dashboard from './pages/Dashboard';
import AdminDashboard from './pages/AdminDashboard';
import MeetingDetails from './pages/MeetingDetails';
import MeetingPreview from './pages/MeetingPreview';
import AdminManagement from './pages/AdminManagement';
import PromptManagementPage from './pages/PromptManagementPage';
import PromptConfigPage from './pages/PromptConfigPage';
import KnowledgeBasePage from './pages/KnowledgeBasePage';
import EditKnowledgeBase from './pages/EditKnowledgeBase';
import ClientDownloadPage from './pages/ClientDownloadPage';
import AccountSettings from './pages/AccountSettings';
import MeetingCenterPage from './pages/MeetingCenterPage';
import MainLayout from './components/MainLayout';
import menuService from './services/menuService';
import configService from './utils/configService';
import './App.css';
import './styles/console-theme.css';
// Layout Wrapper to inject user and handleLogout
const AuthenticatedLayout = ({ user, handleLogout }) => {
// 如果还在加载中或用户不存在,不渲染,避免闪烁
if (!user) return null;
return (
);
};
const DefaultMenuRedirect = ({ user }) => {
const [targetPath, setTargetPath] = useState(null);
useEffect(() => {
let active = true;
const resolveDefaultPath = async () => {
try {
const path = await menuService.getDefaultPath();
if (active) {
setTargetPath(path || '/dashboard');
}
} catch (error) {
console.error('Resolve default menu path failed:', error);
if (active) {
setTargetPath('/dashboard');
}
}
};
if (user) {
resolveDefaultPath();
}
return () => {
active = false;
};
}, [user]);
if (!user) {
return ;
}
if (!targetPath) {
return (
);
}
return ;
};
function App() {
const [user, setUser] = useState(null);
const [isLoading, setIsLoading] = useState(true);
// Load user from localStorage on app start
useEffect(() => {
const savedAuth = localStorage.getItem('iMeetingUser');
if (savedAuth && savedAuth !== "undefined" && savedAuth !== "null") {
try {
const authData = JSON.parse(savedAuth);
// 如果数据包含 user 字段,则提取 user 字段(适应新结构)
// 否则使用整个对象(兼容旧结构)
const userData = authData.user || authData;
if (userData && typeof userData === 'object' && (userData.user_id || userData.id)) {
setUser(userData);
} else {
localStorage.removeItem('iMeetingUser');
}
} catch (error) {
console.error('Error parsing saved user:', error);
localStorage.removeItem('iMeetingUser');
}
}
setIsLoading(false);
}, []);
useEffect(() => {
let active = true;
configService.getBrandingConfig().then((branding) => {
if (active && branding?.app_name) {
document.title = branding.app_name;
}
}).catch(() => {});
return () => {
active = false;
};
}, []);
const handleLogin = (authData) => {
if (authData) {
menuService.clearCache();
// 提取用户信息用于 UI 展示
const userData = authData.user || authData;
setUser(userData);
// 存入完整 auth 数据(包含 token)供拦截器使用
localStorage.setItem('iMeetingUser', JSON.stringify(authData));
}
};
const handleLogout = async () => {
try {
await apiClient.post(buildApiUrl(API_ENDPOINTS.AUTH.LOGOUT));
} catch (error) {
console.error('Logout API error:', error);
} finally {
setUser(null);
localStorage.removeItem('iMeetingUser');
menuService.clearCache();
window.location.href = '/';
}
};
if (isLoading) {
return (
);
}
return (
{/* Public Routes */}
:
} />
} />
} />
{/* Authenticated Routes */}
: }>
:
} />
:
} />
} />
} />
} />
} />
:
} />
:
} />
:
} />
} />
:
} />
} />
} />
} />
{/* Catch all */}
} />
);
}
export default App;