BBC 8.0 前端架构
本文档描述 BBC 8.0 PC UI 的三个 Web 应用与共享包边界、运行时启动、登录会话、请求链路、动态权限路由、配置系统和构建产物。
本文只覆盖 PC UI workspace,不描述移动端工程、后端服务内部实现、数据库或中间件架构。具体编码步骤见新增页面开发指引,环境变量见配置文件,构建命令见运行发布,正式部署见部署 PC 前端。
页面存在不等于功能可见
BBC 8.0 平台端和商家端的菜单、层级、排序及角色权限以后端 current 菜单接口为准;部分页面还受功能开关控制。不能只根据源码中存在某个页面就判断当前环境已开放该功能。
1. 架构定位
BBC 8.0 PC UI 是一个基于 Vue 3、TypeScript、Vite 和 pnpm workspace 的多应用单仓库。仓库同时包含三个可独立构建的 Web 应用和多个共享包:
apps/pc:买家 PC 端。apps/seller:商家后台。apps/admin:平台管理后台。packages/*:跨端运行时、接口类型、通用组件、配置、装修和客服等共享能力。
架构的核心目标是:
- 三端共享基础设施,但保留端内业务边界。
- 登录、接口和菜单等端侧差异通过注入接入共享运行时。
- 后台菜单结构和权限以后端返回为准,前端只维护页面能力。
- 环境与部署差异通过配置与构建链路表达,不散落在页面代码中。
2. 系统上下文
前端仓库不定义后端服务、数据库或中间件架构。前端只依赖部署环境提供的 Base、Buyer、Seller、Admin API 地址,以及上传和实时消息所需的外部能力。
3. 代码组织与职责
3.1 顶层目录
.
├── apps/
│ ├── pc/
│ ├── seller/
│ └── admin/
├── packages/
│ ├── api/
│ ├── chat/
│ ├── components/
│ ├── config/
│ ├── core/
│ ├── decor/
│ ├── locales/
│ └── utils/
├── config/ # 仓库默认配置
├── scripts/ # 质量检查和辅助脚本
├── types/ # 全局类型声明
├── package.json
└── pnpm-workspace.yaml
3.2 应用层
| 应用 | 主要职责 | 关键入口 |
|---|---|---|
apps/pc | 买家页面、购物车、结算、会员中心和前台客服入口 | src/main.ts、src/router/index.ts、src/store、src/api |
apps/seller | 商家商品、订单、促销、店铺、财务和客服后台 | src/main.ts、src/router/*、src/views、src/api |
apps/admin | 平台商品、订单、会员、店铺、财务、运营、系统与权限后台 | src/main.ts、src/router/*、src/views、src/api |
应用拥有自己的页面、端内路由、端内 API 和业务状态。应用之间不得互相导入内部文件;确实需要跨端复用的能力应提升到合适的 packages/*。
3.3 共享包
| 包 | 职责 | 关键入口 |
|---|---|---|
@shop-tnt/core | 应用启动、路由守卫、登录会话、权限菜单、主题、全局上下文和 push-channel | packages/core/index.ts |
@shop-tnt/api | 请求辅助、Base 公共接口、上传能力和后端生成类型 | packages/api/index.ts |
@shop-tnt/components | 跨端公共组件、反馈服务和后台布局组件 | packages/components/index.ts |
@shop-tnt/config | API、域名、部署路径、资源前缀、功能开关和 Vite 配置工厂 | packages/config/index.ts |
@shop-tnt/utils | HTTP 客户端、存储、HTML 安全、导航、国际化辅助和指令 | packages/utils/index.ts |
@shop-tnt/locales | 语言资源、语言类型和路由标题映射 | packages/locales/index.ts |
@shop-tnt/chat | 客服页面、聊天状态、未读消息和 push 消费 | packages/chat/index.ts |
@shop-tnt/decor | 页面装修类型、编辑器、预览渲染和数据转换 | packages/decor/index.ts |
decor 的根入口刻意保持轻量:买家预览使用 @shop-tnt/decor/pc-preview,后台装修编辑器使用 @shop-tnt/decor/editor,避免前台构建意外引入完整编辑器运行时。
3.4 依赖方向
必须保持以下依赖约束:
packages/*不得引用apps/*。- 一个 app 不得直接引用另一个 app。
utils不持有页面、路由、Pinia 或具体业务端依赖。core只编排公共运行时;端侧接口通过参数注入,不在 core 中硬编码业务 URL。- 共享包之间出现循环依赖时,应重新检查职责,而不是通过路径技巧绕过。
4. 应用启动架构
三个应用都从各自的 src/main.ts 调用 @shop-tnt/core 的 bootstrapApp:
- PC 注入买家刷新 token 接口和 PC store。
- Seller 注入商家认证服务、权限路由安装器和 Seller store。
- Admin 注入平台认证服务、权限路由安装器和 Admin store。
4.1 启动时序
启动阶段先使用本地缓存的站点信息和主题完成首屏,再异步请求最新数据。站点信息或主题请求失败时保留已知缓存,不阻断应用挂载。
4.2 shopApp 运行时上下文
bootstrapApp 创建并注入 shopApp 上下文,主要包含:
- 当前
appName、应用配置和 API scope。 - 当前端隔离的存储 key。
- 站点信息和主题状态。
- 登录会话操作。
- i18n 翻译函数和语言切换能力。
- 后台明暗主题状态。
- 公共 app store。
- push-channel 客户端。
页面和共享组件通过注入读取这些能力,不应自行创建第二套全局运行时。
5. 状态分层
状态按生命周期分为三层:
| 层级 | 典型内容 | 位置 |
|---|---|---|
| 应用运行时上下文 | 配置、站点、主题、认证服务、push-channel | packages/core/shop-app.ts |
| 跨端全局状态 | 页面 Loading、站点信息 | packages/core/app-store.ts |
| 端内业务状态 | PC 用户、购物车;各端业务页面状态 | apps/*/src/store 或页面内 composable |
Admin 和 Seller 当前只安装公共 app store,并把客服运行时接入 shopApp;PC 另外维护用户与购物车 store。页面私有、生命周期短的状态优先留在页面或 composable,不应全部提升到 Pinia。
6. 路由与权限架构
6.1 PC 静态路由
PC 路由主要维护在 apps/pc/src/router/index.ts,页面组件和路由层级由前端静态声明。常用 meta 包括:
title/i18nKey:页面标题。requiresAuth:是否要求本地登录会话。loginMode:需要登录时使用页面跳转还是弹窗。layout:页面布局类型。backName/backPath:二级页直达时的返回兜底。
PC 还可以在端内守卫中处理分销等业务访问条件;这些条件不进入后台动态菜单模型。
6.2 Admin/Seller 动态权限路由
Admin 和 Seller 使用“本地页面能力 + 后端菜单结构”的组合:
| 文件 | 职责 |
|---|---|
router/index.ts | 登录页、根布局、首页、兜底页等基础静态路由 |
router/page-routes.ts | 扁平页面注册:name、绝对 path、meta、隐藏页关系 |
router/route-components.ts | route name 到实际 Vue 页面组件的映射 |
router/menu-icon-fallbacks.ts | 后端 icon 为空时的扁平兜底,不表达菜单层级 |
router/permission-routes.ts | 组合页面注册、组件、功能开关并创建权限安装器 |
后端 current 菜单树决定:
- 用户可见哪些菜单。
- 菜单父子层级和同级顺序。
- 菜单标题。
- 非空菜单图标。
前端决定:
- identifier 对应哪个本地 route name。
- 页面真实 URL 和 Vue 组件。
- 隐藏详情页、编辑页和登录后流程页。
- 后端 icon 缺失时的兼容图标。
6.3 权限路由生成时序
有 children 的后端节点由 RouterView 承载,不需要创建前端占位页面。后端 identifier 找不到本地页面时,开发环境输出权限诊断;不应静默生成一个功能不明的兼容页面。
6.4 隐藏页与返回链路
详情、编辑和审核页通常不出现在菜单中。它们通过 hidden 和 backName / backNames 跟随入口页面授权;authFlow 用于店铺认证、支付进件等登录后可访问但不属于菜单的系统流程。
二级页返回由 usePageBack() 统一解析:
- 优先使用入口通过
withReturnTo()写入的安全returnTo。 - 其次使用路由 meta 的
backName。 - 再使用安全的
backPath。 - 最后回到端内默认兜底页面。
7. 登录会话与请求架构
7.1 会话所有权
packages/core/auth.ts 负责会话数据的标准化、读写、清理和跨标签通知;具体登录、登出、刷新 token、用户信息和菜单接口由各 app 的 authService 实现。
不同 app 通过 getStorageKeys(appName) 使用隔离的 user、access token、refresh token 等 key。登录态广播只携带 app 名和变化类型,不广播 token 内容。
7.2 请求分层
| 层级 | 路径 | 职责 |
|---|---|---|
| HTTP 运行时 | packages/utils/request.ts | Axios 实例、token、序列化、请求取消、刷新重试、Loading 和错误回调 |
| API 辅助 | packages/api/request.ts | apiGet、apiPost、apiPut、apiDel、响应解包 |
| 公共基础接口 | packages/api/base.ts | 站点、主题、验证码、地区等跨端基础接口 |
| 后端生成类型 | packages/api/models/*.ts | Java VO/DTO/DO 对应的 TypeScript 类型,不承载运行时请求函数 |
| 端内业务接口 | apps/*/src/api | 当前应用使用的商品、订单、会员、店铺等接口函数 |
packages/api/index.ts 对 models 使用 type-only 导出。真正需要跨端复用的运行时接口应拥有明确模块和导出入口,不能依靠向生成类型文件加入函数来隐式暴露。
7.3 受保护请求时序
请求默认策略:
- GET 请求默认登记为
routescope,路由切换时取消。 - 提交、上传等请求默认不随路由切换取消。
- 同一取消 scope 可通过
cancelKey让新请求替换旧请求。 - 并发请求刷新 token 时复用同一个 Promise,避免刷新风暴。
- 命中会话过期条件的请求最多自动重试一次。
- 页面只有在明确接管反馈时才关闭统一错误消息。
8. UI 与业务能力分层
组件按复用范围放置:
- 只服务单个应用:
apps/<app>/src/components。 - 跨端通用交互:
packages/components。 - 页面装修:
packages/decor。 - 客服聊天:
packages/chat。
packages/components 既包含基础交互,也包含跨后台复用的业务组件。高影响公共模式包括:
AppShell:后台整体布局和菜单宿主。PageActionBar:二级页返回与主操作。TableLayout:后台列表表格、分页、工具栏和抽屉。BackendTabs:Admin/Seller 统一页签外观。MoneyInput/UnitNumberInput:金额和单位数值输入契约。- 各类 Picker、导入导出、上传和验证码组件。
业务页面应从公共入口复用这些模式,不应通过复制组件或覆盖 TDesign 内部样式形成第二套交互规范。
9. 配置架构
配置分为“仓库默认值”和“环境覆盖值”:
| 配置 | 默认值 | 读取封装 |
|---|---|---|
| API 地址 | config/api.ts | packages/config/api.ts |
| 页面域名 | config/domain.ts | packages/config/domain.ts |
| 部署虚拟路径 | config/alias.ts | packages/config/alias.ts |
| 静态资源前缀 | config/assets.ts | packages/config/assets.ts |
| 功能开关 | config/features.ts | packages/config/features.ts |
| 应用名称与标题 | 无独立根配置 | packages/config/apps.ts |
API、域名、虚拟路径、CDN 和功能开关不应硬编码在页面。VITE_* 会进入浏览器产物,不能存放密钥、数据库密码或服务端令牌。
10. 构建架构
三端 vite.config.ts 都调用 packages/config/build/vite.ts 的 createViteConfig。共享工厂统一负责:
- 注入
VITE_APP_NAME、应用标题和构建版本标识。 - workspace 包别名。
- rpx 到 px 转换。
@shop-tnt/components直接导入处理。- Vue 和 TDesign 组件自动导入。
- Vite base、CDN 资源前缀和 SCSS 配置。
- 生产构建分包和压缩。
- 生成固定路径的
version文件,用于覆盖部署后的版本提示。
构建模式只决定读取的环境和优化行为,不改变源码职责边界。测试、生产部署地址必须由对应 .env.* 明确提供。
11. 核心架构决策
| 决策 | 原因 | 影响 |
|---|---|---|
三端共用 bootstrapApp | 避免登录、请求、主题和路由守卫分叉 | 端侧差异必须通过 service/setup hook 注入 |
| 后台菜单以后端 current 树为准 | 权限、层级和展示保持同一事实来源 | 前端页面 name 必须与 identifier 对齐 |
| API 运行时与生成类型分离 | 防止生成覆盖业务逻辑,保持契约可追踪 | models 只放生成类型,接口函数使用明确模块 |
| 共享包按真实复用提升 | 避免三个 app 复制公共能力,也避免过度抽象 | 单端业务默认留在 app,第二个真实使用方出现后再提升 |
decor 拆分 editor/preview 入口 | 防止前台加载沉重编辑器依赖 | 调用方必须选择正确子路径 |
12. 扩展点判断
新增能力前,按以下顺序判断归属:
几个常见边界:
- 新增 PC 页面:页面和静态路由都在
apps/pc。 - 新增 Admin/Seller 菜单页:页面注册、组件映射和后端菜单 identifier 必须共同成立。
- 新增业务接口:当前端专用接口放端内
src/api;公共类型复用生成模型;真正跨端运行时接口使用明确共享模块。 - 新增选择器、上传、表格等交互:先检查公共组件,确认多端真实复用后再新增共享组件。
- 修改请求、会话、路由或构建:优先改共享入口并检查全部调用端。
13. 验证边界
架构层改动往往影响多个端。按当前项目规则使用以下最小验证:
| 改动范围 | 最小验证 |
|---|---|
| 单个 app | pnpm build:dev:<端> |
packages/*、config/*、根构建配置或多个 app | pnpm build:dev |
| API、共享类型 | pnpm typecheck,并执行受影响构建 |
| 语言包 | pnpm locale:check |
| Markdown 文档 | pnpm exec prettier --check <文件> |
构建通过只证明代码可以产出静态文件,不等于登录、菜单权限、接口契约或产品部署已经完成业务验收。
14. 文档事实来源
为避免架构文档变成过时描述,发生冲突时优先检查以下代码来源:
| 事实 | 代码来源 |
|---|---|
| workspace 应用和包 | pnpm-workspace.yaml、各目录 package.json |
| 启动顺序和全局上下文 | packages/core/app.ts、apps/*/src/main.ts |
| 登录会话 | packages/core/auth.ts、各端 session/passport API |
| HTTP 行为 | packages/utils/request.ts、packages/api/request.ts |
| 后台权限路由 | packages/core/permission.ts、apps/{admin,seller}/src/router |
| 组件公开入口 | packages/components/index.ts |
| 配置变量和优先级 | config/*、packages/config/* |
| 构建命令 | 根和各 app 的 package.json |
修改这些架构入口时,应在同一个变更中检查并更新本文档;具体实现始终优先于已经过时的描述。