更新移动端组件,简化认证和主题上下文的创建,添加错误页面组件以提升用户体验,同时更新相关依赖和配置,确保代码结构的清晰性和可维护性。
This commit is contained in:
@@ -19,9 +19,6 @@
|
||||
"lodash": "https://esm.d8d.fun/lodash@4.17.21"
|
||||
},
|
||||
"compilerOptions": {
|
||||
"lib": ["dom", "dom.iterable", "esnext", "deno.ns"],
|
||||
"jsx": "react",
|
||||
"jsxFactory": "React.createElement",
|
||||
"jsxFragmentFactory": "React.Fragment"
|
||||
"lib": ["dom", "dom.iterable", "esnext", "deno.ns"]
|
||||
}
|
||||
}
|
||||
|
||||
3
client/admin/deno.lock
generated
3
client/admin/deno.lock
generated
@@ -53,6 +53,7 @@
|
||||
"https://esm.d8d.fun/@types/debug@~4.1.12/index.d.ts": "https://esm.d8d.fun/@types/debug@4.1.12/index.d.ts",
|
||||
"https://esm.d8d.fun/@types/follow-redirects@~1.14.4/index.d.ts": "https://esm.d8d.fun/@types/follow-redirects@1.14.4/index.d.ts",
|
||||
"https://esm.d8d.fun/@types/lodash-es@~4.17.12/index.d.ts": "https://esm.d8d.fun/@types/lodash-es@4.17.12/index.d.ts",
|
||||
"https://esm.d8d.fun/@types/lodash@~4.17.16/index.d.ts": "https://esm.d8d.fun/@types/lodash@4.17.16/index.d.ts",
|
||||
"https://esm.d8d.fun/@types/mime-types@~2.1.4/index.d.ts": "https://esm.d8d.fun/@types/mime-types@2.1.4/index.d.ts",
|
||||
"https://esm.d8d.fun/@types/ms@~2.1.0/index.d.ts": "https://esm.d8d.fun/@types/ms@2.1.0/index.d.ts",
|
||||
"https://esm.d8d.fun/@types/proxy-from-env@~1.0.4/index.d.ts": "https://esm.d8d.fun/@types/proxy-from-env@1.0.4/index.d.ts",
|
||||
@@ -764,6 +765,8 @@
|
||||
"https://esm.d8d.fun/isexe@3.1.1?target=denonext": "b3c61e7e70b9d56865de461fbcdae702ebf93743143457079a15a60e30dfcf83",
|
||||
"https://esm.d8d.fun/lodash-es@4.17.21/denonext/lodash-es.mjs": "83b25b8f85872b2805e6b0273c90d6c96960c80a710c55e89a7b399107fc6fa8",
|
||||
"https://esm.d8d.fun/lodash-es@4.17.21?target=denonext": "1252ccd86311d14f2dd05282cf3e40e1ff76bfa79c71ca49b903e902129944cb",
|
||||
"https://esm.d8d.fun/lodash@4.17.21": "c2f90ffd948b7a30f054986888bdc2667824115fa48ad583ca8b3a579ca4a5a8",
|
||||
"https://esm.d8d.fun/lodash@4.17.21/denonext/lodash.mjs": "9d2a44e584d91008f61f974c6d0a32bf9afb1563761e60c366af0a293e8c759b",
|
||||
"https://esm.d8d.fun/mime-db@1.52.0/denonext/mime-db.mjs": "f93feb3d7150014b71bd0d06c5bd819db56a089b31b8b79a3b0466bb37ef005e",
|
||||
"https://esm.d8d.fun/mime-types@2.1.35/denonext/mime-types.mjs": "704bdb318816fe1360c90a196f7cb3ba6e25fe207707cc2df873f890ad2e5f44",
|
||||
"https://esm.d8d.fun/mime-types@2.1.35?target=denonext": "e4cc9a1aabecc1be22d194375ec3b99cc9d51700cc4629ab689975451c0a8ce5",
|
||||
|
||||
21
client/mobile/deno.lock
generated
21
client/mobile/deno.lock
generated
@@ -1,5 +1,19 @@
|
||||
{
|
||||
"version": "4",
|
||||
"specifiers": {
|
||||
"npm:@types/node@*": "22.12.0"
|
||||
},
|
||||
"npm": {
|
||||
"@types/node@22.12.0": {
|
||||
"integrity": "sha512-Fll2FZ1riMjNmlmJOdAyY5pUbkftXslB5DgEzlIuNaiWhXd00FhWxVC/r4yV/4wBb9JfImTu+jiSvXTkJ7F/gA==",
|
||||
"dependencies": [
|
||||
"undici-types"
|
||||
]
|
||||
},
|
||||
"undici-types@6.20.0": {
|
||||
"integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="
|
||||
}
|
||||
},
|
||||
"redirects": {
|
||||
"https://esm.d8d.fun/@deno/shim-deno-test@^0.5.0?target=denonext": "https://esm.d8d.fun/@deno/shim-deno-test@0.5.0?target=denonext",
|
||||
"https://esm.d8d.fun/@deno/shim-deno@~0.18.0?target=denonext": "https://esm.d8d.fun/@deno/shim-deno@0.18.2?target=denonext",
|
||||
@@ -11,6 +25,7 @@
|
||||
"https://esm.d8d.fun/@types/ms@~2.1.0/index.d.ts": "https://esm.d8d.fun/@types/ms@2.1.0/index.d.ts",
|
||||
"https://esm.d8d.fun/@types/proxy-from-env@~1.0.4/index.d.ts": "https://esm.d8d.fun/@types/proxy-from-env@1.0.4/index.d.ts",
|
||||
"https://esm.d8d.fun/@types/react-dom@~19.0.4/X-ZHJlYWN0QDE5LjAuMA/index.d.ts": "https://esm.d8d.fun/@types/react-dom@19.0.6/X-ZHJlYWN0QDE5LjAuMA/index.d.ts",
|
||||
"https://esm.d8d.fun/@types/react-dom@~19.0.4/client.d.ts": "https://esm.d8d.fun/@types/react-dom@19.0.6/client.d.ts",
|
||||
"https://esm.d8d.fun/@types/react-dom@~19.0.6/X-ZHJlYWN0QDE5LjAuMA/client.d.ts": "https://esm.d8d.fun/@types/react-dom@19.0.6/X-ZHJlYWN0QDE5LjAuMA/client.d.ts",
|
||||
"https://esm.d8d.fun/@types/react@~19.0.12/index.d.ts": "https://esm.d8d.fun/@types/react@19.0.14/index.d.ts",
|
||||
"https://esm.d8d.fun/@types/react@~19.0.12/jsx-runtime.d.ts": "https://esm.d8d.fun/@types/react@19.0.14/jsx-runtime.d.ts",
|
||||
@@ -61,7 +76,9 @@
|
||||
"https://esm.d8d.fun/@deno/shim-deno@0.18.2/denonext/shim-deno.mjs": "819d8ac34fdaf60658cf03d137f14adaff3f13a279ffd79cd8797d84a6ac46ab",
|
||||
"https://esm.d8d.fun/@deno/shim-deno@0.18.2?target=denonext": "ffa3ca347bb6b6530720158f307a2e31b16728fbb52e6432254a07d52fcbc404",
|
||||
"https://esm.d8d.fun/@heroicons/react@2.1.1/24/outline?deps=react@19.0.0": "d2c14cfd7f3090062c9f968f25d0ddbb277ca76055af1ac3fd22045276571a75",
|
||||
"https://esm.d8d.fun/@heroicons/react@2.1.1/24/outline?deps=react@19.0.0,react-dom@19.0.0": "5e99f4d40ce60c55b5cf421c3cf3f13df1707cf53152e447b2332570412cd77a",
|
||||
"https://esm.d8d.fun/@heroicons/react@2.1.1/24/solid?deps=react@19.0.0": "546051c9fdfdca5c7d51cd4cf588fe709da509274c5fcf203d616a5e87bdd595",
|
||||
"https://esm.d8d.fun/@heroicons/react@2.1.1/X-ZHJlYWN0LWRvbUAxOS4wLjAscmVhY3RAMTkuMC4w/denonext/24/outline.mjs": "640f934a0c987f682032049e5d4a455567db676de47bca0d44e76b72023661f7",
|
||||
"https://esm.d8d.fun/@heroicons/react@2.1.1/X-ZHJlYWN0QDE5LjAuMA/denonext/24/outline.mjs": "640f934a0c987f682032049e5d4a455567db676de47bca0d44e76b72023661f7",
|
||||
"https://esm.d8d.fun/@heroicons/react@2.1.1/X-ZHJlYWN0QDE5LjAuMA/denonext/24/solid.mjs": "dcbd0c377d92857b6eb23c7dbb2ee6e650b12aa6ae1ef7fcc10dc1964df8ba47",
|
||||
"https://esm.d8d.fun/@socket.io/component-emitter@3.1.2/denonext/component-emitter.mjs": "3c6c5f2d64d4933b577a7117df1d8855c51ff01ab3dea8f42af1adcb1a5989e7",
|
||||
@@ -158,11 +175,15 @@
|
||||
"https://esm.d8d.fun/proxy-from-env@1.1.0?target=denonext": "bf02a050a1a6aa56ddba25dbea2c355da294630e5c5520fddea4b2f30a9292bc",
|
||||
"https://esm.d8d.fun/react-dom@19.0.0/X-ZHJlYWN0QDE5LjAuMA/denonext/client.mjs": "af662fd134eea98f37fdcea6142accd0f8a7d2e13c1c3c9e98dc37a8c7aad46b",
|
||||
"https://esm.d8d.fun/react-dom@19.0.0/X-ZHJlYWN0QDE5LjAuMA/denonext/react-dom.mjs": "a2f7bc344e1d5b7ca47e68665291e206ae4db17ee84f234f3d3e2533b9119f63",
|
||||
"https://esm.d8d.fun/react-dom@19.0.0/client": "c972c16184c695fc5828dfa61d7f341edbc463d20d8108765c93a98027c24227",
|
||||
"https://esm.d8d.fun/react-dom@19.0.0/client?deps=react@19.0.0,react-dom@19.0.0": "eaf0dd9b4937f9f7674c02491ce718f461ad49377d8515e71c24ae841f407ded",
|
||||
"https://esm.d8d.fun/react-dom@19.0.0/denonext/client.mjs": "af662fd134eea98f37fdcea6142accd0f8a7d2e13c1c3c9e98dc37a8c7aad46b",
|
||||
"https://esm.d8d.fun/react-dom@19.0.0/denonext/react-dom.mjs": "a2f7bc344e1d5b7ca47e68665291e206ae4db17ee84f234f3d3e2533b9119f63",
|
||||
"https://esm.d8d.fun/react-dom@19.0.0?deps=react@19.0.0,react-dom@19.0.0": "0e49978c3f0fb4a94db9c9318aebd7e1b35651678050871a91ebb080cc3e1f83",
|
||||
"https://esm.d8d.fun/react-router@7.3.0/X-ZHJlYWN0LWRvbUAxOS4wLjAscmVhY3RAMTkuMC4w/denonext/dist/development/chunk-K6CSEXPM.mjs": "441898046ad7c4fd9a6b53e13a398c9c74c4412c519e942f82b8a77f7af9f9d6",
|
||||
"https://esm.d8d.fun/react-router@7.3.0/X-ZHJlYWN0LWRvbUAxOS4wLjAscmVhY3RAMTkuMC4w/denonext/react-router.mjs": "b0b05fcfc3a03c5f679cd0bc69ca19aa10abaa977395df00e86b3fb114e5e346",
|
||||
"https://esm.d8d.fun/react-router@7.3.0?deps=react@19.0.0,react-dom@19.0.0": "ad747718e32a45020d67eb4ff98f9734cb06a10ceb393baac0a965043e96cdf0",
|
||||
"https://esm.d8d.fun/react@19.0.0": "ab1f4aa20ac56c237bbb204632bdb55f03a0ab005d21944eeb447e5e37879637",
|
||||
"https://esm.d8d.fun/react@19.0.0/X-ZHJlYWN0LWRvbUAxOS4wLjA/denonext/react.mjs": "87fdb28d39ca8983bdba3e7ec329305f95463cfc70c015b2620b4900fa15efdd",
|
||||
"https://esm.d8d.fun/react@19.0.0/denonext/jsx-runtime.mjs": "643b749fa9666fbf73619a99fd708722edb4acaa34c8cea7be783a3432367780",
|
||||
"https://esm.d8d.fun/react@19.0.0/denonext/react.mjs": "87fdb28d39ca8983bdba3e7ec329305f95463cfc70c015b2620b4900fa15efdd",
|
||||
|
||||
@@ -57,24 +57,10 @@ const defaultThemeSettings: ThemeSettings = {
|
||||
};
|
||||
|
||||
// 创建认证上下文
|
||||
const AuthContext = createContext<AuthContextType>({
|
||||
user: null,
|
||||
token: null,
|
||||
login: async () => {},
|
||||
logout: async () => {},
|
||||
isAuthenticated: false,
|
||||
isLoading: true
|
||||
});
|
||||
const AuthContext = createContext<AuthContextType | null>(null);
|
||||
|
||||
// 创建主题上下文
|
||||
const ThemeContext = createContext<ThemeContextType>({
|
||||
isDark: false,
|
||||
currentTheme: defaultThemeSettings,
|
||||
updateTheme: () => {},
|
||||
saveTheme: async () => defaultThemeSettings,
|
||||
resetTheme: async () => defaultThemeSettings,
|
||||
toggleTheme: () => {}
|
||||
});
|
||||
const ThemeContext = createContext<ThemeContextType | null>(null);
|
||||
|
||||
// 认证提供者组件
|
||||
export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
|
||||
@@ -107,7 +93,6 @@ export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children
|
||||
setLocalStorageWithExpiry('token', token, 24); // 24小时过期
|
||||
setLocalStorageWithExpiry('user', user, 24);
|
||||
|
||||
return user;
|
||||
} catch (error) {
|
||||
console.error('登录失败:', error);
|
||||
throw error;
|
||||
@@ -255,18 +240,19 @@ export const ThemeProvider: React.FC<{ children: React.ReactNode }> = ({ childre
|
||||
);
|
||||
};
|
||||
|
||||
// 主题hook
|
||||
export const useTheme = () => useContext(ThemeContext);
|
||||
// 使用上下文的钩子
|
||||
export const useAuth = () => {
|
||||
const context = useContext(AuthContext);
|
||||
if (!context) {
|
||||
throw new Error('useAuth必须在AuthProvider内部使用');
|
||||
}
|
||||
return context;
|
||||
};
|
||||
|
||||
// 认证hook
|
||||
export const useAuth = () => useContext(AuthContext);
|
||||
|
||||
// API hook
|
||||
export const useApi = () => {
|
||||
const { token } = useAuth();
|
||||
|
||||
return {
|
||||
api,
|
||||
isAuthenticated: !!token
|
||||
};
|
||||
};
|
||||
export const useTheme = () => {
|
||||
const context = useContext(ThemeContext);
|
||||
if (!context) {
|
||||
throw new Error('useTheme必须在ThemeProvider内部使用');
|
||||
}
|
||||
return context;
|
||||
};
|
||||
@@ -7,7 +7,8 @@ import {
|
||||
Navigate,
|
||||
useLocation,
|
||||
useNavigate,
|
||||
Link
|
||||
Link,
|
||||
useRouteError
|
||||
} from 'react-router';
|
||||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
||||
import dayjs from 'dayjs';
|
||||
@@ -16,6 +17,7 @@ import { AuthProvider, ThemeProvider, useAuth } from './hooks.tsx';
|
||||
import HomePage from './pages_index.tsx';
|
||||
import LoginPage from './pages_login.tsx';
|
||||
import { GlobalConfig } from "../share/types.ts";
|
||||
import { ExclamationTriangleIcon, HomeIcon, BellIcon, UserIcon } from '@heroicons/react/24/outline';
|
||||
|
||||
// 设置中文语言
|
||||
dayjs.locale('zh-cn');
|
||||
@@ -153,6 +155,42 @@ const PageNotFound = () => (
|
||||
</div>
|
||||
);
|
||||
|
||||
// 添加错误页面组件
|
||||
const ErrorPage = () => {
|
||||
const error = useRouteError() as any;
|
||||
const errorMessage = error?.statusText || error?.message || '未知错误';
|
||||
|
||||
return (
|
||||
<div className="flex flex-col items-center justify-center min-h-screen p-6 text-center bg-gray-50">
|
||||
<div className="max-w-md w-full bg-white rounded-lg shadow-lg p-8">
|
||||
<ExclamationTriangleIcon className="h-16 w-16 text-red-600 mx-auto mb-4" />
|
||||
<h1 className="text-2xl font-medium mb-2">出错了</h1>
|
||||
<div className="text-gray-500 mb-4">抱歉,页面加载过程中发生错误</div>
|
||||
|
||||
<div className="bg-red-50 border border-red-200 rounded-md p-4 mb-6">
|
||||
<p className="text-red-700 text-sm font-medium">错误信息:</p>
|
||||
<p className="text-red-600 mt-1 text-sm break-all">{errorMessage}</p>
|
||||
{error?.stack && (
|
||||
<details className="mt-2">
|
||||
<summary className="text-red-700 text-sm cursor-pointer">查看详细信息</summary>
|
||||
<pre className="mt-2 text-xs text-red-600 overflow-auto p-2 bg-red-50 rounded">
|
||||
{error.stack}
|
||||
</pre>
|
||||
</details>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<a
|
||||
href="/mobile"
|
||||
className="inline-flex items-center justify-center px-4 py-2 bg-red-600 text-white rounded-lg hover:bg-red-700 transition-colors w-full"
|
||||
>
|
||||
返回首页
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
// 添加个人页面组件
|
||||
const ProfilePage = () => (
|
||||
<div className="p-4">
|
||||
@@ -232,7 +270,7 @@ const MobileLayout = () => {
|
||||
location.pathname === '/mobile' ? 'text-blue-600' : 'text-gray-500'
|
||||
}`}
|
||||
>
|
||||
<div className="text-xl mb-1">🏠</div>
|
||||
<HomeIcon className="w-6 h-6 mb-1" />
|
||||
<span className="text-xs">首页</span>
|
||||
</Link>
|
||||
<Link
|
||||
@@ -241,7 +279,7 @@ const MobileLayout = () => {
|
||||
location.pathname === '/mobile/notifications' ? 'text-blue-600' : 'text-gray-500'
|
||||
}`}
|
||||
>
|
||||
<div className="text-xl mb-1">🔔</div>
|
||||
<BellIcon className="w-6 h-6 mb-1" />
|
||||
<span className="text-xs">通知</span>
|
||||
</Link>
|
||||
<Link
|
||||
@@ -250,7 +288,7 @@ const MobileLayout = () => {
|
||||
location.pathname === '/mobile/profile' ? 'text-blue-600' : 'text-gray-500'
|
||||
}`}
|
||||
>
|
||||
<div className="text-xl mb-1">👤</div>
|
||||
<UserIcon className="w-6 h-6 mb-1" />
|
||||
<span className="text-xs">我的</span>
|
||||
</Link>
|
||||
</div>
|
||||
@@ -265,11 +303,13 @@ const App = () => {
|
||||
const router = createBrowserRouter([
|
||||
{
|
||||
path: '/',
|
||||
element: <Navigate to="/mobile" replace />
|
||||
element: <Navigate to="/mobile" replace />,
|
||||
errorElement: <ErrorPage />
|
||||
},
|
||||
{
|
||||
path: '/mobile/login',
|
||||
element: <LoginPage />
|
||||
element: <LoginPage />,
|
||||
errorElement: <ErrorPage />
|
||||
},
|
||||
{
|
||||
path: '/mobile',
|
||||
@@ -278,6 +318,7 @@ const App = () => {
|
||||
<MobileLayout />
|
||||
</ProtectedRoute>
|
||||
),
|
||||
errorElement: <ErrorPage />,
|
||||
children: [
|
||||
{
|
||||
index: true,
|
||||
|
||||
Reference in New Issue
Block a user