18 KiB
18 KiB
前端架构
**本文引用的文件** - [main.js](file://fms-vue/src/main.js) - [App.vue](file://fms-vue/src/App.vue) - [vite.config.js](file://fms-vue/vite.config.js) - [package.json](file://fms-vue/package.json) - [router/index.js](file://fms-vue/src/router/index.js) - [stores/app.js](file://fms-vue/src/stores/app.js) - [stores/auth.js](file://fms-vue/src/stores/auth.js) - [stores/permissions.js](file://fms-vue/src/stores/permissions.js) - [services/http.js](file://fms-vue/src/services/http.js) - [services/api.js](file://fms-vue/src/services/api.js) - [theme/index.js](file://fms-vue/src/theme/index.js) - [theme/presets.js](file://fms-vue/src/theme/presets.js) - [utils/i18n.js](file://fms-vue/src/utils/i18n.js) - [layouts/DefaultLayout.vue](file://fms-vue/src/layouts/DefaultLayout.vue) - [layouts/components/AppSidebar.vue](file://fms-vue/src/layouts/components/AppSidebar.vue) - [layouts/components/AppTabs.vue](file://fms-vue/src/layouts/components/AppTabs.vue)目录
简介
本文件为 FMS 前端(基于 Vue 3 + Vite)的架构文档,围绕组件化架构、状态管理(Pinia)、路由与服务层设计展开,系统阐述应用初始化流程、模块加载机制与插件系统、前后端通信架构(HTTP 封装、错误处理、响应拦截器)、主题系统与国际化实现、组件间通信模式与事件处理最佳实践,以及性能优化策略与代码组织规范。目标是帮助开发者快速理解并高效扩展该前端工程。
项目结构
- 入口与初始化:应用通过 main.js 创建 Vue 实例、注册 Pinia 与路由,并在挂载前完成主题同步,确保首屏无闪烁;同时提供启动失败兜底 UI。
- 路由与布局:router/index.js 定义静态路由与全局前置守卫,结合 DefaultLayout 实现侧栏、顶栏、页签与内容区布局;页签与 keep-alive 配合实现页面缓存与按需销毁。
- 状态管理:Pinia 存储分为 app(主题、语言、页签等)、auth(登录态与会话信息)、permissions(权限数据与鉴权能力)。
- 服务层:http.js 基于 axios 封装请求/响应拦截器,统一注入 token、处理鉴权错误与网络异常;api.js 按业务域暴露接口方法。
- 主题与国际化:theme/index.js 与 theme/presets.js 提供主色与深浅色切换;utils/i18n.js 提供 i18n 键生成与校验工具。
- 构建与开发:vite.config.js 配置别名、开发服务器代理;package.json 定义脚本与依赖。
graph TB
A["main.js<br/>应用初始化"] --> B["router/index.js<br/>路由与守卫"]
A --> C["stores/app.js<br/>主题/页签/语言"]
B --> D["layouts/DefaultLayout.vue<br/>布局壳"]
D --> E["layouts/components/AppTabs.vue<br/>页签"]
D --> F["layouts/components/AppSidebar.vue<br/>侧边栏"]
C --> G["theme/index.js<br/>主题应用"]
C --> H["theme/presets.js<br/>预设色板"]
B --> I["stores/auth.js<br/>认证状态"]
B --> J["stores/permissions.js<br/>权限数据"]
J --> K["services/api.js<br/>API 方法"]
K --> L["services/http.js<br/>HTTP 封装"]
图表来源
- main.js:1-78
- router/index.js:1-140
- layouts/DefaultLayout.vue:1-288
- layouts/components/AppTabs.vue:1-200
- layouts/components/AppSidebar.vue:1-200
- stores/app.js:1-123
- stores/auth.js:1-49
- stores/permissions.js:1-196
- services/api.js:1-81
- services/http.js:1-111
- theme/index.js:1-16
- theme/presets.js:1-16
章节来源
核心组件
- 应用入口与引导:创建 Vue 应用、安装 Pinia 与路由、挂载前执行主题同步、路由就绪后挂载并优雅移除启动屏。
- 根组件:仅渲染当前路由视图,并在开发环境异步挂载 FPS 监控浮层。
- 布局壳:DefaultLayout 组合 Splitter、侧栏、顶栏、页签与内容区,实现可拖拽侧栏宽度、页签缓存与过渡动画。
- 侧边栏:根据权限模块构建树渲染导航菜单,支持团队切换与折叠态。
- 页签:监听路由变化自动添加页签,支持关闭、刷新、批量操作与滚动溢出控制。
章节来源
- App.vue:1-15
- layouts/DefaultLayout.vue:1-288
- layouts/components/AppSidebar.vue:1-200
- layouts/components/AppTabs.vue:1-200
架构总览
整体采用“入口 -> 路由 -> 布局 -> 页面”的分层结构,状态集中在 Pinia,服务层统一封装 HTTP,主题与国际化以纯函数与工具形式提供。
sequenceDiagram
participant Boot as "main.js"
participant Router as "router/index.js"
participant Layout as "DefaultLayout.vue"
participant Tabs as "AppTabs.vue"
participant Store as "stores/*"
participant API as "services/api.js"
participant HTTP as "services/http.js"
Boot->>Router : 等待路由就绪
Boot->>Store : 初始化主题同步
Boot->>Boot : 挂载 #app
Router-->>Layout : 渲染默认布局
Layout->>Tabs : 监听路由变化维护页签
Note over Router,Store : 前置守卫加载权限数据
Tabs->>Store : 增删改 visitedTabs / 刷新计数
Layout->>Layout : keep-alive 缓存与过渡
API->>HTTP : 调用后端接口
HTTP-->>API : 统一响应/错误处理
图表来源
- main.js:1-78
- router/index.js:1-140
- layouts/DefaultLayout.vue:1-288
- layouts/components/AppTabs.vue:1-200
- stores/app.js:1-123
- services/api.js:1-81
- services/http.js:1-111
详细组件分析
应用初始化与启动流程
- 创建 Pinia 并启用持久化插件,安装路由与样式。
- 在挂载前执行主题同步,避免首屏闪烁。
- 路由就绪后,将启动屏移动到 body 下,挂载应用,下一帧触发淡出,完成后清理 DOM。
- 捕获启动异常,显示错误提示并提供重试按钮。
flowchart TD
Start(["应用启动"]) --> Init["创建 Pinia/路由/样式"]
Init --> ThemeSync["主题同步(恢复持久化)"]
ThemeSync --> Ready{"路由就绪?"}
Ready -- 否 --> Wait["等待 isReady()"]
Ready -- 是 --> Mount["挂载到 #app"]
Mount --> FadeOut["下一帧淡出启动屏"]
FadeOut --> Cleanup["移除启动屏/清理临时类"]
Cleanup --> End(["完成"])
Wait --> Ready
图表来源
章节来源
路由与权限守卫
- 定义静态路由,包含登录、工作台、测试页、详情页、模块管理等。
- 全局前置守卫:未登录访问受保护路由跳转登录;已登录访问登录页重定向至工作台。
- 在受保护路由进入时统一加载权限数据(去重并发),并根据 meta.moduleId 进行访问控制。
- 后置钩子结束进度条。
sequenceDiagram
participant User as "用户"
participant Router as "router/index.js"
participant Auth as "stores/auth.js"
participant Perm as "stores/permissions.js"
User->>Router : 访问受保护路由
Router->>Auth : 检查 isAuthenticated
alt 未登录
Router-->>User : 重定向到 /login?redirect=...
else 已登录
Router->>Perm : load() 加载权限
Perm-->>Router : 返回权限集合
Router->>Router : 校验 canAccess(moduleId)
alt 无权限
Router-->>User : 重定向到 /dashboard
else 有权限
Router-->>User : 放行
end
end
图表来源
章节来源
状态管理(Pinia)
- app store:管理深色模式、主色、侧栏折叠、语言、页签列表、路由刷新计数;提供持久化策略(仅主题与语言)。
- auth store:管理登录会话与用户信息,区分会话级与跨会话存储;提供登录态计算属性。
- permissions store:拉取模块、用户授权与功能点,支持管理员提权;对同一主体并发请求去重;提供 canAccess/canPower 判断。
classDiagram
class AppStore {
+darkMode
+primaryColor
+sidebarCollapsed
+locale
+visitedTabs
+routeRefreshKeys
+setDark(value)
+toggleDark()
+setPrimary(color)
+toggleSidebar()
+addVisitedTab(tab)
+removeVisitedTab(key)
+refreshRoute(key)
}
class AuthStore {
+loginInfo
+userInfo
+isAuthenticated
+setSession(session, orgId)
+clearSession()
+saveLastLogin(orgId, account)
}
class PermissionStore {
+modules
+grantedPageIds
+moduleCodes
+powerCodes
+load()
+canAccess(code)
+canPower(scope, power)
+clear()
}
AppStore <.. PermissionStore : "页签/刷新"
PermissionStore <.. AuthStore : "读取用户信息"
图表来源
章节来源
服务层与前后端通信
- http 封装:axios 实例配置 baseURL、超时与通用头;请求拦截器注入 Authorization;响应拦截器统一解析 ApiResult,处理 401/40101 登出跳转,非业务错误弹 Message。
- api 层:按业务域导出登录、数据加载、分页、保存、ID/编号生成、描述查询等方法。
- 错误处理:区分鉴权错误与网络/服务端错误;对不可达或 5xx 统一提示,业务 4xx 由调用方决定提示方式。
sequenceDiagram
participant View as "页面/组件"
participant API as "services/api.js"
participant HTTP as "services/http.js"
participant Server as "后端"
View->>API : 调用业务方法
API->>HTTP : post(path, data)
HTTP->>HTTP : 请求拦截器(注入Token)
HTTP->>Server : 发送请求
Server-->>HTTP : 返回响应
HTTP->>HTTP : 响应拦截器(解析ApiResult/错误)
alt 成功
HTTP-->>API : 返回data
API-->>View : 业务结果
else 鉴权错误
HTTP->>HTTP : 清除会话并跳转登录
HTTP-->>View : 抛出错误
else 网络/服务端错误
HTTP->>HTTP : 弹出Message提示
HTTP-->>View : 抛出错误
end
图表来源
章节来源
主题系统与国际化
- 主题系统:通过 applyPrimaryColor 写入 CSS 变量,applyDarkMode 切换 html 的 dark 类;store 中 watch 变更即时生效;预设色板集中管理。
- 国际化:提供键校验、命名空间提取、模块项/模块键构建、透视行合并与归并等工具,便于后续接入 i18n 资源与多语言展示。
flowchart TD
Change["store 状态变更"] --> Apply["应用主题(CSS变量/dark类)"]
Apply --> Render["UI 实时响应"]
KeyGen["i18n 工具"] --> Validate["键格式校验"]
KeyGen --> Build["构建模块/项键"]
KeyGen --> Merge["合并透视行/补全空值"]
图表来源
章节来源
布局与页签机制
- 布局壳:Splitter 控制侧栏宽度,折叠态固定 48px;主区域包含顶栏、页签与内容区;内容区使用 keep-alive 与 transition 实现缓存与过渡。
- 页签:监听路由变化自动添加;支持关闭、刷新、批量关闭;右键菜单提供常用操作;滚动溢出检测与滚轮横向滚动。
- 详情页缓存:针对带参数的详情页,按 fullPath 生成独立缓存名,实现参数隔离与关闭即销毁。
sequenceDiagram
participant Route as "vue-router"
participant Layout as "DefaultLayout.vue"
participant Tabs as "AppTabs.vue"
participant Store as "stores/app.js"
Route-->>Layout : 路由变化
Layout->>Tabs : 更新 activeKey/滚动定位
Tabs->>Store : addVisitedTab/removeVisitedTab/closeOthers...
Layout->>Layout : keep-alive include 更新
Layout->>Layout : 内层组件 key 变化触发刷新
图表来源
章节来源
- layouts/DefaultLayout.vue:1-288
- layouts/components/AppTabs.vue:1-200
- layouts/components/AppSidebar.vue:1-200
组件间通信与事件处理最佳实践
- 通过 Pinia 共享全局状态(主题、页签、权限),避免跨层级 props 传递。
- 路由驱动视图与页签联动,减少自定义事件风暴。
- 使用 keep-alive 与动态 key 控制页面缓存与刷新粒度。
- 对外部库(如 stk-table-vue)通过特性注册与事件绑定进行集成。
章节来源
依赖关系分析
- 构建与开发:Vite 作为构建工具,Vue 插件启用单文件组件;开发服务器代理 /api 到后端。
- 运行时依赖:Vue、Vue Router、Pinia、Axios、Dayjs、NProgress、stk-table-vue、Lucide 图标等。
- 模块耦合:路由与权限强耦合(守卫加载权限),布局与页签通过 store 解耦,服务层与业务组件通过 api 解耦。
graph LR
Vite["vite.config.js"] --> Dev["开发服务器代理 /api"]
Pkg["package.json"] --> Deps["运行时依赖"]
Deps --> Vue["Vue/Router/Pinia"]
Deps --> Axios["Axios"]
Deps --> Table["stk-table-vue"]
Vue --> Layout["DefaultLayout.vue"]
Axios --> HTTP["services/http.js"]
图表来源
章节来源
性能考虑
- 首屏优化:路由就绪后再挂载,避免阻塞;启动屏移至 body 下,使用合成属性过渡提升流畅度。
- 缓存策略:keep-alive 限制最大实例数,按页签精确 include;详情页按 fullPath 独立缓存,关闭即销毁。
- 懒加载:路由与组件采用动态 import,减少初始包体积。
- 网络优化:统一超时与错误提示,避免重复弹窗;鉴权错误统一处理,减少无效请求。
- 交互体验:页签滚动溢出检测与滚轮横向滚动,提升长列表可读性。
[本节为通用指导,不直接分析具体文件]
故障排查指南
- 启动失败:检查 main.js 中的启动错误处理逻辑,确认 #app-boot 元素存在与重试按钮行为。
- 登录态异常:检查 http.js 响应拦截器对 401/40101 的处理,确认是否清除了会话并跳转登录。
- 权限空白:检查 permissions store 的 load 去重逻辑与主体 key,确认并发请求版本控制正确。
- 页签异常:检查 AppTabs 的路由监听与 visitedTabs 更新,确认 close/refresh 操作后路由回退逻辑。
- 主题闪烁:确认 initThemeSync 在挂载前执行,且 applyDarkMode/applyPrimaryColor 及时生效。
章节来源
- main.js:24-78
- services/http.js:30-103
- stores/permissions.js:41-108
- layouts/components/AppTabs.vue:21-132
- stores/app.js:104-123
结论
FMS 前端采用清晰的层次化架构:入口负责初始化与引导,路由与布局负责导航与呈现,Pinia 集中管理状态,服务层统一封装通信,主题与国际化以纯函数与工具形式提供。通过路由守卫与权限 store 实现细粒度访问控制,借助 keep-alive 与动态 key 实现高效的页面缓存与刷新。整体设计兼顾了可维护性、可扩展性与用户体验。
附录
- 开发脚本:dev/build/preview/test/lint/fmt/check 等命令位于 package.json。
- 构建别名:@ 指向 src 目录,简化导入路径。
- 代理配置:开发环境将 /api 请求转发至后端地址。
章节来源