# 前端选型与代码组织 > 状态:推荐方案,尚未创建脚手架或安装依赖。 ## 一、推荐结论 推荐使用 Vue 3 + TypeScript + Vite,并采用 pnpm workspace 单仓库、三个独立应用: ```text 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`: ```text Go 路由和 DTO ↓ docs/admin-openapi.yaml ↓ openapi-typescript ↓ packages/api 类型与客户端 ↓ admin-web / admin-h5 / client-h5 ``` 暂不共享: - 页面组件。 - 登录页面。 - 业务状态管理。 - Web/H5 表单组件。 - 业务流程组合函数。 等两个应用出现真实、稳定、完全相同的重复后再提取。不要先创建 `shared-business`、`shared-components` 等空泛包。 ## 五、推荐仓库结构 ```text 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` 只负责: 1. OpenAPI 生成类型。 2. Base URL。 3. Bearer Token 注入。 4. 管理端 Refresh Token 单次刷新队列。 5. `{code,msg,data,timestamp}` 解包。 6. 401、403、429、5xx 统一错误事件。 7. Request ID 提取。 它不负责: - 页面跳转。 - Toast 文案拼接。 - 将订单状态改成本地状态机。 - 缓存所有列表和详情。 - 替页面吞掉错误。 这使请求模块保持为一个小接口、深实现的公共模块,三应用只需要学习同一套调用方式。 ## 七、动态菜单实现 后端菜单只决定“哪些本地路由可见和可进入”,不能直接决定加载哪个源代码文件。 正确方式: 1. 每个管理应用维护静态路由表:`routeKey/url → 页面组件`。 2. 登录后读取 `menus`。 3. 将后端菜单与本地静态路由表求交集。 4. 生成导航和路由白名单。 5. 未匹配到本地页面时记录告警并隐藏,不允许字符串动态 import 任意组件。 6. 使用 `buttons` 控制操作按钮。 ## 八、状态管理边界 Pinia 首期只保存: - Access Token、Refresh Token。 - 当前用户。 - 菜单、按钮和权限码。 - 当前终端类型。 - 少量跨页面 UI 状态。 订单列表、资产详情、套餐列表等服务端数据由页面直接请求并局部刷新。等出现跨页面共享缓存、复杂失效和后台自动刷新需求后,再评估服务端状态库。 ## 九、OpenAPI 工作流 建议建立固定流水线: ```text 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 统一而增加学习成本。 ## 十一、创建脚手架前需要确认 1. 前端团队更熟 Vue 还是 React。 2. 管理 H5 是否作为独立正式产品发布。 3. 三个应用的域名、Base URL 和环境配置。 4. 微信公众号 OAuth 回调域名和支付回跳地址。 5. 首期页面范围及权限种子数据。 6. 后端企业数据范围和逐接口权限门禁是否完成验证。 ## 十二、参考资料 - [Vue TypeScript](https://vuejs.org/guide/typescript/overview.html) - [Pinia](https://pinia.vuejs.org/) - [Element Plus](https://element-plus.org/) - [Vant](https://vant-ui.github.io/vant/) - [openapi-typescript](https://openapi-ts.dev/introduction) - [openapi-fetch](https://openapi-ts.dev/openapi-fetch/) - [pnpm Workspace](https://pnpm.io/workspaces)