@spacego/fe-components 0.0.1-alpha.2 → 0.0.1-alpha.4

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 (59) hide show
  1. package/lib/assets/svg/chrome-bg-after.svg.js +3 -2
  2. package/lib/assets/svg/chrome-bg-before.svg.js +2 -1
  3. package/lib/assets/svg/icon-arrows-right.svg.js +3 -2
  4. package/lib/assets/svg/icon-bell.svg.js +3 -2
  5. package/lib/assets/svg/icon-custom.svg.js +3 -2
  6. package/lib/assets/svg/icon-sun-moon.svg.js +3 -2
  7. package/lib/assets/svg/icon-system.svg.js +3 -2
  8. package/lib/assets/svg/login-view.svg.js +871 -2
  9. package/lib/fe-layouts/auth-layout/index.js +8 -8
  10. package/lib/fe-layouts/basics-layout/components/basics-layout/tabs.js +5 -5
  11. package/package.json +9 -1
  12. package/src/assets/styles/animate.css +0 -62
  13. package/src/assets/styles/index.css +0 -49
  14. package/src/assets/svg/chrome-bg-after.svg +0 -1
  15. package/src/assets/svg/chrome-bg-before.svg +0 -1
  16. package/src/assets/svg/icon-arrows-right.svg +0 -1
  17. package/src/assets/svg/icon-bell.svg +0 -1
  18. package/src/assets/svg/icon-custom.svg +0 -1
  19. package/src/assets/svg/icon-sun-moon.svg +0 -1
  20. package/src/assets/svg/icon-system.svg +0 -1
  21. package/src/assets/svg/loading.svg +0 -13
  22. package/src/assets/svg/login-view.svg +0 -193
  23. package/src/config/constants.ts +0 -19
  24. package/src/config/index.ts +0 -2
  25. package/src/config/theme.ts +0 -20
  26. package/src/fe-layouts/auth-layout/index.scss +0 -34
  27. package/src/fe-layouts/auth-layout/index.tsx +0 -121
  28. package/src/fe-layouts/basics-layout/components/basics-layout/header.tsx +0 -148
  29. package/src/fe-layouts/basics-layout/components/basics-layout/setting-custom-color.tsx +0 -52
  30. package/src/fe-layouts/basics-layout/components/basics-layout/setting-drawer.tsx +0 -165
  31. package/src/fe-layouts/basics-layout/components/basics-layout/sidebar.tsx +0 -88
  32. package/src/fe-layouts/basics-layout/components/basics-layout/tabs.tsx +0 -94
  33. package/src/fe-layouts/basics-layout/components/utils/index.ts +0 -142
  34. package/src/fe-layouts/basics-layout/index.scss +0 -110
  35. package/src/fe-layouts/basics-layout/index.tsx +0 -207
  36. package/src/fe-layouts/blank-layout/index.tsx +0 -12
  37. package/src/fe-layouts/context/context.ts +0 -11
  38. package/src/fe-layouts/context/global-context.d.ts +0 -241
  39. package/src/fe-layouts/context/global-context.provider.tsx +0 -81
  40. package/src/fe-layouts/context/index.ts +0 -10
  41. package/src/fe-layouts/index.ts +0 -13
  42. package/src/fe-layouts/layout.tsx +0 -74
  43. package/src/hooks/index.ts +0 -1
  44. package/src/hooks/use-auth.hook.ts +0 -54
  45. package/src/index.ts +0 -24
  46. package/src/router/index.ts +0 -110
  47. package/src/router/permission.tsx +0 -134
  48. package/src/router/routes.tsx +0 -94
  49. package/src/router/utils.ts +0 -283
  50. package/src/store/index.ts +0 -9
  51. package/src/store/modules/layout-config.store.ts +0 -343
  52. package/src/store/modules/theme.store.ts +0 -99
  53. package/src/typings/index.d.ts +0 -59
  54. package/src/typings/shims-axios.d.ts +0 -38
  55. package/src/utils/icon.tsx +0 -32
  56. package/src/utils/index.ts +0 -9
  57. package/src/utils/theme.ts +0 -219
  58. package/tsconfig.json +0 -28
  59. package/vite.config.ts +0 -85
