Skip to content

Commit 278f712

Browse files
committed
feat: 多项目模式
1 parent 5c826fd commit 278f712

32 files changed

Lines changed: 1100 additions & 190 deletions

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
.DS_Store
22
/.idea
33
/dist
4+
/dist-projectA
5+
/dist-projectB
46
/node_modules
57
/.eslintcache
68
tsconfig.tsbuildinfo

README.md

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
- **<span role="img" aria-label="puzzle">🧩</span> 丰富组件生态**:沉淀 **120+** 高质量业务组件,支持 **独立打包发布 (@w.ui/wui-react)**。集成 **Storybook** 实现组件可视化开发与文档管理,显著提升复用效率。
3434
- **<span role="img" aria-label="shield">🛡️</span> 企业级权限体系**:完善的 **RBAC** 模型,支持路由、菜单、按钮级细粒度权限控制。内置路由自动过滤、`useSafeNavigate` 防越权跳转,保障系统安全。
3535
- **<span role="img" aria-label="zap">⚡️</span> 前沿技术栈**:采用 **TypeScript v5** + **Ant Design v6** + **Vite v7/Webpack v5** 双构建模式,紧跟社区最新标准,提供最佳开发体验。
36+
- **<span role="img" aria-label="layers">🧩</span> 多项目模式(共享依赖)**:通过 `PROJECT` 环境变量在同一仓库/同一套依赖(一个 `node_modules`)下承载多个业务项目;支持项目级入口与路由覆盖,按项目输出产物目录(`dist` / `dist-<project>`),新项目仅需新增 `src/projects/<project>` 业务代码。
3637
- **<span role="img" aria-label="robot">🤖</span> AI 智能化集成**:内置 ChatGPT 演示(支持 SSE 流式响应)、Markmap 思维导图生成、Mermaid 流程图渲染,探索 AI 在后台管理中的应用场景。
3738
- **<span role="img" aria-label="test-tube">🧪</span> 全链路质量保障**:集成 **Playwright** E2E 自动化测试,配合 Mock Service Worker (MSW) 实现真实的网络模拟与多角色权限切换测试。
3839
- **<span role="img" aria-label="mobile">📱</span> 极致移动端适配**:精心打磨的响应式布局,从 PC 到手机端提供一致的流畅体验。
@@ -54,6 +55,7 @@
5455
- <span role="img" aria-label="earth">🌍</span> **主题与国际化**:内置明亮/暗黑模式一键切换,支持多语言(i18n)动态切换。
5556
- <span role="img" aria-label="test-tube">🧪</span> **Mock 数据模拟**:基于 Faker.js 和 MSW 的纯前端 Mock 方案,脱离后端独立开发。
5657
- <span role="img" aria-label="package">📦</span> **组件库发布**:支持将 `src/components` 独立打包为 NPM 库 (`@w.ui/wui-react`),提供 ESM/UMD 格式,支持按需加载与类型提示。
58+
- <span role="img" aria-label="layers">🧩</span> **多项目模式(共享依赖)**:一个仓库 + 一个 `node_modules` 承载多个业务项目;通过 `PROJECT` 选择入口与路由,Vite/Webpack 均可按项目构建与预览,避免重复 clone/重复安装依赖。
5759

5860
---
5961

