| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246 |
- import React, { createContext, useContext, useState, useEffect } from 'react';
- import axios from 'axios';
- import { useQuery, useQueryClient } from '@tanstack/react-query';
- import { getLocalStorageWithExpiry, setLocalStorageWithExpiry } from './utils.ts';
- import type { User, AuthContextType, ThemeContextType, ThemeSettings } from '../share/types.ts';
- import { ThemeMode, FontSize, CompactMode } from '../share/types.ts';
- import { AuthAPI, ThemeAPI } from './api.ts';
- // 默认主题设置
- const defaultThemeSettings: ThemeSettings = {
- user_id: 0,
- theme_mode: ThemeMode.LIGHT,
- primary_color: '#3B82F6', // 蓝色
- background_color: '#F9FAFB',
- text_color: '#111827',
- border_radius: 8,
- font_size: FontSize.MEDIUM,
- is_compact: CompactMode.NORMAL
- };
- // 创建认证上下文
- const AuthContext = createContext<AuthContextType | null>(null);
- // 创建主题上下文
- const ThemeContext = createContext<ThemeContextType | null>(null);
- // 认证提供者组件
- export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
- const [user, setUser] = useState<User | null>(null);
- const [token, setToken] = useState<string | null>(getLocalStorageWithExpiry('mobile_token'));
- const [isAuthenticated, setIsAuthenticated] = useState(false);
- const queryClient = useQueryClient();
- // 使用useQuery检查登录状态
- const { isLoading: isAuthChecking } = useQuery({
- queryKey: ['auth', 'status', token],
- queryFn: async () => {
- if (!token) {
- setUser(null);
- setIsAuthenticated(false);
- return null;
- }
-
- try {
- // 设置请求头
- axios.defaults.headers.common['Authorization'] = `Bearer ${token}`;
- // 获取当前用户信息
- const currentUser = await AuthAPI.getCurrentUser();
- setUser(currentUser);
- setIsAuthenticated(true);
- setLocalStorageWithExpiry('mobile_user', currentUser, 24);
- return { isValid: true, user: currentUser };
- } catch (error) {
- // 如果API调用失败,自动登出
- logout();
- return { isValid: false };
- }
- },
- enabled: !!token,
- refetchOnWindowFocus: false,
- retry: false,
- });
-
- // 登录函数
- const login = async (username: string, password: string, latitude?: number, longitude?: number) => {
- try {
- const response = await AuthAPI.login(username, password, latitude, longitude);
- const { token, user } = response;
-
- // 保存到状态和本地存储
- setToken(token);
- setUser(user);
- setLocalStorageWithExpiry('mobile_token', token, 24); // 24小时过期
- setLocalStorageWithExpiry('mobile_user', user, 24);
-
- // 设置请求头
- axios.defaults.headers.common['Authorization'] = `Bearer ${token}`;
-
- } catch (error) {
- console.error('登录失败:', error);
- throw error;
- }
- };
- // 登出函数
- const logout = async () => {
- try {
- // 调用登出API
- await AuthAPI.logout();
- } catch (error) {
- console.error('登出API调用失败:', error);
- } finally {
- // 无论API调用成功与否,都清除本地状态
- setToken(null);
- setUser(null);
- localStorage.removeItem('mobile_token');
- localStorage.removeItem('mobile_user');
- // 清除请求头
- delete axios.defaults.headers.common['Authorization'];
- // 清除所有查询缓存
- queryClient.clear();
- }
- };
- return (
- <AuthContext.Provider
- value={{
- user,
- token,
- login,
- logout,
- isAuthenticated,
- isLoading: isAuthChecking
- }}
- >
- {children}
- </AuthContext.Provider>
- );
- };
- // 主题提供者组件
- export const ThemeProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
- const [currentTheme, setCurrentTheme] = useState<ThemeSettings>(() => {
- const storedTheme = localStorage.getItem('theme');
- return storedTheme ? JSON.parse(storedTheme) : defaultThemeSettings;
- });
-
- const isDark = currentTheme.theme_mode === ThemeMode.DARK;
- // 更新主题(实时预览)
- const updateTheme = (theme: Partial<ThemeSettings>) => {
- setCurrentTheme(prev => {
- const updatedTheme = { ...prev, ...theme };
- localStorage.setItem('theme', JSON.stringify(updatedTheme));
- return updatedTheme;
- });
- };
- // 保存主题到后端
- const saveTheme = async (theme: Partial<ThemeSettings>): Promise<ThemeSettings> => {
- try {
- const updatedTheme = { ...currentTheme, ...theme };
- const data = await ThemeAPI.updateThemeSettings(updatedTheme);
-
- setCurrentTheme(data);
- localStorage.setItem('theme', JSON.stringify(data));
-
- return data;
- } catch (error) {
- console.error('保存主题失败:', error);
- throw error;
- }
- };
- // 重置主题
- const resetTheme = async (): Promise<ThemeSettings> => {
- try {
- const data = await ThemeAPI.resetThemeSettings();
-
- setCurrentTheme(data);
- localStorage.setItem('theme', JSON.stringify(data));
-
- return data;
- } catch (error) {
- console.error('重置主题失败:', error);
-
- // 如果API失败,至少重置到默认主题
- setCurrentTheme(defaultThemeSettings);
- localStorage.setItem('theme', JSON.stringify(defaultThemeSettings));
-
- return defaultThemeSettings;
- }
- };
- // 切换主题模式(亮色/暗色)
- const toggleTheme = () => {
- const newMode = isDark ? ThemeMode.LIGHT : ThemeMode.DARK;
- const updatedTheme = {
- ...currentTheme,
- theme_mode: newMode,
- // 暗色和亮色模式下自动调整背景色和文字颜色
- background_color: newMode === ThemeMode.DARK ? '#121212' : '#F9FAFB',
- text_color: newMode === ThemeMode.DARK ? '#E5E7EB' : '#111827'
- };
-
- setCurrentTheme(updatedTheme);
- localStorage.setItem('theme', JSON.stringify(updatedTheme));
- };
- // 主题变化时应用CSS变量
- useEffect(() => {
- document.documentElement.style.setProperty('--primary-color', currentTheme.primary_color);
- document.documentElement.style.setProperty('--background-color', currentTheme.background_color || '#F9FAFB');
- document.documentElement.style.setProperty('--text-color', currentTheme.text_color || '#111827');
- document.documentElement.style.setProperty('--border-radius', `${currentTheme.border_radius || 8}px`);
-
- // 设置字体大小
- let rootFontSize = '16px'; // 默认中等字体
- if (currentTheme.font_size === FontSize.SMALL) {
- rootFontSize = '14px';
- } else if (currentTheme.font_size === FontSize.LARGE) {
- rootFontSize = '18px';
- }
- document.documentElement.style.setProperty('--font-size', rootFontSize);
-
- // 设置暗色模式类
- if (isDark) {
- document.documentElement.classList.add('dark');
- } else {
- document.documentElement.classList.remove('dark');
- }
- }, [currentTheme, isDark]);
- return (
- <ThemeContext.Provider
- value={{
- isDark,
- currentTheme,
- updateTheme,
- saveTheme,
- resetTheme,
- toggleTheme
- }}
- >
- {children}
- </ThemeContext.Provider>
- );
- };
- // 使用上下文的钩子
- export const useAuth = () => {
- const context = useContext(AuthContext);
- if (!context) {
- throw new Error('useAuth必须在AuthProvider内部使用');
- }
- return context;
- };
- export const useTheme = () => {
- const context = useContext(ThemeContext);
- if (!context) {
- throw new Error('useTheme必须在ThemeProvider内部使用');
- }
- return context;
- };
|