diff --git a/crop-x/src/app/(auth)/login/components/LoginForm.tsx b/crop-x/src/app/(auth)/login/components/LoginForm.tsx
index f8cbcc0..e19eb7c 100644
--- a/crop-x/src/app/(auth)/login/components/LoginForm.tsx
+++ b/crop-x/src/app/(auth)/login/components/LoginForm.tsx
@@ -28,7 +28,7 @@ import {
import { toast } from 'sonner';
import { useAuth } from '@/components/auth/AuthContext';
import { authReducer, initialAuthState, AuthState, AuthAction } from './authReducer';
-import { loginApiV1AuthLoginPost } from '@/lib/api/sdk.gen';
+import { getCaptchaApiV1AuthCaptchaGet, loginApiV1AuthLoginPost } from '@/lib/api/sdk.gen';
import type { CaptchaResponse } from '@/lib/api/types.gen';
interface LoginFormProps {
@@ -128,6 +128,8 @@ export function LoginForm({ onRegisterClick }: LoginFormProps) {
enterpriseId: response.data.enterprise_id || '',
enterpriseName: response.data.enterprise_name || '',
createdAt: response.data.created_at || new Date().toISOString(),
+ // 重要:存储token到用户对象中
+ token: response.data.access_token || response.data.token || null,
};
// 打印登录成功日志
@@ -137,10 +139,20 @@ export function LoginForm({ onRegisterClick }: LoginFormProps) {
timestamp: new Date().toISOString()
});
+ // 验证token是否正确存储
+ if (userData.token) {
+ console.log('🔑 Token已存储:', userData.token.substring(0, 20) + '...');
+ } else {
+ console.warn('⚠️ 未找到token,请检查API响应格式');
+ }
+
login(userData);
- toast.success('登录成功!');
- // 暂时不实现页面跳转
- console.log('✅ 登录流程完成,等待后续页面跳转实现');
+ toast.success('登录成功!正在跳转...');
+
+ // 跳转到个人中心页面
+ setTimeout(() => {
+ window.location.href = '/central-config/personal-center/personal-info';
+ }, 1000);
} else {
dispatch({ type: 'SET_ERROR', payload: '登录失败,请检查用户名和密码' });
toast.error('登录失败,请检查用户名和密码');
diff --git a/crop-x/src/app/layout.tsx b/crop-x/src/app/layout.tsx
index 3dda49b..860741e 100644
--- a/crop-x/src/app/layout.tsx
+++ b/crop-x/src/app/layout.tsx
@@ -1,12 +1,24 @@
+'use client';
+
+import { useEffect } from 'react';
+import { AuthProvider } from '@/components/auth/AuthContext';
+
export default function AppLayout({
children,
}: {
children: React.ReactNode
}) {
+ // 使用useEffect确保主题只在客户端设置,避免水合问题
+ useEffect(() => {
+ // 可以在这里添加客户端特定的初始化逻辑
+ }, []);
+
return (
-
-
- {children}
+
+
+
+ {children}
+
)
diff --git a/crop-x/src/app/page.tsx b/crop-x/src/app/page.tsx
index 06c5e69..c6e2fda 100644
--- a/crop-x/src/app/page.tsx
+++ b/crop-x/src/app/page.tsx
@@ -1,12 +1,25 @@
-export default function HomePage({
- children,
-}: {
- children: React.ReactNode
-}) {
+'use client';
+
+import { useAuth } from '@/components/auth/AuthContext';
+import { LoadingScreen } from '@/components/auth/LoadingScreen';
+import { redirect } from 'next/navigation';
+
+export default function HomePage() {
+ const { user, loading, isAuthenticated } = useAuth();
+
+ // 如果正在加载,显示加载屏幕
+ if (loading) {
+ return ;
+ }
+ console.log('isAuthenticated:',isAuthenticated)
+ // 如果未认证,重定向到登录页
+ if (!isAuthenticated) {
+ redirect('/login');
+ }
+
+ // 用户已认证,显示主页内容
return (
-
- {children}
-
- )
+
+ );
}
\ No newline at end of file
diff --git a/crop-x/src/components/auth/AuthContext.tsx b/crop-x/src/components/auth/AuthContext.tsx
index 280973e..e5038ab 100644
--- a/crop-x/src/components/auth/AuthContext.tsx
+++ b/crop-x/src/components/auth/AuthContext.tsx
@@ -1,6 +1,7 @@
'use client';
import React, { createContext, useContext, useState, ReactNode } from 'react';
+import { getCurrentUserInfoApiV1AuthMeGet } from '@/lib/api/sdk.gen';
interface User {
id: string;
@@ -9,6 +10,7 @@ interface User {
email?: string;
phone?: string;
enterpriseId?: string;
+ token?: string;
}
interface AuthContextType {
@@ -16,6 +18,8 @@ interface AuthContextType {
login: (user: User) => void;
logout: () => void;
isAuthenticated: boolean;
+ loading: boolean;
+ validateUser: () => Promise;
}
const AuthContext = createContext(undefined);
@@ -26,29 +30,65 @@ interface AuthProviderProps {
export function AuthProvider({ children }: AuthProviderProps) {
const [user, setUser] = useState(null);
+ const [loading, setLoading] = useState(true);
const login = (userData: User) => {
setUser(userData);
// 存储到 localStorage
localStorage.setItem('user', JSON.stringify(userData));
+ setLoading(false);
};
const logout = () => {
setUser(null);
localStorage.removeItem('user');
+ setLoading(false);
};
- // 初始化时检查 localStorage
- React.useEffect(() => {
- const storedUser = localStorage.getItem('user');
- if (storedUser) {
- try {
- setUser(JSON.parse(storedUser));
- } catch (error) {
- console.error('Failed to parse stored user data:', error);
- localStorage.removeItem('user');
+ // 验证当前用户信息
+ const validateUser = async () => {
+ try {
+ const storedUser = localStorage.getItem('user');
+ if (!storedUser) {
+ setLoading(false);
+ return;
}
+
+ const userData = JSON.parse(storedUser);
+
+ // 使用 SDK 调用 /api/v1/auth/me 验证用户信息
+ const response = await getCurrentUserInfoApiV1AuthMeGet({
+ headers: {
+ 'Authorization': `Bearer ${userData.token}`,
+ },
+ });
+
+ if (response.data) {
+ // 更新用户信息(可能包含最新的权限、角色等)
+ setUser({
+ ...userData,
+ ...response.data.data, // 合并最新的用户信息
+ });
+ console.log('✅ 用户验证成功,最新用户信息:', response.data.data);
+ } else {
+ // Token无效,清除用户信息
+ console.warn('⚠️ Token验证失败,清除用户信息');
+ localStorage.removeItem('user');
+ setUser(null);
+ }
+ } catch (error: any) {
+ console.error('❌ 用户验证失败:', error);
+ // 验证失败时也清除用户信息,避免不一致状态
+ localStorage.removeItem('user');
+ setUser(null);
+ } finally {
+ setLoading(false);
}
+ };
+
+ // 初始化时检查 localStorage并验证用户
+ React.useEffect(() => {
+ validateUser();
}, []);
const value: AuthContextType = {
@@ -56,6 +96,8 @@ export function AuthProvider({ children }: AuthProviderProps) {
login,
logout,
isAuthenticated: !!user,
+ loading,
+ validateUser, // 暴露验证方法供手动刷新使用
};
return {children};
diff --git a/crop-x/src/components/auth/LoadingScreen.tsx b/crop-x/src/components/auth/LoadingScreen.tsx
new file mode 100644
index 0000000..2185478
--- /dev/null
+++ b/crop-x/src/components/auth/LoadingScreen.tsx
@@ -0,0 +1,32 @@
+'use client';
+
+import React from 'react';
+import { Loader2 } from 'lucide-react';
+
+interface LoadingScreenProps {
+ message?: string;
+}
+
+export function LoadingScreen({ message = '正在验证用户身份...' }: LoadingScreenProps) {
+ return (
+
+
+
+
+
+
智慧农业生产管理系统
+
{message}
+
+ {/* 加载进度条 */}
+
+
+ {/* 提示信息 */}
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/crop-x/src/lib/api/README.md b/crop-x/src/lib/api/README.md
new file mode 100644
index 0000000..4b68b7e
--- /dev/null
+++ b/crop-x/src/lib/api/README.md
@@ -0,0 +1,127 @@
+# API SDK 使用指南
+
+## 🚀 概述
+
+本项目提供了两种API调用方式:
+
+1. **原始SDK** (`sdk.gen.ts`) - 直接API调用,无认证
+2. **认证SDK** (`authenticated-sdk.ts`) - 自动添加Token的API调用
+
+## 📝 使用方法
+
+### 1. 无认证的API调用(如登录、获取验证码)
+
+```typescript
+import { api } from '@/lib/api/authenticated-sdk';
+
+// 获取验证码 - 不需要token
+const captchaResponse = await api.getCaptcha();
+
+// 用户登录 - 不需要token
+const loginResponse = await api.login({
+ body: {
+ identifier: 'admin',
+ password: 'password123',
+ captcha_id: 'captcha-id',
+ captcha_text: 'ABCD'
+ }
+});
+```
+
+### 2. 需要认证的API调用
+
+```typescript
+import { authenticatedSdk } from '@/lib/api/authenticated-sdk';
+
+// 获取当前用户信息 - 自动添加token
+const userResponse = await authenticatedSdk.getCurrentUser();
+
+// 获取用户列表 - 自动添加token
+const usersResponse = await authenticatedSdk.getUsers({
+ query: { page: 1, size: 10 }
+});
+
+// 创建用户 - 自动添加token
+const createResponse = await authenticatedSdk.createUser({
+ body: {
+ username: 'newuser',
+ email: 'user@example.com'
+ }
+});
+```
+
+## 🔧 核心特性
+
+### 自动Token管理
+- 自动从localStorage获取token
+- 自动添加到请求头 `Authorization: Bearer {token}`
+- 请求日志记录
+
+### 401错误处理
+- 检测401响应自动清除本地认证信息
+- 自动跳转到登录页面
+
+### 类型安全
+- 完整的TypeScript类型支持
+- 智能代码提示
+
+## 📋 API方法列表
+
+### 认证相关
+- `api.getCaptcha()` - 获取验证码
+- `api.login(options)` - 用户登录
+- `api.logout(options)` - 用户登出
+- `authenticatedSdk.getCurrentUser()` - 获取当前用户
+- `authenticatedSdk.refreshToken()` - 刷新token
+
+### 用户管理
+- `authenticatedSdk.getUsers()` - 获取用户列表
+- `authenticatedSdk.getUser(options)` - 获取单个用户
+- `authenticatedSdk.createUser(options)` - 创建用户
+- `authenticatedSdk.updateUser(options)` - 更新用户
+- `authenticatedSdk.deleteUser(options)` - 删除用户
+
+### 部门管理
+- `authenticatedSdk.getDepartments()` - 获取部门列表
+- `authenticatedSdk.getDepartment(options)` - 获取单个部门
+- `authenticatedSdk.createDepartment(options)` - 创建部门
+- `authenticatedSdk.updateDepartment(options)` - 更新部门
+- `authenticatedSdk.deleteDepartment(options)` - 删除部门
+
+### 租户管理
+- `authenticatedSdk.getTenants()` - 获取租户列表
+- `authenticatedSdk.getTenant(options)` - 获取单个租户
+- `authenticatedSdk.getCurrentTenant()` - 获取当前租户信息
+
+## ⚠️ 重要说明
+
+1. **API生成覆盖**: `sdk.gen.ts` 和 `client.gen.ts` 会被 `npm run api:generate` 覆盖
+2. **安全文件**: `authenticated-sdk.ts` 不会被覆盖,可安全使用
+3. **Token存储**: Token存储在localStorage的user对象中
+4. **代理配置**: 所有请求通过Next.js代理到真实API地址
+
+## 🔄 迁移指南
+
+### 从原始SDK迁移到认证SDK
+
+```typescript
+// ❌ 旧方式 - 可能缺少认证
+import { getUsersApiV1UsersGet } from '@/lib/api/sdk.gen';
+const response = await getUsersApiV1UsersGet();
+
+// ✅ 新方式 - 自动添加认证
+import { authenticatedSdk } from '@/lib/api/authenticated-sdk';
+const response = await authenticatedSdk.getUsers();
+```
+
+### 混合使用场景
+
+```typescript
+import { api, authenticatedSdk } from '@/lib/api/authenticated-sdk';
+
+// 登录 - 不需要认证
+const loginResponse = await api.login({ body: loginData });
+
+// 登录成功后,获取用户信息 - 需要认证
+const userInfo = await authenticatedSdk.getCurrentUser();
+```
\ No newline at end of file
diff --git a/crop-x/src/lib/api/authenticated-sdk.ts b/crop-x/src/lib/api/authenticated-sdk.ts
new file mode 100644
index 0000000..8fb74d8
--- /dev/null
+++ b/crop-x/src/lib/api/authenticated-sdk.ts
@@ -0,0 +1,108 @@
+/**
+ * 认证API SDK包装器
+ * 为所有API调用自动添加Token,不会被API生成工具覆盖
+ */
+
+import { client } from './client.gen';
+import * as api from './sdk.gen';
+import type { Options } from './sdk.gen';
+
+// 获取存储的token
+const getAuthToken = (): string | null => {
+ try {
+ const storedUser = localStorage.getItem('user');
+ const user = storedUser ? JSON.parse(storedUser) : null;
+ return user?.token || null;
+ } catch (error) {
+ console.error('获取token失败:', error);
+ return null;
+ }
+};
+
+// 创建带认证的客户端选项
+const createAuthenticatedOptions = (
+ originalOptions: Options = {} as Options
+): Options => {
+ const token = getAuthToken();
+
+ const authenticatedOptions: Options = {
+ ...originalOptions,
+ client: client
+ };
+
+ // 如果有token,添加到请求头
+ if (token) {
+ authenticatedOptions.headers = {
+ ...originalOptions.headers,
+ Authorization: `Bearer ${token}`
+ };
+ console.log('🔑 为API请求添加Token:', (originalOptions as any).url || 'unknown endpoint');
+ } else {
+ console.log('🚫 无Token,API请求可能未授权:', (originalOptions as any).url || 'unknown endpoint');
+ }
+
+ return authenticatedOptions;
+};
+
+// 包装所有API方法,自动添加认证
+export const authenticatedSdk = {
+ // 认证相关
+ getCaptcha: (options?: Options) => api.getCaptchaApiV1AuthCaptchaGet(options),
+ login: (options: Options) => api.loginApiV1AuthLoginPost(options),
+ logout: (options?: Options) => api.logoutApiV1AuthLogoutPost(createAuthenticatedOptions(options)),
+ getCurrentUser: (options?: Options) => api.getCurrentUserInfoApiV1AuthMeGet(createAuthenticatedOptions(options)),
+ refreshToken: (options?: Options) => api.refreshTokenApiV1AuthRefreshPost(createAuthenticatedOptions(options)),
+
+ // 用户管理
+ getUsers: (options?: Options) => api.getUsersApiV1UsersGet(createAuthenticatedOptions(options)),
+ getUser: (options: Options) => api.getUserApiV1UsersUserIdGet(createAuthenticatedOptions(options)),
+ createUser: (options: Options) => api.createUserApiV1UsersPost(createAuthenticatedOptions(options)),
+ updateUser: (options: Options) => api.updateUserApiV1UsersUserIdPut(createAuthenticatedOptions(options)),
+ deleteUser: (options: Options) => api.deleteUserApiV1UsersUserIdDelete(createAuthenticatedOptions(options)),
+
+ // 系统用户管理
+ getSystemUsers: (options?: Options) => api.listSystemUsersApiV1UsersSystemUsersGet(createAuthenticatedOptions(options)),
+ getSystemUser: (options: Options) => api.getSystemUserApiV1UsersSystemUsersUserIdGet(createAuthenticatedOptions(options)),
+ createSystemUser: (options: Options) => api.createSystemUserApiV1UsersSystemUsersPost(createAuthenticatedOptions(options)),
+ updateSystemUser: (options: Options) => api.updateSystemUserApiV1UsersSystemUsersUserIdPut(createAuthenticatedOptions(options)),
+ deleteSystemUser: (options: Options) => api.deleteSystemUserApiV1UsersSystemUsersUserIdDelete(createAuthenticatedOptions(options)),
+
+ // 部门管理
+ getDepartments: (options?: Options) => api.getDepartmentsApiV1DepartmentsDepartmentsGet(createAuthenticatedOptions(options)),
+ getDepartment: (options: Options) => api.getDepartmentApiV1DepartmentsDepartmentsDepartmentIdGet(createAuthenticatedOptions(options)),
+ createDepartment: (options: Options) => api.createDepartmentApiV1DepartmentsDepartmentsPost(createAuthenticatedOptions(options)),
+ updateDepartment: (options: Options) => api.updateDepartmentApiV1DepartmentsDepartmentsDepartmentIdPut(createAuthenticatedOptions(options)),
+ deleteDepartment: (options: Options) => api.deleteDepartmentApiV1DepartmentsDepartmentsDepartmentIdDelete(createAuthenticatedOptions(options)),
+ getDepartmentTree: (options?: Options) => api.getDepartmentTreeApiV1DepartmentsDepartmentsTreeGet(createAuthenticatedOptions(options)),
+
+ // 租户管理
+ getTenants: (options?: Options) => api.listTenantsApiV1TenantsGet(createAuthenticatedOptions(options)),
+ getTenant: (options: Options) => api.getTenantApiV1TenantsTenantIdGet(createAuthenticatedOptions(options)),
+ createTenant: (options: Options) => api.createTenantApiV1TenantsPost(createAuthenticatedOptions(options)),
+ updateTenant: (options: Options) => api.updateTenantApiV1TenantsTenantIdPut(createAuthenticatedOptions(options)),
+ deleteTenant: (options: Options) => api.deleteTenantApiV1TenantsTenantIdDelete(createAuthenticatedOptions(options)),
+ getCurrentTenant: (options?: Options) => api.getCurrentTenantApiV1TenantsMeGet(createAuthenticatedOptions(options)),
+
+ // 消息管理
+ sendMessage: (options: Options) => api.sendMessageApiV1MessagesSendPost(createAuthenticatedOptions(options)),
+ scheduleMessage: (options: Options) => api.scheduleMessageApiV1MessagesSchedulePost(createAuthenticatedOptions(options)),
+ getMessageLogs: (options?: Options) => api.listMessageLogsApiV1MessagesLogsMessageLogsGet(createAuthenticatedOptions(options)),
+ getMessageReceipts: (options: Options) => api.getMessageReceiptsApiV1MessagesLogsMessageLogsMessageIdReceiptsGet(createAuthenticatedOptions(options)),
+
+ // 系统信息
+ getSystemInfo: (options?: Options) => api.getSystemInfoApiV1SystemInfoGet(createAuthenticatedOptions(options)),
+ getSystemStats: (options?: Options) => api.getSystemStatsApiV1SystemStatsGet(createAuthenticatedOptions(options)),
+ getSystemMetrics: (options?: Options) => api.getSystemMetricsApiV1SystemMetricsGet(createAuthenticatedOptions(options)),
+ healthCheck: (options?: Options) => api.healthCheckApiV1HealthGet(createAuthenticatedOptions(options)),
+
+ // 日志管理
+ getLoginLogs: (options?: Options) => api.getLoginLogsApiV1LogsLogsLoginGet(createAuthenticatedOptions(options)),
+ getOperationLogs: (options?: Options) => api.getOperationLogsApiV1LogsLogsOperationGet(createAuthenticatedOptions(options)),
+ getNetworkLogs: (options?: Options) => api.getNetworkLogsApiV1LogsLogsNetworkGet(createAuthenticatedOptions(options)),
+};
+
+// 导出原始API以供特殊情况使用(如登录)
+export { api };
+
+// 导出类型
+export type { Options };
\ No newline at end of file