2026-05-21-11-13-49 独立Docker程序包
This commit is contained in:
200
WebSite/src/App.tsx
Normal file
200
WebSite/src/App.tsx
Normal file
@@ -0,0 +1,200 @@
|
||||
/**
|
||||
* @license
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @license
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import React, { useState, useEffect, useRef } from 'react';
|
||||
import { AnimatePresence, motion } from 'motion/react';
|
||||
import Login from './components/Login';
|
||||
import Sidebar from './components/Sidebar';
|
||||
import Overview from './components/Overview';
|
||||
import ProjectLibrary from './components/ProjectLibrary';
|
||||
import ReverseWorkspace from './components/ReverseWorkspace';
|
||||
import UserManagement from './components/UserManagement';
|
||||
import { ViewType } from './types';
|
||||
import { api } from './lib/api';
|
||||
|
||||
export default function App() {
|
||||
const [isAuthenticated, setIsAuthenticated] = useState(false);
|
||||
const [sessionLoading, setSessionLoading] = useState(true);
|
||||
const [activeView, setActiveView] = useState<ViewType>(ViewType.OVERVIEW);
|
||||
const [sidebarCollapsed, setSidebarCollapsed] = useState(false);
|
||||
const [activeProjectId, setActiveProjectId] = useState('head-ct-demo');
|
||||
const [projectLibraryInitialView, setProjectLibraryInitialView] = useState<'dicom' | 'model' | 'mask'>('dicom');
|
||||
const workspaceLeaveGuardRef = useRef<(() => Promise<boolean>) | null>(null);
|
||||
const bootSessionResetRef = useRef(false);
|
||||
|
||||
// Automatically collapse main sidebar when entering Project Library or Workspace
|
||||
useEffect(() => {
|
||||
if (activeView === ViewType.PROJECTS || activeView === ViewType.WORKSPACE) {
|
||||
setSidebarCollapsed(true);
|
||||
} else {
|
||||
setSidebarCollapsed(false);
|
||||
}
|
||||
}, [activeView]);
|
||||
|
||||
useEffect(() => {
|
||||
let mounted = true;
|
||||
|
||||
const syncSession = async () => {
|
||||
try {
|
||||
if (!bootSessionResetRef.current) {
|
||||
bootSessionResetRef.current = true;
|
||||
const session = await api.logout();
|
||||
if (!mounted) {
|
||||
return;
|
||||
}
|
||||
setIsAuthenticated(session.authenticated);
|
||||
setActiveView(ViewType.OVERVIEW);
|
||||
return;
|
||||
}
|
||||
const session = await api.getSession();
|
||||
if (!mounted) {
|
||||
return;
|
||||
}
|
||||
setIsAuthenticated(session.authenticated);
|
||||
if (!session.authenticated) {
|
||||
setActiveView(ViewType.OVERVIEW);
|
||||
}
|
||||
} catch {
|
||||
if (mounted) {
|
||||
setIsAuthenticated(false);
|
||||
}
|
||||
} finally {
|
||||
if (mounted) {
|
||||
setSessionLoading(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
syncSession();
|
||||
const interval = window.setInterval(syncSession, 2500);
|
||||
return () => {
|
||||
mounted = false;
|
||||
window.clearInterval(interval);
|
||||
};
|
||||
}, []);
|
||||
|
||||
const handleLogin = () => {
|
||||
setIsAuthenticated(true);
|
||||
};
|
||||
|
||||
const requestActiveView = (nextView: ViewType) => {
|
||||
if (nextView === activeView) {
|
||||
return;
|
||||
}
|
||||
|
||||
const leaveWorkspace = activeView === ViewType.WORKSPACE && nextView !== ViewType.WORKSPACE;
|
||||
const switchView = () => {
|
||||
if (leaveWorkspace && nextView === ViewType.PROJECTS) {
|
||||
setProjectLibraryInitialView('mask');
|
||||
}
|
||||
setActiveView(nextView);
|
||||
};
|
||||
|
||||
if (!leaveWorkspace || !workspaceLeaveGuardRef.current) {
|
||||
switchView();
|
||||
return;
|
||||
}
|
||||
|
||||
workspaceLeaveGuardRef.current()
|
||||
.then((canLeave) => {
|
||||
if (canLeave) {
|
||||
switchView();
|
||||
}
|
||||
})
|
||||
.catch(() => undefined);
|
||||
};
|
||||
|
||||
const handleLogout = async () => {
|
||||
if (activeView === ViewType.WORKSPACE && workspaceLeaveGuardRef.current) {
|
||||
const canLeave = await workspaceLeaveGuardRef.current();
|
||||
if (!canLeave) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
await api.logout();
|
||||
setIsAuthenticated(false);
|
||||
setActiveView(ViewType.OVERVIEW);
|
||||
};
|
||||
|
||||
if (sessionLoading) {
|
||||
return (
|
||||
<div className="min-h-screen bg-neutral-50 flex items-center justify-center text-slate-500 font-medium">
|
||||
正在同步登录状态...
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (!isAuthenticated) {
|
||||
return <Login onLogin={handleLogin} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex h-screen bg-[#f8fafc] overflow-hidden font-sans antialiased text-slate-900">
|
||||
<Sidebar
|
||||
activeView={activeView}
|
||||
setActiveView={requestActiveView}
|
||||
onLogout={handleLogout}
|
||||
collapsed={sidebarCollapsed}
|
||||
setCollapsed={setSidebarCollapsed}
|
||||
/>
|
||||
|
||||
<main className="flex-1 flex flex-col min-w-0 overflow-hidden">
|
||||
{/* Top Navigation */}
|
||||
<header className="h-16 bg-white border-b border-slate-200 px-8 flex items-center justify-between z-10 shrink-0">
|
||||
<div className="flex items-center gap-4 text-sm font-medium">
|
||||
<span className="text-slate-900 font-bold capitalize">
|
||||
{activeView === ViewType.OVERVIEW && '总体概况'}
|
||||
{activeView === ViewType.PROJECTS && '项目库'}
|
||||
{activeView === ViewType.WORKSPACE && '逆向工作区'}
|
||||
{activeView === ViewType.SYSTEM && '系统管理工作区'}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-6">
|
||||
</div>
|
||||
</header>
|
||||
|
||||
{/* Content Area */}
|
||||
<div className="flex-1 overflow-y-auto overflow-x-hidden p-8">
|
||||
<AnimatePresence mode="wait">
|
||||
<motion.div
|
||||
key={activeView}
|
||||
initial={{ opacity: 0, x: 10 }}
|
||||
animate={{ opacity: 1, x: 0 }}
|
||||
exit={{ opacity: 0, x: -10 }}
|
||||
transition={{ duration: 0.2, ease: "easeOut" }}
|
||||
className="h-full"
|
||||
>
|
||||
{activeView === ViewType.OVERVIEW && <Overview />}
|
||||
{activeView === ViewType.PROJECTS && (
|
||||
<ProjectLibrary
|
||||
initialViewMode={projectLibraryInitialView}
|
||||
onReverse={(projectId) => {
|
||||
setActiveProjectId(projectId);
|
||||
setActiveView(ViewType.WORKSPACE);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{activeView === ViewType.WORKSPACE && (
|
||||
<ReverseWorkspace
|
||||
projectId={activeProjectId}
|
||||
onLeaveGuardChange={(handler) => {
|
||||
workspaceLeaveGuardRef.current = handler;
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{activeView === ViewType.SYSTEM && <UserManagement />}
|
||||
</motion.div>
|
||||
</AnimatePresence>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user