ソースを参照

✨ feat(mobile): 添加字体设置页面

- 新建FontSettingsPage组件,实现字体大小选择功能
- 在个人资料页添加字体设置入口链接
- 配置字体设置页面路由
- 移除个人资料页中原有的字体设置部分
- 实现字体大小预览功能,提供4种尺寸选项
- 添加字体大小更新与保存功能,与用户偏好设置API交互
- 优化UI设计,提升视觉体验和交互反馈
yourname 7 ヶ月 前
コミット
f98c4fc436

+ 231 - 0
src/client/mobile/pages/FontSettingsPage.tsx

@@ -0,0 +1,231 @@
+import React, { useState, useEffect } from 'react';
+import { useAuth } from '../hooks/AuthProvider';
+import { useNavigate } from 'react-router-dom';
+import { userPreferenceClient } from '@/client/api';
+import { FontSizeType } from '@/server/modules/silver-users/user-preference.entity';
+import { App, Card, Button } from 'antd';
+import { ArrowLeftIcon } from '@heroicons/react/24/outline';
+
+const fontSizeOptions = [
+  { value: FontSizeType.SMALL, label: '小', description: '14px - 适合视力较好的用户' },
+  { value: FontSizeType.MEDIUM, label: '中', description: '16px - 默认大小' },
+  { value: FontSizeType.LARGE, label: '大', description: '18px - 适合视力一般的用户' },
+  { value: FontSizeType.EXTRA_LARGE, label: '超大', description: '20px - 适合视力较弱的用户' }
+];
+
+const FontSettingsPage: React.FC = () => {
+  const { user } = useAuth();
+  const navigate = useNavigate();
+  const { message } = App.useApp();
+  const [fontSize, setFontSize] = useState<FontSizeType>(FontSizeType.MEDIUM);
+  const [loading, setLoading] = useState(false);
+  const [preferenceId, setPreferenceId] = useState<number | null>(null);
+
+  useEffect(() => {
+    if (user) {
+      loadUserPreference();
+    }
+  }, [user]);
+
+  const loadUserPreference = async () => {
+    if (!user) return;
+    
+    try {
+      const response = await (userPreferenceClient as any).$get({
+        query: { filters: JSON.stringify({ userId: user.id }) }
+      });
+      
+      if (response.status === 200) {
+        const data = await response.json();
+        if (data.data && data.data.length > 0) {
+          const preference = data.data[0];
+          setFontSize(preference.fontSize);
+          setPreferenceId(preference.id);
+        }
+      }
+    } catch (error) {
+      console.error('加载用户偏好设置失败:', error);
+      message.error('加载设置失败,请重试');
+    }
+  };
+
+  const handleFontSizeChange = async (size: FontSizeType) => {
+    if (!user) return;
+    
+    setLoading(true);
+    try {
+      let response;
+      
+      if (preferenceId) {
+        // 更新现有设置
+        response = await (userPreferenceClient as any)[preferenceId].$put({
+          json: { fontSize: size }
+        });
+      } else {
+        // 创建新设置
+        response = await (userPreferenceClient as any).$post({
+          json: {
+            userId: user.id,
+            fontSize: size,
+            isDarkMode: 0
+          }
+        });
+      }
+
+      if (response.status === 200) {
+        const data = await response.json();
+        setFontSize(size);
+        setPreferenceId(data.id);
+        message.success('字体大小设置已更新');
+        updateDocumentFontSize(size);
+      }
+    } catch (error) {
+      console.error('更新字体大小失败:', error);
+      message.error('更新失败,请重试');
+    } finally {
+      setLoading(false);
+    }
+  };
+
+  const updateDocumentFontSize = (size: FontSizeType) => {
+    const fontSizes: Record<FontSizeType, string> = {
+      [FontSizeType.SMALL]: '14px',
+      [FontSizeType.MEDIUM]: '16px',
+      [FontSizeType.LARGE]: '18px',
+      [FontSizeType.EXTRA_LARGE]: '20px'
+    };
+    document.documentElement.style.setProperty('--mobile-font-size', fontSizes[size]);
+  };
+
+  const handleBack = () => {
+    navigate(-1);
+  };
+
+  if (!user) {
+    return (
+      <div className="min-h-screen flex items-center justify-center" style={{ backgroundColor: 'var(--ink-light)' }}>
+        <div className="text-center">
+          <p className="text-lg mb-4" style={{ color: 'var(--text-primary)' }}>请先登录</p>
+          <Button type="primary" onClick={() => navigate('/login')}>
+            去登录
+          </Button>
+        </div>
+      </div>
+    );
+  }
+
+  return (
+    <div className="min-h-screen" style={{ backgroundColor: 'var(--ink-light)' }}>
+      {/* 头部导航 */}
+      <header 
+        className="sticky top-0 z-10 px-4 py-3 flex items-center border-b"
+        style={{ 
+          backgroundColor: 'var(--ink-light)',
+          borderColor: 'var(--ink-medium)',
+          borderColorOpacity: 0.2
+        }}
+      >
+        <button
+          onClick={handleBack}
+          className="p-2 rounded-full hover:bg-opacity-10"
+          style={{ backgroundColor: 'var(--ink-medium)' }}
+        >
+          <ArrowLeftIcon className="w-5 h-5" style={{ color: 'var(--text-primary)' }} />
+        </button>
+        <h1 className="ml-4 font-serif text-lg font-semibold" style={{ color: 'var(--text-primary)' }}>
+          字体设置
+        </h1>
+      </header>
+
+      {/* 内容区域 */}
+      <div className="p-4">
+        <Card 
+          className="rounded-xl shadow-sm hover:shadow-lg transition-all duration-300 backdrop-blur-sm"
+          style={{
+            backgroundColor: 'rgba(255,255,255,0.8)',
+            border: `1px solid var(--ink-medium)`,
+            borderColor: 'var(--ink-medium)'
+          }}
+        >
+          <div className="mb-6">
+            <h2 className="font-serif text-xl font-semibold mb-2" style={{ color: 'var(--text-primary)' }}>
+              选择字体大小
+            </h2>
+            <p className="text-sm" style={{ color: 'var(--text-secondary)' }}>
+              根据您的视力情况选择合适的字体大小,提升阅读体验
+            </p>
+          </div>
+
+          <div className="space-y-3">
+            {fontSizeOptions.map((option) => (
+              <div
+                key={option.value}
+                className={`p-4 rounded-xl border-2 cursor-pointer transition-all duration-300 ${
+                  fontSize === option.value
+                    ? 'border-opacity-100 shadow-md'
+                    : 'border-opacity-50 hover:border-opacity-100 hover:shadow-md'
+                }`}
+                style={{
+                  borderColor: fontSize === option.value ? 'var(--accent-blue)' : 'var(--ink-medium)',
+                  backgroundColor: fontSize === option.value ? 'rgba(74, 107, 124, 0.1)' : 'transparent'
+                }}
+                onClick={() => handleFontSizeChange(option.value)}
+              >
+                <div className="flex items-center justify-between">
+                  <div>
+                    <h3 className="font-semibold" style={{ color: 'var(--text-primary)' }}>
+                      {option.label}
+                    </h3>
+                    <p className="text-sm mt-1" style={{ color: 'var(--text-secondary)' }}>
+                      {option.description}
+                    </p>
+                  </div>
+                  <div className="flex items-center">
+                    <div
+                      className={`w-5 h-5 rounded-full border-2 flex items-center justify-center ${
+                        fontSize === option.value ? 'border-opacity-100' : 'border-opacity-50'
+                      }`}
+                      style={{
+                        borderColor: fontSize === option.value ? 'var(--accent-blue)' : 'var(--ink-medium)'
+                      }}
+                    >
+                      {fontSize === option.value && (
+                        <div 
+                          className="w-3 h-3 rounded-full"
+                          style={{ backgroundColor: 'var(--accent-blue)' }}
+                        />
+                      )}
+                    </div>
+                  </div>
+                </div>
+                <div 
+                  className="mt-3 p-3 rounded-lg"
+                  style={{ backgroundColor: 'rgba(255,255,255,0.5)' }}
+                >
+                  <p 
+                    className="font-sans leading-relaxed"
+                    style={{ 
+                      fontSize: option.value === FontSizeType.SMALL ? '14px' :
+                               option.value === FontSizeType.MEDIUM ? '16px' :
+                               option.value === FontSizeType.LARGE ? '18px' : '20px'
+                    }}
+                  >
+                    银龄智慧,让晚年生活更精彩。这里是示例文字,展示{option.label}字体的实际效果。
+                  </p>
+                </div>
+              </div>
+            ))}
+          </div>
+
+          <div className="mt-6 p-4 rounded-lg" style={{ backgroundColor: 'rgba(92, 124, 92, 0.1)' }}>
+            <p className="text-sm" style={{ color: 'var(--accent-green)' }}>
+              💡 提示:字体大小设置将应用于整个应用的阅读体验
+            </p>
+          </div>
+        </Card>
+      </div>
+    </div>
+  );
+};
+
+export default FontSettingsPage;

