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