From 6194222289494a5894e879f449f665dd30e103a1 Mon Sep 17 00:00:00 2001 From: zhaoying Date: Mon, 2 Feb 2026 16:25:17 +0800 Subject: [PATCH] docs: add comments to the src/hooks directory --- web/src/hooks/useBreadcrumbManager.ts | 94 +++++++++++++++-------- web/src/hooks/useNavigationBreadcrumbs.ts | 41 +++++++--- web/src/hooks/useRouteGuard.ts | 53 +++++++++---- 3 files changed, 130 insertions(+), 58 deletions(-) diff --git a/web/src/hooks/useBreadcrumbManager.ts b/web/src/hooks/useBreadcrumbManager.ts index 54095925..161fbb65 100644 --- a/web/src/hooks/useBreadcrumbManager.ts +++ b/web/src/hooks/useBreadcrumbManager.ts @@ -1,25 +1,52 @@ +/* + * @Author: ZhaoYing + * @Date: 2026-02-02 16:24:44 + * @Last Modified by: ZhaoYing + * @Last Modified time: 2026-02-02 16:24:44 + */ +/** + * useBreadcrumbManager Hook + * + * Manages breadcrumb navigation for knowledge base pages with: + * - Dynamic breadcrumb generation based on folder/document paths + * - Separate breadcrumb handling for list and detail views + * - Click handlers for navigation between folders + * - Support for custom callbacks + * + * @hook + */ + import { useCallback } from 'react'; import { useNavigate } from 'react-router-dom'; import { useMenu } from '@/store/menu'; import type { MenuItem } from '@/store/menu'; +/** Breadcrumb item interface */ export interface BreadcrumbItem { id: string; name: string; type?: 'knowledgeBase' | 'folder' | 'document'; } +/** Breadcrumb path structure */ export interface BreadcrumbPath { - knowledgeBaseFolderPath: BreadcrumbItem[]; // 知识库文件夹路径 - knowledgeBase?: BreadcrumbItem; // 知识库信息 - documentFolderPath: BreadcrumbItem[]; // 文档文件夹路径 - document?: BreadcrumbItem; // 文档信息 + /** Knowledge base folder path */ + knowledgeBaseFolderPath: BreadcrumbItem[]; + /** Knowledge base information */ + knowledgeBase?: BreadcrumbItem; + /** Document folder path */ + documentFolderPath: BreadcrumbItem[]; + /** Document information */ + document?: BreadcrumbItem; } +/** Options for breadcrumb manager */ export interface BreadcrumbOptions { + /** Callback when knowledge base menu is clicked */ onKnowledgeBaseMenuClick?: () => void; + /** Callback when knowledge base folder is clicked */ onKnowledgeBaseFolderClick?: (folderId: string, folderPath: BreadcrumbItem[]) => void; - // 新增:区分面包屑类型 + /** Breadcrumb type: list or detail view */ breadcrumbType?: 'list' | 'detail'; } @@ -27,14 +54,15 @@ export const useBreadcrumbManager = (options?: BreadcrumbOptions) => { const { allBreadcrumbs, setCustomBreadcrumbs } = useMenu(); const navigate = useNavigate(); + /** Update breadcrumbs based on current path and type */ const updateBreadcrumbs = useCallback((breadcrumbPath: BreadcrumbPath) => { const breadcrumbType = options?.breadcrumbType || 'list'; - // 对于详情页面,直接使用固定的知识库管理面包屑,不依赖可能被污染的 allBreadcrumbs + /** For detail pages, use fixed knowledge base breadcrumb */ let baseBreadcrumbs: MenuItem[] = []; if (breadcrumbType === 'detail') { - // 详情页面:始终使用固定的知识库管理面包屑 + /** Detail page: always use fixed knowledge base management breadcrumb */ baseBreadcrumbs = [ { id: 6, @@ -61,14 +89,14 @@ export const useBreadcrumbManager = (options?: BreadcrumbOptions) => { } ]; } else { - // 列表页面:从 space 获取基础面包屑,但确保包含知识库管理 + /** List page: get base breadcrumbs from space, ensure knowledge base management is included */ const spaceBreadcrumbs = allBreadcrumbs['space'] || []; const knowledgeBaseMenuIndex = spaceBreadcrumbs.findIndex(item => item.path === '/knowledge-base'); if (knowledgeBaseMenuIndex >= 0) { baseBreadcrumbs = spaceBreadcrumbs.slice(0, knowledgeBaseMenuIndex + 1); } else { - // 如果没有找到知识库菜单,使用默认的知识库管理面包屑 + /** If knowledge base menu not found, use default knowledge base management breadcrumb */ baseBreadcrumbs = [ { id: 6, @@ -99,7 +127,7 @@ export const useBreadcrumbManager = (options?: BreadcrumbOptions) => { const filteredBaseBreadcrumbs = baseBreadcrumbs; - // 给"知识库管理"添加点击事件 + /** Add click event to "Knowledge Base Management" */ const breadcrumbsWithClick = filteredBaseBreadcrumbs.map((item) => { if (item.path === '/knowledge-base') { return { @@ -109,10 +137,10 @@ export const useBreadcrumbManager = (options?: BreadcrumbOptions) => { e?.stopPropagation(); if (options?.onKnowledgeBaseMenuClick) { - // 如果提供了回调函数,执行回调 + /** If callback provided, execute callback */ options.onKnowledgeBaseMenuClick(); } else if (breadcrumbType === 'detail') { - // 知识库详情页面:没有回调函数时,返回到知识库列表页面 + /** Knowledge base detail page: return to knowledge base list page when no callback */ navigate('/knowledge-base', { state: { resetToRoot: true, @@ -129,7 +157,7 @@ export const useBreadcrumbManager = (options?: BreadcrumbOptions) => { let customBreadcrumbs: MenuItem[] = [...breadcrumbsWithClick]; if (breadcrumbType === 'list') { - // 知识库列表页面:只显示知识库文件夹路径 + /** Knowledge base list page: only show knowledge base folder path */ customBreadcrumbs = [ ...breadcrumbsWithClick, ...breadcrumbPath.knowledgeBaseFolderPath.map((folder, index) => ({ @@ -158,11 +186,11 @@ export const useBreadcrumbManager = (options?: BreadcrumbOptions) => { e?.preventDefault(); e?.stopPropagation(); - // 如果有回调函数,直接调用回调函数来更新状态 + /** If callback provided, call callback to update state */ if (options?.onKnowledgeBaseFolderClick) { options.onKnowledgeBaseFolderClick(folder.id, breadcrumbPath.knowledgeBaseFolderPath.slice(0, index + 1)); } else { - // 否则使用导航(兜底逻辑) + /** Otherwise use navigation (fallback logic) */ navigate('/knowledge-base', { state: { navigateToFolder: folder.id, @@ -175,11 +203,11 @@ export const useBreadcrumbManager = (options?: BreadcrumbOptions) => { })), ]; } else { - // 知识库详情页面:显示知识库名称 + 文档文件夹路径 + 文档名称 + /** Knowledge base detail page: show knowledge base name + document folder path + document name */ customBreadcrumbs = [ ...breadcrumbsWithClick, - // 添加知识库名称 + /** Add knowledge base name */ ...(breadcrumbPath.knowledgeBase ? [{ id: 0, parent: 0, @@ -205,27 +233,27 @@ export const useBreadcrumbManager = (options?: BreadcrumbOptions) => { onClick: (e?: React.MouseEvent) => { e?.preventDefault(); e?.stopPropagation(); - // 返回到知识库详情页的根目录 + /** Return to knowledge base detail page root directory */ const navigationState = { fromKnowledgeBaseList: true, knowledgeBaseFolderPath: breadcrumbPath.knowledgeBaseFolderPath, - resetToRoot: true, // 添加重置到根目录的标志 - refresh: true, // 添加刷新标志 - timestamp: Date.now(), // 添加时间戳确保状态变化 + resetToRoot: true, /** Add flag to reset to root directory */ + refresh: true, /** Add refresh flag */ + timestamp: Date.now(), /** Add timestamp to ensure state change */ }; - // 使用当前页面路径进行导航,避免不必要的路由变化 + /** Use current page path for navigation to avoid unnecessary route changes */ const currentPath = window.location.pathname; const targetPath = `/knowledge-base/${breadcrumbPath.knowledgeBase!.id}/private`; if (currentPath === targetPath) { - // 如果已经在目标页面,直接更新状态而不导航 + /** If already on target page, update state directly without navigation */ navigate(targetPath, { state: navigationState, - replace: true // 使用 replace 避免历史记录堆积 + replace: true /** Use replace to avoid history stack buildup */ }); } else { - // 如果不在目标页面,正常导航 + /** If not on target page, navigate normally */ navigate(targetPath, { state: navigationState }); @@ -234,7 +262,7 @@ export const useBreadcrumbManager = (options?: BreadcrumbOptions) => { }, }] : []), - // 添加文档文件夹路径 + /** Add document folder path */ ...breadcrumbPath.documentFolderPath.map((folder, index) => ({ id: 0, parent: 0, @@ -260,24 +288,24 @@ export const useBreadcrumbManager = (options?: BreadcrumbOptions) => { onClick: (e?: React.MouseEvent) => { e?.preventDefault(); e?.stopPropagation(); - // 返回到知识库详情页的对应文件夹 + /** Return to corresponding folder in knowledge base detail page */ const navigationState = { fromKnowledgeBaseList: true, knowledgeBaseFolderPath: breadcrumbPath.knowledgeBaseFolderPath, navigateToDocumentFolder: folder.id, documentFolderPath: breadcrumbPath.documentFolderPath.slice(0, index + 1), - refresh: true, // 添加刷新标志 - timestamp: Date.now(), // 添加时间戳确保状态变化 + refresh: true, /** Add refresh flag */ + timestamp: Date.now(), /** Add timestamp to ensure state change */ }; navigate(`/knowledge-base/${breadcrumbPath.knowledgeBase!.id}/private`, { state: navigationState, - replace: true // 使用 replace 避免历史记录堆积 + replace: true /** Use replace to avoid history stack buildup */ }); return false; }, })), - // 添加文档名称(如果存在) + /** Add document name (if exists) */ ...(breadcrumbPath.document ? [{ id: 0, parent: 0, @@ -300,12 +328,12 @@ export const useBreadcrumbManager = (options?: BreadcrumbOptions) => { disposable: false, appSystem: null, subs: [], - // 文档名称不可点击 + /** Document name is not clickable */ }] : []), ]; } - // 根据面包屑类型使用不同的键,实现独立的面包屑路径 + /** Use different keys based on breadcrumb type to implement independent breadcrumb paths */ const breadcrumbKey = breadcrumbType === 'list' ? 'space' : 'space-detail'; diff --git a/web/src/hooks/useNavigationBreadcrumbs.ts b/web/src/hooks/useNavigationBreadcrumbs.ts index c314399b..2b9cd06a 100644 --- a/web/src/hooks/useNavigationBreadcrumbs.ts +++ b/web/src/hooks/useNavigationBreadcrumbs.ts @@ -1,7 +1,30 @@ +/* + * @Author: ZhaoYing + * @Date: 2026-02-02 16:24:49 + * @Last Modified by: ZhaoYing + * @Last Modified time: 2026-02-02 16:24:49 + */ +/** + * useNavigationBreadcrumbs Hook + * + * Automatically updates breadcrumbs based on current route: + * - Matches current path against menu structure + * - Supports dynamic routes with parameters + * - Handles nested menu hierarchies + * - Updates breadcrumbs on route changes + * + * @hook + */ + import { useEffect } from 'react'; import { useLocation } from 'react-router-dom'; import { useMenu } from '@/store/menu'; +/** + * Hook to automatically update breadcrumbs based on navigation. + * + * @param source - Menu source type ('space' or 'manage') + */ export const useNavigationBreadcrumbs = (source: 'space' | 'manage' = 'manage') => { const location = useLocation(); const { allMenus, updateBreadcrumbs } = useMenu(); @@ -10,26 +33,26 @@ export const useNavigationBreadcrumbs = (source: 'space' | 'manage' = 'manage') const currentPath = location.pathname; const menus = allMenus[source] || []; - // 查找匹配的菜单项并构建keyPath + /** Find matching menu item and build key path */ const findMenuKeyPath = (menuList: any[], parentKeys: string[] = []): string[] | null => { let bestMatch: { path: string; parentId?: string; score: number } | null = null; for (const menu of menuList) { - // 检查子菜单 + /** Check submenus */ if (menu.subs && menu.subs.length > 0) { const menuPath = menu.path ? (menu.path[0] !== '/' ? '/' + menu.path : menu.path) : ''; for (const sub of menu.subs) { if (sub.path) { const subPath = sub.path[0] !== '/' ? '/' + sub.path : sub.path; - // 精确匹配优先 + /** Exact match has priority */ if (subPath === currentPath) { return [sub.path, `${menu.id}`]; } console.log('menuPath', menuPath) - // 动态路由匹配 + /** Dynamic route matching */ if (subPath.includes(':')) { - // 检查是否在父菜单下 + /** Check if under parent menu */ if (menuPath && currentPath.startsWith(menuPath + '/')) { const relativePath = currentPath.replace(menuPath, ''); const pathSegments = subPath.split('/'); @@ -42,7 +65,7 @@ export const useNavigationBreadcrumbs = (source: 'space' | 'manage' = 'manage') } } } - // 直接匹配子菜单路径 + /** Direct match submenu path */ const pathSegments = subPath.split('/'); const currentSegments = currentPath.split('/'); if (pathSegments.length === currentSegments.length) { @@ -57,14 +80,14 @@ export const useNavigationBreadcrumbs = (source: 'space' | 'manage' = 'manage') } } - // 检查主菜单 + /** Check main menu */ if (menu.path) { const menuPath = menu.path[0] !== '/' ? '/' + menu.path : menu.path; - // 精确匹配优先 + /** Exact match has priority */ if (menuPath === currentPath) { return [menu.path, ...parentKeys].reverse(); } - // 动态路由匹配 + /** Dynamic route matching */ if (menuPath.includes(':')) { const pathSegments = menuPath.split('/'); const currentSegments = currentPath.split('/'); diff --git a/web/src/hooks/useRouteGuard.ts b/web/src/hooks/useRouteGuard.ts index 0bf6d485..900fab2c 100644 --- a/web/src/hooks/useRouteGuard.ts +++ b/web/src/hooks/useRouteGuard.ts @@ -1,27 +1,44 @@ +/* + * @Author: ZhaoYing + * @Date: 2026-02-02 16:24:54 + * @Last Modified by: ZhaoYing + * @Last Modified time: 2026-02-02 16:24:54 + */ +/** + * useRouteGuard Hook + * + * Provides route authentication and permission checking: + * - Validates user authentication status + * - Checks route permissions against menu structure + * - Redirects unauthorized users + * - Monitors route changes + * + * @hook + */ + import { useEffect } from 'react'; import { useNavigate, useLocation } from 'react-router-dom'; import { useMenu, type MenuItem } from '@/store/menu' -// 模拟认证状态检查函数 +/** Check authentication status */ export const checkAuthStatus = (): boolean => { - // 在实际应用中,这里应该检查localStorage或cookie中的认证信息 - // 这里为了演示,我们假设首页不需要认证,其他页面需要认证 - return true; // 暂时返回true以便测试 + /** In production, check localStorage or cookie for auth info */ + return true; /** Temporarily return true for testing */ }; -// 递归检查路由是否存在于菜单数据中 +/** Recursively check if route exists in menu data */ export const checkRoutePermission = (menus: MenuItem[], currentPath: string): boolean => { - // 首页和知识库相关页面默认有权限 + /** Home and knowledge base pages have default permission */ if (currentPath === '/' || currentPath.includes('knowledge-detail') || currentPath.includes('knowledge-base')) { return true; } for (const menu of menus) { - // 检查当前菜单的path是否匹配 + /** Check if current menu path matches */ if (menu.path && currentPath.includes(menu.path)) { return true; } - // 递归检查子菜单 + /** Recursively check submenus */ if (menu.subs && menu.subs.length > 0) { if (checkRoutePermission(menu.subs, currentPath)) { return true; @@ -32,40 +49,44 @@ export const checkRoutePermission = (menus: MenuItem[], currentPath: string): bo return false; }; -// 路由守卫Hook,用于处理路由权限检查 +/** + * Route guard hook for handling route permission checks. + * + * @param source - Menu source type ('space' or 'manage') + */ export const useRouteGuard = (source: 'space' | 'manage') => { const navigate = useNavigate(); const location = useLocation(); const { allMenus } = useMenu(); const menus = allMenus[source]; - // 确保在路由变化时重新执行所有检查逻辑 + /** Re-execute all checks on route changes */ useEffect(() => { - // 模拟认证检查逻辑 + /** Simulate authentication check */ const isAuthenticated = checkAuthStatus(); if (!isAuthenticated && location.pathname !== '/') { - // TODO: 未认证用户重定向到登录页(这里是首页) + /** Redirect unauthenticated users to home/login page */ navigate('/', { replace: true }); return; } - // 认证通过后,检查路由权限 + /** After authentication, check route permissions */ if (isAuthenticated && location.pathname !== '/' && location.pathname !== '/not-found') { const hasPermission = checkRoutePermission(menus, location.pathname); if (!hasPermission) { - // 无权限访问该路由,重定向到无权限页面 + /** No permission, redirect to no-permission page */ // navigate('/no-permission', { replace: true }); } } }, [navigate, location.pathname, location.search, location.hash, menus]); - // 返回当前路径和权限状态,确保组件能感知到路由变化 + /** Return current path and permission status */ return { currentPath: location.pathname, search: location.search, hash: location.hash, - isChecking: false, // 可以扩展添加加载状态 + isChecking: false, /** Can be extended to add loading state */ }; };