feat: 初始化移动端项目基础结构
添加项目基础配置和核心功能模块: - 配置 TypeScript 和 React Native 环境 - 实现认证状态管理 - 封装 API 请求客户端 - 搭建应用导航框架
This commit is contained in:
56
App.tsx
Normal file
56
App.tsx
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
/**
|
||||||
|
* Timeline 移动端 App 入口
|
||||||
|
*
|
||||||
|
* 功能描述:
|
||||||
|
* React Native 应用入口,配置导航和全局状态管理。
|
||||||
|
*
|
||||||
|
* @author Timeline Team
|
||||||
|
* @date 2024
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import { StatusBar } from 'react-native';
|
||||||
|
import { SafeAreaProvider } from 'react-native-safe-area-context';
|
||||||
|
import { GestureHandlerRootView } from 'react-native-gesture-handler';
|
||||||
|
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
||||||
|
import AppNavigator from './src/navigation/AppNavigator';
|
||||||
|
|
||||||
|
// 创建 React Query 客户端
|
||||||
|
const queryClient = new QueryClient({
|
||||||
|
defaultOptions: {
|
||||||
|
queries: {
|
||||||
|
staleTime: 5 * 60 * 1000, // 5分钟
|
||||||
|
cacheTime: 10 * 60 * 1000, // 10分钟
|
||||||
|
retry: 2,
|
||||||
|
refetchOnWindowFocus: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* App 根组件
|
||||||
|
*
|
||||||
|
* 组件层级:
|
||||||
|
* 1. GestureHandlerRootView - 手势处理
|
||||||
|
* 2. SafeAreaProvider - 安全区域
|
||||||
|
* 3. QueryClientProvider - 数据请求
|
||||||
|
* 4. AppNavigator - 导航
|
||||||
|
*/
|
||||||
|
const App: React.FC = () => {
|
||||||
|
return (
|
||||||
|
<GestureHandlerRootView style={{ flex: 1 }}>
|
||||||
|
<SafeAreaProvider>
|
||||||
|
<QueryClientProvider client={queryClient}>
|
||||||
|
<StatusBar
|
||||||
|
barStyle="dark-content"
|
||||||
|
backgroundColor="transparent"
|
||||||
|
translucent
|
||||||
|
/>
|
||||||
|
<AppNavigator />
|
||||||
|
</QueryClientProvider>
|
||||||
|
</SafeAreaProvider>
|
||||||
|
</GestureHandlerRootView>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default App;
|
||||||
67
package.json
Normal file
67
package.json
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
/**
|
||||||
|
* Timeline 移动端 App - React Native 项目配置
|
||||||
|
*
|
||||||
|
* 功能描述:
|
||||||
|
* 定义 React Native 移动端应用的项目配置。
|
||||||
|
*
|
||||||
|
* 技术栈:
|
||||||
|
* - React Native 0.73+
|
||||||
|
* - TypeScript
|
||||||
|
* - React Navigation 6
|
||||||
|
* - Redux Toolkit / Zustand
|
||||||
|
* - React Query
|
||||||
|
* - NativeWind (TailwindCSS for RN)
|
||||||
|
*
|
||||||
|
* @author Timeline Team
|
||||||
|
* @date 2024
|
||||||
|
*/
|
||||||
|
|
||||||
|
// package.json 配置
|
||||||
|
{
|
||||||
|
"name": "timeline-mobile",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"android": "react-native run-android",
|
||||||
|
"ios": "react-native run-ios",
|
||||||
|
"start": "react-native start",
|
||||||
|
"test": "jest",
|
||||||
|
"lint": "eslint .",
|
||||||
|
"type-check": "tsc --noEmit"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"react": "18.2.0",
|
||||||
|
"react-native": "0.73.0",
|
||||||
|
"@react-navigation/native": "^6.1.0",
|
||||||
|
"@react-navigation/native-stack": "^6.9.0",
|
||||||
|
"@react-navigation/bottom-tabs": "^6.5.0",
|
||||||
|
"@react-navigation/drawer": "^6.6.0",
|
||||||
|
"react-native-screens": "^3.29.0",
|
||||||
|
"react-native-safe-area-context": "^4.8.0",
|
||||||
|
"react-native-gesture-handler": "^2.14.0",
|
||||||
|
"react-native-reanimated": "^3.6.0",
|
||||||
|
"@react-native-async-storage/async-storage": "^1.21.0",
|
||||||
|
"@tanstack/react-query": "^5.17.0",
|
||||||
|
"zustand": "^4.4.0",
|
||||||
|
"axios": "^1.6.0",
|
||||||
|
"date-fns": "^3.2.0",
|
||||||
|
"react-native-image-picker": "^7.1.0",
|
||||||
|
"react-native-camera": "^4.2.1",
|
||||||
|
"react-native-geolocation-service": "^5.3.1",
|
||||||
|
"react-native-push-notification": "^8.1.1",
|
||||||
|
"@react-native-community/netinfo": "^11.3.0",
|
||||||
|
"nativewind": "^2.0.11",
|
||||||
|
"react-native-svg": "^14.1.0",
|
||||||
|
"react-native-fast-image": "^8.6.3"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/react": "^18.2.0",
|
||||||
|
"@types/react-native": "^0.73.0",
|
||||||
|
"typescript": "^5.3.0",
|
||||||
|
"eslint": "^8.56.0",
|
||||||
|
"@typescript-eslint/eslint-plugin": "^6.18.0",
|
||||||
|
"prettier": "^3.2.0",
|
||||||
|
"tailwindcss": "^3.4.0",
|
||||||
|
"react-native-svg-transformer": "^1.3.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
200
src/navigation/AppNavigator.tsx
Normal file
200
src/navigation/AppNavigator.tsx
Normal file
@@ -0,0 +1,200 @@
|
|||||||
|
/**
|
||||||
|
* AppNavigator - 应用导航配置
|
||||||
|
*
|
||||||
|
* 功能描述:
|
||||||
|
* 配置应用的路由导航结构,包括:
|
||||||
|
* - 认证流程(登录/注册)
|
||||||
|
* - 主应用流程(底部Tab导航)
|
||||||
|
* - 详情页面(Stack导航)
|
||||||
|
*
|
||||||
|
* 导航结构:
|
||||||
|
* RootStack
|
||||||
|
* ├── AuthStack (未登录)
|
||||||
|
* │ ├── Login
|
||||||
|
* │ └── Register
|
||||||
|
* └── MainStack (已登录)
|
||||||
|
* ├── TabNavigator
|
||||||
|
* │ ├── Home
|
||||||
|
* │ ├── Timeline
|
||||||
|
* │ ├── Gallery
|
||||||
|
* │ └── Profile
|
||||||
|
* └── StoryDetail
|
||||||
|
*
|
||||||
|
* @author Timeline Team
|
||||||
|
* @date 2024
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import { NavigationContainer } from '@react-navigation/native';
|
||||||
|
import { createNativeStackNavigator } from '@react-navigation/native-stack';
|
||||||
|
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
|
||||||
|
import { useAuthStore } from '@stores/authStore';
|
||||||
|
|
||||||
|
// 屏幕组件
|
||||||
|
import LoginScreen from '@screens/auth/LoginScreen';
|
||||||
|
import RegisterScreen from '@screens/auth/RegisterScreen';
|
||||||
|
import HomeScreen from '@screens/home/HomeScreen';
|
||||||
|
import TimelineScreen from '@screens/timeline/TimelineScreen';
|
||||||
|
import TimelineDetailScreen from '@screens/timeline/TimelineDetailScreen';
|
||||||
|
import GalleryScreen from '@screens/gallery/GalleryScreen';
|
||||||
|
import ProfileScreen from '@screens/profile/ProfileScreen';
|
||||||
|
import NotificationScreen from '@screens/notification/NotificationScreen';
|
||||||
|
import SettingsScreen from '@screens/settings/SettingsScreen';
|
||||||
|
|
||||||
|
// 图标组件
|
||||||
|
import Icon from '@components/common/Icon';
|
||||||
|
|
||||||
|
// 导航类型定义
|
||||||
|
export type RootStackParamList = {
|
||||||
|
Auth: undefined;
|
||||||
|
Main: undefined;
|
||||||
|
Login: undefined;
|
||||||
|
Register: undefined;
|
||||||
|
StoryDetail: { storyId: string };
|
||||||
|
Notification: undefined;
|
||||||
|
Settings: undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type TabParamList = {
|
||||||
|
Home: undefined;
|
||||||
|
Timeline: undefined;
|
||||||
|
Gallery: undefined;
|
||||||
|
Profile: undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
const Stack = createNativeStackNavigator<RootStackParamList>();
|
||||||
|
const Tab = createBottomTabNavigator<TabParamList>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tab 导航器
|
||||||
|
* 主应用的底部标签导航
|
||||||
|
*/
|
||||||
|
const TabNavigator: React.FC = () => {
|
||||||
|
return (
|
||||||
|
<Tab.Navigator
|
||||||
|
screenOptions={{
|
||||||
|
headerShown: false,
|
||||||
|
tabBarActiveTintColor: '#1890ff',
|
||||||
|
tabBarInactiveTintColor: '#999',
|
||||||
|
tabBarStyle: {
|
||||||
|
backgroundColor: '#fff',
|
||||||
|
borderTopWidth: 1,
|
||||||
|
borderTopColor: '#f0f0f0',
|
||||||
|
paddingBottom: 8,
|
||||||
|
paddingTop: 8,
|
||||||
|
height: 60,
|
||||||
|
},
|
||||||
|
tabBarLabelStyle: {
|
||||||
|
fontSize: 12,
|
||||||
|
fontWeight: '500',
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Tab.Screen
|
||||||
|
name="Home"
|
||||||
|
component={HomeScreen}
|
||||||
|
options={{
|
||||||
|
tabBarLabel: '首页',
|
||||||
|
tabBarIcon: ({ color, size }) => (
|
||||||
|
<Icon name="home" size={size} color={color} />
|
||||||
|
),
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Tab.Screen
|
||||||
|
name="Timeline"
|
||||||
|
component={TimelineScreen}
|
||||||
|
options={{
|
||||||
|
tabBarLabel: '时间线',
|
||||||
|
tabBarIcon: ({ color, size }) => (
|
||||||
|
<Icon name="timeline" size={size} color={color} />
|
||||||
|
),
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Tab.Screen
|
||||||
|
name="Gallery"
|
||||||
|
component={GalleryScreen}
|
||||||
|
options={{
|
||||||
|
tabBarLabel: '相册',
|
||||||
|
tabBarIcon: ({ color, size }) => (
|
||||||
|
<Icon name="image" size={size} color={color} />
|
||||||
|
),
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Tab.Screen
|
||||||
|
name="Profile"
|
||||||
|
component={ProfileScreen}
|
||||||
|
options={{
|
||||||
|
tabBarLabel: '我的',
|
||||||
|
tabBarIcon: ({ color, size }) => (
|
||||||
|
<Icon name="user" size={size} color={color} />
|
||||||
|
),
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Tab.Navigator>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 认证导航器
|
||||||
|
* 未登录用户的认证流程
|
||||||
|
*/
|
||||||
|
const AuthStack: React.FC = () => {
|
||||||
|
return (
|
||||||
|
<Stack.Navigator
|
||||||
|
screenOptions={{
|
||||||
|
headerShown: false,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Stack.Screen name="Login" component={LoginScreen} />
|
||||||
|
<Stack.Screen name="Register" component={RegisterScreen} />
|
||||||
|
</Stack.Navigator>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 主导航器
|
||||||
|
* 已登录用户的主应用流程
|
||||||
|
*/
|
||||||
|
const MainStack: React.FC = () => {
|
||||||
|
return (
|
||||||
|
<Stack.Navigator
|
||||||
|
screenOptions={{
|
||||||
|
headerShown: false,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Stack.Screen name="TabNavigator" component={TabNavigator} />
|
||||||
|
<Stack.Screen
|
||||||
|
name="StoryDetail"
|
||||||
|
component={TimelineDetailScreen}
|
||||||
|
options={{
|
||||||
|
headerShown: true,
|
||||||
|
title: '时间线详情',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Stack.Screen name="Notification" component={NotificationScreen} />
|
||||||
|
<Stack.Screen name="Settings" component={SettingsScreen} />
|
||||||
|
</Stack.Navigator>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根导航器
|
||||||
|
* 根据登录状态切换认证/主应用流程
|
||||||
|
*/
|
||||||
|
const AppNavigator: React.FC = () => {
|
||||||
|
const { isAuthenticated } = useAuthStore();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<NavigationContainer>
|
||||||
|
<Stack.Navigator screenOptions={{ headerShown: false }}>
|
||||||
|
{isAuthenticated ? (
|
||||||
|
<Stack.Screen name="Main" component={MainStack} />
|
||||||
|
) : (
|
||||||
|
<Stack.Screen name="Auth" component={AuthStack} />
|
||||||
|
)}
|
||||||
|
</Stack.Navigator>
|
||||||
|
</NavigationContainer>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default AppNavigator;
|
||||||
138
src/services/apiClient.ts
Normal file
138
src/services/apiClient.ts
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
/**
|
||||||
|
* apiClient - API 请求客户端
|
||||||
|
*
|
||||||
|
* 功能描述:
|
||||||
|
* 封装 Axios 请求客户端,处理:
|
||||||
|
* - 请求拦截(添加 Token)
|
||||||
|
* - 响应拦截(错误处理)
|
||||||
|
* - Token 刷新
|
||||||
|
* - 网络状态检测
|
||||||
|
*
|
||||||
|
* @author Timeline Team
|
||||||
|
* @date 2024
|
||||||
|
*/
|
||||||
|
|
||||||
|
import axios, { AxiosInstance, AxiosError, InternalAxiosRequestConfig } from 'axios';
|
||||||
|
import { useAuthStore } from '@stores/authStore';
|
||||||
|
import NetInfo from '@react-native-community/netinfo';
|
||||||
|
|
||||||
|
// API 基础配置
|
||||||
|
const BASE_URL = __DEV__
|
||||||
|
? 'http://10.0.2.2:8080' // Android 模拟器
|
||||||
|
: 'https://api.timeline.com';
|
||||||
|
|
||||||
|
const TIMEOUT = 30000;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建 Axios 实例
|
||||||
|
*/
|
||||||
|
const apiClient: AxiosInstance = axios.create({
|
||||||
|
baseURL: BASE_URL,
|
||||||
|
timeout: TIMEOUT,
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 请求拦截器
|
||||||
|
* 添加 Token 到请求头
|
||||||
|
*/
|
||||||
|
apiClient.interceptors.request.use(
|
||||||
|
async (config: InternalAxiosRequestConfig) => {
|
||||||
|
// 检查网络状态
|
||||||
|
const netInfo = await NetInfo.fetch();
|
||||||
|
if (!netInfo.isConnected) {
|
||||||
|
return Promise.reject(new Error('网络不可用,请检查网络连接'));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加 Token
|
||||||
|
const token = useAuthStore.getState().token;
|
||||||
|
if (token && config.headers) {
|
||||||
|
config.headers.Authorization = `Bearer ${token}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return config;
|
||||||
|
},
|
||||||
|
(error: AxiosError) => {
|
||||||
|
return Promise.reject(error);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 响应拦截器
|
||||||
|
* 处理错误和 Token 刷新
|
||||||
|
*/
|
||||||
|
apiClient.interceptors.response.use(
|
||||||
|
(response) => {
|
||||||
|
return response.data;
|
||||||
|
},
|
||||||
|
async (error: AxiosError) => {
|
||||||
|
const originalRequest = error.config as InternalAxiosRequestConfig & { _retry?: boolean };
|
||||||
|
|
||||||
|
// 处理 401 未授权错误
|
||||||
|
if (error.response?.status === 401 && !originalRequest._retry) {
|
||||||
|
originalRequest._retry = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 尝试刷新 Token
|
||||||
|
const refreshToken = useAuthStore.getState().refreshToken;
|
||||||
|
if (refreshToken) {
|
||||||
|
const response = await axios.post(`${BASE_URL}/auth/refresh`, {
|
||||||
|
refreshToken,
|
||||||
|
});
|
||||||
|
|
||||||
|
const { token } = response.data;
|
||||||
|
useAuthStore.getState().setToken(token);
|
||||||
|
|
||||||
|
// 重试原请求
|
||||||
|
if (originalRequest.headers) {
|
||||||
|
originalRequest.headers.Authorization = `Bearer ${token}`;
|
||||||
|
}
|
||||||
|
return apiClient(originalRequest);
|
||||||
|
}
|
||||||
|
} catch (refreshError) {
|
||||||
|
// Token 刷新失败,登出
|
||||||
|
useAuthStore.getState().logout();
|
||||||
|
return Promise.reject(refreshError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理其他错误
|
||||||
|
const errorMessage = getErrorMessage(error);
|
||||||
|
return Promise.reject(new Error(errorMessage));
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取错误消息
|
||||||
|
*/
|
||||||
|
function getErrorMessage(error: AxiosError): string {
|
||||||
|
if (error.message === 'Network Error') {
|
||||||
|
return '网络错误,请检查网络连接';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error.response) {
|
||||||
|
const status = error.response.status;
|
||||||
|
const data: any = error.response.data;
|
||||||
|
|
||||||
|
switch (status) {
|
||||||
|
case 400:
|
||||||
|
return data?.message || '请求参数错误';
|
||||||
|
case 401:
|
||||||
|
return '登录已过期,请重新登录';
|
||||||
|
case 403:
|
||||||
|
return '没有权限访问';
|
||||||
|
case 404:
|
||||||
|
return '请求的资源不存在';
|
||||||
|
case 500:
|
||||||
|
return '服务器错误,请稍后重试';
|
||||||
|
default:
|
||||||
|
return data?.message || `请求失败 (${status})`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return error.message || '未知错误';
|
||||||
|
}
|
||||||
|
|
||||||
|
export default apiClient;
|
||||||
123
src/stores/authStore.ts
Normal file
123
src/stores/authStore.ts
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
/**
|
||||||
|
* authStore - 认证状态管理 Store
|
||||||
|
*
|
||||||
|
* 功能描述:
|
||||||
|
* 使用 Zustand 管理用户认证状态。
|
||||||
|
*
|
||||||
|
* 状态:
|
||||||
|
* - isAuthenticated: 是否已认证
|
||||||
|
* - user: 用户信息
|
||||||
|
* - token: 访问令牌
|
||||||
|
*
|
||||||
|
* 操作:
|
||||||
|
* - login: 登录
|
||||||
|
* - logout: 登出
|
||||||
|
* - updateUser: 更新用户信息
|
||||||
|
*
|
||||||
|
* 持久化:
|
||||||
|
* - 使用 AsyncStorage 持久化 token
|
||||||
|
*
|
||||||
|
* @author Timeline Team
|
||||||
|
* @date 2024
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { create } from 'zustand';
|
||||||
|
import { persist, createJSONStorage } from 'zustand/middleware';
|
||||||
|
import AsyncStorage from '@react-native-async-storage/async-storage';
|
||||||
|
import { User } from '@types/user';
|
||||||
|
|
||||||
|
interface AuthState {
|
||||||
|
// 状态
|
||||||
|
isAuthenticated: boolean;
|
||||||
|
user: User | null;
|
||||||
|
token: string | null;
|
||||||
|
refreshToken: string | null;
|
||||||
|
isLoading: boolean;
|
||||||
|
|
||||||
|
// 操作
|
||||||
|
login: (token: string, refreshToken: string, user: User) => void;
|
||||||
|
logout: () => void;
|
||||||
|
updateUser: (user: Partial<User>) => void;
|
||||||
|
setToken: (token: string) => void;
|
||||||
|
setLoading: (loading: boolean) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 认证状态 Store
|
||||||
|
*/
|
||||||
|
export const useAuthStore = create<AuthState>()(
|
||||||
|
persist(
|
||||||
|
(set, get) => ({
|
||||||
|
// 初始状态
|
||||||
|
isAuthenticated: false,
|
||||||
|
user: null,
|
||||||
|
token: null,
|
||||||
|
refreshToken: null,
|
||||||
|
isLoading: true,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 登录
|
||||||
|
* 保存 token 和用户信息
|
||||||
|
*/
|
||||||
|
login: (token, refreshToken, user) => {
|
||||||
|
set({
|
||||||
|
isAuthenticated: true,
|
||||||
|
token,
|
||||||
|
refreshToken,
|
||||||
|
user,
|
||||||
|
isLoading: false,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 登出
|
||||||
|
* 清除所有认证信息
|
||||||
|
*/
|
||||||
|
logout: () => {
|
||||||
|
set({
|
||||||
|
isAuthenticated: false,
|
||||||
|
user: null,
|
||||||
|
token: null,
|
||||||
|
refreshToken: null,
|
||||||
|
isLoading: false,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新用户信息
|
||||||
|
*/
|
||||||
|
updateUser: (userData) => {
|
||||||
|
const currentUser = get().user;
|
||||||
|
if (currentUser) {
|
||||||
|
set({
|
||||||
|
user: { ...currentUser, ...userData },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置 Token
|
||||||
|
*/
|
||||||
|
setToken: (token) => {
|
||||||
|
set({ token });
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置加载状态
|
||||||
|
*/
|
||||||
|
setLoading: (loading) => {
|
||||||
|
set({ isLoading: loading });
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
name: 'auth-storage',
|
||||||
|
storage: createJSONStorage(() => AsyncStorage),
|
||||||
|
partialize: (state) => ({
|
||||||
|
token: state.token,
|
||||||
|
refreshToken: state.refreshToken,
|
||||||
|
user: state.user,
|
||||||
|
isAuthenticated: state.isAuthenticated,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
32
tsconfig.json
Normal file
32
tsconfig.json
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "esnext",
|
||||||
|
"module": "commonjs",
|
||||||
|
"lib": ["es2017"],
|
||||||
|
"allowJs": true,
|
||||||
|
"jsx": "react-native",
|
||||||
|
"noEmit": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"strict": true,
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"baseUrl": "./",
|
||||||
|
"paths": {
|
||||||
|
"@/*": ["src/*"],
|
||||||
|
"@components/*": ["src/components/*"],
|
||||||
|
"@screens/*": ["src/screens/*"],
|
||||||
|
"@hooks/*": ["src/hooks/*"],
|
||||||
|
"@services/*": ["src/services/*"],
|
||||||
|
"@stores/*": ["src/stores/*"],
|
||||||
|
"@utils/*": ["src/utils/*"],
|
||||||
|
"@types/*": ["src/types/*"],
|
||||||
|
"@assets/*": ["src/assets/*"]
|
||||||
|
},
|
||||||
|
"allowSyntheticDefaultImports": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"forceConsistentCasingInFileNames": true
|
||||||
|
},
|
||||||
|
"include": ["src/**/*", "index.js", "App.tsx"],
|
||||||
|
"exclude": ["node_modules", "babel.config.js", "metro.config.js", "jest.config.js"]
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user