diff --git a/crop-x/src/app/(app)/central-config/user/department/components/DepartmentStatsCards.tsx b/crop-x/src/app/(app)/central-config/user/department/components/DepartmentStatsCards.tsx
index 0cdf1e7..7e1fb78 100644
--- a/crop-x/src/app/(app)/central-config/user/department/components/DepartmentStatsCards.tsx
+++ b/crop-x/src/app/(app)/central-config/user/department/components/DepartmentStatsCards.tsx
@@ -8,7 +8,7 @@
'use client';
import { Card } from '@/components/ui/card';
-import { Building2, Users, Layers } from 'lucide-react';
+import { Building2, Layers, GitBranch } from 'lucide-react';
import { DepartmentStats } from '../types';
interface DepartmentStatsCardsProps {
@@ -31,7 +31,7 @@ export function DepartmentStatsCards({
{
label: '二级部门',
value: stats.level2,
- icon: ,
+ icon: ,
color: 'text-green-600 dark:text-green-400',
bg: 'bg-green-50 dark:bg-green-950',
},
diff --git a/crop-x/src/app/(app)/central-config/user/department/page.tsx b/crop-x/src/app/(app)/central-config/user/department/page.tsx
index bac7a70..7373c9b 100644
--- a/crop-x/src/app/(app)/central-config/user/department/page.tsx
+++ b/crop-x/src/app/(app)/central-config/user/department/page.tsx
@@ -171,21 +171,33 @@ export default function DepartmentManagementPage() {
}
};
- // 统计部门数量
+ // 统计部门数量(递归计算所有层级)
const countDepartments = (depts: Department[]): { level1: number; level2: number; total: number } => {
let level1 = 0;
let level2 = 0;
+ let total = 0;
- depts.forEach(dept => {
- if (!dept.parentId) {
- level1++;
- if (dept.children) {
- level2 += dept.children.length;
+ const countRecursive = (departments: Department[]) => {
+ departments.forEach(dept => {
+ total++; // 每个部门都计入总数
+
+ // 根据level字段统计各级部门
+ if (dept.level === 1) {
+ level1++; // 统计一级部门
+ } else if (dept.level === 2) {
+ level2++; // 统计二级部门
}
- }
- });
- return { level1, level2, total: level1 + level2 };
+ // 递归计算子部门
+ if (dept.children && dept.children.length > 0) {
+ countRecursive(dept.children);
+ }
+ });
+ };
+
+ countRecursive(depts);
+
+ return { level1, level2, total };
};
const stats = countDepartments(state.departments);
diff --git a/crop-x/src/components/auth/AuthContext.tsx b/crop-x/src/components/auth/AuthContext.tsx
index 261b9e5..7c9b84c 100644
--- a/crop-x/src/components/auth/AuthContext.tsx
+++ b/crop-x/src/components/auth/AuthContext.tsx
@@ -2,6 +2,7 @@
import React, { createContext, useContext, useState, ReactNode, useRef } from 'react';
import { getCurrentUserInfoApiV1AuthMeGet, refreshTokenApiV1AuthRefreshPost } from '@/lib/api/sdk.gen';
+import { setAuthUser, getAuthUser, AuthUser } from '@/stores/modules/auth';
// Cookie 操作工具
const setTokenCookie = (token: string) => {
@@ -216,7 +217,11 @@ export function AuthProvider({ children }: AuthProviderProps) {
...response.data, // 合并最新的用户信息
};
setUser(updatedUserData);
+
+ // 存储到 Zustand store
+ setAuthUser(response.data);
console.log('✅ 用户验证成功,最新用户信息:', response.data);
+ console.log('📦 从 Zustand store 取出的用户数据:', getAuthUser());
// 验证成功后,启动 token 自动刷新定时器
startTokenRefresh();
diff --git a/crop-x/src/stores/modules/auth.ts b/crop-x/src/stores/modules/auth.ts
index 1d6e6a3..621abee 100644
--- a/crop-x/src/stores/modules/auth.ts
+++ b/crop-x/src/stores/modules/auth.ts
@@ -1,150 +1,86 @@
-import { create } from 'zustand'
-import { persist } from 'zustand/middleware'
-import { User, LoginRequest, PhoneLoginRequest, LoginResponse } from '@api/modules/auth'
-import { authApi } from '@api'
+import { create } from 'zustand';
+import { getCurrentUserInfoApiV1AuthMeGet } from '@/lib/api/sdk.gen';
-interface AuthState {
- user: User | null
- token: string | null
- refreshToken: string | null
- isAuthenticated: boolean
- isLoading: boolean
- error: string | null
+// Auth user interface definition
+export interface AuthUser {
+ email: string;
+ username: string;
+ full_name: string | null;
+ phone: string;
+ id: string;
+ tenant_id: string;
+ is_active: boolean;
+ is_superuser: boolean;
+ is_verified: boolean;
+ created_at: string;
+ updated_at: string;
+ last_login_at: string;
+ avatar_url: string | null;
+ bio: string | null;
+ display_name: string;
+ department_id: string | null;
+ department_name: string | null;
}
-interface AuthActions {
- login: (credentials: LoginRequest) => Promise
- loginByPhone: (credentials: PhoneLoginRequest) => Promise
- logout: () => void
- refreshToken: () => Promise
- getUserInfo: () => Promise
- updateUserInfo: (data: Partial) => Promise
- clearError: () => void
- setLoading: (loading: boolean) => void
+// Auth state interface
+export interface AuthState {
+ user: AuthUser | null;
+ setAuthUser: (user: AuthUser | null) => void;
+ getAuthUser: () => AuthUser | null;
}
-export const useAuthStore = create()(
- persist(
- (set, get) => ({
- // State
- user: null,
- token: null,
- refreshToken: null,
- isAuthenticated: false,
- isLoading: false,
- error: null,
+// Create auth store
+export const useAuthStore = create((set, get) => ({
+ user: null,
- // Actions
- login: async (credentials) => {
- try {
- set({ isLoading: true, error: null })
- const response = await authApi.login(credentials)
- const { token, refreshToken, user } = response.data
+ setAuthUser: (user: AuthUser | null) => {
+ set({ user });
+ },
- localStorage.setItem('token', token)
- localStorage.setItem('refreshToken', refreshToken)
+ getAuthUser: () => {
+ return get().user;
+ },
+}));
- set({
- user,
- token,
- refreshToken,
- isAuthenticated: true,
- isLoading: false
- })
- } catch (error) {
- set({
- error: error instanceof Error ? error.message : '登录失败',
- isLoading: false
- })
- }
+// Export functions for direct usage
+export const setAuthUser = (user: AuthUser | null) => {
+ useAuthStore.getState().setAuthUser(user);
+};
+
+export const getAuthUser = (): AuthUser | null => {
+ return useAuthStore.getState().getAuthUser();
+};
+
+// Validate and update user info from API
+export const validateAndUpdateUser = async (token: string): Promise => {
+ try {
+ // 使用 SDK 调用 /api/v1/auth/me 验证用户信息
+ const response = await getCurrentUserInfoApiV1AuthMeGet({
+ headers: {
+ 'Authorization': `Bearer ${token}`,
},
+ });
- loginByPhone: async (credentials) => {
- try {
- set({ isLoading: true, error: null })
- const response = await authApi.loginByPhone(credentials)
- const { token, refreshToken, user } = response.data
+ if (response.data) {
+ // 更新用户信息(可能包含最新的权限、角色等)
+ const currentUser = getAuthUser();
+ if (currentUser) {
+ const updatedUserData = {
+ ...currentUser,
+ ...response.data, // 合并最新的用户信息
+ };
+ setAuthUser(updatedUserData);
- localStorage.setItem('token', token)
- localStorage.setItem('refreshToken', refreshToken)
-
- set({
- user,
- token,
- refreshToken,
- isAuthenticated: true,
- isLoading: false
- })
- } catch (error) {
- set({
- error: error instanceof Error ? error.message : '登录失败',
- isLoading: false
- })
- }
- },
-
- logout: () => {
- localStorage.removeItem('token')
- localStorage.removeItem('refreshToken')
- set({
- user: null,
- token: null,
- refreshToken: null,
- isAuthenticated: false,
- error: null
- })
- },
-
- refreshToken: async () => {
- try {
- const { refreshToken } = get()
- if (!refreshToken) {
- get().logout()
- return
- }
-
- const response = await authApi.refreshToken(refreshToken)
- const { token } = response.data
-
- localStorage.setItem('token', token)
- set({ token })
- } catch (error) {
- get().logout()
- }
- },
-
- getUserInfo: async () => {
- try {
- const response = await authApi.getUserInfo()
- set({ user: response.data })
- } catch (error) {
- console.error('获取用户信息失败:', error)
- }
- },
-
- updateUserInfo: async (data) => {
- try {
- const response = await authApi.updateUserInfo(data)
- set({ user: response.data })
- } catch (error) {
- set({
- error: error instanceof Error ? error.message : '更新用户信息失败'
- })
- }
- },
-
- clearError: () => set({ error: null }),
-
- setLoading: (loading) => set({ isLoading: loading })
- }),
- {
- name: 'auth-storage',
- partialize: (state) => ({
- user: state.user,
- token: state.token,
- refreshToken: state.refreshToken,
- isAuthenticated: state.isAuthenticated
- })
+ // 打印存储后的用户数据,用于调试
+ console.log('从Zustand取出的用户数据:', getAuthUser());
+ }
+ return true;
}
- )
-)
\ No newline at end of file
+ return false;
+ } catch (error) {
+ console.error('Failed to validate user info:', error);
+ return false;
+ }
+};
+
+export default useAuthStore;
\ No newline at end of file