Skip to content

Commit abe6aed

Browse files
committed
feat: 聚合router相关的
1 parent 9b2fed8 commit abe6aed

File tree

6 files changed

+195
-34
lines changed

6 files changed

+195
-34
lines changed

src/features/router/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,12 @@ export * from './router';
22

33
export * from './router-context';
44

5+
export * from './routerHooks';
6+
57
export * from './RouterProvider';
68

9+
export * from './routeStore';
10+
711
export * from './useRoute';
812

913
export * from './useRouter';

src/features/router/routeStore.ts

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import type { PayloadAction } from '@reduxjs/toolkit';
2+
3+
import { createAppSlice } from '../../store/createAppSlice';
4+
5+
interface InitialStateType {
6+
cacheRoutes: string[];
7+
removeCacheKey: string | null;
8+
routeHomePath: string;
9+
}
10+
11+
const initialState: InitialStateType = {
12+
/** - 需要进行缓存的页面 */
13+
cacheRoutes: [],
14+
/** - 需要删除的缓存页面 */
15+
removeCacheKey: null,
16+
/** - 首页路由 */
17+
routeHomePath: import.meta.env.VITE_ROUTE_HOME
18+
};
19+
20+
export const routeSlice = createAppSlice({
21+
initialState,
22+
name: 'route',
23+
reducers: create => ({
24+
addCacheRoutes: create.reducer((state, { payload }: PayloadAction<string>) => {
25+
state.cacheRoutes.push(payload);
26+
}),
27+
resetRouteStore: create.reducer(() => initialState),
28+
setCacheRoutes: create.reducer((state, { payload }: PayloadAction<string[]>) => {
29+
state.cacheRoutes = payload;
30+
}),
31+
setRemoveCacheKey: create.reducer((state, { payload }: PayloadAction<string | null>) => {
32+
state.removeCacheKey = payload;
33+
})
34+
}),
35+
selectors: {
36+
selectCacheRoutes: route => route.cacheRoutes,
37+
selectRemoveCacheKey: route => route.removeCacheKey,
38+
selectRouteHomePath: route => route.routeHomePath
39+
}
40+
});
41+
42+
export const { addCacheRoutes, resetRouteStore, setCacheRoutes, setRemoveCacheKey } = routeSlice.actions;
43+
44+
export const { selectCacheRoutes, selectRemoveCacheKey, selectRouteHomePath } = routeSlice.selectors;

src/features/router/router.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
1-
import type { RouterNavigateOptions, To } from 'react-router-dom';
1+
import type { RouteObject, RouterNavigateOptions, To } from 'react-router-dom';
2+
import { createBrowserRouter } from 'react-router-dom';
23

3-
import { router as reactRouter } from '@/router';
4+
import { routes } from '@/router';
45

