style: 美化用户隧道权限分配表单UI并统一代码格式

- 使用HeroUI Checkbox组件替换原生checkbox

- 重构隧道列表为card-based tile风格

- 添加选中/未选中/已分配状态视觉区分

- 应用eslint --fix代码格式统一

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
root
2026-02-04 02:22:28 +00:00
parent 72c2e28667
commit 6f6fececa8
31 changed files with 8080 additions and 5740 deletions
+68 -58
View File
@@ -12,11 +12,9 @@ import ProfilePage from "@/pages/profile";
import LimitPage from "@/pages/limit";
import ConfigPage from "@/pages/config";
import { SettingsPage } from "@/pages/settings";
import AdminLayout from "@/layouts/admin";
import H5Layout from "@/layouts/h5";
import H5SimpleLayout from "@/layouts/h5-simple";
import { isLoggedIn } from "@/utils/auth";
import { siteConfig } from "@/config/site";
@@ -27,11 +25,14 @@ const useH5Mode = () => {
// 检测移动设备或小屏幕
const isMobile = window.innerWidth <= 768;
// 检测是否为移动端浏览器
const isMobileBrowser = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
const isMobileBrowser =
/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(
navigator.userAgent,
);
// 检测URL参数是否包含h5模式
const urlParams = new URLSearchParams(window.location.search);
const isH5Param = urlParams.get('h5') === 'true';
const isH5Param = urlParams.get("h5") === "true";
return isMobile || isMobileBrowser || isH5Param;
};
@@ -42,39 +43,50 @@ const useH5Mode = () => {
// 检测移动设备或小屏幕
const isMobile = window.innerWidth <= 768;
// 检测是否为移动端浏览器
const isMobileBrowser = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
const isMobileBrowser =
/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(
navigator.userAgent,
);
// 检测URL参数是否包含h5模式
const urlParams = new URLSearchParams(window.location.search);
const isH5Param = urlParams.get('h5') === 'true';
const isH5Param = urlParams.get("h5") === "true";
setIsH5(isMobile || isMobileBrowser || isH5Param);
};
window.addEventListener('resize', checkH5Mode);
return () => window.removeEventListener('resize', checkH5Mode);
window.addEventListener("resize", checkH5Mode);
return () => window.removeEventListener("resize", checkH5Mode);
}, []);
return isH5;
};
// 简化的路由保护组件 - 使用 React Router 导航避免循环
const ProtectedRoute = ({ children, useSimpleLayout = false, skipLayout = false }: { children: React.ReactNode, useSimpleLayout?: boolean, skipLayout?: boolean }) => {
const ProtectedRoute = ({
children,
useSimpleLayout = false,
skipLayout = false,
}: {
children: React.ReactNode;
useSimpleLayout?: boolean;
skipLayout?: boolean;
}) => {
const authenticated = isLoggedIn();
const isH5 = useH5Mode();
const navigate = useNavigate();
useEffect(() => {
if (!authenticated) {
// 使用 React Router 导航,避免无限跳转
navigate('/', { replace: true });
navigate("/", { replace: true });
}
}, [authenticated, navigate]);
if (!authenticated) {
return (
<div className="flex items-center justify-center min-h-screen bg-white dark:bg-black">
<div className="text-lg text-gray-700 dark:text-gray-200"></div>
<div className="text-lg text-gray-700 dark:text-gray-200" />
</div>
);
}
@@ -86,6 +98,7 @@ const ProtectedRoute = ({ children, useSimpleLayout = false, skipLayout = false
// 根据模式和页面类型选择布局
let Layout;
if (isH5 && useSimpleLayout) {
Layout = H5SimpleLayout;
} else if (isH5) {
@@ -93,31 +106,30 @@ const ProtectedRoute = ({ children, useSimpleLayout = false, skipLayout = false
} else {
Layout = AdminLayout;
}
return <Layout>{children}</Layout>;
};
// 登录页面路由组件 - 已登录则重定向到dashboard
const LoginRoute = () => {
const authenticated = isLoggedIn();
const navigate = useNavigate();
useEffect(() => {
if (authenticated) {
// 使用 React Router 导航,避免无限跳转
navigate('/dashboard', { replace: true });
navigate("/dashboard", { replace: true });
}
}, [authenticated, navigate]);
if (authenticated) {
return (
<div className="flex items-center justify-center min-h-screen bg-gray-100 dark:bg-black">
<div className="text-lg text-gray-700 dark:text-gray-200"></div>
<div className="text-lg text-gray-700 dark:text-gray-200" />
</div>
);
}
return <IndexPage />;
};
@@ -125,18 +137,19 @@ function App() {
// 立即设置页面标题(使用已从缓存读取的配置)
useEffect(() => {
document.title = siteConfig.name;
// 异步检查是否有配置更新
const checkTitleUpdate = async () => {
try {
// 引入必要的函数
const { getCachedConfig } = await import('@/config/site');
const cachedAppName = await getCachedConfig('app_name');
const { getCachedConfig } = await import("@/config/site");
const cachedAppName = await getCachedConfig("app_name");
if (cachedAppName && cachedAppName !== document.title) {
document.title = cachedAppName;
}
} catch (error) {
console.warn('检查标题更新失败:', error);
console.warn("检查标题更新失败:", error);
}
};
@@ -148,83 +161,80 @@ function App() {
return (
<Routes>
<Route path="/" element={<LoginRoute />} />
<Route
path="/change-password"
<Route element={<LoginRoute />} path="/" />
<Route
element={
<ProtectedRoute skipLayout={true}>
<ChangePasswordPage />
</ProtectedRoute>
}
}
path="/change-password"
/>
<Route
path="/dashboard"
<Route
element={
<ProtectedRoute>
<DashboardPage />
</ProtectedRoute>
}
}
path="/dashboard"
/>
<Route
path="/forward"
<Route
element={
<ProtectedRoute>
<ForwardPage />
</ProtectedRoute>
}
}
path="/forward"
/>
<Route
path="/tunnel"
<Route
element={
<ProtectedRoute>
<TunnelPage />
</ProtectedRoute>
}
}
path="/tunnel"
/>
<Route
path="/node"
<Route
element={
<ProtectedRoute>
<NodePage />
</ProtectedRoute>
}
}
path="/node"
/>
<Route
path="/user"
<Route
element={
<ProtectedRoute useSimpleLayout={true}>
<UserPage />
</ProtectedRoute>
}
}
path="/user"
/>
<Route
path="/profile"
<Route
element={
<ProtectedRoute>
<ProfilePage />
</ProtectedRoute>
}
}
path="/profile"
/>
<Route
path="/limit"
<Route
element={
<ProtectedRoute useSimpleLayout={true}>
<LimitPage />
</ProtectedRoute>
}
}
path="/limit"
/>
<Route
path="/config"
<Route
element={
<ProtectedRoute useSimpleLayout={true}>
<ConfigPage />
</ProtectedRoute>
}
/>
<Route
path="/settings"
element={<SettingsPage />}
}
path="/config"
/>
<Route element={<SettingsPage />} path="/settings" />
</Routes>
);
}