feat: Add base project structure with API and web components
This commit is contained in:
101
web/src/hooks/useNavigationBreadcrumbs.ts
Normal file
101
web/src/hooks/useNavigationBreadcrumbs.ts
Normal file
@@ -0,0 +1,101 @@
|
||||
import { useEffect } from 'react';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import { useMenu } from '@/store/menu';
|
||||
|
||||
export const useNavigationBreadcrumbs = (source: 'space' | 'manage' = 'manage') => {
|
||||
const location = useLocation();
|
||||
const { allMenus, updateBreadcrumbs } = useMenu();
|
||||
|
||||
useEffect(() => {
|
||||
const currentPath = location.pathname;
|
||||
const menus = allMenus[source] || [];
|
||||
|
||||
// 查找匹配的菜单项并构建keyPath
|
||||
const findMenuKeyPath = (menuList: any[], parentKeys: string[] = []): string[] | null => {
|
||||
let bestMatch: { path: string; parentId?: string; score: number } | null = null;
|
||||
|
||||
for (const menu of menuList) {
|
||||
// 检查子菜单
|
||||
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;
|
||||
|
||||
// 精确匹配优先
|
||||
if (subPath === currentPath) {
|
||||
return [sub.path, `${menu.id}`];
|
||||
}
|
||||
console.log('menuPath', menuPath)
|
||||
// 动态路由匹配
|
||||
if (subPath.includes(':')) {
|
||||
// 检查是否在父菜单下
|
||||
if (menuPath && currentPath.startsWith(menuPath + '/')) {
|
||||
const relativePath = currentPath.replace(menuPath, '');
|
||||
const pathSegments = subPath.split('/');
|
||||
const relativeSegments = relativePath.split('/');
|
||||
if (pathSegments.length === relativeSegments.length) {
|
||||
const pathPattern = subPath.replace(/:[\w-]+/g, '[^/]+').replace(/\[[\w-]+\]/g, '[^/]+');
|
||||
const regex = new RegExp(`^${pathPattern}$`);
|
||||
if (regex.test(relativePath)) {
|
||||
return [sub.path, `${menu.id}`];
|
||||
}
|
||||
}
|
||||
}
|
||||
// 直接匹配子菜单路径
|
||||
const pathSegments = subPath.split('/');
|
||||
const currentSegments = currentPath.split('/');
|
||||
if (pathSegments.length === currentSegments.length) {
|
||||
const pathPattern = subPath.replace(/:[\w-]+/g, '[^/]+').replace(/\[[\w-]+\]/g, '[^/]+');
|
||||
const regex = new RegExp(`^${pathPattern}$`);
|
||||
if (regex.test(currentPath)) {
|
||||
return [sub.path, `${menu.id}`];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 检查主菜单
|
||||
if (menu.path) {
|
||||
const menuPath = menu.path[0] !== '/' ? '/' + menu.path : menu.path;
|
||||
// 精确匹配优先
|
||||
if (menuPath === currentPath) {
|
||||
return [menu.path, ...parentKeys].reverse();
|
||||
}
|
||||
// 动态路由匹配
|
||||
if (menuPath.includes(':')) {
|
||||
const pathSegments = menuPath.split('/');
|
||||
const currentSegments = currentPath.split('/');
|
||||
if (pathSegments.length === currentSegments.length) {
|
||||
const pathPattern = menuPath.replace(/:[\w-]+/g, '[^/]+').replace(/\[[\w-]+\]/g, '[^/]+');
|
||||
const regex = new RegExp(`^${pathPattern}$`);
|
||||
if (regex.test(currentPath)) {
|
||||
const score = menuPath.split('/').length;
|
||||
if (!bestMatch || score > bestMatch.score) {
|
||||
bestMatch = { path: menu.path, score };
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (currentPath.startsWith(menuPath + '/')) {
|
||||
const score = menuPath.split('/').length;
|
||||
if (!bestMatch || score > bestMatch.score) {
|
||||
bestMatch = { path: menu.path, score };
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (bestMatch) {
|
||||
return bestMatch.parentId ? [bestMatch.path, bestMatch.parentId] : [bestMatch.path];
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
const keyPath = findMenuKeyPath(menus);
|
||||
if (keyPath) {
|
||||
updateBreadcrumbs(keyPath, source);
|
||||
}
|
||||
}, [location.pathname, allMenus, source, updateBreadcrumbs]);
|
||||
};
|
||||
69
web/src/hooks/useRouteGuard.ts
Normal file
69
web/src/hooks/useRouteGuard.ts
Normal file
@@ -0,0 +1,69 @@
|
||||
import { useEffect } from 'react';
|
||||
import { useNavigate, useLocation } from 'react-router-dom';
|
||||
import { useMenu, type MenuItem } from '@/store/menu'
|
||||
|
||||
// 模拟认证状态检查函数
|
||||
export const checkAuthStatus = (): boolean => {
|
||||
// 在实际应用中,这里应该检查localStorage或cookie中的认证信息
|
||||
// 这里为了演示,我们假设首页不需要认证,其他页面需要认证
|
||||
return true; // 暂时返回true以便测试
|
||||
};
|
||||
|
||||
// 递归检查路由是否存在于菜单数据中
|
||||
export const checkRoutePermission = (menus: MenuItem[], currentPath: string): boolean => {
|
||||
// 首页默认有权限
|
||||
if (currentPath === '/' || currentPath.includes('knowledge-detail')) return true;
|
||||
|
||||
for (const menu of menus) {
|
||||
// 检查当前菜单的path是否匹配
|
||||
if (menu.path && currentPath.includes(menu.path)) {
|
||||
return true;
|
||||
}
|
||||
// 递归检查子菜单
|
||||
if (menu.subs && menu.subs.length > 0) {
|
||||
if (checkRoutePermission(menu.subs, currentPath)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
// 路由守卫Hook,用于处理路由权限检查
|
||||
export const useRouteGuard = (source: 'space' | 'manage') => {
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
const { allMenus } = useMenu();
|
||||
const menus = allMenus[source];
|
||||
|
||||
// 确保在路由变化时重新执行所有检查逻辑
|
||||
useEffect(() => {
|
||||
// 模拟认证检查逻辑
|
||||
const isAuthenticated = checkAuthStatus();
|
||||
|
||||
if (!isAuthenticated && location.pathname !== '/') {
|
||||
// TODO: 未认证用户重定向到登录页(这里是首页)
|
||||
navigate('/', { replace: true });
|
||||
return;
|
||||
}
|
||||
|
||||
// 认证通过后,检查路由权限
|
||||
if (isAuthenticated && location.pathname !== '/' && location.pathname !== '/not-found') {
|
||||
const hasPermission = checkRoutePermission(menus, location.pathname);
|
||||
if (!hasPermission) {
|
||||
// 无权限访问该路由,重定向到无权限页面
|
||||
// navigate('/not-found', { replace: true });
|
||||
}
|
||||
}
|
||||
}, [navigate, location.pathname, location.search, location.hash, menus]);
|
||||
|
||||
// 返回当前路径和权限状态,确保组件能感知到路由变化
|
||||
return {
|
||||
currentPath: location.pathname,
|
||||
search: location.search,
|
||||
hash: location.hash,
|
||||
isChecking: false, // 可以扩展添加加载状态
|
||||
};
|
||||
};
|
||||
|
||||
export default useRouteGuard;
|
||||
Reference in New Issue
Block a user