瀏覽代碼

更新移动端组件,简化认证和主题上下文的创建,添加错误页面组件以提升用户体验,同时更新相关依赖和配置,确保代码结构的清晰性和可维护性。

zyh 8 月之前
父節點
當前提交
5d338ca7c5
共有 5 個文件被更改,包括 89 次插入41 次删除
  1. 1 4
      client/admin/deno.json
  2. 3 0
      client/admin/deno.lock
  3. 21 0
      client/mobile/deno.lock
  4. 17 31
      client/mobile/hooks.tsx
  5. 47 6
      client/mobile/mobile_app.tsx

+ 1 - 4
client/admin/deno.json

@@ -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 - 0
client/admin/deno.lock

@@ -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 - 0
client/mobile/deno.lock

@@ -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",

+ 17 - 31
client/mobile/hooks.tsx

@@ -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);
-
-// 认证hook
-export const useAuth = () => useContext(AuthContext);
+// 使用上下文的钩子
+export const useAuth = () => {
+  const context = useContext(AuthContext);
+  if (!context) {
+    throw new Error('useAuth必须在AuthProvider内部使用');
+  }
+  return context;
+};
 
-// 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;
+};

+ 47 - 6
client/mobile/mobile_app.tsx

@@ -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,