@spacego/fe-components 0.0.1-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (113) hide show
  1. package/README.md +37 -0
  2. package/lib/assets/svg/chrome-bg-after.svg.js +4 -0
  3. package/lib/assets/svg/chrome-bg-before.svg.js +4 -0
  4. package/lib/assets/svg/icon-arrows-right.svg.js +4 -0
  5. package/lib/assets/svg/icon-bell.svg.js +4 -0
  6. package/lib/assets/svg/icon-custom.svg.js +4 -0
  7. package/lib/assets/svg/icon-sun-moon.svg.js +4 -0
  8. package/lib/assets/svg/icon-system.svg.js +4 -0
  9. package/lib/assets/svg/login-view.svg.js +4 -0
  10. package/lib/config/constants.js +10 -0
  11. package/lib/config/theme.js +8 -0
  12. package/lib/fe-layouts/auth-layout/index.js +95 -0
  13. package/lib/fe-layouts/basics-layout/components/basics-layout/header.js +102 -0
  14. package/lib/fe-layouts/basics-layout/components/basics-layout/setting-custom-color.js +34 -0
  15. package/lib/fe-layouts/basics-layout/components/basics-layout/setting-drawer.js +118 -0
  16. package/lib/fe-layouts/basics-layout/components/basics-layout/sidebar.js +60 -0
  17. package/lib/fe-layouts/basics-layout/components/basics-layout/tabs.js +60 -0
  18. package/lib/fe-layouts/basics-layout/components/utils/index.js +72 -0
  19. package/lib/fe-layouts/basics-layout/index.js +98 -0
  20. package/lib/fe-layouts/blank-layout/index.js +8 -0
  21. package/lib/fe-layouts/context/context.js +6 -0
  22. package/lib/fe-layouts/context/global-context.provider.js +53 -0
  23. package/lib/fe-layouts/layout.js +49 -0
  24. package/lib/hooks/use-auth.hook.js +23 -0
  25. package/lib/index.css +1 -0
  26. package/lib/index.js +48 -0
  27. package/lib/router/index.js +43 -0
  28. package/lib/router/permission.js +43 -0
  29. package/lib/router/routes.js +57 -0
  30. package/lib/router/utils.js +105 -0
  31. package/lib/store/modules/layout-config.store.js +217 -0
  32. package/lib/store/modules/theme.store.js +60 -0
  33. package/lib/types/config/constants.d.ts +9 -0
  34. package/lib/types/config/index.d.ts +2 -0
  35. package/lib/types/config/theme.d.ts +7 -0
  36. package/lib/types/fe-layouts/auth-layout/index.d.ts +1 -0
  37. package/lib/types/fe-layouts/basics-layout/components/basics-layout/header.d.ts +7 -0
  38. package/lib/types/fe-layouts/basics-layout/components/basics-layout/setting-custom-color.d.ts +3 -0
  39. package/lib/types/fe-layouts/basics-layout/components/basics-layout/setting-drawer.d.ts +6 -0
  40. package/lib/types/fe-layouts/basics-layout/components/basics-layout/sidebar.d.ts +11 -0
  41. package/lib/types/fe-layouts/basics-layout/components/basics-layout/tabs.d.ts +9 -0
  42. package/lib/types/fe-layouts/basics-layout/components/utils/index.d.ts +41 -0
  43. package/lib/types/fe-layouts/basics-layout/index.d.ts +1 -0
  44. package/lib/types/fe-layouts/blank-layout/index.d.ts +1 -0
  45. package/lib/types/fe-layouts/context/context.d.ts +7 -0
  46. package/lib/types/fe-layouts/context/global-context.provider.d.ts +2 -0
  47. package/lib/types/fe-layouts/context/index.d.ts +3 -0
  48. package/lib/types/fe-layouts/index.d.ts +6 -0
  49. package/lib/types/fe-layouts/layout.d.ts +7 -0
  50. package/lib/types/hooks/index.d.ts +1 -0
  51. package/lib/types/hooks/use-auth.hook.d.ts +8 -0
  52. package/lib/types/index.d.ts +10 -0
  53. package/lib/types/router/index.d.ts +40 -0
  54. package/lib/types/router/permission.d.ts +23 -0
  55. package/lib/types/router/routes.d.ts +11 -0
  56. package/lib/types/router/utils.d.ts +56 -0
  57. package/lib/types/store/index.d.ts +2 -0
  58. package/lib/types/store/modules/layout-config.store.d.ts +612 -0
  59. package/lib/types/store/modules/theme.store.d.ts +186 -0
  60. package/lib/types/utils/icon.d.ts +14 -0
  61. package/lib/types/utils/index.d.ts +2 -0
  62. package/lib/types/utils/theme.d.ts +43 -0
  63. package/lib/utils/icon.js +13 -0
  64. package/lib/utils/theme.js +100 -0
  65. package/package.json +60 -0
  66. package/src/assets/styles/animate.css +62 -0
  67. package/src/assets/styles/index.css +49 -0
  68. package/src/assets/svg/chrome-bg-after.svg +1 -0
  69. package/src/assets/svg/chrome-bg-before.svg +1 -0
  70. package/src/assets/svg/icon-arrows-right.svg +1 -0
  71. package/src/assets/svg/icon-bell.svg +1 -0
  72. package/src/assets/svg/icon-custom.svg +1 -0
  73. package/src/assets/svg/icon-sun-moon.svg +1 -0
  74. package/src/assets/svg/icon-system.svg +1 -0
  75. package/src/assets/svg/loading.svg +13 -0
  76. package/src/assets/svg/login-view.svg +193 -0
  77. package/src/config/constants.ts +19 -0
  78. package/src/config/index.ts +2 -0
  79. package/src/config/theme.ts +20 -0
  80. package/src/fe-layouts/auth-layout/index.scss +34 -0
  81. package/src/fe-layouts/auth-layout/index.tsx +121 -0
  82. package/src/fe-layouts/basics-layout/components/basics-layout/header.tsx +148 -0
  83. package/src/fe-layouts/basics-layout/components/basics-layout/setting-custom-color.tsx +52 -0
  84. package/src/fe-layouts/basics-layout/components/basics-layout/setting-drawer.tsx +165 -0
  85. package/src/fe-layouts/basics-layout/components/basics-layout/sidebar.tsx +88 -0
  86. package/src/fe-layouts/basics-layout/components/basics-layout/tabs.tsx +94 -0
  87. package/src/fe-layouts/basics-layout/components/utils/index.ts +142 -0
  88. package/src/fe-layouts/basics-layout/index.scss +110 -0
  89. package/src/fe-layouts/basics-layout/index.tsx +207 -0
  90. package/src/fe-layouts/blank-layout/index.tsx +12 -0
  91. package/src/fe-layouts/context/context.ts +11 -0
  92. package/src/fe-layouts/context/global-context.d.ts +241 -0
  93. package/src/fe-layouts/context/global-context.provider.tsx +81 -0
  94. package/src/fe-layouts/context/index.ts +10 -0
  95. package/src/fe-layouts/index.ts +13 -0
  96. package/src/fe-layouts/layout.tsx +74 -0
  97. package/src/hooks/index.ts +1 -0
  98. package/src/hooks/use-auth.hook.ts +54 -0
  99. package/src/index.ts +21 -0
  100. package/src/router/index.ts +110 -0
  101. package/src/router/permission.tsx +134 -0
  102. package/src/router/routes.tsx +94 -0
  103. package/src/router/utils.ts +283 -0
  104. package/src/store/index.ts +9 -0
  105. package/src/store/modules/layout-config.store.ts +343 -0
  106. package/src/store/modules/theme.store.ts +99 -0
  107. package/src/typings/index.d.ts +59 -0
  108. package/src/typings/shims-axios.d.ts +38 -0
  109. package/src/utils/icon.tsx +32 -0
  110. package/src/utils/index.ts +9 -0
  111. package/src/utils/theme.ts +219 -0
  112. package/tsconfig.json +28 -0
  113. package/vite.config.ts +85 -0
