feat: 平台管理员端 — SUPER_ADMIN 角色 + 企业租户管理 + 用户管理 + 数据总览

This commit is contained in:
selfrelease
2026-07-29 11:49:48 +08:00
parent fb36b10402
commit 987a4678f7
17 changed files with 1744 additions and 14 deletions
+47
View File
@@ -37,6 +37,13 @@ const HealthCheck = lazy(() => import('./pages/tools/HealthCheck'))
const AnnualValueReport = lazy(() => import('./pages/tools/AnnualValueReport'))
const CalendarPage = lazy(() => import('./pages/Calendar'))
// 平台管理端
const PlatformLogin = lazy(() => import('./pages/platform/PlatformLogin'))
const PlatformDashboard = lazy(() => import('./pages/platform/PlatformDashboard'))
const PlatformOrgs = lazy(() => import('./pages/platform/PlatformOrgs'))
const PlatformUsers = lazy(() => import('./pages/platform/PlatformUsers'))
const PlatformSidebar = lazy(() => import('./components/layout/PlatformSidebar'))
function ProtectedRoute({ children }: { children: React.ReactNode }) {
const isAuthenticated = useAuthStore((s) => s.isAuthenticated)
if (!isAuthenticated) return <Navigate to="/login" replace />
@@ -67,6 +74,40 @@ function AdminLayout({ children }: { children: React.ReactNode }) {
)
}
function PlatformRoute({ children }: { children: React.ReactNode }) {
const user = useAuthStore((s) => s.user)
const isAuthenticated = useAuthStore((s) => s.isAuthenticated)
if (!isAuthenticated || user?.role !== 'SUPER_ADMIN') return <Navigate to="/platform/login" replace />
return <>{children}</>
}
function PlatformLayout({ children }: { children: React.ReactNode }) {
const [sidebarOpen, setSidebarOpen] = useState(false)
return (
<div className="flex min-h-screen bg-slate-100">
<PlatformSidebar mobileOpen={sidebarOpen} onClose={() => setSidebarOpen(false)} />
<div className="flex-1 flex flex-col min-w-0">
<header className="h-14 bg-slate-950 border-b border-slate-800 flex items-center px-4 shrink-0">
<button
className="md:hidden mr-3 text-slate-400"
onClick={() => setSidebarOpen(true)}
aria-label="打开菜单"
>
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><line x1="3" y1="6" x2="21" y2="6"/><line x1="3" y1="12" x2="21" y2="12"/><line x1="3" y1="18" x2="21" y2="18"/></svg>
</button>
<span className="text-sm text-slate-400"></span>
<span className="ml-2 px-2 py-0.5 rounded text-[10px] font-bold bg-amber-400 text-slate-950">ADMIN</span>
</header>
<main className="flex-1 py-6 px-4 md:px-6">
<div className="max-w-7xl mx-auto">
<Suspense fallback={<SkeletonPage />}>{children}</Suspense>
</div>
</main>
</div>
</div>
)
}
function PortalLayoutWrapper({ children, showNav = true }: { children: React.ReactNode; showNav?: boolean }) {
if (!showNav) {
return (
@@ -112,6 +153,12 @@ export default function App() {
<Route path="/tools/health-check" element={<ProtectedRoute><AdminLayout><HealthCheck /></AdminLayout></ProtectedRoute>} />
<Route path="/tools/annual-value" element={<ProtectedRoute><AdminLayout><AnnualValueReport /></AdminLayout></ProtectedRoute>} />
{/* 平台管理端 */}
<Route path="/platform/login" element={<Suspense fallback={<SkeletonPage />}><PlatformLogin /></Suspense>} />
<Route path="/platform/dashboard" element={<PlatformRoute><PlatformLayout><PlatformDashboard /></PlatformLayout></PlatformRoute>} />
<Route path="/platform/orgs" element={<PlatformRoute><PlatformLayout><PlatformOrgs /></PlatformLayout></PlatformRoute>} />
<Route path="/platform/users" element={<PlatformRoute><PlatformLayout><PlatformUsers /></PlatformLayout></PlatformRoute>} />
{/* 员工端 */}
<Route path="/portal/login" element={<PortalLayoutWrapper showNav={false}><PortalLogin /></PortalLayoutWrapper>} />
<Route path="/portal/payslip" element={<PortalLayoutWrapper><Payslip /></PortalLayoutWrapper>} />