generator-mico-cli 0.2.8 → 0.2.9
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.
- package/generators/micro-react/index.js +8 -3
- package/generators/micro-react/templates/apps/layout/src/app.tsx +16 -4
- package/generators/micro-react/templates/apps/layout/src/common/menu/types.ts +4 -0
- package/generators/micro-react/templates/apps/layout/src/common/request/index.ts +2 -2
- package/generators/micro-react/templates/apps/layout/src/components/AppTabs/index.tsx +2 -2
- package/generators/micro-react/templates/apps/layout/src/constants/index.ts +71 -5
- package/generators/micro-react/templates/apps/layout/src/hooks/useRoutePermissionRefresh.ts +2 -2
- package/generators/micro-react/templates/apps/layout/src/layouts/index.tsx +2 -2
- package/generators/micro-react/templates/apps/layout/src/services/user.ts +1 -0
- package/generators/subapp-react/index.js +10 -1
- package/generators/subapp-react/templates/homepage/config/config.prod.development.ts +0 -1
- package/generators/subapp-react/templates/homepage/config/config.prod.testing.ts +0 -1
- package/package.json +1 -1
|
@@ -139,15 +139,20 @@ module.exports = class extends Generator {
|
|
|
139
139
|
}
|
|
140
140
|
}
|
|
141
141
|
|
|
142
|
+
install() {
|
|
143
|
+
this.log('');
|
|
144
|
+
this.log('📦 正在安装依赖...');
|
|
145
|
+
this.spawnCommandSync('pnpm', ['install'], {
|
|
146
|
+
cwd: this.destDir
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
|
|
142
150
|
end() {
|
|
143
151
|
this.log('');
|
|
144
152
|
this.log('✅ 项目创建成功!');
|
|
145
153
|
this.log('');
|
|
146
154
|
this.log(' 后续步骤:');
|
|
147
155
|
this.log('');
|
|
148
|
-
this.log(' # 安装依赖');
|
|
149
|
-
this.log(' pnpm install');
|
|
150
|
-
this.log('');
|
|
151
156
|
this.log(' # 启动开发服务器');
|
|
152
157
|
this.log(' pnpm dev');
|
|
153
158
|
this.log('');
|
|
@@ -10,15 +10,18 @@ import { getStoredAuthToken } from './common/auth/auth-manager';
|
|
|
10
10
|
import type { IUserInfo } from './common/auth/type';
|
|
11
11
|
import { fetchUserInfo } from './services/user';
|
|
12
12
|
import { extractRoutes, getWindowMenus } from './common/menu';
|
|
13
|
-
import { ensureSsoSession } from './common/request/sso';
|
|
14
13
|
import {
|
|
15
14
|
clearMicroAppProps,
|
|
16
15
|
type IMicroAppProps,
|
|
17
16
|
setMicroAppProps,
|
|
18
17
|
} from './common/micro';
|
|
18
|
+
import {
|
|
19
|
+
ensureSsoSession,
|
|
20
|
+
handleAuthFailureRedirect,
|
|
21
|
+
} from './common/request/sso';
|
|
19
22
|
import { initTheme } from './common/theme';
|
|
20
23
|
import MicroAppLoader from './components/MicroAppLoader';
|
|
21
|
-
import {
|
|
24
|
+
import { isNoAuthRoute } from '@/constants';
|
|
22
25
|
import './global.less';
|
|
23
26
|
|
|
24
27
|
// ==================== qiankun 全局错误处理 ====================
|
|
@@ -110,10 +113,10 @@ export async function getInitialState(): Promise<{
|
|
|
110
113
|
};
|
|
111
114
|
|
|
112
115
|
const { location } = history;
|
|
113
|
-
const
|
|
116
|
+
const noAuthRoute = isNoAuthRoute(location.pathname);
|
|
114
117
|
|
|
115
118
|
// 非免认证路由:走 SSO 流程
|
|
116
|
-
if (!
|
|
119
|
+
if (!noAuthRoute) {
|
|
117
120
|
await ensureSsoSession();
|
|
118
121
|
}
|
|
119
122
|
|
|
@@ -128,6 +131,15 @@ export async function getInitialState(): Promise<{
|
|
|
128
131
|
}
|
|
129
132
|
}
|
|
130
133
|
|
|
134
|
+
// 非免认证路由且没有 token,跳转到 SSO 登录
|
|
135
|
+
if (!noAuthRoute) {
|
|
136
|
+
handleAuthFailureRedirect();
|
|
137
|
+
// 返回空状态,页面会被重定向
|
|
138
|
+
return {
|
|
139
|
+
fetchUserInfo: fetchUserInfoFn,
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
|
|
131
143
|
return {
|
|
132
144
|
fetchUserInfo: fetchUserInfoFn,
|
|
133
145
|
};
|
|
@@ -157,6 +157,10 @@ declare global {
|
|
|
157
157
|
apiBaseUrl?: string;
|
|
158
158
|
/** 默认重定向路径,访问 "/" 时自动跳转到此路径 */
|
|
159
159
|
defaultPath?: string;
|
|
160
|
+
/** 动态配置的免鉴权路由列表,支持精确匹配和前缀匹配(以 /* 结尾) */
|
|
161
|
+
noAuthRouteList?: string[];
|
|
162
|
+
/** 动态配置的不显示布局路由列表,支持精确匹配和前缀匹配(以 /* 结尾) */
|
|
163
|
+
noLayoutRouteList?: string[];
|
|
160
164
|
[key: string]: unknown;
|
|
161
165
|
};
|
|
162
166
|
__MICO_WORKSPACE__?: WorkspaceConfig | null;
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
|
|
17
17
|
import { request as rawRequest } from '@umijs/max';
|
|
18
18
|
import { setStoredAuthToken } from '../auth/auth-manager';
|
|
19
|
-
import {
|
|
19
|
+
import { isNoAuthRoute } from '@/constants';
|
|
20
20
|
|
|
21
21
|
// 配置相关
|
|
22
22
|
import {
|
|
@@ -64,7 +64,7 @@ initDefaultInterceptors(isFetchingToken, addToPendingQueue);
|
|
|
64
64
|
* 判断当前路由是否跳过认证
|
|
65
65
|
*/
|
|
66
66
|
const shouldSkipAuth = (): boolean => {
|
|
67
|
-
return
|
|
67
|
+
return isNoAuthRoute(location.pathname);
|
|
68
68
|
};
|
|
69
69
|
|
|
70
70
|
/**
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { findRouteByPath } from '@/common/menu';
|
|
2
|
-
import {
|
|
2
|
+
import { isNoLayoutRoute } from '@/constants';
|
|
3
3
|
import useMenu from '@/hooks/useMenu';
|
|
4
4
|
import { Tabs } from '@arco-design/web-react';
|
|
5
5
|
import { history, useLocation } from '@umijs/max';
|
|
@@ -47,7 +47,7 @@ function AppTabs() {
|
|
|
47
47
|
const [tabs, setTabs] = useState<TabItem[]>([]);
|
|
48
48
|
|
|
49
49
|
const showTabs = useMemo(() => {
|
|
50
|
-
return !
|
|
50
|
+
return !isNoLayoutRoute(location.pathname);
|
|
51
51
|
}, [location.pathname]);
|
|
52
52
|
|
|
53
53
|
const microAppPrefixes = useMemo(() => {
|
|
@@ -19,7 +19,7 @@ export const ROUTES = {
|
|
|
19
19
|
} as const;
|
|
20
20
|
|
|
21
21
|
/**
|
|
22
|
-
*
|
|
22
|
+
* 无需认证的路由列表(静态配置)
|
|
23
23
|
*/
|
|
24
24
|
export const NO_AUTH_ROUTE_LIST: string[] = [
|
|
25
25
|
ROUTES.LOGIN,
|
|
@@ -29,12 +29,78 @@ export const NO_AUTH_ROUTE_LIST: string[] = [
|
|
|
29
29
|
ROUTES.NOT_FOUND,
|
|
30
30
|
];
|
|
31
31
|
|
|
32
|
+
/**
|
|
33
|
+
* 获取合并后的免鉴权路由列表
|
|
34
|
+
* 合并静态常量 + window.__MICO_CONFIG__.noAuthRouteList
|
|
35
|
+
*/
|
|
36
|
+
export const getNoAuthRouteList = (): string[] => {
|
|
37
|
+
const dynamicRoutes = window.__MICO_CONFIG__?.noAuthRouteList ?? [];
|
|
38
|
+
return [...new Set([...NO_AUTH_ROUTE_LIST, ...dynamicRoutes])];
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* 判断路径是否匹配路由列表(支持精确匹配和前缀匹配)
|
|
43
|
+
* @param pathname - 当前路由路径
|
|
44
|
+
* @param routes - 路由列表
|
|
45
|
+
* @returns 是否匹配
|
|
46
|
+
*/
|
|
47
|
+
const matchRouteList = (pathname: string, routes: string[]): boolean => {
|
|
48
|
+
return routes.some((route) => {
|
|
49
|
+
// 前缀匹配:/public/* 匹配 /public/xxx
|
|
50
|
+
if (route.endsWith('/*')) {
|
|
51
|
+
const prefix = route.slice(0, -1); // 去掉末尾的 *,保留 /
|
|
52
|
+
return pathname.startsWith(prefix) || pathname === prefix.slice(0, -1);
|
|
53
|
+
}
|
|
54
|
+
// 精确匹配
|
|
55
|
+
return pathname === route;
|
|
56
|
+
});
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* 判断指定路径是否为免鉴权路由
|
|
61
|
+
* 支持精确匹配和前缀匹配(以 /* 结尾的模式)
|
|
62
|
+
* @param pathname - 当前路由路径
|
|
63
|
+
* @returns 是否免鉴权
|
|
64
|
+
*/
|
|
65
|
+
export const isNoAuthRoute = (pathname: string): boolean => {
|
|
66
|
+
return matchRouteList(pathname, getNoAuthRouteList());
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* 不显示布局的路由列表(静态配置)
|
|
71
|
+
* 注意:403/404 保留布局,方便用户通过导航返回
|
|
72
|
+
*/
|
|
73
|
+
export const NO_LAYOUT_ROUTE_LIST: string[] = [
|
|
74
|
+
ROUTES.LOGIN,
|
|
75
|
+
ROUTES.REGISTER,
|
|
76
|
+
ROUTES.REGISTER_RESULT,
|
|
77
|
+
];
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* 获取合并后的不显示布局路由列表
|
|
81
|
+
* 合并静态常量 + window.__MICO_CONFIG__.noLayoutRouteList
|
|
82
|
+
*/
|
|
83
|
+
export const getNoLayoutRouteList = (): string[] => {
|
|
84
|
+
const dynamicRoutes = window.__MICO_CONFIG__?.noLayoutRouteList ?? [];
|
|
85
|
+
return [...new Set([...NO_LAYOUT_ROUTE_LIST, ...dynamicRoutes])];
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* 判断指定路径是否不显示布局
|
|
90
|
+
* 支持精确匹配和前缀匹配(以 /* 结尾的模式)
|
|
91
|
+
* @param pathname - 当前路由路径
|
|
92
|
+
* @returns 是否不显示布局
|
|
93
|
+
*/
|
|
94
|
+
export const isNoLayoutRoute = (pathname: string): boolean => {
|
|
95
|
+
return matchRouteList(pathname, getNoLayoutRouteList());
|
|
96
|
+
};
|
|
97
|
+
|
|
32
98
|
/**
|
|
33
99
|
* 主题相关常量
|
|
34
100
|
*/
|
|
35
101
|
export const THEME = {
|
|
36
102
|
/** localStorage 存储键 */
|
|
37
|
-
STORAGE_KEY: '
|
|
103
|
+
STORAGE_KEY: '<%= ProjectName %>-theme',
|
|
38
104
|
/** 默认主题 */
|
|
39
105
|
DEFAULT: 'light' as const,
|
|
40
106
|
/** 可选主题值 */
|
|
@@ -46,9 +112,9 @@ export const THEME = {
|
|
|
46
112
|
*/
|
|
47
113
|
export const TIMEZONE = {
|
|
48
114
|
/** localStorage 存储键(IANA 时区,如 Asia/Shanghai) */
|
|
49
|
-
STORAGE_KEY: '
|
|
115
|
+
STORAGE_KEY: '<%= ProjectName %>-timezone',
|
|
50
116
|
/** localStorage 存储键(用于展示的地区/名称,可选) */
|
|
51
|
-
REGION_STORAGE_KEY: '
|
|
117
|
+
REGION_STORAGE_KEY: '<%= ProjectName %>-timezone-region',
|
|
52
118
|
} as const;
|
|
53
119
|
|
|
54
120
|
/**
|
|
@@ -56,7 +122,7 @@ export const TIMEZONE = {
|
|
|
56
122
|
*/
|
|
57
123
|
export const PRESENCE = {
|
|
58
124
|
/** localStorage 存储键 */
|
|
59
|
-
STORAGE_KEY: '
|
|
125
|
+
STORAGE_KEY: '<%= ProjectName %>-presence-status',
|
|
60
126
|
} as const;
|
|
61
127
|
|
|
62
128
|
/**
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useLocation, useModel } from '@umijs/max';
|
|
2
2
|
import { useEffect, useRef, useState } from 'react';
|
|
3
3
|
import { layoutLogger } from '@/common/logger';
|
|
4
|
-
import {
|
|
4
|
+
import { isNoAuthRoute } from '@/constants';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* 路由切换时自动刷新用户权限
|
|
@@ -39,7 +39,7 @@ export function useRoutePermissionRefresh() {
|
|
|
39
39
|
prevPathRef.current = location.pathname;
|
|
40
40
|
|
|
41
41
|
// 免认证路由不需要刷新
|
|
42
|
-
if (
|
|
42
|
+
if (isNoAuthRoute(location.pathname)) {
|
|
43
43
|
return;
|
|
44
44
|
}
|
|
45
45
|
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
import { getAppNameFromEntry } from '@/common/micro';
|
|
9
9
|
import AppTabs from '@/components/AppTabs';
|
|
10
10
|
import MicroAppLoader from '@/components/MicroAppLoader';
|
|
11
|
-
import {
|
|
11
|
+
import { isNoLayoutRoute } from '@/constants';
|
|
12
12
|
import { useRoutePermissionRefresh } from '@/hooks/useRoutePermissionRefresh';
|
|
13
13
|
import ForbiddenPage from '@/pages/403';
|
|
14
14
|
import { Layout, Spin } from '@arco-design/web-react';
|
|
@@ -83,7 +83,7 @@ const BasicLayout: React.FC = () => {
|
|
|
83
83
|
}, [currentRoute, allRoutes, allowedRoutes, location.pathname, filterOptions]);
|
|
84
84
|
|
|
85
85
|
// 判断是否需要显示布局
|
|
86
|
-
const showLayout = !
|
|
86
|
+
const showLayout = !isNoLayoutRoute(location.pathname);
|
|
87
87
|
|
|
88
88
|
// 渲染页面内容
|
|
89
89
|
const renderContent = () => {
|
|
@@ -17,6 +17,7 @@ export interface IUserInfoResponse {
|
|
|
17
17
|
export async function fetchUserInfo(): Promise<IUserInfo> {
|
|
18
18
|
const response = await request<IUserInfoResponse>(USER_INFO_API, {
|
|
19
19
|
method: 'GET',
|
|
20
|
+
skipProxy: true,
|
|
20
21
|
});
|
|
21
22
|
if (response.code === 200 && response.data) {
|
|
22
23
|
return response.data;
|
|
@@ -177,12 +177,21 @@ module.exports = class extends Generator {
|
|
|
177
177
|
}
|
|
178
178
|
}
|
|
179
179
|
|
|
180
|
+
install() {
|
|
181
|
+
this.log('');
|
|
182
|
+
this.log('📦 正在安装依赖...');
|
|
183
|
+
this.spawnCommandSync('pnpm', ['install'], {
|
|
184
|
+
cwd: this.monorepoRoot
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
|
|
180
188
|
end() {
|
|
181
189
|
this.log('');
|
|
182
190
|
this.log('✅ 子应用创建成功!');
|
|
183
191
|
this.log('');
|
|
192
|
+
this.log(' 后续步骤:');
|
|
193
|
+
this.log('');
|
|
184
194
|
this.log(` cd apps/${this.appName}`);
|
|
185
|
-
this.log(' pnpm install');
|
|
186
195
|
this.log(' pnpm dev');
|
|
187
196
|
this.log('');
|
|
188
197
|
}
|