7.6 KiB
前端选型与代码组织
状态:推荐方案,尚未创建脚手架或安装依赖。
一、推荐结论
推荐使用 Vue 3 + TypeScript + Vite,并采用 pnpm workspace 单仓库、三个独立应用:
frontend/
├── apps/
│ ├── admin-web/ # 管理 Web
│ ├── admin-h5/ # 管理 H5
│ └── client-h5/ # 独立 C 端 H5
└── packages/
└── api/ # OpenAPI 生成类型和请求客户端
推荐组合:
| 领域 | 选择 | 理由 |
|---|---|---|
| 框架 | Vue 3 + TypeScript | 管理后台和 H5 生态成熟,单文件组件适合页面型业务 |
| 构建 | Vite | 配置少,三应用可独立构建和发布 |
| 工作区 | pnpm workspace | 只解决多应用依赖和共享 API 包,不引入额外编排平台 |
| 管理 Web UI | Element Plus | 表格、表单、树、分页、弹窗等后台能力完整 |
| 管理 H5/C 端 UI | Vant | 移动表单、列表、弹层、支付结果页等 H5 交互成熟 |
| 路由 | Vue Router | 三个应用分别维护静态路由表 |
| 客户端状态 | Pinia | 只保存登录态、当前用户、菜单和按钮权限 |
| 接口类型 | openapi-typescript | 从现有 OpenAPI 生成 TypeScript 类型 |
| 请求客户端 | openapi-fetch + 原生 fetch | 类型直接复用,减少手写接口和额外封装 |
不在首期引入:Nx、Turborepo、大型后台模板、服务端状态缓存库、自研组件库、微前端。
二、为什么建议三个应用
管理 Web 和管理 H5 使用同一套后端业务与权限,但它们是两个真实的交互界面:
- Web 以高密度表格、批量操作、复杂配置为主。
- 管理 H5 以资产查询、现场操作、审批、资金和通知为主。
- 两端登录分别传
device=web和device=h5,后端会返回不同菜单。 - Element Plus 和 Vant 的布局与交互模型不同,强行在一个应用混用会增加条件渲染和包体积。
- C 端 H5 使用完全不同的个人客户认证、微信 OAuth、支付回跳和资产绑定流程,必须独立。
三个应用放在同一仓库,可以统一 TypeScript、Lint、构建和 OpenAPI 类型,又不会把三种页面体验绑在一个发布包中。
三、何时可以改成两个应用
如果管理 H5 最终只保留以下少量页面,可以使用“自适应管理端 + C 端 H5”两应用方案:
- 登录和个人中心。
- 通知。
- 统一资产搜索和只读详情。
- 少量订单、退款、换货审批。
一旦管理 H5 需要独立导航、设备控制、企业授权、资金、上传或大量移动表单,就应使用独立 admin-h5。当前页面矩阵已经超过纯只读壳层,因此默认采用三个应用。
四、共享边界
首期只共享 packages/api:
Go 路由和 DTO
↓
docs/admin-openapi.yaml
↓
openapi-typescript
↓
packages/api 类型与客户端
↓
admin-web / admin-h5 / client-h5
暂不共享:
- 页面组件。
- 登录页面。
- 业务状态管理。
- Web/H5 表单组件。
- 业务流程组合函数。
等两个应用出现真实、稳定、完全相同的重复后再提取。不要先创建 shared-business、shared-components 等空泛包。
五、推荐仓库结构
frontend/
├── apps/
│ ├── admin-web/
│ │ └── src/
│ │ ├── app/
│ │ ├── pages/
│ │ ├── routes/
│ │ └── stores/
│ ├── admin-h5/
│ │ └── src/
│ │ ├── app/
│ │ ├── pages/
│ │ ├── routes/
│ │ └── stores/
│ └── client-h5/
│ └── src/
│ ├── app/
│ ├── pages/
│ ├── routes/
│ └── stores/
├── packages/
│ └── api/
│ ├── generated/
│ ├── client.ts
│ └── index.ts
├── package.json
├── pnpm-workspace.yaml
└── tsconfig.base.json
目录保持扁平。页面内部先就近放置组件,不按 api/service/model/controller 重建一套前端分层。
六、请求模块的唯一职责
packages/api 只负责:
- OpenAPI 生成类型。
- Base URL。
- Bearer Token 注入。
- 管理端 Refresh Token 单次刷新队列。
{code,msg,data,timestamp}解包。- 401、403、429、5xx 统一错误事件。
- Request ID 提取。
它不负责:
- 页面跳转。
- Toast 文案拼接。
- 将订单状态改成本地状态机。
- 缓存所有列表和详情。
- 替页面吞掉错误。
这使请求模块保持为一个小接口、深实现的公共模块,三应用只需要学习同一套调用方式。
七、动态菜单实现
后端菜单只决定“哪些本地路由可见和可进入”,不能直接决定加载哪个源代码文件。
正确方式:
- 每个管理应用维护静态路由表:
routeKey/url → 页面组件。 - 登录后读取
menus。 - 将后端菜单与本地静态路由表求交集。
- 生成导航和路由白名单。
- 未匹配到本地页面时记录告警并隐藏,不允许字符串动态 import 任意组件。
- 使用
buttons控制操作按钮。
八、状态管理边界
Pinia 首期只保存:
- Access Token、Refresh Token。
- 当前用户。
- 菜单、按钮和权限码。
- 当前终端类型。
- 少量跨页面 UI 状态。
订单列表、资产详情、套餐列表等服务端数据由页面直接请求并局部刷新。等出现跨页面共享缓存、复杂失效和后台自动刷新需求后,再评估服务端状态库。
九、OpenAPI 工作流
建议建立固定流水线:
go run ./cmd/gendocs
↓
更新 docs/admin-openapi.yaml
↓
生成 packages/api/generated
↓
TypeScript 类型检查
↓
构建三个应用
生成文件不手改。字段不正确时修改 Go DTO、RouteSpec 或文档生成器,然后重新生成。
当前 admin-openapi.yaml 同时包含管理端、C 端、开放接口和回调,文件名虽然偏旧,但首期无需为前端拆分多份 OpenAPI。
十、方案对比
| 方案 | 优点 | 风险 | 结论 |
|---|---|---|---|
| Vue 三应用单仓库 | Web/H5 体验清晰;共享接口类型;独立发布 | 有少量工程重复 | 推荐 |
| Vue 自适应管理端 + C端H5 | 少一个应用,首期代码更少 | Web/H5 组件混用、条件页面和包体增长 | 仅管理 H5 很小时使用 |
| React 三应用 | 也能满足需求,团队成熟时可选 | 当前没有团队技术偏好,初始约定和库选择更多 | 团队明显更熟 React 时替换推荐方案 |
如果实施团队已有稳定 React 经验,应优先服从团队能力,使用 React + TypeScript + Vite、Ant Design、Ant Design Mobile,仍保持“三应用单仓库+只共享 OpenAPI 包”的结构,不必为追求 Vue 统一而增加学习成本。
十一、创建脚手架前需要确认
- 前端团队更熟 Vue 还是 React。
- 管理 H5 是否作为独立正式产品发布。
- 三个应用的域名、Base URL 和环境配置。
- 微信公众号 OAuth 回调域名和支付回跳地址。
- 首期页面范围及权限种子数据。
- 后端企业数据范围和逐接口权限门禁是否完成验证。