You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
20 KiB
20 KiB
RuoYi-Vue 前端架构分析
项目版本: 3.8.0 | 技术栈: Vue 2.6.12 + Element UI 2.15.6 + Vuex 3.6 + Vue Router 3.4.9
构建工具: Vue CLI 4.4.6 | 描述: 摸金分析系统
分析日期: 2026-06-28
1. 组件结构分析
1.1 组件树图(ASCII)
src/
├── components/ # 全局通用组件(22 个组件组)
│ ├── Breadcrumb/ # ── 面包屑导航
│ ├── Crontab/ # ── Cron 表达式生成器(9 个子组件)
│ │ ├── day.vue / hour.vue / min.vue # 时间维度选择
│ │ ├── month.vue / week.vue / year.vue
│ │ ├── second.vue / result.vue / index.vue
│ ├── DictData/ # ── 字典数据管理器(JS 模块)
│ ├── DictTag/ # ── 字典标签渲染
│ ├── Editor/ # ── 富文本编辑器(Quill)
│ ├── FileUpload/ # ── 文件上传
│ ├── Hamburger/ # ── 汉堡菜单按钮
│ ├── HeaderSearch/ # ── 头部搜索(Fuse.js)
│ ├── IconSelect/ # ── SVG 图标选择器
│ ├── ImagePreview/ # ── 图片预览
│ ├── ImageUpload/ # ── 图片上传
│ ├── Kdialog/ # ── K 线对话框(业务组件)
│ ├── NegativeDialog/ # ── 涨跌停对话框(业务组件)
│ ├── Pagination/ # ── 分页器
│ ├── PanThumb/ # ── 全景缩略图
│ ├── ParentView/ # ── 父级视图占位
│ ├── RightPanel/ # ── 右侧面板
│ ├── RightToolbar/ # ── 右侧工具栏
│ ├── RuoYi/ # ── 外部链接(Doc / Git)
│ ├── Screenfull/ # ── 全屏切换
│ ├── SizeSelect/ # ── 组件尺寸选择
│ ├── SvgIcon/ # ── SVG 图标封装
│ ├── ThemePicker/ # ── 主题选择器
│ ├── TopNav/ # ── 顶部导航
│ ├── TrendStocksDialog/ # ── 板块趋势对话框(业务)
│ └── iFrame/ # ── 内嵌 iframe
│
├── layout/ # 布局框架
│ ├── index.vue # ── 主布局容器
│ ├── components/
│ │ ├── AppMain.vue # ── 主内容区(含 keep-alive)
│ │ ├── Navbar.vue # ── 顶部导航栏
│ │ ├── Sidebar/ # ── 侧边栏
│ │ │ ├── index.vue / SidebarItem.vue / Item.vue / Link.vue / Logo.vue
│ │ ├── TagsView/ # ── 标签页视图
│ │ │ ├── index.vue / ScrollPane.vue
│ │ ├── Settings/ # ── 系统设置面板
│ │ └── InnerLink/ # ── 内部链接
│ └── mixin/ResizeHandler.js # ── 响应式 Mixin
│
├── views/ # 页面视图(按业务模块)
│ ├── dashboard/ # ── 仪表盘(5 个图表组件)
│ ├── system/ # ── 系统管理(8 个子模块)
│ ├── booksystem/ # ── 账本系统(6 个子模块)
│ ├── stocksystem/ # ── 股票系统(8 个子模块)
│ ├── monitor/ # ── 系统监控(7 个子模块)
│ ├── tool/ # ── 系统工具(代码生成等)
│ ├── error/ # ── 错误页面
│ ├── login.vue / register.vue # ── 认证页面
│ └── index.vue # ── 首页
1.2 分类统计
| 分类 | 组件/模块数量 | 说明 |
|---|---|---|
| 通用 UI 组件 | 10 | Breadcrumb, Pagination, SvgIcon, Hamburger, Screenfull, SizeSelect, ThemePicker, PanThumb, ParentView, iFrame |
| 业务组件 | 4 | Kdialog, NegativeDialog, TrendStocksDialog, RightToolbar |
| 功能组件 | 7 | Crontab(9), DictTag, DictData, Editor, FileUpload, ImageUpload, ImagePreview, IconSelect, HeaderSearch |
| 布局组件 | 8 | Layout, AppMain, Navbar, Sidebar(5), TagsView(2), Settings, InnerLink |
| 视图模块 | 6 大模块 | dashboard, system, booksystem, stocksystem, monitor, tool |
| 视图页面总数 | ~40 个 | 包含子页面和嵌套路由页面 |
1.3 组件复用程度分析
高复用组件(被全局注册或在多个视图中引用):
Pagination— 几乎所有列表页面DictTag— 所有涉及字典展示的页面RightToolbar— 所有 CRUD 列表页Editor/FileUpload/ImageUpload— 表单页面Breadcrumb— 布局层全局使用SvgIcon— 全局图标渲染基础组件
中复用组件(特定业务场景):
Crontab— 仅定时任务模块Kdialog/TrendStocksDialog/NegativeDialog— 股票业务模块HeaderSearch— 仅 Navbar 使用
低复用组件(单一职责或一次性使用):
PanThumb— 仅用户个人资料页RightPanel— 仅布局设置入口RuoYi/Doc/RuoYi/Git— 外部链接
1.4 组件职责分析
| 组件 | 职责单一性 | 评价 |
|---|---|---|
| Crontab | 一般 | 9 个子组件拆分合理,但耦合度较高 |
| Kdialog / NegativeDialog | 低 | 业务逻辑与 UI 混合,体积过大 |
| Dashboard 图表组件 | 好 | 每个图表独立文件,职责清晰 |
| Layout 子组件 | 好 | Sidebar、Navbar、TagsView 分离清晰 |
2. 路由与权限控制
2.1 路由结构图
路由系统
├── 静态路由 (constantRoutes) — 始终可访问
│ ├── /redirect/:path — 重定向页
│ ├── /login — 登录页
│ ├── /register — 注册页
│ ├── /404 — 404 错误页
│ ├── /401 — 401 错误页
│ ├── / (首页) — Dashboard
│ └── /user/profile — 个人中心
│
├── 动态路由 (dynamicRoutes) — 按权限加载
│ ├── /system/user-auth/role/:userId — 分配角色 [system:user:edit]
│ ├── /system/role-auth/user/:roleId — 分配用户 [system:role:edit]
│ ├── /system/dict-data/index/:dictId — 字典数据 [system:dict:list]
│ ├── /monitor/job-log — 调度日志 [monitor:job:list]
│ └── /tool/gen-edit — 修改生成配置 [tool:gen:edit]
│
└── 服务端路由 — 登录后从后端获取
├── filterAsyncRouter() ── 转换为组件对象
├── filterDynamicRoutes() ── 权限过滤
└── router.addRoutes() ── 动态注册
2.2 权限加载流程
用户登录
│
├─ 1. 输入用户名/密码/验证码
├─ 2. 调用 POST /login → 获取 token → 存入 Cookie (Admin-Token)
├─ 3. 路由守卫 beforeEach 拦截
│ │
│ ├─ 有 token ? ──NO──→ 跳转 /login
│ │ │
│ │ YES
│ │ │
│ │ ├─ roles.length === 0 ? ──NO──→ next()
│ │ │ │
│ │ │ YES
│ │ │ │
│ │ │ ├─ GetInfo() → 获取用户信息(roles + permissions)
│ │ │ │
│ │ │ └─ GenerateRoutes() → 请求 /getRouters
│ │ │ │
│ │ │ ├─ filterAsyncRouter() → 字符串转组件
│ │ │ ├─ filterDynamicRoutes() → 权限过滤
│ │ │ └─ router.addRoutes() → 注册路由
│ │ │
│ │ └─ next({ ...to, replace: true }) → 重新导航
│ │
│ └─ 无 token → 白名单 ? ──YES──→ next()
│ │
│ NO
│ │
│ 跳转 /login?redirect=xxx
│
└─ 4. 页面渲染 → 动态菜单展示
2.3 权限控制机制
项目采用 三层权限控制:
| 层级 | 实现方式 | 代码位置 |
|---|---|---|
| 路由级 | 路由守卫 + 服务端返回菜单 | src/permission.js + src/store/modules/permission.js |
| 页面级 | v-hasPermi / v-hasRole 指令 |
src/directive/permission/ |
| 按钮级 | $auth.hasPermi() / $auth.hasRole() |
src/plugins/auth.js |
权限标识格式:模块:资源:操作(如 system:user:add)
超级权限:*:*:*(拥有所有权限)
超级角色:admin(拥有所有角色权限)
2.4 路由懒加载
// 开发环境:使用 require 实现
if (process.env.NODE_ENV === 'development') {
return (resolve) => require([`@/views/${view}`], resolve)
}
// 生产环境:使用 import 实现
else {
return () => import(`@/views/${view}`)
}
3. 状态管理分析
3.1 Vuex 模块图
Vuex Store
├── getters.js # 统一访问入口
│ ├── sidebar, size, device → state.app
│ ├── visitedViews, cachedViews → state.tagsView
│ ├── token, avatar, name, roles → state.user
│ └── permission_routes, topbarRouters, sidebarRouters → state.permission
│
├── modules/app.js # 应用级状态
│ ├── sidebar { opened, withoutAnimation }
│ ├── device ('desktop' | 'mobile')
│ └── size ('medium' | 'small' | 'mini')
│ └── 持久化:Cookies (sidebarStatus, size)
│
├── modules/user.js # 用户状态
│ ├── token, name, avatar
│ ├── roles: string[]
│ ├── permissions: string[]
│ └── Actions: Login, GetInfo, LogOut, FedLogOut
│ └── 持久化:Cookies (Admin-Token)
│
├── modules/permission.js # 权限/路由状态
│ ├── routes, addRoutes — 完整路由表 / 动态路由
│ ├── defaultRoutes, topbarRouters, sidebarRouters
│ └── Actions: GenerateRoutes
│
├── modules/tagsView.js # 标签页状态
│ ├── visitedViews: [] — 已访问的标签页
│ ├── cachedViews: [] — 被 keep-alive 缓存的页面
│ └── Actions: addView, delView, delOthersViews, delAllViews,
│ delRightTags, delLeftTags
│
└── modules/settings.js # 系统设置状态
├── theme, sideTheme, showSettings
├── topNav, tagsView, fixedHeader, sidebarLogo, dynamicTitle
└── 持久化:localStorage ('layout-setting')
3.2 数据流向
┌──────────────┐
│ Vue 组件 │
└──────┬───────┘
│ dispatch
▼
┌──────────────┐
│ Actions │ ──→ 异步操作(API 调用)
└──────┬───────┘
│ commit
▼
┌──────────────┐
│ Mutations │ ──→ 同步修改 state
└──────┬───────┘
│
▼
┌──────────────┐
│ State │ ──→ 响应式数据
└──────┬───────┘
│
┌──────┴───────┐
│ Getters │ ──→ 计算属性
└──────┬───────┘
│
┌──────┴───────┐
│ Vue 组件 │ ←── 重新渲染
└──────────────┘
3.3 状态持久化
| 状态 | 存储方式 | 键名 | 说明 |
|---|---|---|---|
| 用户 Token | Cookie | Admin-Token |
会话认证 |
| 侧边栏状态 | Cookie | sidebarStatus |
展开/折叠 |
| 组件尺寸 | Cookie | size |
Element UI 默认尺寸 |
| 布局设置 | localStorage | layout-setting |
JSON 对象(主题、顶栏、标签页等) |
注意:Vuex 本身无持久化插件,数据分散在 Cookie 和 localStorage 中手动读写。
4. API 调用层
4.1 Axios 封装架构
src/api/ # API 模块目录
├── booksystem/ # ── 账本系统 API(6 个文件)
├── stocksystem/ # ── 股票系统 API(9 个文件)
├── system/ # ── 系统管理 API(7 个文件 + dict 子目录)
├── monitor/ # ── 系统监控 API(7 个文件)
├── tool/ # ── 系统工具 API
├── login.js # ── 登录/获取信息/退出
├── menu.js # ── 获取动态路由
└── index.js # ── 通用 API
src/utils/request.js # Axios 实例封装
├── baseURL: process.env.VUE_APP_BASE_API
├── timeout: 60000ms (60秒)
├── Content-Type: application/json;charset=utf-8
└── download() 方法 # ── 文件下载封装
4.2 拦截器实现
请求拦截器(Request Interceptor)
请求发出
│
├─ 1. 检查是否需要 token(config.headers.isToken === false 可跳过)
├─ 2. 自动添加 Authorization: Bearer {token}
├─ 3. GET 请求 params 序列化(tansParams 处理)
│ └─ 将 ?a=1&b=2 拼接到 URL,清空 config.params
└─ 4. 发送请求
响应拦截器(Response Interceptor)
收到响应
│
├─ 解析业务状态码 res.data.code
│
├─ code === 401 → MessageBox 弹窗 → 确认后 LogOut → 跳首页
├─ code === 500 → Message.error(msg) → Promise.reject
├─ code !== 200 → Notification.error({ title: msg }) → Promise.reject
├─ code === 200 → 返回 res.data
│
└─ HTTP 错误捕获(error)
├─ Network Error → "后端接口连接异常"
├─ timeout → "系统接口请求超时"
├─ status code 4xx/5xx → "系统接口 XXX 异常"
└─ Message.error(message, duration: 5s)
4.3 错误处理机制
| 错误类型 | 处理方式 | 用户体验 |
|---|---|---|
| 401 未授权 | 弹窗确认 → 退出登录 | 强提醒,需用户操作 |
| 500 服务端错误 | Message 提示 | 轻量提示,自动消失 |
| 其他业务错误 | Notification 通知 | 右上角通知 |
| 网络异常 | Message 提示(5 秒) | 长时提示 |
| 请求超时 | Message 提示(5 秒) | 长时提示 |
| 下载失败 | 解析 blob 中的 JSON 错误信息 | 自动处理登录态失效 |
4.4 错误码映射
通过 src/utils/errorCode.js 实现业务码到提示文本的映射,支持:
- 预定义错误码映射
- 默认兜底文本
- 服务端返回的自定义 msg
5. 存在的问题
5.1 问题清单
| # | 问题 | 影响 | 严重程度 |
|---|---|---|---|
| 1 | Vue 2 + Vue CLI 技术栈老化:Vue 2 已于 2023 年底停止官方维护,Vue Router 3、Vuex 3、Vue CLI 4 均为旧版本,存在安全风险且无法使用 Composition API 等现代特性 | 长期维护成本高,无法使用新生态,第三方库兼容性逐步下降 | 🔴 高 |
| 2 | 权限逻辑重复实现:权限校验在 4 个位置分别实现(plugins/auth.js、utils/permission.js、directive/permission/hasPermi.js、directive/permission/hasRole.js),逻辑高度重复但存在细微差异,如超级权限标识散落在各处 |
维护困难,修改权限规则需同时修改多处,易遗漏导致权限漏洞 | 🟡 中 |
| 3 | 状态持久化分散且不统一:Token 存 Cookie、sidebar 状态存 Cookie、布局设置存 localStorage,手动读写无统一管理,无 vuex-persistedstate 等插件,刷新后部分状态可能不一致 | 状态管理混乱,调试困难,存在不同步风险 | 🟡 中 |
| 4 | 业务组件与通用组件混放:Kdialog、TrendStocksDialog、NegativeDialog 等强业务耦合组件放在 src/components/ 通用组件目录,且通过全局注册加载,增加了首屏包体积 |
首屏加载变慢,通用组件库被业务代码污染,不利于复用 | 🟡 中 |
| 5 | 视图页面高度模板化:system/、booksystem/、stocksystem/ 下的 CRUD 页面结构高度相似(搜索表单 + 表格 + 分页 + 弹窗),但未抽取为可配置的高阶组件或 Schema 驱动表单 |
代码冗余度高,新增 CRUD 页面工作量大,修改通用逻辑需改多个文件 | 🟡 中 |
| 6 | 生产/开发环境使用不同的懒加载方式:开发环境用 require(),生产环境用 import(),两份逻辑增加了维护复杂度,且开发环境无法享受代码分割的优化效果 |
开发构建产物大,热更新慢,行为不一致可能导致线上问题 | 🟢 低 |
| 7 | 响应拦截器中 401 处理使用同步 location.href 跳转:location.href = '/index' 是硬跳转,绕过了 Vue Router 的导航守卫和状态清理流程 |
可能导致 Vuex 状态未完全清理、标签页数据残留 | 🟢 低 |
| 8 | 缺少统一的请求重试机制:网络波动或后端短暂不可用时,所有请求直接失败,无自动重试策略 | 用户体验下降,偶发性网络问题导致操作失败 | 🟢 低 |
5.2 优化建议
| 优先级 | 建议 | 预期收益 |
|---|---|---|
| P0 | 规划升级至 Vue 3 + Vite + Pinia | 获得性能提升、Composition API、更好的类型支持 |
| P1 | 抽取统一的权限校验核心模块,消除重复代码 | 降低维护成本,消除权限逻辑不一致风险 |
| P1 | 引入 vuex-persistedstate 或自定义统一持久化层 | 状态管理规范化,减少手动读写 Cookie/LocalStorage |
| P2 | 将业务组件移至对应业务模块目录,或采用按需异步加载 | 减少首屏包体积,保持通用组件库纯净 |
| P2 | 抽取 CRUD 页面为 Schema 驱动的高阶组件 | 减少 60%+ 的 CRUD 页面代码量 |
| P3 | 统一开发/生产的组件加载方式 | 简化代码,提升开发体验 |
| P3 | 401 处理改用 router.replace() 代替 location.href | 状态清理更完整,避免残留数据 |
| P3 | 在 request.js 中添加可配置的重试机制 | 提升网络不稳定场景下的用户体验 |
附录:技术栈概览
| 类别 | 技术 | 版本 |
|---|---|---|
| 核心框架 | Vue | 2.6.12 |
| UI 框架 | Element UI | 2.15.6 |
| 路由 | Vue Router | 3.4.9 |
| 状态管理 | Vuex | 3.6.0 |
| HTTP 客户端 | Axios | 0.24.0 |
| 构建工具 | Vue CLI | 4.4.6 |
| CSS 预处理器 | Sass | 1.32.13 |
| 图表库 | ECharts | 4.9.0 |
| 富文本 | Quill | 1.3.7 |
| 代码高亮 | highlight.js | 9.18.5 |
| 拖拽排序 | SortableJS / vuedraggable | 1.10.2 / 2.24.3 |
| SVG 图标 | svg-sprite-loader | 5.1.1 |
| 加密 | jsencrypt | 3.2.1 |
| 进度条 | NProgress | 0.2.0 |
| Cookie | js-cookie | 3.0.1 |