refactor: 代码架构重构 - 提取组件、统一状态管理和数据访问层

- 新增布局组件(SidebarBrand、SidebarUser、SidebarNavItem)
- 新增通用UI组件(EmptyState、StatusBadge、TagInput、SearchBar)
- 新增全局状态管理(UserContext)
- 新增自定义Hooks(usePageState、useNavigation)
- 新增统一数据访问层(src/services/api.js)
- 新增常量配置(constants/pages.js、constants/storageKeys.js)
- 样式文件模块化,拆分页面特定样式
- 更新README文档,添加组件和使用说明
- 同步OpenSpec规范到主specs目录
This commit is contained in:
2026-03-20 10:19:31 +08:00
parent f2e0ec047e
commit 56c08a34ff
27 changed files with 1812 additions and 199 deletions

View File

@@ -0,0 +1,50 @@
import { useState, useCallback } from 'react';
/**
* 导航逻辑 Hook
* 统一处理页面导航和附加数据状态管理
*
* @param {Function} setPageCallback - 设置当前页面的回调函数
* @returns {Object} 导航操作函数
*/
function useNavigation(setPageCallback) {
const [extraData, setExtraData] = useState({});
/**
* 导航到指定页面
* @param {string} pageId - 目标页面 ID
* @param {Object} data - 附加数据(如 skillId、taskId 等)
*/
const navigateToPage = useCallback((pageId, data = {}) => {
setPageCallback(pageId);
// 如果有附加数据,更新 extraData
if (Object.keys(data).length > 0) {
setExtraData(data);
}
}, [setPageCallback]);
/**
* 设置附加数据
* @param {Object} data - 附加数据对象
*/
const setExtraDataValue = useCallback((data) => {
setExtraData(prev => ({ ...prev, ...data }));
}, []);
/**
* 清除附加数据
*/
const clearExtraData = useCallback(() => {
setExtraData({});
}, []);
return {
extraData,
navigateToPage,
setExtraData: setExtraDataValue,
clearExtraData,
};
}
export default useNavigation;

58
src/hooks/usePageState.js Normal file
View File

@@ -0,0 +1,58 @@
import { useState, useEffect } from 'react';
import { useLocation, useNavigate } from 'react-router-dom';
/**
* 页面状态持久化 Hook
* 封装页面状态管理、localStorage 同步和主页跳转重置逻辑
*
* @param {Object} options - 配置选项
* @param {string} options.storageKey - localStorage 存储键名
* @param {string} options.defaultPage - 默认页面 ID
* @param {Object} options.pageTitles - 页面标题映射对象
* @param {Function} options.getPageTitle - 自定义获取页面标题函数(可选)
* @returns {Object} 状态和操作函数
*/
function usePageState({
storageKey,
defaultPage,
pageTitles,
getPageTitle: customGetPageTitle,
}) {
const location = useLocation();
const navigate = useNavigate();
// 从 localStorage 恢复或使用默认值
const [currentPage, setCurrentPage] = useState(() => {
const saved = localStorage.getItem(`${storageKey}_currentPage`);
return saved || defaultPage;
});
// 处理主页跳转重置
useEffect(() => {
if (location.state?.fromHome) {
setCurrentPage(defaultPage);
navigate('.', { replace: true, state: {} });
}
}, [location.state, navigate, defaultPage]);
// 同步到 localStorage
useEffect(() => {
localStorage.setItem(`${storageKey}_currentPage`, currentPage);
}, [storageKey, currentPage]);
// 获取页面标题
const getPageTitle = (pageId = currentPage) => {
if (customGetPageTitle) {
return customGetPageTitle(pageId, currentPage);
}
return pageTitles[pageId] || '';
};
return {
currentPage,
setCurrentPage,
getPageTitle,
};
}
export default usePageState;