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