@@ -1,134 +0,0 @@
1
- /*
2
- * @Author: dushuai
3
- * @Date: 2024-08-19 21:19:46
4
- * @LastEditors: dushuai
5
- * @LastEditTime: 2026-02-01 11:30:00
6
- * @description: permission 权限控制的最佳使用
7
- *
8
- * 用法:/login、/logout、/(id: 'root') 为一级路由,所有功能路由均存在于/下 或者新建一级路由
9
- *
10
- * 受保护:需要登录后才能访问的路由 使用loader: ProtectedLoader
11
- * 登录:外部调用登录接口成功后 调用用 useSubmit() 方法
12
- * useSubmit()({ token, redirectTo: params.get('from') || '/' }, { method: 'post', replace: true });
13
- * 登出:有外部接口时 在接口成功后 调用 useFetcher() 方法
14
- * useFetcher().submit(null, { action: '/logout', method: 'post' });
15
- *
16
- * 所有逻辑均可根据实际情况进行修改
17
- */
18
- import { message } from 'antd';
19
- import { type LoaderFunctionArgs, redirect } from 'react-router-dom';
20
-
21
- export type AuthStatus = {
22
- token: string | void | null;
23
- }
24
-
25
- export interface TokenHelpers {
26
- /** 获取token */
27
- getToken: () => string | void | null;
28
- /** 设置token */
29
- setToken: (token: string) => void;
30
- /** 移除token */
31
- removeToken: () => void;
32
- }
33
-
34
- /**
35
- * 创建权限相关的 loader 和 action
36
- * @param tokenHelpers token 操作方法
37
- */
38
- export function createPermissionHelpers(tokenHelpers: TokenHelpers) {
39
- const { getToken, setToken, removeToken } = tokenHelpers;
40
-
41
- /**
42
- * root根目录的Loader 根据需要设置使用
43
- */
44
- function RootLoader(): AuthStatus {
45
- const token = getToken();
46
-
47
- return { token };
48
- // Get our logged in user, if they exist, from the root route loader data
49
- // const { token } = useRouteLoaderData('root') as AuthStatus;
50
- }
51
-
52
- /**
53
- * 受保护页面的Loader
54
- */
55
- function ProtectedLoader({ request }: LoaderFunctionArgs) {
56
- // If the user is not logged in and tries to access `/protected`, we redirect
57
- // them to `/login` with a `from` parameter that allows login to redirect back
58
- // to this page upon successful authentication
59
- const token = getToken();
60
-
61
- if(!token) {
62
- const params = new URLSearchParams();
63
- params.set('from', new URL(request.url).pathname);
64
- return redirect('/auth/login?' + params.toString());
65
- }
66
- return null;
67
- }
68
-
69
- /**
70
- * login页面的Loader 主要用来频闭登陆后再次进入/login
71
- * 所有有该需求的页面都可以使用
72
- */
73
- async function LoginLoader() {
74
- const token = getToken();
75
- if(token) {
76
- return redirect('/');
77
- }
78
- return null;
79
- }
80
-
81
- /**
82
- * 登录的action 登陆后自动跳转到之前想要访问的页面
83
- */
84
- async function LoginAction({ request }: LoaderFunctionArgs) {
85
- const formData = await request.formData();
86
- const token = formData.get('token') as string | null;
87
-
88
- // Validate our form inputs and return validation errors via useActionData()
89
- if(!token) {
90
- return message.warning('You must provide a token to log in');
91
- // return {
92
- // error: 'You must provide a token to log in'
93
- /**
94
- * action内return error时 只能通过 useActionData() as { error: string } | undefined 来获取,根据需要使用
95
- */
96
- // };
97
- }
98
-
99
- // Sign in and redirect to the proper destination if successful.
100
- try {
101
- setToken(token);
102
- message.success('登录成功');
103
- } catch(error) {
104
- // Unused as of now but this is how you would handle invalid
105
- // username/password combinations - just like validating the inputs
106
- // above
107
- return message.warning('Invalid login attempt');
108
- // return {
109
- // error: 'Invalid login attempt'
110
- // };
111
- }
112
-
113
- const redirectTo = formData.get('redirectTo') as string | null;
114
- return redirect(redirectTo || '/');
115
- }
116
-
117
- /**
118
- * 退出登录的action
119
- */
120
- function LogoutAction() {
121
- // We signout in a "resource route" that we can hit from a fetcher.Form
122
- removeToken();
123
- message.success('退出成功');
124
- return redirect('/auth/login');
125
- }
126
-
127
- return {
128
- RootLoader,
129
- ProtectedLoader,
130
- LoginLoader,
131
- LoginAction,
132
- LogoutAction
133
- };
134
- }
@@ -1,94 +0,0 @@
1
- /*
2
- * @Author: dushuai
3
- * @Date: 2024-03-29 16:17:20
4
- * @LastEditors: dushuai
5
- * @LastEditTime: 2026-02-01 12:15:00
6
- * @description: 路由表
7
- */
8
- import type { ComponentType } from 'react';
9
- import type { RouteObject } from 'react-router-dom';
10
-
11
- import type { createPermissionHelpers } from './permission';
12
- import { createLazyComponent, type TModule } from './utils';
13
-
14
- /**
15
- * 创建默认路由表
16
- * @param modules 页面模块集合
17
- * @param permissionHelpers 权限相关的 loader 和 action
18
- * @param BaseLayout 基础布局组件
19
- */
20
- export function createDefaultRoutes(
21
- modules: TModule,
22
- permissionHelpers: ReturnType<typeof createPermissionHelpers>,
23
- BaseLayout: ComponentType<any>
24
- ): RouteObject[] {
25
- const { RootLoader, LoginLoader, LoginAction, LogoutAction } = permissionHelpers;
26
-
27
- /**
28
- * 路由表
29
- *
30
- * Component vs element 区别:
31
- *
32
- * 1. Component (推荐用于简单场景)
33
- * - 接收组件类型,React Router 自动实例化
34
- * - 自动传递路由数据(通过 useLoaderData() 获取)
35
- * - 不能传递自定义 props
36
- * - 代码更简洁
37
- *
38
- * 2. element (用于需要传递 props 的场景)
39
- * - 接收 React 元素(JSX)
40
- * - 可以传递自定义 props
41
- * - 可以包装 Suspense、ErrorBoundary 等
42
- * - 仍然可以通过 useLoaderData() 获取 loader 数据
43
- */
44
- const routes: RouteObject[] = [
45
- {
46
- id: 'root',
47
- path: '/',
48
- // 使用 element: 可以传递自定义 props
49
- element: <BaseLayout layoutType="basic" />,
50
- loader: RootLoader,
51
- children: [] // ...dynamicRoutes
52
- },
53
- {
54
- id: 'blank',
55
- path: '/blank',
56
- element: <BaseLayout layoutType="blank" />,
57
- loader: RootLoader,
58
- children: []
59
- },
60
- {
61
- id: 'auth',
62
- element: <BaseLayout layoutType="auth" />,
63
- loader: RootLoader,
64
- children: [
65
- {
66
- path: '/auth/login',
67
- loader: LoginLoader,
68
- action: LoginAction,
69
- // 使用 Component: 简洁,但不能传递自定义 props
70
- Component: createLazyComponent('/basics/login', modules)
71
- },
72
- {
73
- path: '/auth/register',
74
- loader: LoginLoader,
75
- action: LoginAction,
76
- // 使用 Component: 简洁,但不能传递自定义 props
77
- Component: createLazyComponent('/basics/register', modules)
78
- }
79
- ]
80
- },
81
- {
82
- // logout路由只用来退出登录,不展示页面
83
- path: '/logout',
84
- action: LogoutAction,
85
- Component: createLazyComponent('/basics/error', modules)
86
- },
87
- {
88
- path: '*',
89
- Component: createLazyComponent('/basics/error', modules)
90
- }
91
- ];
92
-
93
- return routes;
94
- }
@@ -1,283 +0,0 @@
1
- import { type ComponentType, lazy } from 'react';
2
- import type { RouteObject } from 'react-router-dom';
3
-
4
- import type { IRoute, MenuType } from '@/typings';
5
-
6
- export type TModule = {
7
- [keys in string]: () => Promise<{ default: ComponentType<any>; }>
8
- }
9
-
10
- /**
11
- * 获取页面路径
12
- * @param name
13
- * @returns
14
- */
15
- export function getPath(name: string) {
16
- // 移除开头的第一个斜杠
17
- const cleanName = name.replace(/^\/+/, '');
18
- return `/src/pages/${cleanName}/index.tsx`;
19
- }
20
-
21
- /**
22
- * 获取动态页面
23
- * @param name
24
- * @param modules
25
- * @returns
26
- */
27
- export function createLazyComponent(name: string, modules: TModule) {
28
- // return lazy(components[`${name}/index.tsx`])
29
- const moduleFn = modules[getPath(name)];
30
- if(!moduleFn) {
31
- console.warn(`Module not found for path: ${name}`);
32
- return undefined;
33
- }
34
- return lazy(moduleFn);
35
- }
36
-
37
- /**
38
- * 规范化路径格式
39
- * @param path 路径
40
- * @returns 规范化后的路径
41
- */
42
- function normalizePath(path?: string): string | undefined {
43
- if(!path) return void 0;
44
- // 确保路径以 / 开头
45
- return path.startsWith('/') ? path : `/${path}`;
46
- }
47
-
48
- /** 路由节点类型 */
49
- type RouteNode = RouteObject & { parentId: string; children?: RouteNode[] };
50
-
51
- /** 菜单项类型(用于 antd Menu 组件) */
52
- export interface MenuItem {
53
- key: string;
54
- label: string;
55
- icon?: string;
56
- /** 对应的路由节点信息(不包含 children) */
57
- routeNode?: Omit<RouteNode, 'children'>;
58
- children?: MenuItem[];
59
- menuType?: MenuType;
60
- /** 是否独立菜单 */
61
- isIndependentMenu?: boolean;
62
- }
63
-
64
- /** 处理结果类型 */
65
- export interface ProcessedRoutes {
66
- /** react-router 路由配置 */
67
- routes: RouteNode[];
68
- /** antd Menu 菜单配置 */
69
- menus: MenuItem[];
70
- /** 按钮权限列表 */
71
- permissions: string[];
72
- }
73
-
74
- /**
75
- * 按 sort 字段排序
76
- */
77
- function sortByOrder(a: IRoute, b: IRoute): number {
78
- return (a.sort ?? 0) - (b.sort ?? 0);
79
- }
80
-
81
- /**
82
- * 将扁平的 IRoute 数组处理成三份数据
83
- * @param rawRoutes 扁平路由数组
84
- * @param modules 页面模块集合
85
- * @param protectedLoader 受保护路由的加载器
86
- * @returns { routes, menus, permissions }
87
- */
88
- export function processRoutes(
89
- rawRoutes: IRoute[],
90
- modules: TModule,
91
- protectedLoader?: any
92
- ): ProcessedRoutes {
93
- // 按 sort 排序
94
- const sortedRoutes = [...rawRoutes].sort(sortByOrder);
95
-
96
- // 按类型分组:1目录、2菜单、3按钮
97
- const routesByType = sortedRoutes.reduce((acc, route) => {
98
- const type = route.menuType || 2;
99
- if(!acc[type]) acc[type] = [];
100
- acc[type].push(route);
101
- return acc;
102
- }, {} as Record<MenuType, IRoute[]>);
103
-
104
- // 提取按钮权限列表(使用 menuCode 作为权限标识)
105
- const permissions = (routesByType[3] || [])
106
- .filter(r => r.menuCode)
107
- .map(r => r.menuCode!);
108
-
109
- // 只处理目录和菜单(用于路由和菜单)
110
- const directoriesAndMenus = [
111
- ...(routesByType[1] || []),
112
- ...(routesByType[2] || [])
113
- ].sort(sortByOrder);
114
-
115
- // 构建路由树
116
- const routeMap = new Map<string, RouteNode>();
117
- const rootRoutes: RouteNode[] = [];
118
-
119
- // 第一遍遍历:转换为 RouteNode
120
- directoriesAndMenus.forEach(route => {
121
- const parentId = route.parentId || '/';
122
- const path = normalizePath(route.path);
123
-
124
- const node: RouteNode = {
125
- id: route.menuId,
126
- path,
127
- parentId,
128
- // 将 title、icon 放入 handle 字段
129
- handle: {
130
- title: route.title,
131
- icon: route.icon,
132
- isShowMenu: route.isShowMenu ?? true,
133
- isIndependentMenu: route.isIndependentMenu ?? false,
134
- activeRouteMenuId: route.activeRouteMenuId ?? route.menuId
135
- }
136
- };
137
-
138
- // 菜单类型(2)需要组件(使用 menuCode 作为组件路径)
139
- if(route.menuType === 2 && route.menuCode) {
140
- const Component = createLazyComponent(route.menuCode, modules);
141
- if(Component) {
142
- node.Component = Component;
143
- }
144
-
145
- if(route.index) {
146
- // index 路由需要特殊处理:不能同时有 path 和 index
147
- node.index = true as any;
148
- delete node.path;
149
- }
150
-
151
- if(route.protected !== false && protectedLoader) {
152
- node.loader = protectedLoader;
153
- }
154
- }
155
-
156
- routeMap.set(route.menuId, node);
157
- });
158
-
159
- // 第二遍遍历:构建父子关系
160
- directoriesAndMenus.forEach(route => {
161
- const node = routeMap.get(route.menuId)!;
162
- const parentId = route.parentId;
163
-
164
- if(parentId && parentId !== '/') {
165
- const parent = routeMap.get(parentId);
166
-
167
- if(parent) {
168
- if(!parent.children) parent.children = [];
169
- parent.children.push(node);
170
- } else {
171
- rootRoutes.push(node);
172
- }
173
- } else {
174
- rootRoutes.push(node);
175
- }
176
- });
177
-
178
- // 构建菜单树(只包含需要在菜单中显示的项)
179
- const menuMap = new Map<string, MenuItem>();
180
- const rootMenus: MenuItem[] = [];
181
-
182
- // 过滤出需要在菜单中显示的项(isShowMenu !== false,默认为true)
183
- const menuVisibleRoutes = directoriesAndMenus.filter(route => route.isShowMenu !== false);
184
-
185
- // 第一遍遍历:转换为 MenuItem
186
- menuVisibleRoutes.forEach(route => {
187
- const routeNode = routeMap.get(route.menuId);
188
- // 从 RouteNode 中提取信息,去除 children 字段
189
- const routeNodeInfo: Omit<RouteNode, 'children'> | undefined = routeNode
190
- ? (() => {
191
- const { children, ...rest } = routeNode;
192
- return rest;
193
- })()
194
- : undefined;
195
-
196
- const menuItem: MenuItem = {
197
- key: route.menuId,
198
- label: route.title || route.menuId,
199
- icon: route.icon,
200
- routeNode: routeNodeInfo,
201
- menuType: route.menuType,
202
- isIndependentMenu: route.isIndependentMenu
203
- };
204
-
205
- menuMap.set(route.menuId, menuItem);
206
- });
207
-
208
- // 第二遍遍历:构建父子关系
209
- menuVisibleRoutes.forEach(route => {
210
- const menuItem = menuMap.get(route.menuId)!;
211
- const parentId = route.parentId;
212
-
213
- if(parentId && parentId !== '/') {
214
- const parent = menuMap.get(parentId);
215
-
216
- if(parent) {
217
- if(!parent.children) parent.children = [];
218
- parent.children.push(menuItem);
219
- } else {
220
- rootMenus.push(menuItem);
221
- }
222
- } else {
223
- rootMenus.push(menuItem);
224
- }
225
- });
226
-
227
- /**
228
- * 递归处理菜单:根据 isIndependentMenu 字段处理独立菜单
229
- * 如果是独立菜单,在 menus 内移除对应目录,直接展示此菜单
230
- * 独立菜单的判断只针对直接父级,不是无限向外查找
231
- */
232
- function simplifyMenus(menus: MenuItem[]): MenuItem[] {
233
- const result: MenuItem[] = [];
234
-
235
- menus.forEach(menu => {
236
- // 先递归处理子菜单
237
- if(menu.children && menu.children.length > 0) {
238
- menu.children = simplifyMenus(menu.children);
239
- }
240
-
241
- // 如果是目录,检查是否有独立菜单的子菜单
242
- if(menu.menuType === 1 && menu.children && menu.children.length > 0) {
243
- // 查找所有独立菜单的子菜单(只处理直接子菜单,不递归)
244
- const independentMenus = menu.children.filter(child => {
245
- // 只处理直接子菜单,且是独立菜单,且没有被提升过的
246
- return child.isIndependentMenu === true &&
247
- child.menuType === 2 &&
248
- !(child as any).customType;
249
- });
250
-
251
- if(independentMenus.length > 0) {
252
- // 如果有独立菜单,移除目录,直接展示独立菜单
253
- // 将独立菜单提升到当前层级,并添加内部标识
254
- const promotedMenus = independentMenus.map(child => ({
255
- ...child,
256
- customType: 'menu-promoted' // 内部标识,表示这个菜单是被提升的
257
- }));
258
-
259
- // 保留其他非独立菜单
260
- const otherMenus = menu.children.filter(child => {
261
- return !(child.isIndependentMenu === true && child.menuType === 2 && !(child as any).customType);
262
- });
263
-
264
- result.push(...promotedMenus, ...otherMenus);
265
- } else {
266
- // 没有独立菜单,保留原目录
267
- result.push(menu);
268
- }
269
- } else {
270
- // 不是目录,或者没有子菜单,直接保留
271
- result.push(menu);
272
- }
273
- });
274
-
275
- return result;
276
- }
277
-
278
- return {
279
- routes: rootRoutes,
280
- menus: simplifyMenus(rootMenus),
281
- permissions
282
- };
283
- }
@@ -1,9 +0,0 @@
1
- /*
2
- * @Author: dushuai
3
- * @Date: 2024-04-01 15:34:55
4
- * @LastEditors: dushuai
5
- * @LastEditTime: 2026-01-31 23:38:26
6
- * @description: store
7
- */
8
- export * from './modules/layout-config.store';
9
- export * from './modules/theme.store';