+ {/* 3D 交互式场景 */}
+
+
+ {/* 页面标题 */}
+
+
+
+ 3D 智能实验室
+
+
+ Interactive Laboratory Visualization
+
+
+
+
+ {/* 设备列表按钮 */}
+
+
+
+
+ {/* 设备列表侧边栏 */}
+ {showDeviceList && (
+
+
+ {/* 头部 */}
+
+
+ 实验室设备
+
+
+
+
+ {/* 滚动内容区域 */}
+
+
+ {deviceIds.map((deviceId) => {
+ const info = getDeviceInfo(deviceId);
+ if (!info) return null;
+
+ return (
+
+ );
+ })}
+
+
+
+
+ )}
+
+ {/* 操作指南 */}
+
+
+
+
+ 🖱️
+ 拖动旋转
+
+
+ ⚙️
+ 滚轮缩放
+
+
+ 👆
+ 点击设备查看
+
+
+ 📋
+ 右上查看列表
+
+
+
+
+
+ {/* 快速访问设备卡片(底部) */}
+
+
+
+
+ {['liquid-handler', 'microscope', 'agv-robot', 'centrifuge'].map(
+ (deviceId) => {
+ const info = getDeviceInfo(deviceId);
+ if (!info) return null;
+
+ return (
+
+ );
+ }
+ )}
+
+
+
+
+
+ {/* 设备详情模态框 */}
+ {selectedDevice && (
+
setSelectedDevice(null)}
+ isAnimating={animatingDevice === selectedDevice}
+ onToggleAnimation={() => handleToggleAnimation(selectedDevice)}
+ />
+ )}
+
+ );
+}
diff --git a/web/src/app/3D_lab/styles.css b/web/src/app/3D_lab/styles.css
new file mode 100644
index 0000000..73a0a84
--- /dev/null
+++ b/web/src/app/3D_lab/styles.css
@@ -0,0 +1,104 @@
+/* 3D Lab 页面样式 */
+
+@keyframes fade-in {
+ from {
+ opacity: 0;
+ transform: translateY(-10px);
+ }
+
+ to {
+ opacity: 1;
+ transform: translateY(0);
+ }
+}
+
+.animate-fade-in {
+ animation: fade-in 0.3s ease-out;
+}
+
+@keyframes pulse-slow {
+
+ 0%,
+ 100% {
+ opacity: 1;
+ }
+
+ 50% {
+ opacity: 0.5;
+ }
+}
+
+.animate-pulse-slow {
+ animation: pulse-slow 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
+}
+
+/* 延迟动画 */
+.delay-75 {
+ animation-delay: 75ms;
+}
+
+.delay-150 {
+ animation-delay: 150ms;
+}
+
+/* 自定义滚动条样式 - 覆盖 Tailwind utility */
+.custom-scrollbar {
+ /* Firefox */
+ scrollbar-width: thin !important;
+ scrollbar-color: rgba(156, 163, 175, 0.5) rgba(0, 0, 0, 0.05) !important;
+ /* 强制显示滚动条 */
+ overflow-y: auto !important;
+ overflow-x: hidden !important;
+}
+
+/* Webkit 浏览器(Chrome, Safari, Edge)的滚动条样式 */
+.custom-scrollbar::-webkit-scrollbar {
+ width: 10px !important;
+ height: 10px !important;
+ -webkit-appearance: none;
+ display: block !important;
+}
+
+.custom-scrollbar::-webkit-scrollbar-track {
+ background: rgba(0, 0, 0, 0.05) !important;
+ border-radius: 5px;
+ margin: 4px 0;
+}
+
+.dark .custom-scrollbar::-webkit-scrollbar-track {
+ background: rgba(255, 255, 255, 0.05) !important;
+}
+
+.custom-scrollbar::-webkit-scrollbar-thumb {
+ background-color: rgba(156, 163, 175, 0.5) !important;
+ border-radius: 5px !important;
+ border: 2px solid transparent;
+ background-clip: padding-box;
+ -webkit-appearance: none;
+ min-height: 20px;
+}
+
+.custom-scrollbar::-webkit-scrollbar-thumb:hover {
+ background-color: rgba(156, 163, 175, 0.6) !important;
+}
+
+.custom-scrollbar::-webkit-scrollbar-thumb:active {
+ background-color: rgba(156, 163, 175, 0.8) !important;
+}
+
+/* 深色模式下的滚动条 */
+.dark .custom-scrollbar {
+ scrollbar-color: rgba(156, 163, 175, 0.5) transparent !important;
+}
+
+.dark .custom-scrollbar::-webkit-scrollbar-thumb {
+ background-color: rgba(156, 163, 175, 0.5) !important;
+}
+
+.dark .custom-scrollbar::-webkit-scrollbar-thumb:hover {
+ background-color: rgba(156, 163, 175, 0.7) !important;
+}
+
+.dark .custom-scrollbar::-webkit-scrollbar-thumb:active {
+ background-color: rgba(156, 163, 175, 0.9) !important;
+}
diff --git a/web/src/app/navbar/NavbarFullWidth.tsx b/web/src/app/navbar/NavbarFullWidth.tsx
index b08aad4..3347edf 100644
--- a/web/src/app/navbar/NavbarFullWidth.tsx
+++ b/web/src/app/navbar/NavbarFullWidth.tsx
@@ -4,6 +4,7 @@ import clsx from 'clsx';
import { Fragment } from 'react';
import { useTranslation } from 'react-i18next';
+import { Link } from 'react-router-dom';
import NavbarFullWidthFooter from './NavbarFullWidthFooter';
import type { NavbarFullWidthProps } from './types';
@@ -102,13 +103,25 @@ export default function NavbarFullWidth({
/>
-
- {item.name}
-
-
+ {item.href && item.href.startsWith('http') ? (
+
+ {item.name}
+
+
+ ) : (
+
+ {item.name}
+
+
+ )}
{item.description}
diff --git a/web/src/app/navbar/Projects.tsx b/web/src/app/navbar/Projects.tsx
index 6db9cb6..14cdb78 100644
--- a/web/src/app/navbar/Projects.tsx
+++ b/web/src/app/navbar/Projects.tsx
@@ -134,7 +134,7 @@ import {
// RectangleGroupIcon,
} from '@heroicons/react/24/outline';
-import { SiUnrealengine,SiUnity,SiProton,SiX,SiStmicroelectronics } from 'react-icons/si';
+import { SiUnrealengine,SiUnity,SiProton,SiX,SiStmicroelectronics,SiBlender } from 'react-icons/si';
import { GitHubIcon } from '@/assets/SocialIcons';
import NavbarFullWidth from './NavbarFullWidth';
import type { NavbarFullWidthProps } from './types';
@@ -165,7 +165,7 @@ const resources = [
{
name: 'Anti',
description: '用于实验室模拟的3D数字孪生平台',
- href: `/deepmd-kit`,
+ href: `/3D_lab`,
icon: SiUnity,
color:'text-rose-500',
},
@@ -175,7 +175,14 @@ const resources = [
href: `/deepmd-kit`,
icon: SiUnrealengine,
color:'text-emerald-500',
- }
+ },
+ {
+ name:'3D Lab',
+ description: '展示3D实验室的实验仪器和场景',
+ href: `/3D_lab`,
+ icon: SiBlender,
+ color:'text-purple-500',
+ },
];
const callsToAction = [
diff --git a/web/src/router.tsx b/web/src/router.tsx
index 10a1f92..367c438 100644
--- a/web/src/router.tsx
+++ b/web/src/router.tsx
@@ -1,22 +1,23 @@
-import { Suspense, lazy } from "react";
-import { BrowserRouter, Route, Routes } from "react-router-dom";
-import App from "./app/App";
-import ProtectedDashboardLayout from "./components/layout/ProtectedDashboardPage";
+import { lazy, Suspense } from 'react';
+import { BrowserRouter, Route, Routes } from 'react-router-dom';
+import App from './app/App';
+import ProtectedDashboardLayout from './components/layout/ProtectedDashboardPage';
// 路由懒加载
-const ChatPage = lazy(() => import("./app/chat/page"));
+const ChatPage = lazy(() => import('./app/chat/page'));
const EnvironmentPage = lazy(() =>
- import("./app/dashboard/environment").then((module) => ({
+ import('./app/dashboard/environment').then((module) => ({
default: module.EnvironmentPage,
}))
);
const EnvironmentDetail = lazy(
- () => import("./app/dashboard/environment/EnvironmentDetail")
+ () => import('./app/dashboard/environment/EnvironmentDetail')
);
-const DesktopWindow = lazy(() => import("./app/dashboard/Desktop"));
-const CallbackPage = lazy(() => import("./app/login/CallbackPage"));
-const LoginPage = lazy(() => import("./app/login/LoginPage"));
-const UiTestPage = lazy(() => import("./app/ui/page"));
+const DashboardHome = lazy(() => import('./app/dashboard/Home'));
+const CallbackPage = lazy(() => import('./app/login/CallbackPage'));
+const LoginPage = lazy(() => import('./app/login/LoginPage'));
+const UiTestPage = lazy(() => import('./app/ui/page'));
+const Lab3DPage = lazy(() => import('./app/3D_lab/page'));
const LoadingFallback = () => (
@@ -30,18 +31,19 @@ export default function Router() {
}>
{/* 根路径 - App 组件根据登录状态分流 */}
+ } />
} />
- {/* 公开路由 */}
- } />
- } />
- } />
- } />
+ {/* 公开路由 */}
+ } />
+ } />
+ } />
+ } />
+ } />
{/* 所有需要侧边栏和登录保护的页面 */}
}>
- {/*} />*/}
- } />
+ } />
} />