56
export function navigator() {
6-
async function navigate(path: To | null, options?: RouterNavigateOptions) {
7+
const reactRouter = createBrowserRouter(routes, { basename: import.meta.env.VITE_BASE_URL });
8+
9+
async function navigate(path: To | null, options?: RouterNavigateOptions) {
710
reactRouter.navigate(path, options);
811
}
912

@@ -35,7 +38,12 @@ export function navigator() {
3538
reactRouter.navigate('/');
3639
}
3740

41+
function addRoutes(newRoutes: RouteObject[], parent: string | null = null) {
42+
reactRouter.patchRoutes(parent, newRoutes);
43+
}
44+
3845
return {
46+
addRoutes,
3947
back,
4048
forward,
4149
go,

src/features/router/routerHooks.ts

Lines changed: 38 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,45 @@
1-
import type { RouteObject } from 'react-router';
2-
3-
import { routes as allRoutes } from '@/router';
4-
import { setCacheRoutes } from '@/store/slice/route';
5-
6-
function filterCacheRoutes(routes: RouteObject[]) {
7-
const cacheRoutes: string[] = [];
8-
9-
for (const route of routes) {
10-
const { children, handle, path } = route;
11-
// 如果节点存在 path(注意:这里假设空字符串或 undefined 均视为无 path)
12-
if (path) {
13-
if (handle?.keepAlive) {
14-
cacheRoutes.push(path);
15-
}
16-
17-
if (children && children.length) {
18-
cacheRoutes.push(...filterCacheRoutes(children));
1+
import { emitter } from '@sa/hooks';
2+
3+
import { authRoutes } from '@/router';
4+
5+
import type { RouterContextType } from './router';
6+
import { filterAuthRoutesByRoles, mergeValuesByParent } from './shared';
7+
8+
export function useInitAuthRoutes() {
9+
const authRouteMode = import.meta.env.VITE_AUTH_ROUTE_MODE;
10+
11+
const reactAuthRoutes = mergeValuesByParent(authRoutes).reverse();
12+
13+
function initAuthRoutes(isStaticSuper: boolean, roles: string[], addRoutes: RouterContextType['addRoutes']) {
14+
// 静态模式
15+
if (authRouteMode === 'static') {
16+
// 超级管理员
17+
if (isStaticSuper) {
18+
reactAuthRoutes.forEach(route => {
19+
emitter.emit('ADD_MENUS', route.route);
20+
addRoutes(route.route, route.parent);
21+
});
22+
} else {
23+
// 非超级管理员
24+
const filteredRoutes = filterAuthRoutesByRoles(reactAuthRoutes, roles);
25+
filteredRoutes.forEach((route, index) => {
26+
emitter.emit('ADD_MENUS', route);
27+
addRoutes(route, reactAuthRoutes[index].parent);
28+
});
1929
}
20-
} else if (children && children.length) {
21-
// 如果当前节点没有 path,但有 children,则递归处理 children,
22-
cacheRoutes.push(...filterCacheRoutes(children));
23-
// 如果既没有 path 也没有 children,则该节点直接被过滤掉
30+
} else {
31+
// 动态模式
32+
// await dispatch(initDynamicAuthRoute());
2433
}
25-
}
2634

27-
return cacheRoutes;
28-
}
35+
// const routeHomeName = getRouteHome(getState());
2936

30-
export function useCacheRoutes() {
31-
const dispatch = useAppDispatch();
37+
// const homeRoute = router.getRouteByName(routeHomeName);
3238

33-
const cacheRoutes = filterCacheRoutes(allRoutes);
39+
// if (homeRoute) dispatch(initHomeTab({ homeRouteName: routeHomeName as LastLevelRouteKey, route: homeRoute }));
40+
}
3441

35-
useEffect(() => {
36-
dispatch(setCacheRoutes(cacheRoutes));
37-
}, [cacheRoutes]);
42+
return {
43+
initAuthRoutes
44+
};
3845
}

src/features/router/shared.ts

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
import type { RouteObject } from 'react-router';
2+
3+
import type { AuthRoute } from '@/router/routes';
4+
5+
export function filterCacheRoutes(routes: RouteObject[]) {
6+
const cacheRoutes: string[] = [];
7+
8+
for (const route of routes) {
9+
const { children, handle, path } = route;
10+
// 如果节点存在 path(注意:这里假设空字符串或 undefined 均视为无 path)
11+
if (path) {
12+
if (handle?.keepAlive) {
13+
cacheRoutes.push(path);
14+
}
15+
16+
if (children && children.length) {
17+
cacheRoutes.push(...filterCacheRoutes(children));
18+
}
19+
} else if (children && children.length) {
20+
// 如果当前节点没有 path,但有 children,则递归处理 children,
21+
cacheRoutes.push(...filterCacheRoutes(children));
22+
// 如果既没有 path 也没有 children,则该节点直接被过滤掉
23+
}
24+
}
25+
26+
return cacheRoutes;
27+
}
28+
29+
/**
30+
* Merge routes by parent
31+
*
32+
* @param data Auth routes
33+
* @returns Merged routes
34+
*/
35+
export function mergeValuesByParent(data: AuthRoute[]) {
36+
const merged: Record<string, { parent: string | null; route: RouteObject[] }> = {};
37+
data.forEach(item => {
38+
// 使用一个变量作为 key,若 parent 为 null,则转换为字符串 "null"
39+
const key = item.parent === null ? 'null' : item.parent;
40+
if (!merged[key]) {
41+
merged[key] = {
42+
parent: item.parent, // 保持原始 parent 值,包括 null
43+
route: []
44+
};
45+
}
46+
merged[key].route.push(item.route);
47+
});
48+
return Object.values(merged);
49+
}
50+
51+
/**
52+
* Filter auth routes by roles
53+
*
54+
* @param routes Auth routes
55+
* @param roles Roles
56+
*/
57+
export function filterAuthRoutesByRoles(routes: { parent: string | null; route: RouteObject[] }[], roles: string[]) {
58+
return routes.flatMap(route => {
59+
const filteredRoutes = route.route.map(item => filterAuthRouteByRoles(item, roles));
60+
61+
return filteredRoutes;
62+
});
63+
}
64+
65+
/**
66+
* Filter auth route by roles
67+
*
68+
* @param route Auth route
69+
* @param roles Roles
70+
*/
71+
function filterAuthRouteByRoles(route: RouteObject, roles: string[]) {
72+
const routeRoles: string[] = (route.handle && route.handle.roles) || [];
73+
74+
// if the route's "roles" is empty, then it is allowed to access
75+
const isEmptyRoles = !routeRoles.length;
76+
77+
// if the user's role is included in the route's "roles", then it is allowed to access
78+
const hasPermission = routeRoles.some(role => roles.includes(role));
79+
80+
const filterRoute = { ...route };
81+
82+
if (filterRoute.children?.length) {
83+
filterRoute.children = filterRoute.children.flatMap(item => filterAuthRouteByRoles(item, roles));
84+
}
85+
86+
return hasPermission || isEmptyRoles ? [filterRoute] : [];
87+
}

src/features/router/useRoureStore.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { selectRouteHomePath } from './routeStore';
2+
3+
export function useGetRouteHomePath() {
4+
return useAppSelector(selectRouteHomePath);
5+
}
6+
7+
export function useInitAuthRoutes() {
8+
const routeHomePath = useGetRouteHomePath();
9+
10+
const { authRoutes } = useGetRoutes();
11+
}

0 commit comments

Comments
 (0)