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