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.
Files changed (58) hide show
  1. package/README.md +5 -20
  2. package/bin/mico.js +27 -62
  3. package/generators/micro-react/index.js +8 -0
  4. package/generators/micro-react/templates/.cursor/rules/always-read-docs.mdc +3 -0
  5. package/generators/micro-react/templates/.cursor/rules/project-overview.mdc +1 -0
  6. package/generators/micro-react/templates/CLAUDE.md +1 -0
  7. package/generators/micro-react/templates/README.md +1 -1
  8. package/generators/micro-react/templates/apps/layout/config/config.dev.ts +7 -4
  9. package/generators/micro-react/templates/apps/layout/config/config.prod.development.ts +2 -2
  10. package/generators/micro-react/templates/apps/layout/config/config.prod.testing.ts +2 -2
  11. package/generators/micro-react/templates/apps/layout/config/config.prod.ts +3 -3
  12. 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
  13. 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
  14. 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
  15. 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
  16. 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
  17. 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
  18. package/generators/micro-react/templates/apps/layout/mock/api.mock.ts +23 -31
  19. package/generators/micro-react/templates/apps/layout/mock/pages.ts +5 -6
  20. package/generators/micro-react/templates/apps/layout/package.json +2 -1
  21. package/generators/micro-react/templates/apps/layout/src/app.tsx +31 -2
  22. package/generators/micro-react/templates/apps/layout/src/common/auth/type.ts +15 -27
  23. package/generators/micro-react/templates/apps/layout/src/common/menu/parser.ts +148 -85
  24. package/generators/micro-react/templates/apps/layout/src/common/menu/types.ts +2 -6
  25. package/generators/micro-react/templates/apps/layout/src/common/portal-data.ts +46 -2
  26. package/generators/micro-react/templates/apps/layout/src/components/MicroAppLoader/index.tsx +5 -1
  27. package/generators/micro-react/templates/apps/layout/src/components/PermissionFilter/index.tsx +51 -0
  28. package/generators/micro-react/templates/apps/layout/src/components/RightContent/AvatarDropdown.tsx +10 -1
  29. package/generators/micro-react/templates/apps/layout/src/layouts/components/menu/index.tsx +3 -3
  30. package/generators/micro-react/templates/apps/layout/src/layouts/index.tsx +105 -60
  31. package/generators/micro-react/templates/apps/layout/src/locales/en-US.ts +17 -0
  32. package/generators/micro-react/templates/apps/layout/src/locales/zh-CN.ts +16 -0
  33. package/generators/micro-react/templates/apps/layout/src/pages/404/index.tsx +7 -3
  34. package/generators/micro-react/templates/apps/layout/src/pages/Home/index.less +5 -0
  35. package/generators/micro-react/templates/apps/layout/src/pages/Home/index.tsx +49 -1
  36. package/generators/micro-react/templates/apps/layout/src/services/user.ts +28 -21
  37. package/generators/micro-react/templates/packages/common-intl/README.md +77 -369
  38. package/generators/micro-react/templates/packages/common-intl/package.json +3 -13
  39. package/generators/micro-react/templates/packages/common-intl/src/index.ts +3 -6
  40. package/generators/micro-react/templates/packages/common-intl/src/intl.ts +20 -23
  41. package/generators/micro-react/templates/packages/common-intl/tsconfig.json +2 -4
  42. package/generators/subapp-react/index.js +28 -22
  43. package/generators/subapp-react/templates/homepage/README.md +1 -0
  44. package/generators/subapp-react/templates/homepage/config/config.prod.development.ts +1 -0
  45. package/generators/subapp-react/templates/homepage/config/config.prod.testing.ts +1 -0
  46. package/generators/subapp-react/templates/homepage/config/config.prod.ts +1 -0
  47. package/generators/subapp-react/templates/homepage/docs/feature-PermissionFilter/346/214/211/351/222/256/346/235/203/351/231/220.md +35 -0
  48. package/generators/subapp-react/templates/homepage/package.json +2 -1
  49. package/generators/subapp-react/templates/homepage/src/app.tsx +7 -0
  50. package/generators/subapp-react/templates/homepage/src/common/mainApp.ts +39 -2
  51. package/generators/subapp-react/templates/homepage/src/components/PermissionFilter/index.tsx +48 -0
  52. package/generators/subapp-react/templates/homepage/src/pages/index.tsx +35 -1
  53. package/lib/utils.js +0 -1
  54. package/package.json +2 -2
  55. package/generators/micro-react/templates/apps/layout/docs/common-intl.md +0 -372
  56. package/generators/micro-react/templates/packages/common-intl/src/indexedDBUtils.ts +0 -51
  57. package/generators/micro-react/templates/packages/common-intl/src/utils.ts +0 -482
  58. 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