@@ -0,0 +1,241 @@
1
+ /*
2
+ * @Author: dushuai
3
+ * @Date: 2026-01-30 23:02:55
4
+ * @LastEditors: dushuai
5
+ * @LastEditTime: 2026-02-01 12:45:00
6
+ * @description: global context
7
+ */
8
+ import { MenuProps } from 'antd';
9
+ import { ReactNode } from 'react';
10
+ import { RouteObject } from 'react-router-dom';
11
+
12
+ import { MenuItem } from '@/router';
13
+
14
+ export interface IUserInfo {
15
+ username?: string;
16
+ avatar?: string;
17
+ roleName?: string;
18
+ [key: string]: any;
19
+ }
20
+
21
+ export interface IGlobalContextProps {
22
+ /**
23
+ * @name 项目名称
24
+ */
25
+ appName?: ReactNode;
26
+ /**
27
+ * @name 项目logo
28
+ */
29
+ logo?: ReactNode;
30
+ /**
31
+ * @name 通知的reactNode
32
+ */
33
+ noticeRender?: ReactNode;
34
+ /**
35
+ * @name 项目说明
36
+ */
37
+ description?: ReactNode;
38
+ /**
39
+ * @name 项目详细说明
40
+ */
41
+ descriptionDetail?: ReactNode;
42
+ /**
43
+ * @name Copyright
44
+ */
45
+ copyright?: ReactNode;
46
+ /**
47
+ * @name 用户菜单
48
+ */
49
+ userMenuItems?: MenuProps['items'];
50
+ /**
51
+ * @name 用户信息
52
+ */
53
+ userInfo?: IUserInfo;
54
+ /**
55
+ * @name 侧边栏前置内容
56
+ */
57
+ sidebarBefore?: ReactNode;
58
+ /**
59
+ * @name 侧边栏后置内容
60
+ */
61
+ sidebarAfter?: ReactNode;
62
+ /**
63
+ * @name tabs前置内容
64
+ */
65
+ tabsBefore?: ReactNode;
66
+ /**
67
+ * @name tabs后置内容
68
+ */
69
+ tabsAfter?: ReactNode;
70
+ /**
71
+ * @name header按钮组render
72
+ */
73
+ headerActionsRender?: ReactNode;
74
+ /**
75
+ * @name 搜索框render
76
+ */
77
+ searchRender?: ReactNode;
78
+ /**
79
+ * @name 面包屑前置render
80
+ */
81
+ breadcrumbBeforeRender?: ReactNode;
82
+ /**
83
+ * @name 登录凭证
84
+ */
85
+ token?: string;
86
+ /**
87
+ * @name 菜单列表
88
+ */
89
+ menus?: MenuItem[];
90
+ /**
91
+ * @name 路由配置
92
+ */
93
+ routes?: RouteObject[];
94
+ }
95
+
96
+ export interface IGlobalContext {
97
+ /**
98
+ * @name 项目名称
99
+ */
100
+ appName?: ReactNode;
101
+ /**
102
+ * @name 通知的reactNode
103
+ */
104
+ noticeRender?: ReactNode;
105
+ /**
106
+ * @name 项目logo
107
+ */
108
+ logo?: ReactNode;
109
+ /**
110
+ * @name 项目说明
111
+ */
112
+ description?: ReactNode;
113
+ /**
114
+ * @name 项目详细说明
115
+ */
116
+ descriptionDetail?: ReactNode;
117
+ /**
118
+ * @name Copyright
119
+ */
120
+ copyright?: ReactNode;
121
+ /**
122
+ * @name 用户菜单
123
+ */
124
+ userMenuItems?: MenuProps['items'];
125
+ /**
126
+ * @name 用户信息
127
+ */
128
+ userInfo?: IUserInfo;
129
+ /**
130
+ * @name 设置项目名称
131
+ */
132
+ setAppName: (name?: ReactNode) => void;
133
+ /**
134
+ * @name 设置通知的reactNode,用于在顶部通知图标点击显示通知
135
+ */
136
+ setNoticeRender: (render?: ReactNode) => void;
137
+ /**
138
+ * @name 设置项目logo
139
+ */
140
+ setLogo: (logo?: ReactNode) => void;
141
+ /**
142
+ * @name 设置项目说明
143
+ */
144
+ setDescription: (description?: ReactNode) => void;
145
+ /**
146
+ * @name 设置项目详细说明
147
+ */
148
+ setDescriptionDetail: (descriptionDetail?: ReactNode) => void;
149
+ /**
150
+ * @name 设置Copyright
151
+ */
152
+ setCopyright: (copyright?: ReactNode) => void;
153
+ /**
154
+ * @name 设置用户菜单
155
+ */
156
+ setUserMenuItems: (items?: MenuProps['items']) => void;
157
+ /**
158
+ * @name 设置用户信息
159
+ */
160
+ setUserInfo: (info?: IUserInfo) => void;
161
+ /**
162
+ * @name 侧边栏前置内容
163
+ */
164
+ sidebarBefore?: ReactNode;
165
+ /**
166
+ * @name 侧边栏后置内容
167
+ */
168
+ sidebarAfter?: ReactNode;
169
+ /**
170
+ * @name tabs前置内容
171
+ */
172
+ tabsBefore?: ReactNode;
173
+ /**
174
+ * @name tabs后置内容
175
+ */
176
+ tabsAfter?: ReactNode;
177
+ /**
178
+ * @name header按钮组render
179
+ */
180
+ headerActionsRender?: ReactNode;
181
+ /**
182
+ * @name 搜索框render
183
+ */
184
+ searchRender?: ReactNode;
185
+ /**
186
+ * @name 面包屑前置render
187
+ */
188
+ breadcrumbBeforeRender?: ReactNode;
189
+ /**
190
+ * @name 设置侧边栏前置内容
191
+ */
192
+ setSidebarBefore: (content?: ReactNode) => void;
193
+ /**
194
+ * @name 设置侧边栏后置内容
195
+ */
196
+ setSidebarAfter: (content?: ReactNode) => void;
197
+ /**
198
+ * @name 设置tabs前置内容
199
+ */
200
+ setTabsBefore: (content?: ReactNode) => void;
201
+ /**
202
+ * @name 设置tabs后置内容
203
+ */
204
+ setTabsAfter: (content?: ReactNode) => void;
205
+ /**
206
+ * @name 设置header按钮组render
207
+ */
208
+ setHeaderActionsRender: (render?: ReactNode) => void;
209
+ /**
210
+ * @name 设置搜索框render
211
+ */
212
+ setSearchRender: (render?: ReactNode) => void;
213
+ /**
214
+ * @name 设置面包屑前置render
215
+ */
216
+ setBreadcrumbBeforeRender: (render?: ReactNode) => void;
217
+ /**
218
+ * @name 登录凭证
219
+ */
220
+ token?: string;
221
+ /**
222
+ * @name 菜单列表
223
+ */
224
+ menus?: MenuItem[];
225
+ /**
226
+ * @name 路由配置
227
+ */
228
+ routes?: RouteObject[];
229
+ /**
230
+ * @name 设置登录凭证
231
+ */
232
+ setToken: (token?: string) => void;
233
+ /**
234
+ * @name 设置菜单列表
235
+ */
236
+ setMenus: (menus?: MenuItem[]) => void;
237
+ /**
238
+ * @name 设置路由配置
239
+ */
240
+ setRoutes: (routes?: RouteObject[]) => void;
241
+ }
@@ -0,0 +1,81 @@
1
+ /*
2
+ * @Author: dushuai
3
+ * @Date: 2026-01-30 23:57:35
4
+ * @LastEditors: dushuai
5
+ * @LastEditTime: 2026-02-01 12:50:00
6
+ * @description: 心平气和
7
+ */
8
+ import type { MenuProps } from 'antd';
9
+ import { type ReactNode, useState } from 'react';
10
+ import type { RouteObject } from 'react-router-dom';
11
+
12
+ import type { MenuItem } from '@/router';
13
+
14
+ import { GlobalContext, type IGlobalContextProps, type IUserInfo } from '.';
15
+
16
+ export default function GlobalContextProvider(props: React.PropsWithChildren<IGlobalContextProps>) {
17
+
18
+ const [appName, setAppName] = useState<ReactNode | undefined>(props.appName);
19
+ const [logo, setLogo] = useState<ReactNode | undefined>(props.logo);
20
+ const [noticeRender, setNoticeRender] = useState<ReactNode | undefined>(props.noticeRender);
21
+ const [description, setDescription] = useState<ReactNode | undefined>(props.description);
22
+ const [descriptionDetail, setDescriptionDetail] = useState<ReactNode | undefined>(props.descriptionDetail);
23
+ const [copyright, setCopyright] = useState<ReactNode | undefined>(props.copyright);
24
+ const [userMenuItems, setUserMenuItems] = useState<MenuProps['items'] | undefined>(props.userMenuItems);
25
+ const [userInfo, setUserInfo] = useState<IUserInfo | undefined>(props.userInfo);
26
+ const [sidebarBefore, setSidebarBefore] = useState<ReactNode | undefined>(props.sidebarBefore);
27
+ const [sidebarAfter, setSidebarAfter] = useState<ReactNode | undefined>(props.sidebarAfter);
28
+ const [tabsBefore, setTabsBefore] = useState<ReactNode | undefined>(props.tabsBefore);
29
+ const [tabsAfter, setTabsAfter] = useState<ReactNode | undefined>(props.tabsAfter);
30
+ const [headerActionsRender, setHeaderActionsRender] = useState<ReactNode | undefined>(props.headerActionsRender);
31
+ const [searchRender, setSearchRender] = useState<ReactNode | undefined>(props.searchRender);
32
+ const [breadcrumbBeforeRender, setBreadcrumbBeforeRender] = useState<ReactNode | undefined>(props.breadcrumbBeforeRender);
33
+ const [token, setToken] = useState<string | undefined>(props.token);
34
+ const [menus, setMenus] = useState<MenuItem[] | undefined>(props.menus);
35
+ const [routes, setRoutes] = useState<RouteObject[] | undefined>(props.routes);
36
+
37
+ return (
38
+ <GlobalContext.Provider
39
+ value={{
40
+ appName,
41
+ logo,
42
+ noticeRender,
43
+ description,
44
+ descriptionDetail,
45
+ copyright,
46
+ userMenuItems,
47
+ userInfo,
48
+ sidebarBefore,
49
+ sidebarAfter,
50
+ tabsBefore,
51
+ tabsAfter,
52
+ headerActionsRender,
53
+ searchRender,
54
+ breadcrumbBeforeRender,
55
+ token,
56
+ menus,
57
+ routes,
58
+ setAppName,
59
+ setLogo,
60
+ setNoticeRender,
61
+ setDescription,
62
+ setDescriptionDetail,
63
+ setCopyright,
64
+ setUserMenuItems,
65
+ setUserInfo,
66
+ setSidebarBefore,
67
+ setSidebarAfter,
68
+ setTabsBefore,
69
+ setTabsAfter,
70
+ setHeaderActionsRender,
71
+ setSearchRender,
72
+ setBreadcrumbBeforeRender,
73
+ setToken,
74
+ setMenus,
75
+ setRoutes
76
+ }}
77
+ >
78
+ {props.children}
79
+ </GlobalContext.Provider>
80
+ );
81
+ }
@@ -0,0 +1,10 @@
1
+ /*
2
+ * @Author: dushuai
3
+ * @Date: 2026-01-30 23:00:18
4
+ * @LastEditors: dushuai
5
+ * @LastEditTime: 2026-02-01 01:53:57
6
+ * @description: global context
7
+ */
8
+ export * from './context';
9
+ export * from './global-context.d';
10
+ export { default as GlobalContextProvider } from './global-context.provider';
@@ -0,0 +1,13 @@
1
+ /*
2
+ * @Author: dushuai
3
+ * @Date: 2026-01-31 23:23:24
4
+ * @LastEditors: dushuai
5
+ * @LastEditTime: 2026-02-01 12:37:31
6
+ * @description: fe-layouts
7
+ */
8
+ export { default as AuthLayout } from './auth-layout';
9
+ export { default as BasicsLayout } from './basics-layout';
10
+ export { default as BlankLayout } from './blank-layout';
11
+ export * from './context';
12
+ export type { IFeLayoutProps } from './layout';
13
+ export { default as FeLayout } from './layout';
@@ -0,0 +1,74 @@
1
+ /*
2
+ * @Author: dushuai
3
+ * @Date: 2024-04-07 10:25:43
4
+ * @LastEditors: dushuai
5
+ * @LastEditTime: 2026-02-01 01:39:31
6
+ * @description: BasicsLayout
7
+ */
8
+ import { useSelector } from '@spacego/zustand';
9
+ import { ConfigProvider, Spin, theme, type ThemeConfig } from 'antd';
10
+ import { useMemo } from 'react';
11
+
12
+ import { useLayoutConfigStore, useThemeStore } from '../';
13
+ import themeConfig from '../config/theme';
14
+ import { getRealTheme } from '../utils/theme';
15
+ import AuthLayout from './auth-layout';
16
+ import BasicsLayout from './basics-layout';
17
+ import BlankLayout from './blank-layout';
18
+ import { GlobalContextProvider } from './context';
19
+
20
+ type ComponentType = () => JSX.Element;
21
+
22
+ export interface IFeLayoutProps {
23
+ /**
24
+ * 布局类型,空白布局和后台布局
25
+ */
26
+ layoutType: 'blank' | 'basic' | 'auth';
27
+ }
28
+
29
+ export default function FeLayout(props: IFeLayoutProps) {
30
+ const { layoutType } = props;
31
+
32
+ const { loadingConfig } = useLayoutConfigStore(useSelector(['loadingConfig']));
33
+ const { theme: themeMode, themeColor } = useThemeStore(useSelector(['theme', 'themeColor']));
34
+
35
+ const sysTheme: Partial<ThemeConfig> = useMemo(() => {
36
+ // 获取实际主题(system 会转换为 light/dark)
37
+ const realTheme = getRealTheme(themeMode);
38
+ return {
39
+ cssVar: {
40
+ prefix: 'ant'
41
+ }, // 启用 CSS 变量模式,生成 --ant-color-primary 等变量
42
+ algorithm: realTheme === 'dark' ? theme.darkAlgorithm : theme.defaultAlgorithm,
43
+ token: {
44
+ colorPrimary: themeColor,
45
+ ...themeConfig.token
46
+ },
47
+ components: {
48
+ ...themeConfig.components
49
+ }
50
+ };
51
+ }, [themeMode, themeColor]);
52
+
53
+ const ComponentMap = new Map<IFeLayoutProps['layoutType'], ComponentType>([
54
+ ['blank', BlankLayout],
55
+ ['basic', BasicsLayout],
56
+ ['auth', AuthLayout]
57
+ ]);
58
+ const Component = ComponentMap.get(layoutType);
59
+
60
+ return (
61
+ <ConfigProvider theme={sysTheme}>
62
+ <GlobalContextProvider>
63
+ <Spin
64
+ spinning={loadingConfig.show && layoutType !== 'basic'}
65
+ wrapperClassName="spin-wrapper-full"
66
+ >
67
+ {
68
+ Component && <Component key={layoutType} />
69
+ }
70
+ </Spin>
71
+ </GlobalContextProvider>
72
+ </ConfigProvider>
73
+ );
74
+ }
@@ -0,0 +1 @@
1
+ export * from './use-auth.hook';
@@ -0,0 +1,54 @@
1
+ /*
2
+ * @Author: dushuai
3
+ * @Date: 2026-01-25 21:30:00
4
+ * @LastEditors: dushuai
5
+ * @LastEditTime: 2026-02-01 01:46:53
6
+ * @description: 登录和退出登录的 hook
7
+ */
8
+ import { useFetcher, useSearchParams, useSubmit } from 'react-router-dom';
9
+
10
+ import { useLayoutConfigStore } from '@/store';
11
+
12
+ import { useGlobal } from '../fe-layouts/context';
13
+
14
+ /**
15
+ * 登录和退出登录的 hook
16
+ * @returns
17
+ */
18
+ export function useAuth() {
19
+ const submit = useSubmit();
20
+ const fetcher = useFetcher();
21
+ const [searchParams] = useSearchParams();
22
+ const { setToken } = useGlobal();
23
+
24
+ /**
25
+ * 登录
26
+ * @param token 登录 token
27
+ * @param redirectTo 登录成功后跳转的路径,默认为 '/' 或从 URL 参数 'from' 获取
28
+ */
29
+ const login = (token: string, redirectTo?: string) => {
30
+ const targetPath = redirectTo || searchParams.get('from') || '/';
31
+ submit({ token, redirectTo: targetPath }, { method: 'post', replace: true });
32
+ };
33
+
34
+ /**
35
+ * 退出登录
36
+ */
37
+ const logout = () => {
38
+ // 1. 调用 GlobalContext 中的 setToken 清空 token
39
+ setToken(void 0);
40
+
41
+ // 2. 触发后端的 logout action (如果需要)
42
+ fetcher.submit(null, { action: '/logout', method: 'post' });
43
+
44
+ // 3. 重置布局配置等其他状态
45
+ setTimeout(() => {
46
+ useLayoutConfigStore.getState().RESET();
47
+ }, 300);
48
+ };
49
+
50
+ return {
51
+ login,
52
+ logout
53
+ };
54
+ }
package/src/index.ts ADDED
@@ -0,0 +1,21 @@
1
+ /*
2
+ * @Author: dushuai
3
+ * @Date: 2026-01-31 23:07:12
4
+ * @LastEditors: dushuai
5
+ * @LastEditTime: 2026-02-01 12:38:34
6
+ * @description: export
7
+ */
8
+ /**------------------------------------------- utils ------------------------------------------- */
9
+ export * from './utils';
10
+
11
+ /**------------------------------------------- store ------------------------------------------- */
12
+ export * from './store';
13
+
14
+ /**------------------------------------------- config ------------------------------------------- */
15
+ export * from './config';
16
+
17
+ /**------------------------------------------- router ------------------------------------------- */
18
+ export * from './router';
19
+
20
+ /**------------------------------------------- components ------------------------------------------- */
21
+ export * from './fe-layouts';
@@ -0,0 +1,110 @@
1
+ /*
2
+ * @Author: dushuai
3
+ * @Date: 2024-03-29 16:05:16
4
+ * @LastEditors: dushuai
5
+ * @LastEditTime: 2026-02-01 01:14:48
6
+ * @description: router
7
+ */
8
+ import type { ComponentType } from 'react';
9
+ import { createBrowserRouter, type RouteObject } from 'react-router-dom';
10
+
11
+ import { createPermissionHelpers, type TokenHelpers } from './permission';
12
+ import { createDefaultRoutes } from './routes';
13
+ import { type TModule } from './utils';
14
+
15
+ export interface CreateRouterOptions {
16
+ /** 页面模块集合 import.meta.glob的结果 */
17
+ modules: TModule;
18
+ /** token 操作方法 */
19
+ token: TokenHelpers;
20
+ /** 基础布局组件 */
21
+ BaseLayout: ComponentType<any>;
22
+ /** 外部传入的路由配置(第一层,RouteObject结构) */
23
+ routes?: RouteObject[];
24
+ }
25
+
26
+ /**
27
+ * 创建路由器配置(第一层)
28
+ * @param options
29
+ * @returns { routes, permissionHelpers }
30
+ */
31
+ export function createRouter(options: CreateRouterOptions) {
32
+ const { modules, token, BaseLayout, routes: customRoutes = [] } = options;
33
+
34
+ // 1. 初始化权限帮助函数
35
+ const permissionHelpers = createPermissionHelpers(token);
36
+
37
+ // 2. 创建默认路由(登录、注册、错误页等)
38
+ const defaultRoutes = createDefaultRoutes(modules, permissionHelpers, BaseLayout);
39
+
40
+ // 3. 合并路由
41
+ const finalRoutes = mergeRoutes(defaultRoutes, customRoutes);
42
+
43
+ return {
44
+ /** 最终合并后的路由配置 */
45
+ routes: finalRoutes,
46
+ /** 权限帮助函数,包含 ProtectedLoader 等,用于外部调用 processRoutes */
47
+ permissionHelpers
48
+ };
49
+ }
50
+
51
+ /**
52
+ * 合并路由(支持 id 去重,优先使用 customRoutes)
53
+ * @param defaultRoutes 默认路由
54
+ * @param customRoutes 自定义路由(已处理为 RouteObject[])
55
+ * @returns 合并后的路由数组
56
+ */
57
+ function mergeRoutes(defaultRoutes: RouteObject[], customRoutes: RouteObject[]): RouteObject[] {
58
+ const routeMap = new Map<string, RouteObject>();
59
+
60
+ // 先放入 defaultRoutes
61
+ defaultRoutes.forEach(route => {
62
+ if(route.id) {
63
+ routeMap.set(route.id, route);
64
+ }
65
+ });
66
+
67
+ // 再放入 customRoutes,如果有相同 id 则覆盖(除了 root 特殊处理)
68
+ customRoutes.forEach(route => {
69
+ if(route.id) {
70
+ if(route.id === 'root' && routeMap.has('root')) {
71
+ // 特殊处理 root 路由:合并 children
72
+ const defaultRoot = routeMap.get('root')!;
73
+ const customRoot = route;
74
+
75
+ // 合并 children
76
+ const mergedChildren = [
77
+ ...(defaultRoot.children || []),
78
+ ...(customRoot.children || [])
79
+ ];
80
+
81
+ routeMap.set('root', {
82
+ ...defaultRoot,
83
+ ...customRoot, // 属性以 customRoot 为准
84
+ children: mergedChildren
85
+ } as RouteObject);
86
+ } else {
87
+ // 其他路由直接覆盖
88
+ routeMap.set(route.id, route as RouteObject);
89
+ }
90
+ } else {
91
+ // 没有 id 的路由,直接添加?
92
+ // 为了安全起见,这里可以考虑生成一个临时 id 或者直接追加,但 React Router 建议有 id
93
+ }
94
+ });
95
+
96
+ return Array.from(routeMap.values());
97
+ }
98
+
99
+ /**
100
+ * 生成路由表
101
+ * @param routes 路由数组
102
+ * @returns
103
+ */
104
+ export function generateRouter(routes: RouteObject[]) {
105
+ return createBrowserRouter(routes);
106
+ }
107
+
108
+ export * from './permission';
109
+ export * from './routes';
110
+ export * from './utils';