+ 8 - 25
src/client/mobile/pages/ProfilePage.tsx

@@ -169,31 +169,6 @@ const ProfilePage: React.FC = () => {
         </div>
       </div>
 
-      {/* 字体大小设置 */}
-      <div className="bg-white rounded-lg shadow p-6 mb-4">
-        <h3 className="text-lg font-semibold text-gray-900 mb-4">字体大小设置</h3>
-        <div className="space-y-2">
-          {fontSizeOptions.map((option) => (
-            <label
-              key={option.value}
-              className="flex items-center p-3 border rounded-lg cursor-pointer hover:bg-gray-50"
-            >
-              <input
-                type="radio"
-                name="fontSize"
-                value={option.value}
-                checked={fontSize === option.value}
-                onChange={() => handleFontSizeChange(option.value)}
-                disabled={loading}
-                className="mr-3"
-              />
-              <span className="text-gray-900">{option.label}</span>
-              <span className="ml-2 text-sm text-gray-600">示例文字</span>
-            </label>
-          ))}
-        </div>
-      </div>
-
       {/* 银龄档案信息 */}
       <Card className="mb-4">
         <div className="flex justify-between items-center mb-4">
@@ -257,6 +232,14 @@ const ProfilePage: React.FC = () => {
             <span className="text-gray-400">›</span>
           </div>
           
+          <div
+            className="p-4 flex items-center justify-between hover:bg-gray-50 cursor-pointer"
+            onClick={() => navigate('/profile/font-settings')}
+          >
+            <span className="text-gray-900">字体设置</span>
+            <span className="text-gray-400">›</span>
+          </div>
+          
           <div
             className="p-4 flex items-center justify-between hover:bg-gray-50 cursor-pointer text-red-600"
             onClick={handleLogout}

+ 9 - 0
src/client/mobile/routes.tsx

@@ -23,6 +23,7 @@ import PointsPage from './pages/PointsPage';
 import MyPostsPage from './pages/MyPostsPage';
 import MyFavoritesPage from './pages/MyFavoritesPage';
 import SkillsPage from './pages/SkillsPage';
+import FontSettingsPage from './pages/FontSettingsPage';
 import LoginPage from './pages/LoginPage';
 import RegisterPage from './pages/RegisterPage';
 
@@ -122,6 +123,14 @@ export const router = createBrowserRouter([
             <SkillsPage />
           </ProtectedRoute>
         )
+      },
+      {
+        path: 'profile/font-settings',
+        element: (
+          <ProtectedRoute>
+            <FontSettingsPage />
+          </ProtectedRoute>
+        )
       }
     ]
   },