@@ -112,8 +114,63 @@ npm install
112114
npm run dev
113115
```
114116

117+
## 🧩 多项目模式(共享依赖)
118+
119+
多项目模式的目标是:把“模板工程 + 基础设施 + 依赖”沉淀成一个仓库,后续新业务只需要在 `src/projects/<project>` 里新增业务代码。
120+
121+
- 📘 详细文档: [多项目(Multi Project)模式](./docs/MULTI_PROJECT.md)
122+
123+
### ✅ 快速上手
124+
125+
项目通过环境变量 `PROJECT` 来选择当前项目(默认不传即主项目)。脚本已使用 `cross-env`,Windows/macOS/Linux 统一。
126+
127+
- Webpack 启动 ProjectA:`npm run start:projectA`
128+
- Webpack 启动 ProjectB:`npm run start:projectB`
129+
- Webpack 构建 ProjectB:`npm run build:production:projectB`
130+
- Vite 启动 ProjectA:`npm run dev:vite:projectA`
131+
- Vite 启动 ProjectB:`npm run dev:vite:projectB`
132+
133+
### 📁 目录约定
134+
135+
```text
136+
src/projects/
137+
<project>/
138+
index.tsx # 项目入口(可复用通用 renderApp)
139+
routers/ # 项目路由(可选,存在则覆盖主路由)
140+
index.jsx
141+
authRouter.jsx
142+
pages/ # 项目页面(可选)
143+
components/ # 项目组件(可选)
144+
public/ # 项目静态资源(可选,构建时叠加到 public)
145+
```
146+
147+
### 🧭 路由覆盖与复用策略
148+
149+
- 默认情况下:`@routers` 指向 `src/routers`(主项目路由)。
150+
- 若存在 `src/projects/<project>/routers/``@routers` 会指向项目路由,从而实现“同框架/同基础设施下的路由隔离”。
151+
- 示例:
152+
- `projectA` 使用“转发主路由”的方式实现最大化复用。
153+
- `projectB` 落地“独立路由 + 独立 pages/components”,并演示直接复用主项目组件(如 `@stateless/*`)。
154+
155+
### 📦 产物目录与本地预览(dist 自动切换)
156+
157+
多项目构建时输出目录采用约定:
158+
159+
- 主项目:`dist/`
160+
- 项目构建:`dist-<project>/`(例如 `dist-projectB/`
161+
162+
同时,`serve/http/clean` 相关脚本已改为自动指向当前项目的产物目录,避免手动切换路径。
163+
115164
提示:运行 `npm run lighthouse` 前请先启动 dev server(例如先执行 `npm run dev`/`npm run start`)。
116165

166+
### 🆕 如何新增一个新项目(推荐流程)
167+
168+
1) 新建目录:`src/projects/<yourProject>/`
169+
2) 添加入口:`src/projects/<yourProject>/index.tsx`
170+
3) (可选)添加路由覆盖:`src/projects/<yourProject>/routers/index.jsx`
171+
4) 新增 pages/components 业务代码
172+
5) 复制一份脚本(参考 `projectA/projectB`)或直接使用 `cross-env PROJECT=<yourProject> ...` 启动/构建
173+
117174
更多用法详见 [详细文档](./docs/README_PERMISSION.md)[用户角色权限说明](./docs/USER_ROLE_PERMISSION.md)
118175

119176
---
@@ -137,12 +194,24 @@ npm run dev
137194
- **🎨 组件重构与优化**: 样式重构,适配移动端与暗黑模式。交互优化,支持自定义提示与自定义样式。规范化导出结构,修复模块解析问题。
138195
- **🛠️ 构建配置升级**: 完善 `vite.config.lib.ts`,配置路径别名 (`@assets`, `@hooks` 等) 与外部依赖,确保构建产物纯净。
139196

197+
- **🧩 多项目模式(共享依赖)**:引入 `PROJECT` 环境变量,在同一仓库/同一套依赖(一个 `node_modules`)下承载多个业务项目;支持 **Vite / Webpack** 双构建链路按项目入口打包。
198+
- **🧭 项目级路由覆盖**:通过别名 `@routers` 支持项目覆盖主路由;示例 `projectB` 已落地独立 `routers/pages/components`,并演示复用主项目组件(`@stateless/*`)。
199+
- **🧰 dist 工具脚本**`serve/http/clean` 相关脚本改为自动指向当前项目产物目录(`dist` / `dist-<project>`),减少多项目场景下的重复维护。
200+
- **✅ 构建验证**`npm run build:production:projectB` 产物输出到 `dist-projectB/`
201+
140202
---
141203

142204
## 🏗️ 技术架构
143205

144206
<img width="1903" height="387" alt="Snipaste_2025-12-31_09-14-38" src="https://github.com/user-attachments/assets/915ba91a-8852-4dc0-8a14-091e781d9f04" />
145207

208+
**多项目架构补充说明(Multi Project)**
209+
210+
- **入口约定**:默认入口为 `src/index.tsx`;项目入口为 `src/projects/<project>/index.tsx`,通过 `PROJECT=<project>` 切换。
211+
- **构建与产物**:Vite/Webpack 均支持按项目构建,产物目录采用 `dist` / `dist-<project>` 的约定,便于部署与本地预览。
212+
- **路由隔离与复用**`@routers` 指向 `src/routers``src/projects/<project>/routers`(存在即覆盖);从而实现“同一套基础设施 + 项目级路由差异”。
213+
- **示例项目**`projectA` 以“转发主路由”演示复用;`projectB` 以“独立路由 + 独立页面/组件”演示隔离与复用并存。
214+
146215
---
147216

148217
## 🦄 脚手架--白泽 baize

docs/MULTI_PROJECT.md

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
# 多项目(Multi Project)模式
2+
3+
目标:一套依赖(一个 `node_modules`)承载多个业务项目;启动/构建时通过环境变量选择“当前项目入口”,做到**按项目入口打包**,避免每个项目重复克隆+重复装依赖。
4+
5+
## 你现在得到什么
6+
7+
- 默认(单项目)完全不变:继续用根目录 `index.html` + `src/index.tsx`
8+
- 多项目模式:通过 `PROJECT=projectA|projectB|...` 选择入口文件 `src/projects/<project>/index.tsx`
9+
- 项目级 `public`:若存在 `src/projects/<project>/public`,则该目录作为 Vite 的 `publicDir`
10+
- 项目级路由覆盖:若存在 `src/projects/<project>/routers`,则 `@routers` 会指向该目录;否则回退到默认 `src/routers`
11+
12+
## 目录约定
13+
14+
推荐结构(可按需增减):
15+
16+
- `src/projects/<project>/index.tsx`:项目入口(类似你描述的 `app.js`)。
17+
- `src/projects/<project>/routers/`:项目路由(可先用“包装转发”复用默认路由)。
18+
- `src/projects/<project>/public/`:项目静态资源(等价于 Vite 的 `public/`)。
19+
20+
## npm scripts
21+
22+
- 默认项目
23+
- `npm run dev:vite`
24+
- `npm run build:vite`
25+
- `npm run preview:vite`
26+
27+
- Project A
28+
- `npm run dev:vite:projectA`
29+
- `npm run build:vite:projectA`
30+
- `npm run preview:vite:projectA`
31+
32+
- Project B
33+
- `npm run dev:vite:projectB`
34+
- `npm run build:vite:projectB`
35+
- `npm run preview:vite:projectB`
36+
37+
## Webpack 静态预览(dist 自动切换)
38+
39+
以下脚本会根据 `PROJECT` 自动选择 `dist``dist-<project>`
40+
41+
- `npm run serve:prod` / `npm run serve:dev` / `npm run serve:test`
42+
- `npm run http:prod` / `npm run http:dev` / `npm run http:test`
43+
44+
示例:
45+
46+
- `PROJECT=projectA npm run build:production:projectA`
47+
- `PROJECT=projectA npm run serve:prod`
48+
- `PROJECT=projectA npm run http:prod`
49+
50+
### Webpack(现有 start/build 链路)
51+
52+
- 默认项目
53+
- `npm run start`
54+
- `npm run build:production`
55+
- `npm run prod:serve`
56+
57+
- Project A
58+
- `npm run start:projectA`
59+
- `npm run build:production:projectA`
60+
- `npm run prod:serve:projectA`
61+
62+
- Project B
63+
- `npm run start:projectB`
64+
- `npm run build:production:projectB`
65+
- `npm run prod:serve:projectB`
66+
67+
构建产物目录:
68+
69+
- 默认:`dist-vite`
70+
- 非默认:`dist-vite-<project>`(例如 `dist-vite-projectA`
71+
72+
Webpack 构建产物目录:
73+
74+
- 默认:`dist`
75+
- 非默认:`dist-<project>`(例如 `dist-projectA`
76+
77+
## 新增一个项目(例如 projectC)
78+
79+
1) 创建入口文件:`src/projects/projectC/index.tsx`
80+
81+
2) (可选)创建路由目录:`src/projects/projectC/routers/`
82+
83+
- 如果你想先复用默认路由:
84+
- `routers/index.jsx` 里写:
85+
- `export { default } from '@src/routers'`
86+
- `export * from '@src/routers'`
87+
- `routers/authRouter.jsx` 里写:
88+
- `export { default } from '@src/routers/authRouter'`
89+
90+
3) (可选)创建静态资源目录:`src/projects/projectC/public/`
91+
92+
4) 增加脚本(两种方式选一种)
93+
94+
- 推荐:直接照着 `projectA`/`projectB` 在根 `package.json` 里添加:
95+
- `cross-env PROJECT=projectC vite --host --config vite.config.ts`
96+
- `cross-env PROJECT=projectC vite build --config vite.config.ts`
97+
98+
## 说明(为什么能做到“按需打包”)
99+
100+
Vite 在构建时只会从 `index.html` 注入的**入口脚本**向下依赖分析。
101+
当前实现会在构建/开发时把 `index.html` 里的默认入口 `/src/index.tsx` 替换为 `/src/projects/<project>/index.tsx`,因此不会把其它项目入口当成必需依赖链去打包。

package-lock.json

Lines changed: 1 addition & 30 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -67,29 +67,48 @@
6767
"prepare": "husky install",
6868
"dev": "npm run start",
6969
"start": "npm run pre:run && cross-env BUILD_GOAL=development NODE_ENV=development NODE_OPTIONS=--trace-deprecation webpack serve --config ./webpack/webpack.dev.js --stats-error-details",
70+
"start:projectA": "npm run pre:run && cross-env PROJECT=projectA BUILD_GOAL=development NODE_ENV=development NODE_OPTIONS=--trace-deprecation webpack serve --config ./webpack/webpack.dev.js --stats-error-details",
71+
"start:projectB": "npm run pre:run && cross-env PROJECT=projectB BUILD_GOAL=development NODE_ENV=development NODE_OPTIONS=--trace-deprecation webpack serve --config ./webpack/webpack.dev.js --stats-error-details",
7072
"dev:vite": "vite --host --config vite.config.ts",
73+
"dev:vite:projectA": "cross-env PROJECT=projectA vite --host --config vite.config.ts",
74+
"dev:vite:projectB": "cross-env PROJECT=projectB vite --host --config vite.config.ts",
7175
"dev:faker": "concurrently -r \"npm run start\" \"npm run faker\"",
7276
"build:production": "cross-env SENTRY_SOURCE_MAP=map BUILD_GOAL=production NODE_ENV=production NODE_OPTIONS=--trace-deprecation webpack --config ./webpack/webpack.prod.js --stats-error-details",
7377
"build:production:zip": "cross-env DIST_ZIP=1 SENTRY_SOURCE_MAP=map BUILD_GOAL=production NODE_ENV=production NODE_OPTIONS=--trace-deprecation webpack --config ./webpack/webpack.prod.js --stats-error-details",
78+
"build:production:projectA": "cross-env PROJECT=projectA SENTRY_SOURCE_MAP=map BUILD_GOAL=production NODE_ENV=production NODE_OPTIONS=--trace-deprecation webpack --config ./webpack/webpack.prod.js --stats-error-details",
79+
"build:production:projectB": "cross-env PROJECT=projectB SENTRY_SOURCE_MAP=map BUILD_GOAL=production NODE_ENV=production NODE_OPTIONS=--trace-deprecation webpack --config ./webpack/webpack.prod.js --stats-error-details",
80+
"build:production:zip:projectA": "cross-env PROJECT=projectA DIST_ZIP=1 SENTRY_SOURCE_MAP=map BUILD_GOAL=production NODE_ENV=production NODE_OPTIONS=--trace-deprecation webpack --config ./webpack/webpack.prod.js --stats-error-details",
81+
"build:production:zip:projectB": "cross-env PROJECT=projectB DIST_ZIP=1 SENTRY_SOURCE_MAP=map BUILD_GOAL=production NODE_ENV=production NODE_OPTIONS=--trace-deprecation webpack --config ./webpack/webpack.prod.js --stats-error-details",
7482
"build:test": "cross-env SENTRY_SOURCE_MAP=no BUILD_GOAL=test NODE_ENV=production webpack --config ./webpack/webpack.prod.js --stats-error-details",
7583
"build:dev": "cross-env SENTRY_SOURCE_MAP=no BUILD_GOAL=dev NODE_ENV=production webpack --config ./webpack/webpack.prod.js --stats-error-details",
7684
"build:vite": "vite build --config vite.config.ts",
85+
"build:vite:projectA": "cross-env PROJECT=projectA vite build --config vite.config.ts",
86+
"build:vite:projectB": "cross-env PROJECT=projectB vite build --config vite.config.ts",
7787
"build:vite:zip": "cross-env ZIP_DIST=1 vite build --config vite.config.ts",
88+
"build:vite:projectA:zip": "cross-env PROJECT=projectA ZIP_DIST=1 vite build --config vite.config.ts",
89+
"build:vite:projectB:zip": "cross-env PROJECT=projectB ZIP_DIST=1 vite build --config vite.config.ts",
7890
"build:vite:sentry": "cross-env SENTRY_DISABLE_TELEMETRY=1 SENTRY_SOURCE_MAP=map vite build --config vite.config.ts",
7991
"preview:vite": "vite preview --host --config vite.config.ts",
92+
"preview:vite:projectA": "cross-env PROJECT=projectA vite preview --host --config vite.config.ts",
93+
"preview:vite:projectB": "cross-env PROJECT=projectB vite preview --host --config vite.config.ts",
8094
"prod:serve": "cross-env SENTRY_SOURCE_MAP=no BUILD_SERVE=prod BUILD_GOAL=production NODE_ENV=production webpack --config ./webpack/webpack.prod.js --stats-error-details",
95+
"prod:serve:projectA": "cross-env PROJECT=projectA SENTRY_SOURCE_MAP=no BUILD_SERVE=prod BUILD_GOAL=production NODE_ENV=production webpack --config ./webpack/webpack.prod.js --stats-error-details",
96+
"prod:serve:projectB": "cross-env PROJECT=projectB SENTRY_SOURCE_MAP=no BUILD_SERVE=prod BUILD_GOAL=production NODE_ENV=production webpack --config ./webpack/webpack.prod.js --stats-error-details",
8197
"prod:serve:debug": "cross-env DEBUG_PROD=1 npm run prod:serve",
8298
"dev:serve": "cross-env SENTRY_SOURCE_MAP=no BUILD_SERVE=prod BUILD_GOAL=dev NODE_ENV=production webpack --config ./webpack/webpack.prod.js --stats-error-details",
8399
"test:serve": "cross-env SENTRY_SOURCE_MAP=no BUILD_SERVE=prod BUILD_GOAL=test NODE_ENV=production webpack --config ./webpack/webpack.prod.js --stats-error-details",
84100
"analyze:build": "cross-env SENTRY_SOURCE_MAP=no BUILD_GOAL=production NODE_ENV=production USE_ANALYZE=1 webpack --config ./webpack/webpack.prod.js --stats-error-details --profile --json=compilation-stats.json",
85101
"analyze:stats": "npx webpack --profile --json=stats.json",
86-
"serve:prod": "npm run clean:dist && npm run prod:serve && serve -s dist -l 5000",
87-
"serve:dev": "npm run clean:dist && npm run dev:serve && serve -s dist -l 5000",
88-
"serve:test": "npm run clean:dist && npm run test:serve && serve -s dist -l 7000",
89-
"http:dev": "npm run clean:dist && npm run dev:serve && npm run http-server",
90-
"http:prod": "npm run clean:dist && npm run prod:serve && npm run http-server",
91-
"http:test": "npm run clean:dist && npm run test:serve && npm run http-server",
92-
"http-server": "http-server ./dist -p 9090 --proxy https://my-json-server.typicode.com -o --cors --log-ip --gzip",
102+
"clean:dist:current": "node scripts/dist-tools.mjs clean",
103+
"serve:dist": "node scripts/dist-tools.mjs serve --port 5000",
104+
"serve:dist:test": "node scripts/dist-tools.mjs serve --port 7000",
105+
"serve:prod": "npm run clean:dist:current && npm run prod:serve && npm run serve:dist",
106+
"serve:dev": "npm run clean:dist:current && npm run dev:serve && npm run serve:dist",
107+
"serve:test": "npm run clean:dist:current && npm run test:serve && npm run serve:dist:test",
108+
"http:dev": "npm run clean:dist:current && npm run dev:serve && npm run http-server",
109+
"http:prod": "npm run clean:dist:current && npm run prod:serve && npm run http-server",
110+
"http:test": "npm run clean:dist:current && npm run test:serve && npm run http-server",
111+
"http-server": "node scripts/dist-tools.mjs http -- -p 9090 --proxy https://my-json-server.typicode.com -o --cors --log-ip --gzip",
93112
"lighthouse": "node scripts/run-lighthouse.mjs",
94113
"faker": "nodemon faker/index.js",
95114
"test": "npm run test:jest && npm run test:coverage",

0 commit comments

Comments
 (0)