refactor(frontend): 重构状态管理使用Zustand替代Context API

feat(auth): 添加账户锁定功能及自动解锁机制
feat(user): 在用户模型中添加lockedUntil字段
feat(api): 实现账户锁定逻辑和剩余尝试次数提示

perf(3d): 优化3D场景状态管理性能
perf(floorplan): 优化平面图状态管理性能

chore(deps): 添加zustand依赖
chore(config): 更新安全配置锁定时间为3分钟

docs: 更新部分组件注释
style: 调整登录页面样式
This commit is contained in:
zhang1106
2026-05-08 11:17:29 +08:00
parent 03e320af9e
commit 2826a00192
33 changed files with 2026 additions and 1442 deletions
@@ -1,12 +1,39 @@
import { useContext } from 'react';
import FloorPlanContext from '../../context/FloorPlanContext';
import { useFloorPlanStore } from '../../stores/floorPlanStore';
const useFloorPlanContext = () => {
const context = useContext(FloorPlanContext);
if (!context) {
throw new Error('useFloorPlanContext 必须在 FloorPlanProvider 内使用');
}
return context;
const selectedRoomId = useFloorPlanStore((s) => s.selectedRoomId);
const selectedRack = useFloorPlanStore((s) => s.selectedRack);
const hoveredRack = useFloorPlanStore((s) => s.hoveredRack);
const zoom = useFloorPlanStore((s) => s.zoom);
const offsetX = useFloorPlanStore((s) => s.offsetX);
const offsetY = useFloorPlanStore((s) => s.offsetY);
const detailRack = useFloorPlanStore((s) => s.detailRack);
const detailVisible = useFloorPlanStore((s) => s.detailVisible);
const setSelectedRoom = useFloorPlanStore((s) => s.setSelectedRoom);
const setSelectedRack = useFloorPlanStore((s) => s.setSelectedRack);
const setHoveredRack = useFloorPlanStore((s) => s.setHoveredRack);
const setViewChange = useFloorPlanStore((s) => s.setViewChange);
const showDetail = useFloorPlanStore((s) => s.showDetail);
const hideDetail = useFloorPlanStore((s) => s.hideDetail);
const reset = useFloorPlanStore((s) => s.reset);
return {
selectedRoomId,
selectedRack,
hoveredRack,
zoom,
offsetX,
offsetY,
detailRack,
detailVisible,
setSelectedRoom,
setSelectedRack,
setHoveredRack,
setViewChange,
showDetail,
hideDetail,
reset,
};
};
export default useFloorPlanContext;
export default useFloorPlanContext;
+42
View File
@@ -0,0 +1,42 @@
/**
* 认证状态 Hook
* 直接使用 Zustand Store 管理认证状态
*/
import { useEffect } from 'react';
import { useAuthStore } from '../stores/authStore';
export const useAuth = () => {
const user = useAuthStore((s) => s.user);
const token = useAuthStore((s) => s.token);
const loading = useAuthStore((s) => s.loading);
const initialized = useAuthStore((s) => s.initialized);
const login = useAuthStore((s) => s.login);
const register = useAuthStore((s) => s.register);
const logout = useAuthStore((s) => s.logout);
const updateUser = useAuthStore((s) => s.updateUser);
const hasPermission = useAuthStore((s) => s.hasPermission);
const checkAdmin = useAuthStore((s) => s.checkAdmin);
const initialize = useAuthStore((s) => s.initialize);
useEffect(() => {
if (!initialized) {
initialize();
}
}, [initialized, initialize]);
return {
user,
token,
loading,
initialized,
login,
register,
logout,
updateUser,
hasPermission,
checkAdmin,
};
};
export default useAuth;
+25
View File
@@ -0,0 +1,25 @@
/**
* 系统配置 Hook
* 直接使用 Zustand Store 管理系统配置
*/
import { useEffect } from 'react';
import { useConfigStore } from '../stores/configStore';
export const useConfig = () => {
const config = useConfigStore((s) => s.config);
const loading = useConfigStore((s) => s.loading);
const updateConfig = useConfigStore((s) => s.updateConfig);
const reloadConfig = useConfigStore((s) => s.reloadConfig);
const loadConfig = useConfigStore((s) => s.loadConfig);
useEffect(() => {
if (loading) {
loadConfig();
}
}, [loading, loadConfig]);
return { config, loading, updateConfig, reloadConfig };
};
export default useConfig;
+5 -9
View File
@@ -1,5 +1,5 @@
import { useMemo } from 'react';
import { useConfig } from '../context/ConfigContext';
import { useConfigStore } from '../stores/configStore';
/**
* 使用设计令牌 Hook
@@ -7,12 +7,10 @@ import { useConfig } from '../context/ConfigContext';
* @returns {Object} 设计令牌对象
*/
export const useDesignTokens = () => {
const { config } = useConfig();
const primaryColor = useConfigStore((s) => s.config.primary_color) || '#667eea';
const secondaryColor = useConfigStore((s) => s.config.secondary_color) || '#764ba2';
const designTokens = useMemo(() => {
const primaryColor = config?.primary_color || '#667eea';
const secondaryColor = config?.secondary_color || '#764ba2';
return {
colors: {
primary: {
@@ -61,7 +59,7 @@ export const useDesignTokens = () => {
lg: '24px',
},
};
}, [config?.primary_color, config?.secondary_color]);
}, [primaryColor, secondaryColor]);
return designTokens;
};
@@ -72,10 +70,8 @@ export const useDesignTokens = () => {
* @returns {string} RGB字符串 (如: "102, 126, 234")
*/
function hexToRgb(hex) {
// 移除 # 号
const cleanHex = hex.replace('#', '');
// 处理简写格式 (如: #fff)
const fullHex =
cleanHex.length === 3
? cleanHex
@@ -91,4 +87,4 @@ function hexToRgb(hex) {
return `${r}, ${g}, ${b}`;
}
export default useDesignTokens;
export default useDesignTokens;
+12
View File
@@ -0,0 +1,12 @@
/**
* 平面图 Hook
* 直接使用 Zustand Store 管理平面图状态
*/
import { useFloorPlanStore } from '../stores/floorPlanStore';
export const useFloorPlan = () => {
return useFloorPlanStore();
};
export default useFloorPlan;
+12
View File
@@ -0,0 +1,12 @@
/**
* 3D场景 Hook
* 直接使用 Zustand Store 管理3D场景状态
*/
import { useScene3DStore } from '../stores/scene3DStore';
export const useScene3D = () => {
return useScene3DStore();
};
export default useScene3D;