generator-mico-cli 0.2.27 → 0.2.29
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/README.md +5 -20
- package/bin/mico.js +27 -62
- package/generators/micro-react/index.js +8 -0
- package/generators/micro-react/templates/.cursor/rules/always-read-docs.mdc +3 -0
- package/generators/micro-react/templates/.cursor/rules/project-overview.mdc +1 -0
- package/generators/micro-react/templates/CLAUDE.md +1 -0
- package/generators/micro-react/templates/README.md +1 -1
- package/generators/micro-react/templates/apps/layout/config/config.dev.ts +7 -4
- package/generators/micro-react/templates/apps/layout/config/config.prod.development.ts +2 -2
- package/generators/micro-react/templates/apps/layout/config/config.prod.testing.ts +2 -2
- package/generators/micro-react/templates/apps/layout/config/config.prod.ts +3 -3
- package/generators/micro-react/templates/apps/layout/docs/feat-/346/236/204/345/273/272define/344/270/216/345/205/215/350/256/244/350/257/201/345/210/235/345/247/213/346/200/201.md +44 -0
- package/generators/micro-react/templates/apps/layout/docs/feature-PermissionFilter/346/214/211/351/222/256/346/235/203/351/231/220.md +116 -0
- package/generators/micro-react/templates/apps/layout/docs/feature-/345/276/256/345/211/215/347/253/257/346/250/241/345/274/217.md +11 -6
- package/generators/micro-react/templates/apps/layout/docs/feature-/350/217/234/345/215/225/346/235/203/351/231/220/346/216/247/345/210/266.md +83 -77
- package/generators/micro-react/templates/apps/layout/docs/feature-/350/267/257/347/224/261/344/270/216/350/217/234/345/215/225/350/247/243/350/200/246.md +50 -35
- package/generators/micro-react/templates/apps/layout/docs/feature-/350/267/257/347/224/261/346/235/203/351/231/220/346/227/245/345/277/227.md +162 -0
- package/generators/micro-react/templates/apps/layout/mock/api.mock.ts +23 -31
- package/generators/micro-react/templates/apps/layout/mock/pages.ts +5 -6
- package/generators/micro-react/templates/apps/layout/package.json +2 -1
- package/generators/micro-react/templates/apps/layout/src/app.tsx +31 -2
- package/generators/micro-react/templates/apps/layout/src/common/auth/type.ts +15 -27
- package/generators/micro-react/templates/apps/layout/src/common/menu/parser.ts +148 -85
- package/generators/micro-react/templates/apps/layout/src/common/menu/types.ts +2 -6
- package/generators/micro-react/templates/apps/layout/src/common/portal-data.ts +46 -2
- package/generators/micro-react/templates/apps/layout/src/components/MicroAppLoader/index.tsx +5 -1
- package/generators/micro-react/templates/apps/layout/src/components/PermissionFilter/index.tsx +51 -0
- package/generators/micro-react/templates/apps/layout/src/components/RightContent/AvatarDropdown.tsx +10 -1
- package/generators/micro-react/templates/apps/layout/src/layouts/components/menu/index.tsx +3 -3
- package/generators/micro-react/templates/apps/layout/src/layouts/index.tsx +105 -60
- package/generators/micro-react/templates/apps/layout/src/locales/en-US.ts +17 -0
- package/generators/micro-react/templates/apps/layout/src/locales/zh-CN.ts +16 -0
- package/generators/micro-react/templates/apps/layout/src/pages/404/index.tsx +7 -3
- package/generators/micro-react/templates/apps/layout/src/pages/Home/index.less +5 -0
- package/generators/micro-react/templates/apps/layout/src/pages/Home/index.tsx +49 -1
- package/generators/micro-react/templates/apps/layout/src/services/user.ts +28 -21
- package/generators/micro-react/templates/packages/common-intl/README.md +77 -369
- package/generators/micro-react/templates/packages/common-intl/package.json +3 -13
- package/generators/micro-react/templates/packages/common-intl/src/index.ts +3 -6
- package/generators/micro-react/templates/packages/common-intl/src/intl.ts +20 -23
- package/generators/micro-react/templates/packages/common-intl/tsconfig.json +2 -4
- package/generators/subapp-react/index.js +28 -22
- package/generators/subapp-react/templates/homepage/README.md +1 -0
- package/generators/subapp-react/templates/homepage/config/config.prod.development.ts +1 -0
- package/generators/subapp-react/templates/homepage/config/config.prod.testing.ts +1 -0
- package/generators/subapp-react/templates/homepage/config/config.prod.ts +1 -0
- package/generators/subapp-react/templates/homepage/docs/feature-PermissionFilter/346/214/211/351/222/256/346/235/203/351/231/220.md +35 -0
- package/generators/subapp-react/templates/homepage/package.json +2 -1
- package/generators/subapp-react/templates/homepage/src/app.tsx +7 -0
- package/generators/subapp-react/templates/homepage/src/common/mainApp.ts +39 -2
- package/generators/subapp-react/templates/homepage/src/components/PermissionFilter/index.tsx +48 -0
- package/generators/subapp-react/templates/homepage/src/pages/index.tsx +35 -1
- package/lib/utils.js +0 -1
- package/package.json +2 -2
- package/generators/micro-react/templates/apps/layout/docs/common-intl.md +0 -372
- package/generators/micro-react/templates/packages/common-intl/src/indexedDBUtils.ts +0 -51
- package/generators/micro-react/templates/packages/common-intl/src/utils.ts +0 -482
- package/generators/micro-react/templates/packages/common-intl/vite.config.ts +0 -25
package/README.md
CHANGED
|
@@ -17,29 +17,21 @@
|
|
|
17
17
|
## 要求
|
|
18
18
|
|
|
19
19
|
- Node >= 18
|
|
20
|
-
- Yeoman CLI: `npm install -g yo`
|
|
21
20
|
|
|
22
21
|
## 安装和使用
|
|
23
22
|
|
|
24
23
|
```bash
|
|
25
|
-
|
|
24
|
+
pnpm install -g generator-mico-cli
|
|
26
25
|
```
|
|
27
26
|
|
|
28
27
|
## 包装器 CLI
|
|
29
28
|
|
|
30
|
-
全局安装后,`mico`
|
|
31
|
-
如果 `generators/<name>` 下存在本地生成器,它将作为 `yo mico-cli:<name>` 运行。
|
|
29
|
+
全局安装后,`mico` 命令可用于运行内置的 Yeoman 生成器。
|
|
32
30
|
|
|
33
31
|
```bash
|
|
34
32
|
mico create subapp-react
|
|
35
33
|
```
|
|
36
34
|
|
|
37
|
-
等同于:
|
|
38
|
-
|
|
39
|
-
```bash
|
|
40
|
-
yo subapp-react
|
|
41
|
-
```
|
|
42
|
-
|
|
43
35
|
列出所有可用的生成器:
|
|
44
36
|
|
|
45
37
|
```bash
|
|
@@ -64,12 +56,6 @@ mico create micro-react --dry-run
|
|
|
64
56
|
mico doctor
|
|
65
57
|
```
|
|
66
58
|
|
|
67
|
-
将额外参数传递给生成器:
|
|
68
|
-
|
|
69
|
-
```bash
|
|
70
|
-
mico create micro-react -- --help
|
|
71
|
-
```
|
|
72
|
-
|
|
73
59
|
## 配置文件
|
|
74
60
|
|
|
75
61
|
可以在项目目录或用户主目录创建 `.micorc` 或 `.micorc.json` 文件来预设默认值。
|
|
@@ -168,8 +154,7 @@ DevTools Sources 面板中可在 `webpack://<name>/src/` 下定位原始 TypeScr
|
|
|
168
154
|
# 1. 安装依赖
|
|
169
155
|
pnpm install
|
|
170
156
|
|
|
171
|
-
# 2.
|
|
172
|
-
npm install -g yo
|
|
157
|
+
# 2. 将本地包链接到全局
|
|
173
158
|
npm link
|
|
174
159
|
|
|
175
160
|
# 3. 验证链接成功
|
|
@@ -216,9 +201,9 @@ pnpm test:watch
|
|
|
216
201
|
| `tests/integration/micro-react.test.js` | micro-react 生成器集成测试 |
|
|
217
202
|
| `tests/integration/subapp-react.test.js` | subapp-react 生成器集成测试 |
|
|
218
203
|
| `tests/integration/subapp-umd.test.js` | subapp-umd 生成器集成测试 |
|
|
219
|
-
| `tests/integration/mico-cli.test.js` | `mico` 入口子进程测试(help / version / list / create
|
|
204
|
+
| `tests/integration/mico-cli.test.js` | `mico` 入口子进程测试(help / version / list / create / doctor) |
|
|
220
205
|
| `tests/helpers/setup.js` | 测试共享工具(路径常量、monorepo fixture 工厂) |
|
|
221
|
-
| `tests/helpers/mico-subprocess.js` | 子进程运行 `bin/mico.js
|
|
206
|
+
| `tests/helpers/mico-subprocess.js` | 子进程运行 `bin/mico.js` 工具函数 |
|
|
222
207
|
|
|
223
208
|
### Scripts 说明
|
|
224
209
|
|
package/bin/mico.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
|
-
const {
|
|
4
|
+
const { spawnSync } = require('node:child_process');
|
|
5
5
|
const fs = require('node:fs');
|
|
6
6
|
const path = require('node:path');
|
|
7
7
|
const readline = require('node:readline');
|
|
@@ -56,7 +56,6 @@ Configuration:
|
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
Notes:
|
|
59
|
-
Requires Yeoman CLI (yo) installed globally.
|
|
60
59
|
If a local generator exists at generators/<name>, it will be used.
|
|
61
60
|
`);
|
|
62
61
|
}
|
|
@@ -164,15 +163,6 @@ async function runDoctor() {
|
|
|
164
163
|
console.log(' Install: npm install -g pnpm');
|
|
165
164
|
}
|
|
166
165
|
|
|
167
|
-
// Yeoman
|
|
168
|
-
const yoIcon = results.yo.available ? '\x1b[32m✓\x1b[0m' : '\x1b[31m✗\x1b[0m';
|
|
169
|
-
if (results.yo.available) {
|
|
170
|
-
console.log(` ${yoIcon} yo ${results.yo.version}`);
|
|
171
|
-
} else {
|
|
172
|
-
console.log(` ${yoIcon} yo not found`);
|
|
173
|
-
console.log(' Install: npm install -g yo');
|
|
174
|
-
}
|
|
175
|
-
|
|
176
166
|
// Git
|
|
177
167
|
const gitIcon = results.git.available ? '\x1b[32m✓\x1b[0m' : '\x1b[33m⚠\x1b[0m';
|
|
178
168
|
if (results.git.available) {
|
|
@@ -205,7 +195,7 @@ async function runDoctor() {
|
|
|
205
195
|
console.log('');
|
|
206
196
|
|
|
207
197
|
// 总结
|
|
208
|
-
const allGood = results.node.satisfied && results.pnpm.available
|
|
198
|
+
const allGood = results.node.satisfied && results.pnpm.available;
|
|
209
199
|
if (allGood) {
|
|
210
200
|
console.log(' \x1b[32m✅ All required dependencies are installed!\x1b[0m');
|
|
211
201
|
} else {
|
|
@@ -351,14 +341,12 @@ function performUpdate(latestVersion) {
|
|
|
351
341
|
/**
|
|
352
342
|
* 运行 Yeoman 生成器
|
|
353
343
|
* @param {string} generator - 生成器名称
|
|
354
|
-
* @param {string[]} rest - 剩余参数
|
|
355
|
-
* @param {string[]} passthroughArgs - 透传参数
|
|
356
344
|
* @param {object} options - 选项
|
|
357
345
|
* @param {boolean} options.verbose - 是否启用详细输出
|
|
358
346
|
* @param {boolean} options.dryRun - 是否启用 dry-run 模式
|
|
359
347
|
* @param {boolean} options.force - 是否强制覆盖已有文件
|
|
360
348
|
*/
|
|
361
|
-
function runGenerator(generator,
|
|
349
|
+
async function runGenerator(generator, options = {}) {
|
|
362
350
|
const localGeneratorEntry = path.join(
|
|
363
351
|
rootDir,
|
|
364
352
|
'generators',
|
|
@@ -366,42 +354,37 @@ function runGenerator(generator, rest, passthroughArgs, options = {}) {
|
|
|
366
354
|
'index.js'
|
|
367
355
|
);
|
|
368
356
|
|
|
357
|
+
if (!fs.existsSync(localGeneratorEntry)) {
|
|
358
|
+
console.error(`❌ Cannot find generator "${generator}".`);
|
|
359
|
+
console.error(` Expected: ${localGeneratorEntry}`);
|
|
360
|
+
console.error('');
|
|
361
|
+
console.error(' Run "mico list" to see available generators.');
|
|
362
|
+
process.exit(1);
|
|
363
|
+
}
|
|
364
|
+
|
|
369
365
|
const pkgName = typeof pkg.name === 'string' ? pkg.name : '';
|
|
370
366
|
const localNamespace =
|
|
371
367
|
pkgName.replace(/^generator-/, '') || pkgName || 'generator';
|
|
368
|
+
const namespace = `${localNamespace}:${generator}`;
|
|
372
369
|
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
: generator;
|
|
376
|
-
|
|
377
|
-
const yoArgs = [yoGenerator, ...rest];
|
|
378
|
-
|
|
379
|
-
// 添加 dry-run 参数给 yeoman
|
|
380
|
-
if (options.dryRun) {
|
|
381
|
-
yoArgs.push('--dry-run');
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
if (options.force) {
|
|
385
|
-
yoArgs.push('--force');
|
|
370
|
+
if (options.verbose) {
|
|
371
|
+
process.env.MICO_VERBOSE = '1';
|
|
386
372
|
}
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
yoArgs.push('--', ...passthroughArgs);
|
|
373
|
+
if (options.dryRun) {
|
|
374
|
+
process.env.MICO_DRY_RUN = '1';
|
|
390
375
|
}
|
|
391
376
|
|
|
392
|
-
|
|
393
|
-
const env = { ...process.env };
|
|
394
|
-
if (options.verbose) {
|
|
395
|
-
env.MICO_VERBOSE = '1';
|
|
396
|
-
}
|
|
377
|
+
const runOptions = {};
|
|
397
378
|
if (options.dryRun) {
|
|
398
|
-
|
|
379
|
+
runOptions.dryRun = true;
|
|
380
|
+
}
|
|
381
|
+
if (options.force) {
|
|
382
|
+
runOptions.force = true;
|
|
399
383
|
}
|
|
400
384
|
|
|
401
385
|
if (options.verbose) {
|
|
402
386
|
console.log('');
|
|
403
|
-
console.log(' \x1b[2m[verbose] Running generator:',
|
|
404
|
-
console.log(' \x1b[2m[verbose] Arguments:', yoArgs.join(' '), '\x1b[0m');
|
|
387
|
+
console.log(' \x1b[2m[verbose] Running generator:', namespace, '\x1b[0m');
|
|
405
388
|
if (options.dryRun) {
|
|
406
389
|
console.log(' \x1b[2m[verbose] Dry-run mode enabled\x1b[0m');
|
|
407
390
|
}
|
|
@@ -411,28 +394,10 @@ function runGenerator(generator, rest, passthroughArgs, options = {}) {
|
|
|
411
394
|
console.log('');
|
|
412
395
|
}
|
|
413
396
|
|
|
414
|
-
const
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
if (!exited) {
|
|
419
|
-
exited = true;
|
|
420
|
-
process.exit(code);
|
|
421
|
-
}
|
|
422
|
-
};
|
|
423
|
-
|
|
424
|
-
child.on('error', (error) => {
|
|
425
|
-
if (error && error.code === 'ENOENT') {
|
|
426
|
-
console.error('Cannot find "yo". Install it with: npm install -g yo');
|
|
427
|
-
} else {
|
|
428
|
-
console.error(error);
|
|
429
|
-
}
|
|
430
|
-
quit(1);
|
|
431
|
-
});
|
|
432
|
-
|
|
433
|
-
child.on('exit', (code) => {
|
|
434
|
-
quit(typeof code === 'number' ? code : 1);
|
|
435
|
-
});
|
|
397
|
+
const yeomanEnv = require('yeoman-environment');
|
|
398
|
+
const env = yeomanEnv.createEnv();
|
|
399
|
+
env.register(localGeneratorEntry, namespace);
|
|
400
|
+
await env.run(namespace, runOptions);
|
|
436
401
|
}
|
|
437
402
|
|
|
438
403
|
/**
|
|
@@ -536,7 +501,7 @@ async function main() {
|
|
|
536
501
|
}
|
|
537
502
|
|
|
538
503
|
// 运行生成器
|
|
539
|
-
runGenerator(generator,
|
|
504
|
+
await runGenerator(generator, {
|
|
540
505
|
verbose: isVerbose,
|
|
541
506
|
dryRun: isDryRun,
|
|
542
507
|
force: isForce
|
|
@@ -109,6 +109,12 @@ module.exports = class extends Generator {
|
|
|
109
109
|
message: 'Author',
|
|
110
110
|
default: rc.author || 'Your Name <email@example.com>',
|
|
111
111
|
},
|
|
112
|
+
{
|
|
113
|
+
type: 'input',
|
|
114
|
+
name: 'intlTag',
|
|
115
|
+
message: '多语言中台 tag(如暂无可留空,后续在 packages/common-intl/src/intl.ts 中配置)',
|
|
116
|
+
default: rc.intlTag || 'fake_tag_does_not_exist',
|
|
117
|
+
},
|
|
112
118
|
]);
|
|
113
119
|
|
|
114
120
|
this.projectName = toKebab(this.answers.projectName);
|
|
@@ -116,6 +122,7 @@ module.exports = class extends Generator {
|
|
|
116
122
|
this.packageScope = this.answers.packageScope;
|
|
117
123
|
this.cdnPrefix = this.answers.cdnPrefix;
|
|
118
124
|
this.author = this.answers.author;
|
|
125
|
+
this.intlTag = this.answers.intlTag;
|
|
119
126
|
this.templateDir = this.templatePath();
|
|
120
127
|
this.destDir = this.projectRoot;
|
|
121
128
|
} catch (error) {
|
|
@@ -172,6 +179,7 @@ module.exports = class extends Generator {
|
|
|
172
179
|
themeVersion: `^${themeVer}`,
|
|
173
180
|
cdnPrefix: this.cdnPrefix,
|
|
174
181
|
cdnPrefixPath, // 用于拼接路径,已包含尾部斜杠
|
|
182
|
+
intlTag: this.intlTag,
|
|
175
183
|
};
|
|
176
184
|
|
|
177
185
|
this.logger.verbose('Template data:', JSON.stringify(templateData, null, 2));
|
|
@@ -25,6 +25,7 @@ apps/layout/docs/
|
|
|
25
25
|
├── feature-菜单权限控制.md # sideMenus 白名单、认证与授权分离
|
|
26
26
|
├── feature-主题色切换.md # 主题系统实现
|
|
27
27
|
├── feature-404页面.md # 404 页面
|
|
28
|
+
├── feature-PermissionFilter按钮权限.md # 按钮级权限 PermissionFilter
|
|
28
29
|
├── arch-请求模块.md # HTTP 请求层模块化设计
|
|
29
30
|
├── arch-日志与常量.md # Logger 工具与常量管理
|
|
30
31
|
├── common-intl.md # 国际化公共包
|
|
@@ -41,6 +42,7 @@ apps/layout/docs/
|
|
|
41
42
|
### Step 1: 确认需求涉及的模块
|
|
42
43
|
- 微前端相关 → 阅读 `apps/layout/docs/feature-微前端模式.md`
|
|
43
44
|
- 路由/菜单/权限相关 → 阅读 `apps/layout/docs/feature-路由与菜单解耦.md` 和 `apps/layout/docs/feature-菜单权限控制.md`
|
|
45
|
+
- 按钮级权限(PermissionFilter)→ 阅读 `apps/layout/docs/feature-PermissionFilter按钮权限.md`;子应用见 `apps/<subapp>/docs/feature-PermissionFilter按钮权限.md`
|
|
44
46
|
- 主题/样式相关 → 阅读 `apps/layout/docs/feature-主题色切换.md`
|
|
45
47
|
- 请求/认证相关 → 阅读 `apps/layout/docs/arch-请求模块.md` 和 `src/common/auth/` 源码
|
|
46
48
|
- 菜单相关 → 阅读 `src/common/menu/` 源码
|
|
@@ -64,6 +66,7 @@ find . -name "*.md" -type f | grep -v node_modules
|
|
|
64
66
|
| 新建子应用 | `apps/layout/docs/feature-微前端模式.md` |
|
|
65
67
|
| 路由/页面配置 | `apps/layout/docs/feature-路由与菜单解耦.md` |
|
|
66
68
|
| 权限控制 | `apps/layout/docs/feature-菜单权限控制.md` |
|
|
69
|
+
| 按钮级权限 PermissionFilter | `apps/layout/docs/feature-PermissionFilter按钮权限.md` |
|
|
67
70
|
| 主题适配 | `apps/layout/docs/feature-主题色切换.md` |
|
|
68
71
|
| 提交代码 | `docs/commit-message.md` |
|
|
69
72
|
| API 请求 | `apps/layout/docs/arch-请求模块.md` |
|
|
@@ -126,6 +126,7 @@ Jenkins 根据环境执行 `CICD/` 下的构建脚本:
|
|
|
126
126
|
- [微前端模式](mdc:apps/layout/docs/feature-微前端模式.md)
|
|
127
127
|
- [路由与菜单解耦](mdc:apps/layout/docs/feature-路由与菜单解耦.md)
|
|
128
128
|
- [菜单权限控制](mdc:apps/layout/docs/feature-菜单权限控制.md)
|
|
129
|
+
- [PermissionFilter 按钮权限](mdc:apps/layout/docs/feature-PermissionFilter按钮权限.md)
|
|
129
130
|
- [主题色切换](mdc:apps/layout/docs/feature-主题色切换.md)
|
|
130
131
|
- [提交规范](mdc:docs/commit-message.md)
|
|
131
132
|
- [部署说明](mdc:deployDesc.md)
|
|
@@ -123,6 +123,7 @@ layout 应用作为 qiankun 主应用,子应用通过 Umi 配置中的 qiankun
|
|
|
123
123
|
| [微前端模式](./apps/layout/docs/feature-微前端模式.md) | qiankun 架构、MicroAppLoader、子应用配置 |
|
|
124
124
|
| [路由与菜单解耦](./apps/layout/docs/feature-路由与菜单解耦.md) | PAGES/MENUS 数据源分离、双层权限控制 |
|
|
125
125
|
| [菜单权限控制](./apps/layout/docs/feature-菜单权限控制.md) | sideMenus 白名单、认证与授权分离 |
|
|
126
|
+
| [PermissionFilter 按钮权限](./apps/layout/docs/feature-PermissionFilter按钮权限.md) | `button_perms`、主/子应用用法与数据流 |
|
|
126
127
|
| [主题色切换](./apps/layout/docs/feature-主题色切换.md) | useTheme Hook、CSS 变量系统 |
|
|
127
128
|
| [请求模块架构](./apps/layout/docs/arch-请求模块.md) | HTTP 请求层模块化设计 |
|
|
128
129
|
| [日志与常量](./apps/layout/docs/arch-日志与常量.md) | Logger 工具与常量管理 |
|
|
@@ -27,12 +27,15 @@ const config: ReturnType<typeof defineConfig> = {
|
|
|
27
27
|
defaultPath: '',
|
|
28
28
|
// 免认证路由(跳过 SSO 登录),支持 /* 前缀匹配
|
|
29
29
|
// noAuthRouteList: ['/*'],
|
|
30
|
-
//
|
|
31
|
-
noPermissionRouteList: [],
|
|
30
|
+
// 免权限校验路由(跳过菜单权限检查),与 mock pages 中 accessControlEnabled 示例一致时可按需配置
|
|
31
|
+
noPermissionRouteList: ['/group-management/*', '/logout/*'],
|
|
32
32
|
// 不显示布局的路由(全屏页面)
|
|
33
33
|
// noLayoutRouteList: [],
|
|
34
34
|
// 关闭权限控制(调试用)
|
|
35
35
|
disableAuth: false,
|
|
36
|
+
// SSO 外跳地址(与 resolveExternalLoginPath 读取的 externalLoginPath 一致;生产由注入的 __MICO_CONFIG__ 提供)
|
|
37
|
+
externalLoginPath:
|
|
38
|
+
'https://micous-idp.cig.tencentcs.com/sso/xxxxxxx/cas',
|
|
36
39
|
};
|
|
37
40
|
`,
|
|
38
41
|
},
|
|
@@ -68,8 +71,8 @@ const config: ReturnType<typeof defineConfig> = {
|
|
|
68
71
|
'https://dashboard-api-test.micoplatform.com/api/yufu_login/',
|
|
69
72
|
'process.env.REFRESH_ENDPOINT':
|
|
70
73
|
'https://dashboard-api-test.micoplatform.com/api/yufu_login/refresh/',
|
|
71
|
-
'process.env.
|
|
72
|
-
'https://
|
|
74
|
+
'process.env.LOCALE_REQUEST_URL':
|
|
75
|
+
'https://api-test.micoplatform.com/lang_server/pull',
|
|
73
76
|
},
|
|
74
77
|
};
|
|
75
78
|
|
|
@@ -33,8 +33,8 @@ const config: ReturnType<typeof defineConfig> = {
|
|
|
33
33
|
'https://dashboard-api-test.micoplatform.com/api/yufu_login/',
|
|
34
34
|
'process.env.REFRESH_ENDPOINT':
|
|
35
35
|
'https://dashboard-api-test.micoplatform.com/api/yufu_login/refresh/',
|
|
36
|
-
'process.env.
|
|
37
|
-
'https://
|
|
36
|
+
'process.env.LOCALE_REQUEST_URL':
|
|
37
|
+
'https://api-test.micoplatform.com/lang_server/pull',
|
|
38
38
|
},
|
|
39
39
|
};
|
|
40
40
|
|
|
@@ -33,8 +33,8 @@ const config: ReturnType<typeof defineConfig> = {
|
|
|
33
33
|
'https://dashboard-api-test.micoplatform.com/api/yufu_login/',
|
|
34
34
|
'process.env.REFRESH_ENDPOINT':
|
|
35
35
|
'https://dashboard-api-test.micoplatform.com/api/yufu_login/refresh/',
|
|
36
|
-
'process.env.
|
|
37
|
-
'https://
|
|
36
|
+
'process.env.LOCALE_REQUEST_URL':
|
|
37
|
+
'https://api-test.micoplatform.com/lang_server/pull',
|
|
38
38
|
},
|
|
39
39
|
};
|
|
40
40
|
|
|
@@ -21,8 +21,8 @@ const config: ReturnType<typeof defineConfig> = {
|
|
|
21
21
|
'https://dashboard-api.micoplatform.com/api/yufu_login/',
|
|
22
22
|
'process.env.REFRESH_ENDPOINT':
|
|
23
23
|
'https://dashboard-api.micoplatform.com/api/yufu_login/refresh/',
|
|
24
|
-
'process.env.
|
|
25
|
-
'https://
|
|
24
|
+
'process.env.LOCALE_REQUEST_URL':
|
|
25
|
+
'https://api.micoplatform.com/lang_server/pull',
|
|
26
26
|
},
|
|
27
27
|
externals: {
|
|
28
28
|
react: 'React',
|
|
@@ -33,7 +33,7 @@ const config: ReturnType<typeof defineConfig> = {
|
|
|
33
33
|
extraBabelPlugins: ['babel-plugin-dynamic-import-node'],
|
|
34
34
|
|
|
35
35
|
// 禁用代码分割,只输出一个 JS 和一个 CSS
|
|
36
|
-
chainWebpack(memo) {
|
|
36
|
+
chainWebpack(memo: any) {
|
|
37
37
|
// 禁用 splitChunks
|
|
38
38
|
memo.optimization.splitChunks(false);
|
|
39
39
|
// 禁用 runtimeChunk
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# 构建 define 精简与免认证页初始态
|
|
2
|
+
|
|
3
|
+
> 创建时间:2026-03-24
|
|
4
|
+
|
|
5
|
+
## 功能概述
|
|
6
|
+
|
|
7
|
+
1. **Umi `define`**:不再通过构建期注入 `process.env.EXTERNAL_LOGIN_PATH`(本模板从未注入 `APP_ID`);SSO 外跳等依赖运行时 `window.__MICO_CONFIG__`。
|
|
8
|
+
2. **`getInitialState`**:免认证路由下即使本地仍有 token,也不请求用户信息接口,减少无效调用。
|
|
9
|
+
|
|
10
|
+
## 技术方案
|
|
11
|
+
|
|
12
|
+
### 开发环境
|
|
13
|
+
|
|
14
|
+
`config/config.dev.ts` 的 `headScripts` 中 `window.__MICO_CONFIG__` 增加 **`externalLoginPath`**(模板默认为占位 URL,生成项目后请按实际 IdP 修改)。
|
|
15
|
+
|
|
16
|
+
`define` 仅保留 `NODE_ENV`、`API_BASE_URL`、`LOGIN_ENDPOINT`、`REFRESH_ENDPOINT`。
|
|
17
|
+
|
|
18
|
+
### 生产 / 测试构建
|
|
19
|
+
|
|
20
|
+
`config.prod.ts`、`config.prod.development.ts`、`config.prod.testing.ts` 的 `define` 中同样**不包含** `EXTERNAL_LOGIN_PATH`,由部署时注入的 `__MICO_CONFIG__.externalLoginPath`(或 `__MICO_WORKSPACE__.casServerLoginUrl`)提供。
|
|
21
|
+
|
|
22
|
+
### 初始态
|
|
23
|
+
|
|
24
|
+
`src/app.tsx` 中仅在 `getStoredAuthToken() && !skipAuth` 时调用 `fetchUserInfoFn()`;`skipAuth = isNoAuthRoute || isPageAuthFree`。
|
|
25
|
+
|
|
26
|
+
## 文件清单
|
|
27
|
+
|
|
28
|
+
| 文件路径 | 说明 |
|
|
29
|
+
| --- | --- |
|
|
30
|
+
| `config/config.dev.ts` | `__MICO_CONFIG__.externalLoginPath`;移除 `EXTERNAL_LOGIN_PATH` define |
|
|
31
|
+
| `config/config.prod.ts` | 移除 `EXTERNAL_LOGIN_PATH` define |
|
|
32
|
+
| `config/config.prod.development.ts` | 同上 |
|
|
33
|
+
| `config/config.prod.testing.ts` | 同上 |
|
|
34
|
+
| `src/app.tsx` | `fetchUserInfo` 条件增加 `!skipAuth` |
|
|
35
|
+
|
|
36
|
+
## 部署注意
|
|
37
|
+
|
|
38
|
+
- 生产 HTML 或网关需保证 `window.__MICO_CONFIG__` 含 **`appId`**、**`externalLoginPath`**(或与现有网关字段对齐),否则代理前缀、SSO 外跳可能异常。
|
|
39
|
+
- 详见 `src/common/request/config.ts` 中 `buildDefaultClientOptions` 的解析顺序。
|
|
40
|
+
|
|
41
|
+
## 相关文档
|
|
42
|
+
|
|
43
|
+
- [fix-SSO无限重定向](./fix-SSO无限重定向.md)
|
|
44
|
+
- [arch-请求模块](./arch-请求模块.md)
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
# PermissionFilter 按钮权限
|
|
2
|
+
|
|
3
|
+
> 创建时间:2025-03-27
|
|
4
|
+
|
|
5
|
+
## 功能概述
|
|
6
|
+
|
|
7
|
+
`PermissionFilter` 用于按**按钮级权限**控制 UI 是否渲染:传入权限标识 `permissionKey`,仅当当前用户具备该权限(或为超级用户)时渲染 `children`,否则渲染可选的 `fallback`。
|
|
8
|
+
|
|
9
|
+
- **主应用(layout)**:权限数据来自 `fetchUserInfo` 返回的用户信息字段 `button_perms`、`is_superuser`(与 `initialState.currentUser` 一致)。
|
|
10
|
+
- **子应用**:不直接请求用户接口,权限列表由主应用通过 qiankun 注入到子应用 props,与主应用同一套 `button_perms` 语义。
|
|
11
|
+
|
|
12
|
+
与侧栏菜单使用的 `menu_perms` / `routeKey` 不同,`button_perms` 面向页面内按钮、操作入口等细粒度控制。详见 [菜单权限控制](./feature-菜单权限控制.md) 中对 `button_perms` 的说明。
|
|
13
|
+
|
|
14
|
+
## 技术方案
|
|
15
|
+
|
|
16
|
+
### 技术栈
|
|
17
|
+
|
|
18
|
+
- React 18 + TypeScript
|
|
19
|
+
- 主应用:`useModel('@@initialState')` 读取 `currentUser`
|
|
20
|
+
- 子应用:`@/common/mainApp` 的 `useMainAppProps()`(内部 `useSyncExternalStore`,随 qiankun `update` 刷新)
|
|
21
|
+
|
|
22
|
+
### 主应用数据流
|
|
23
|
+
|
|
24
|
+
1. `GET /user/info/`(`services/user.ts` → `fetchUserInfo`)返回 `button_perms: string[]`、`is_superuser`。
|
|
25
|
+
2. `getInitialState`(`app.tsx`)在有 **token** 时拉取用户信息;免认证页同样需要 `currentUser` 时,逻辑为「有 token 即请求」,避免免认证页面无 `currentUser` 导致按钮权限恒为无权限。
|
|
26
|
+
3. `MicroAppLoader` 将 `button_perms`、`is_superuser` 放入传给子应用的 qiankun props,子应用 `setMainAppProps` 后可供 `PermissionFilter` 使用。
|
|
27
|
+
|
|
28
|
+
### 子应用数据流
|
|
29
|
+
|
|
30
|
+
1. 主应用 `MicroAppLoader` 构建 props 时写入 `button_perms`、`is_superuser`。
|
|
31
|
+
2. 子应用 `app.tsx` 中 qiankun `mount` / `update` 调用 `setMainAppProps(props)`。
|
|
32
|
+
3. `PermissionFilter` 通过 `useMainAppProps()` 订阅 props 变化;独立运行子应用时无主应用 props,默认视为无对应按钮权限(走 `fallback`)。
|
|
33
|
+
|
|
34
|
+
### 权限判定规则
|
|
35
|
+
|
|
36
|
+
1. `permissionKey` 为空或仅空白:视为**无权限**。
|
|
37
|
+
2. `is_superuser` 为真(含与现有 parser 一致的 `1`):视为**有全部按钮权限**(与 `isSuperuserUser` 行为一致)。
|
|
38
|
+
3. 否则:`button_perms.includes(permissionKey)` 为真则有权限。
|
|
39
|
+
|
|
40
|
+
## 文件清单
|
|
41
|
+
|
|
42
|
+
| 文件路径 | 说明 |
|
|
43
|
+
| --- | --- |
|
|
44
|
+
| `src/components/PermissionFilter/index.tsx` | 主应用组件 |
|
|
45
|
+
| `src/components/MicroAppLoader/index.tsx` | 向子应用注入 `button_perms`、`is_superuser` |
|
|
46
|
+
| `src/services/user.ts` | `fetchUserInfo` 规范化 `button_perms` |
|
|
47
|
+
| `src/app.tsx` | `getInitialState` 拉取 `currentUser` |
|
|
48
|
+
|
|
49
|
+
子应用:
|
|
50
|
+
|
|
51
|
+
| 文件路径 | 说明 |
|
|
52
|
+
| --- | --- |
|
|
53
|
+
| `src/components/PermissionFilter/index.tsx` | 子应用组件(基于 `useMainAppProps`) |
|
|
54
|
+
| `src/common/mainApp.ts` | qiankun props、`useMainAppProps`、`subscribeMainAppProps` |
|
|
55
|
+
|
|
56
|
+
## API / 组件接口
|
|
57
|
+
|
|
58
|
+
### Props(主应用与子应用一致)
|
|
59
|
+
|
|
60
|
+
```typescript
|
|
61
|
+
interface IPermissionFilterProps {
|
|
62
|
+
/** 按钮/操作权限标识,需在用户 button_perms 中命中 */
|
|
63
|
+
permissionKey: string;
|
|
64
|
+
children?: React.ReactNode;
|
|
65
|
+
/** 无权限时渲染;默认 null */
|
|
66
|
+
fallback?: React.ReactNode;
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## 使用示例
|
|
71
|
+
|
|
72
|
+
### 主应用
|
|
73
|
+
|
|
74
|
+
```tsx
|
|
75
|
+
import PermissionFilter from '@/components/PermissionFilter';
|
|
76
|
+
|
|
77
|
+
<PermissionFilter
|
|
78
|
+
permissionKey="cs_web_btn_export"
|
|
79
|
+
fallback={<span>无权限</span>}
|
|
80
|
+
>
|
|
81
|
+
<Button type="primary">导出</Button>
|
|
82
|
+
</PermissionFilter>
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
首页示例见 `src/pages/Home/index.tsx`;本地 Mock 可在 `mock/api.mock.ts` 的 `GET /user/info/` 响应 `data.button_perms` 中加入对应字符串。
|
|
86
|
+
|
|
87
|
+
### 子应用
|
|
88
|
+
|
|
89
|
+
```tsx
|
|
90
|
+
import PermissionFilter from '@/components/PermissionFilter';
|
|
91
|
+
|
|
92
|
+
<PermissionFilter
|
|
93
|
+
permissionKey="cs_web_btn_subapp_demo"
|
|
94
|
+
fallback={<Alert type="warning" content="无权限" />}
|
|
95
|
+
>
|
|
96
|
+
<Button type="primary">操作</Button>
|
|
97
|
+
</PermissionFilter>
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
示例见子应用 `src/pages/index.tsx`。嵌入主应用时,主应用用户信息中的 `button_perms` 需包含该 key;**独立运行**子应用时通常始终为无权限。
|
|
101
|
+
|
|
102
|
+
## Mock / 联调说明
|
|
103
|
+
|
|
104
|
+
- 主应用 Mock:`mock/api.mock.ts` 中 `button_perms` 数组需包含你在页面中使用的 `permissionKey`。
|
|
105
|
+
- 若页面在配置中为**免认证**(`accessControlEnabled: false`),仍依赖「有 token 时拉取用户信息」才能在主应用侧得到 `button_perms`;请确认 `app.tsx` 中 `getInitialState` 与当前模板一致。
|
|
106
|
+
|
|
107
|
+
## 注意事项
|
|
108
|
+
|
|
109
|
+
- 权限标识由后端/配置约定,需与 `button_perms` 中字符串完全一致(区分大小写)。
|
|
110
|
+
- 子应用请勿自行缓存过期的 `button_perms`;以 qiankun 传入的最新 props 为准(已用 `useMainAppProps` 订阅 `update`)。
|
|
111
|
+
|
|
112
|
+
## 相关文档
|
|
113
|
+
|
|
114
|
+
- [微前端模式](./feature-微前端模式.md)(MicroAppLoader、子应用加载)
|
|
115
|
+
- [菜单权限控制](./feature-菜单权限控制.md)(`menu_perms` 与 `button_perms` 分工)
|
|
116
|
+
- [路由与菜单解耦](./feature-路由与菜单解耦.md)
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
|
|
46
46
|
```typescript
|
|
47
47
|
interface MicroAppLoaderProps {
|
|
48
|
-
/**
|
|
48
|
+
/** 微应用在主应用中的挂载路径前缀(来自 ParsedRoute.base / 页面配置 base),经 normalizeMicroAppBase 后传给子应用 */
|
|
49
49
|
base: string;
|
|
50
50
|
/** 微应用入口 URL */
|
|
51
51
|
entry: string;
|
|
@@ -76,10 +76,10 @@ interface MicroAppLoaderProps {
|
|
|
76
76
|
|
|
77
77
|
| 字段 | 含义 |
|
|
78
78
|
| --- | --- |
|
|
79
|
-
| **base** |
|
|
79
|
+
| **base** | 该微应用在主应用中的**挂载前缀**,来自中台 **`page.base`**,解析为 **`ParsedRoute.base`**(缺省时解析为 **`'/'`**);可与动态路由 **`path`** 不同(如 `path` 为 `/subapp/*` 时 `base` 可为 `/subapp`)。 |
|
|
80
80
|
| **routePath** | 当前激活的**完整路由路径**,随浏览器 URL 变化,用于通知子应用做**内部路由同步**。 |
|
|
81
81
|
|
|
82
|
-
|
|
82
|
+
`routePath` 在布局内为 **`currentRoute.path`**。`base` 在 **`patchClientRoutes`** 与布局内均为 **`route.base` / `currentRoute.base`**。**`name`** 在布局侧常为基于 **`entry`** 的 **`getAppNameFromEntry`**,与 **`base`** 来源不同,需注意。
|
|
83
83
|
|
|
84
84
|
#### `normalizeMicroAppBase`
|
|
85
85
|
|
|
@@ -96,8 +96,8 @@ export const normalizeMicroAppBase = (path: string): string =>
|
|
|
96
96
|
|
|
97
97
|
| 位置 | 说明 |
|
|
98
98
|
| --- | --- |
|
|
99
|
-
| `src/app.tsx` `patchClientRoutes` | 动态注册微应用路由时:`React.createElement(MicroAppLoader, { entry, name: route.path, base: route.
|
|
100
|
-
| `src/layouts/index.tsx` | 布局内渲染:`<MicroAppLoader base={currentRoute.
|
|
99
|
+
| `src/app.tsx` `patchClientRoutes` | 动态注册微应用路由时:`React.createElement(MicroAppLoader, { entry, name: route.path, base: route.base, ... })` |
|
|
100
|
+
| `src/layouts/index.tsx` | 布局内渲染:`<MicroAppLoader base={currentRoute.base} routePath={currentRoute.path} ... />` |
|
|
101
101
|
|
|
102
102
|
#### 加载门控(认证)
|
|
103
103
|
|
|
@@ -165,6 +165,8 @@ export async function update(props: MicroAppProps) {
|
|
|
165
165
|
```typescript
|
|
166
166
|
interface ParsedRoute {
|
|
167
167
|
path: string;
|
|
168
|
+
/** 微应用挂载前缀(来自 page.base,解析缺省为 '/') */
|
|
169
|
+
base: string;
|
|
168
170
|
name: string;
|
|
169
171
|
icon: string;
|
|
170
172
|
/** 加载类型: internal(内部路由) | microapp(qiankun微应用) */
|
|
@@ -182,6 +184,8 @@ interface PageConfig {
|
|
|
182
184
|
id: number;
|
|
183
185
|
name: string;
|
|
184
186
|
route: string;
|
|
187
|
+
/** 微应用挂载前缀(可与 route 不同) */
|
|
188
|
+
base: string;
|
|
185
189
|
enabled: boolean;
|
|
186
190
|
/** 微应用 HTML 入口 URL (优先使用) */
|
|
187
191
|
htmlUrl: string | null;
|
|
@@ -254,7 +258,8 @@ const renderContent = () => {
|
|
|
254
258
|
<MicroAppLoader
|
|
255
259
|
entry={currentRoute.entry}
|
|
256
260
|
name={currentRoute.name}
|
|
257
|
-
base={currentRoute.
|
|
261
|
+
base={currentRoute.base}
|
|
262
|
+
routePath={currentRoute.path}
|
|
258
263
|
/>
|
|
259
264
|
);
|
|
260
265
|
}
|