- npm install -g yo generator-mico-cli
24
+ pnpm install -g generator-mico-cli
26
25
  ```
27
26
 
28
27
  ## 包装器 CLI
29
28
 
30
- 全局安装后,`mico` 命令可用于委托给 Yeoman 生成器。
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. 将本地包链接到全局(需要先安装 yo)
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`、假 `yo` 工具函数 |
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 { spawn, spawnSync } = require('node:child_process');
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 && results.yo.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, rest, passthroughArgs, options = {}) {
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
- const yoGenerator = fs.existsSync(localGeneratorEntry)
374
- ? `${localNamespace}:${generator}`
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
- if (passthroughArgs.length > 0) {
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
- env.MICO_DRY_RUN = '1';
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:', yoGenerator, '\x1b[0m');
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 child = spawn('yo', yoArgs, { stdio: 'inherit', env });
415
-
416
- let exited = false;
417
- const quit = (code) => {
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, rest.slice(1), passthroughArgs, {
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 工具与常量管理 |
@@ -79,7 +79,7 @@ pnpm list:preset
79
79
 
80
80
  ```bash
81
81
  # 全局安装(一次性)
82
- pnpm add -g yo mico-cli
82
+ pnpm add -g generator-mico-cli
83
83
 
84
84
  # 在仓库根目录执行
85
85
  mico create subapp-react
@@ -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.EXTERNAL_LOGIN_PATH':
72
- 'https://micous-idp.cig.tencentcs.com/sso/xxxxxxx/cas',
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.EXTERNAL_LOGIN_PATH':
37
- 'https://micous-idp.cig.tencentcs.com/sso/xxxxxxx/cas',
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.EXTERNAL_LOGIN_PATH':
37
- 'https://micous-idp.cig.tencentcs.com/sso/xxxxxxx/cas',
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.EXTERNAL_LOGIN_PATH':
25
- 'https://micous-idp.cig.tencentcs.com/sso/xxxxxxx/cas',
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
- /** 微应用在主应用中的挂载路径前缀(通常与当前页面 route.path 一致),经 normalizeMicroAppBase 后传给子应用 */
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** | 该微应用在主应用中的**根路径前缀**,一般等于菜单/动态路由里的 `path`。 |
79
+ | **base** | 该微应用在主应用中的**挂载前缀**,来自中台 **`page.base`**,解析为 **`ParsedRoute.base`**(缺省时解析为 **`'/'`**);可与动态路由 **`path`** 不同(如 `path` 为 `/subapp/*` 时 `base` 可为 `/subapp`)。 |
80
80
  | **routePath** | 当前激活的**完整路由路径**,随浏览器 URL 变化,用于通知子应用做**内部路由同步**。 |
81
81
 
82
- 二者在 `layouts/index.tsx` 中常同为 `currentRoute.path`;在 `app.tsx` `patchClientRoutes` 里与动态路由项一致,传入 **`base: route.path`**、**`name: route.path`**(`name` 另在布局侧可能改为基于 `entry``getAppNameFromEntry`,与 `base` 来源不同,需注意)。
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.path, ... })` |
100
- | `src/layouts/index.tsx` | 布局内渲染:`<MicroAppLoader base={currentRoute.path} routePath={currentRoute.path} ... />` |
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.path}
261
+ base={currentRoute.base}
262
+ routePath={currentRoute.path}
258
263
  />
259
264
  );
260
265
  }