9.0 KiB
9.0 KiB
abacus-static-framework — 架构文档(ARCHITECTURE)
模块、依赖、契约、目录布局的唯一权威(本仓库视角)。新增/重命名/移动模块或调整依赖方向,必须先改本文,再改代码(同一个 commit)。 上游:
PRD.md(行为)。前后端共享约束见工作区docs/(../../../../docs/coding-standards.md、../../../../docs/architecture.md),本文不复制其正文。
0. 阅读指引
本文描述前端底座的模块划分与依赖方向;§2 模块清单与 §6 目录一一对应,§3.2 是跨模块接口契约(写代码必须遵守)。新会话先读 §2 + §6 定位任务归属,再动手。
1. 领域模型(Domain Model)
1.1 实体
前端无持久化实体;领域概念即运行时状态:
| 实体 | 字段 | 关系 | 持久化? |
|---|---|---|---|
| 用户会话(userStore) | access_token/refresh_token/expires_dt/user_type | 全局单例 | 否(localStorage) |
| 菜单/路由(permissionStore) | routes(过滤后)、menuTree | 依赖后端菜单接口 | 否(内存) |
| 应用设置(settingsStore) | backendServices、loginType、loginAddress | 来自 window.frameBaseConfig | 否(运行时注入) |
1.2 派生概念(非持久化)
backendServices别名→真实服务名映射(window.frameBaseConfig注入)。- 当前路由上下文(
currentRouter())用于组装 commonParam(menuName/moduleId/moduleName)。
1.3 不变量(任何模块都必须维护)
- I1.
src/framework/**只读:业务代码不得 import 后修改框架文件;框架变更只能由框架负责人进行。 - I2. 业务请求一律经
framework/utils/request.ts,禁止裸axios/fetch。 - I3. 服务别名首段必须是
settingsStore.backendServices中已注册的键。 - I4. 多租户上下文来自登录 JWT /
useTenantStore,禁止业务代码硬编码租户标识或手写X-Tenant-Id头;品牌/主题/菜单须按tenantConfig动态加载。详见../../../../docs/architecture.md §7。
2. 模块清单
按依赖层级从低到高排列。
Layer 0 — framework/utils(基础设施)
## 模块名:framework/utils
- 职责:请求管线(request.ts)、标准工具(standard/*:vxe、dataHandleUtil、globalHandle、security、baiduMap)
- 不负责:业务逻辑、页面状态
- 输入:各业务模块的请求配置
- 输出:统一响应体解包后的数据 / Blob;标准工具函数
- 关键类型/接口:request(config) -> Promise<data>;gridDefaultProps(opts);useQueryParamsHandle(...)
- 持有状态:token 刷新队列(模块内闭包)、settingsStore 引用
Layer 0 — framework/components/standard(视觉基座)
## 模块名:framework/components/standard
- 职责:标准 UI 组件(AbForm/AbQueryForm/AbSection/ClickCopy/FilePreviewDialog/ImportExcel/LogShow/RealTimeSearchSelect/SensitiveText/Text/BottomFloat)
- 不负责:业务特有布局(业务自建 components)
- 输入:props/插槽
- 输出:统一风格组件
- 持有状态:无(受控组件)
Layer 1 — framework/store(有状态服务)
## 模块名:framework/store
- 职责:全局 Pinia 状态(settings:backendServices/loginType;permission:菜单生成与过滤;user:token/业务域)
- 不负责:页面级状态(业务自建 store)
- 输入:window.frameBaseConfig、后端菜单接口
- 输出:状态与 actions
- 持有状态:是
Layer 2 — framework/router + hooks + directives(路由与通用逻辑)
## 模块名:framework/router + hooks + directives
- 职责:静态路由与动态路由装配支持;useVxeTableHandle 等通用组合式函数;v-debounce/v-throttle 指令
- 不负责:业务菜单路由(subsystem/router/dynamicRouter.ts)
- 输入:permissionStore 过滤后的路由
- 输出:可用路由表、表格查询联动能力
- 持有状态:路由表(由 permissionStore 维护)
Layer 3 — subsystem(业务可写区)
## 模块名:subsystem
- 职责:业务子系统(api 封装、dynamicRouter 菜单路由、views 页面、extend 扩展、自建 components/hooks/store/styles/types/utils)
- 不负责:框架能力(一律复用 framework)
- 输入:后端接口、用户操作
- 输出:业务页面与接口调用
- 持有状态:业务自身状态(自建 store)
3. 依赖图(DAG)
graph TD
subsystem --> framework_utils[framework/utils]
subsystem --> framework_components[framework/components/standard]
subsystem --> framework_hooks[framework/hooks]
subsystem --> framework_router[framework/router]
framework_router --> framework_store[framework/store]
framework_hooks --> framework_utils
framework_hooks --> framework_components
framework_store --> framework_utils
framework_utils --> framework_store
3.1 依赖方向规则
- 高层依赖低层;业务(subsystem)依赖框架(framework),框架各层按上图单向;反向依赖一律禁止(如 framework 不得 import subsystem)。
- 例外:
framework/utils/request.ts依赖framework/store(settingsStore)获取别名映射——两者同属 framework 层,允许。
3.2 跨模块接口契约(写代码时必须遵守的签名)
// module: framework/utils/request
// request(config: {url, method, params|data, headers?, custom?}) -> Promise<data> // status===200 已解包 data;ArrayBuffer 返回整个 response
// url 首段 = 服务别名(I3),自动携带 token/commonParam(I2)
// module: framework/utils/standard/vxe
// gridDefaultProps(opts: {params, rowConfig, pagerConfig, proxyConfig, columns}) -> VxeGridProps
// module: framework/hooks/useVxeTableHandle
// useQueryParamsHandle(params, page, sorts, filters) -> query params // 分页契约 {data,total},见共享规范 §6.4
// useTableQueryReload(...) / useInVxeGridSearch(...)
4. 持久化与边界
- 无后端式持久化;前端持久化仅 token(localStorage,由 userStore 管理)。
- 外部边界:所有 HTTP 请求必须经 request.ts;开发期经 vite proxy;运行时经 Nginx 反代;
window.frameBaseConfig由宿主页面注入(含 backendServices)。
5. 测试边界
- 无自动化单测(PRD 已声明);每页面人工冒烟(列表/查询/新增/编辑/导出)。
- 真机/真环境必验:多数据源路由、Kafka 消费、XXL-JOB 调度、Nacos 配置拉取、真实数据库迁移(属后端,但前端联调需一并验证)。
6. 目录结构(与 §2 模块清单一一对应)
src/
├── framework/ # Layer 0~2(只读共享)
│ ├── api/ components/ hooks/ layout/ router/ store/ styles/ types/ utils/ views/ directives/
│ └── utils/request.ts # 请求管线(契约见 §3.2)
├── subsystem/ # Layer 3(业务可写区)
│ ├── api/<服务别名>/ # 按 Controller 分 ts(如 sms/{index,applog,abnormal,config}.ts)
│ ├── router/dynamicRouter.ts # 菜单路由唯一注册点
│ ├── views/<一级菜单>/<二级菜单>/index.vue
│ ├── extend/ # 框架扩展钩子
│ └── (自建) components/ hooks/ store/ styles/ types/ utils/
├── permission.ts # 全局路由守卫(生成/过滤动态路由)
└── main.ts # 入口
7. 自检(架构健康度三问)
- 出 bug 了,能否 30 秒内指出是 framework 还是 subsystem、哪个子目录的责任?
- 替换 UI 库/表格库,改动能否控制在 framework/components 与 utils/standard 内 + 适配层?
- 加新业务页面,能否立刻说出落在
subsystem/views/<一级>/<二级>/哪个位置?
8. 待办与已知技术负债
- 前端 README 引用的
docs/文档中心已补齐(../../../../docs/)。 - 旧框架兼容入口
externalOldERPIndex(data.js)仅在需要兼容历史模块时配置。
9. 文档变更协议
- 加/删模块或改依赖方向 → 改本文 §2 + §3,同一 commit。
- 改对外签名(request/gridDefaultProps 等)→ 先改 §3.2,再改代码。
- 与
PRD.md冲突 → 以PRD.md为准,回改本文;与共享docs/coding-standards.md冲突 → 以 docs/ 为准并回改本文。
10. SaaS 多租户架构(指针)
完整设计(租户动态品牌/主题/模块/菜单、前后端协作、扩展)见工作区
../../../../docs/architecture.md §7;编码约束见coding-standards.md §9。本文仅记录前端特有约束:
- 登录后
useTenantStore保存tenantId+tenantConfig并持久化;applyTenantTheme(config)运行时应用品牌/主题。 - 菜单经
permissionStore.filterAsyncRoutes做"权限 ∩ 租户模块订阅"双重过滤。 framework/utils/request.ts自动附加X-Tenant-Id(取自useTenantStore);业务代码不得手写。- 新增前端
subsystem/store/tenant.ts、framework/utils/theme.ts,并扩展request.ts与permissionStore,包归属见 §2 / §6。