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.
RuoYi-Vue/docs/superpowers/plans/2026-06-28-frontend-moderni...

1280 lines
48 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# RuoYi-Vue 前端现代化重构实施计划
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
---
```yaml
---
change: refactor-frontend-modernization
design-doc: docs/superpowers/specs/2026-06-28-frontend-modernization-design.md
base-ref: 1c7d266b7f70eae46abd1f011e443ac54b4cc7f4
---
```
**Goal:** 将 RuoYi-Vue 前端从 Vue2 + Vue CLI + Element UI + Vuex 升级到 Vue3 + Vite + TypeScript + Element Plus + Pinia同时参考 demo-dashboard-heatmap.html 的深色主题和数据可视化风格,保持与现有后端 API 完全兼容。
**Architecture:** 采用渐进式迁移策略,先在独立目录 (`ruoyi-ui-next/`) 构建新项目,按模块逐步迁移组件和页面,确保后端零改动。
**Tech Stack:**
| 类别 | 旧技术栈 | 新技术栈 |
|------|---------|---------|
| 框架 | Vue 2.6.12 | Vue 3.5+ |
| 构建 | Vue CLI 4.4.6 | Vite 6+ |
| 语言 | JavaScript | TypeScript 5+ |
| UI | Element UI 2.15.6 | Element Plus 2.9+ |
| 路由 | Vue Router 3.4.9 | Vue Router 4+ |
| 状态 | Vuex 3.6.0 | Pinia 2.3+ |
| HTTP | Axios 0.24.0 | Axios 1.7+ |
| 图表 | ECharts 4.9.0 | ECharts 5.5+ |
| CSS | Sass 1.32 | SCSS + CSS Variables |
## Global Constraints
- **新项目目录**: `ruoyi-ui-next/`(与 `ruoyi-ui/` 并存,不影响现有前端)
- **后端兼容**: 所有 API 路径、请求格式、响应格式保持完全一致
- **权限兼容**: 沿用 `Admin-Token` Cookie 认证和 `*:*:*` 权限标识体系
- **深色主题默认**: 默认深色主题,配色方案源自 `data/demo-dashboard-heatmap.html`
- **TypeScript 严格模式**: 启用 `strict: true`,所有新增代码必须有类型标注
- **代码风格**: ESLint + Prettier + Vue 3 `<script setup>` 语法
- **组件命名**: PascalCase 文件名kebab-case 标签名
---
## Task 1: 项目初始化
**目标**: 使用 Vite 创建 Vue3 + TypeScript 项目骨架,配置开发工具链
**Files:**
- Create: `ruoyi-ui-next/package.json`
- Create: `ruoyi-ui-next/vite.config.ts`
- Create: `ruoyi-ui-next/tsconfig.json`
- Create: `ruoyi-ui-next/tsconfig.node.json`
- Create: `ruoyi-ui-next/index.html`
- Create: `ruoyi-ui-next/.env.development`
- Create: `ruoyi-ui-next/.env.production`
- Create: `ruoyi-ui-next/.eslintrc.cjs`
- Create: `ruoyi-ui-next/.prettierrc`
- Create: `ruoyi-ui-next/.editorconfig`
- Create: `ruoyi-ui-next/.gitignore`
- Create: `ruoyi-ui-next/src/main.ts`
- Create: `ruoyi-ui-next/src/App.vue`
- Reference: `ruoyi-ui/package.json`(依赖参考)
- Reference: `ruoyi-ui/.env.development`(环境变量参考)
**Description:** 创建全新 Vite + Vue3 + TypeScript 项目,配置完整的开发工具链和项目结构
**Dependencies:**
### Step 1: 初始化项目结构
```
ruoyi-ui-next/
├── public/
│ └── favicon.ico
├── src/
│ ├── api/ # API 调用层
│ │ ├── index.ts # 通用 API
│ │ ├── login.ts # 登录/认证
│ │ ├── menu.ts # 路由菜单
│ │ ├── system/ # 系统管理 API
│ │ │ ├── user.ts
│ │ │ ├── role.ts
│ │ │ ├── menu.ts
│ │ │ ├── dept.ts
│ │ │ ├── config.ts
│ │ │ ├── notice.ts
│ │ │ ├── post.ts
│ │ │ └── dict/
│ │ │ ├── data.ts
│ │ │ └── type.ts
│ │ ├── monitor/ # 系统监控 API
│ │ │ ├── cache.ts
│ │ │ ├── job.ts
│ │ │ ├── logininfor.ts
│ │ │ ├── online.ts
│ │ │ ├── operlog.ts
│ │ │ └── server.ts
│ │ ├── booksystem/ # 账本系统 API
│ │ │ ├── account.ts
│ │ │ ├── book.ts
│ │ │ ├── operations.ts
│ │ │ ├── statistics.ts
│ │ │ ├── statistictotal.ts
│ │ │ └── statisticremain.ts
│ │ └── stocksystem/ # 股票系统 API
│ │ ├── stocks.ts
│ │ ├── stockbasic.ts
│ │ ├── stockindex.ts
│ │ ├── financial.ts
│ │ ├── trends.ts
│ │ ├── trendStocks.ts
│ │ ├── newrecord.ts
│ │ ├── stockslimit.ts
│ │ └── book.ts
│ ├── assets/
│ │ ├── icons/svg/ # SVG 图标(从 ruoyi-ui 复制全部)
│ │ ├── styles/ # 全局样式
│ │ │ ├── index.scss
│ │ │ ├── variables.scss
│ │ │ ├── dark-theme.scss
│ │ │ └── transition.scss
│ │ └── images/ # 静态图片
│ ├── components/ # 全局通用组件
│ │ ├── SvgIcon/
│ │ ├── DictTag/
│ │ ├── Pagination/
│ │ ├── Breadcrumb/
│ │ ├── Hamburger/
│ │ ├── Screenfull/
│ │ ├── SizeSelect/
│ │ ├── ThemePicker/
│ │ ├── TopNav/
│ │ ├── RightToolbar/
│ │ ├── Editor/
│ │ ├── FileUpload/
│ │ ├── ImageUpload/
│ │ ├── ImagePreview/
│ │ ├── IconSelect/
│ │ ├── HeaderSearch/
│ │ ├── Crontab/
│ │ ├── ParentView/
│ │ └── iFrame/
│ ├── components-business/ # 业务组件(解耦通用组件)
│ │ ├── Kdialog/
│ │ ├── NegativeDialog/
│ │ └── TrendStocksDialog/
│ ├── layout/ # 布局框架
│ │ ├── index.vue
│ │ └── components/
│ │ ├── AppMain.vue
│ │ ├── Navbar.vue
│ │ ├── Sidebar/
│ │ ├── TagsView/
│ │ ├── Settings/
│ │ └── InnerLink/
│ ├── router/
│ │ └── index.ts
│ ├── stores/ # Pinia 状态管理
│ │ ├── index.ts
│ │ ├── modules/
│ │ │ ├── app.ts
│ │ │ ├── user.ts
│ │ │ ├── permission.ts
│ │ │ ├── tagsView.ts
│ │ │ └── settings.ts
│ │ └── types.ts
│ ├── views/ # 页面视图
│ │ ├── dashboard/
│ │ ├── system/
│ │ ├── booksystem/
│ │ ├── stocksystem/
│ │ ├── monitor/
│ │ ├── tool/
│ │ ├── error/
│ │ ├── login.vue
│ │ ├── register.vue
│ │ └── index.vue
│ ├── utils/ # 工具函数
│ │ ├── request.ts
│ │ ├── auth.ts
│ │ ├── permission.ts
│ │ ├── validate.ts
│ │ ├── errorCode.ts
│ │ ├── ruoyi.ts
│ │ ├── dict/
│ │ └── types/
│ ├── directives/ # 自定义指令
│ │ ├── index.ts
│ │ └── permission.ts
│ ├── plugins/ # 插件(从 ruoyi-ui 重构)
│ │ ├── index.ts
│ │ ├── auth.ts
│ │ ├── cache.ts
│ │ ├── modal.ts
│ │ └── tab.ts
│ ├── types/ # TypeScript 类型定义
│ │ ├── api.d.ts
│ │ ├── store.d.ts
│ │ ├── router.d.ts
│ │ └── global.d.ts
│ └── permission.ts # 路由权限守卫
├── index.html
├── vite.config.ts
├── tsconfig.json
├── package.json
└── .env.*
```
- [ ] **Step 1.1: 创建 package.json**
```json
{
"name": "ruoyi-ui-next",
"version": "4.0.0",
"private": true,
"type": "module",
"scripts": {
"dev": "vite",
"build": "vue-tsc --noEmit && vite build",
"build:prod": "vite build --mode production",
"preview": "vite preview",
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx --fix",
"format": "prettier --write src/"
},
"dependencies": {
"vue": "^3.5.0",
"vue-router": "^4.4.0",
"pinia": "^2.3.0",
"pinia-plugin-persistedstate": "^4.0.0",
"axios": "^1.7.0",
"element-plus": "^2.9.0",
"@element-plus/icons-vue": "^2.3.0",
"echarts": "^5.5.0",
"nprogress": "^0.2.0",
"js-cookie": "^3.0.5",
"jsencrypt": "^3.3.0",
"lodash-es": "^4.17.21",
"dayjs": "^1.11.0"
},
"devDependencies": {
"@vitejs/plugin-vue": "^5.2.0",
"vite": "^6.0.0",
"vite-plugin-svg-icons": "^2.0.1",
"vue-tsc": "^2.2.0",
"typescript": "^5.7.0",
"sass": "^1.83.0",
"@types/node": "^22.0.0",
"@types/nprogress": "^0.2.3",
"@types/js-cookie": "^3.0.6",
"@types/lodash-es": "^4.17.12",
"eslint": "^9.0.0",
"@eslint/js": "^9.0.0",
"eslint-plugin-vue": "^9.0.0",
"typescript-eslint": "^8.0.0",
"prettier": "^3.4.0",
"unplugin-auto-import": "^0.18.0",
"unplugin-vue-components": "^0.27.0"
}
}
```
- [ ] **Step 1.2: 创建 vite.config.ts**
```typescript
import { defineConfig, loadEnv } from 'vite'
import vue from '@vitejs/plugin-vue'
import { resolve } from 'path'
import { createSvgIconsPlugin } from 'vite-plugin-svg-icons'
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
export default defineConfig(({ mode }) => {
const env = loadEnv(mode, process.cwd())
return {
plugins: [
vue(),
createSvgIconsPlugin({
iconDirs: [resolve(__dirname, 'src/assets/icons/svg')],
symbolId: 'icon-[dir]-[name]',
}),
AutoImport({
imports: ['vue', 'vue-router', 'pinia'],
resolvers: [ElementPlusResolver()],
dts: 'src/types/auto-imports.d.ts',
}),
Components({
resolvers: [ElementPlusResolver()],
dts: 'src/types/components.d.ts',
}),
],
resolve: {
alias: {
'@': resolve(__dirname, 'src'),
},
extensions: ['.ts', '.js', '.vue', '.json'],
},
server: {
port: 80,
host: '0.0.0.0',
open: true,
proxy: {
'/dev-api': {
target: env.VITE_APP_BASE_URL || 'http://localhost:8080',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/dev-api/, ''),
},
},
},
build: {
target: 'es2015',
outDir: 'dist',
sourcemap: false,
rollupOptions: {
output: {
manualChunks: {
'element-plus': ['element-plus'],
echarts: ['echarts'],
},
},
},
},
css: {
preprocessorOptions: {
scss: {
additionalData: `@use "@/assets/styles/variables.scss" as *;`,
},
},
},
}
})
```
- [ ] **Step 1.3: 创建 tsconfig.json**
```json
{
"compilerOptions": {
"target": "ES2020",
"module": "ESNext",
"moduleResolution": "bundler",
"strict": true,
"jsx": "preserve",
"importHelpers": true,
"experimentalDecorators": true,
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"forceConsistentCasingInFileNames": true,
"useDefineForClassFields": true,
"sourceMap": true,
"baseUrl": ".",
"paths": {
"@/*": ["src/*"]
},
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"types": ["vite/client"]
},
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.vue"],
"exclude": ["node_modules", "dist"]
}
```
- [ ] **Step 1.4: 创建环境变量文件**
`.env.development`:
```
VITE_APP_TITLE=摸金分析系统
VITE_APP_BASE_API=/dev-api
VITE_APP_BASE_URL=http://localhost:8080
```
`.env.production`:
```
VITE_APP_TITLE=摸金分析系统
VITE_APP_BASE_API=/prod-api
VITE_APP_BASE_URL=
```
- [ ] **Step 1.5: 创建 .eslintrc.cjs + .prettierrc**
配置 ESLint 规则:
- `@typescript-eslint/strict`
- `vue/vue3-recommended`
- Prettier 集成
Prettier 配置:
- 2 空格缩进
- 单引号
- 末尾无分号
- 行宽 100
- [ ] **Step 1.6: 创建项目入口文件**
`src/main.ts`:
- 创建 Vue App
- 注册 Element Plus含中文语言包
- 注册 Pinia
- 注册 Vue Router
- 注册全局指令
- 注册全局插件
- 挂载应用
`src/App.vue`:
- 根组件,仅包含 `<router-view />`
- [ ] **Step 1.7: 复制 SVG 图标资源**
`ruoyi-ui/src/assets/icons/svg/` 复制所有 SVG 文件到 `ruoyi-ui-next/src/assets/icons/svg/`
**验收标准:**
- [ ] `npm install` 无报错
- [ ] `npm run dev` 成功启动,访问 `http://localhost` 显示空白页面(仅 App.vue
- [ ] TypeScript 编译通过(`vue-tsc --noEmit`
- [ ] ESLint 检查通过(`npm run lint`
- [ ] 项目结构与规范一致
- [ ] `ruoyi-ui/` 不受任何影响
---
## Task 2: 核心架构搭建
**目标**: 配置 Vue Router 4、Pinia 状态管理、Axios 调用层、权限控制机制
**Files:**
- Create: `ruoyi-ui-next/src/router/index.ts`
- Create: `ruoyi-ui-next/src/stores/index.ts`
- Create: `ruoyi-ui-next/src/stores/modules/app.ts`
- Create: `ruoyi-ui-next/src/stores/modules/user.ts`
- Create: `ruoyi-ui-next/src/stores/modules/permission.ts`
- Create: `ruoyi-ui-next/src/stores/modules/tagsView.ts`
- Create: `ruoyi-ui-next/src/stores/modules/settings.ts`
- Create: `ruoyi-ui-next/src/stores/types.ts`
- Create: `ruoyi-ui-next/src/utils/request.ts`
- Create: `ruoyi-ui-next/src/utils/auth.ts`
- Create: `ruoyi-ui-next/src/utils/permission.ts`
- Create: `ruoyi-ui-next/src/utils/errorCode.ts`
- Create: `ruoyi-ui-next/src/utils/types/api.ts`
- Create: `ruoyi-ui-next/src/types/global.d.ts`
- Create: `ruoyi-ui-next/src/types/store.d.ts`
- Create: `ruoyi-ui-next/src/types/router.d.ts`
- Create: `ruoyi-ui-next/src/directives/index.ts`
- Create: `ruoyi-ui-next/src/directives/permission.ts`
- Create: `ruoyi-ui-next/src/plugins/index.ts`
- Create: `ruoyi-ui-next/src/plugins/auth.ts`
- Create: `ruoyi-ui-next/src/plugins/cache.ts`
- Create: `ruoyi-ui-next/src/plugins/modal.ts`
- Create: `ruoyi-ui-next/src/plugins/tab.ts`
- Create: `ruoyi-ui-next/src/permission.ts`
- Reference: `ruoyi-ui/src/router/index.js`
- Reference: `ruoyi-ui/src/store/modules/*.js`
- Reference: `ruoyi-ui/src/utils/request.js`
- Reference: `ruoyi-ui/src/permission.js`
- Reference: `ruoyi-ui/src/plugins/*.js`
- Reference: `ruoyi-ui/src/directive/`
**Dependencies:** Task 1 完成
### Step 2.1: 配置 TypeScript 类型定义
定义全局类型:
```typescript
// src/types/global.d.ts
interface AjaxResult {
code: number
msg: string
data: any
}
interface TableDataInfo {
code: number
msg: string
total: number
rows: any[]
}
interface MenuOption {
name: string
path: string
hidden?: boolean
redirect?: string
component?: string
alwaysShow?: boolean
meta?: {
title: string
icon?: string
noCache?: boolean
link?: string
}
children?: MenuOption[]
}
```
- [ ] 定义 `AjaxResult` 通用响应类型
- [ ] 定义 `TableDataInfo` 分页响应类型
- [ ] 定义 `MenuOption` 菜单路由类型
- [ ] 定义 `DictDataType` 字典数据类型
- [ ] 定义 `PaginationState` 分页状态类型
### Step 2.2: 配置 Vue Router 4
- [ ] 创建 `router/index.ts`,使用 `createRouter` + `createWebHistory`
- [ ] 配置静态路由(`/login`、`/register`、`/404`、`/401`、首页重定向)
- [ ] 配置动态路由占位(登录后从后端加载)
- [ ] 实现 `loadView` 懒加载函数(统一使用 `() => import()`
- [ ] 路由类型使用 `RouteRecordRaw`Vue Router 4
- [ ] 配置滚动行为 `scrollBehavior`
### Step 2.3: 配置 Pinia 状态管理
替换 Vuex 5 个模块为 Pinia stores
**app store** (`stores/modules/app.ts`):
- [ ] `sidebar` (opened, withoutAnimation) — 侧边栏状态
- [ ] `device` ('desktop' | 'mobile') — 设备类型
- [ ] `size` ('default' | 'large' | 'small') — 组件尺寸Element Plus 命名调整)
- [ ] 使用 `pinia-plugin-persistedstate` 持久化到 Cookie/localStorage
**user store** (`stores/modules/user.ts`):
- [ ] `token`, `name`, `avatar`, `roles`, `permissions`
- [ ] Actions: `login()`, `getInfo()`, `logout()`, `fedLogout()`
- [ ] 登录调用 `POST /login`(保持与现有后端一致)
- [ ] Token 存储到 Cookie键名 `Admin-Token`,与后端一致)
**permission store** (`stores/modules/permission.ts`):
- [ ] `routes`: 完整路由表
- [ ] `addRoutes`: 动态添加的路由
- [ ] `sidebarRouters`: 侧边栏显示的路由
- [ ] Action: `generateRoutes(roles)` — 从后端 `/getRouters` 获取菜单并转换为路由
- [ ] 实现 `filterAsyncRouter` 字符串组件路径转 import 函数
- [ ] 实现权限过滤逻辑
**tagsView store** (`stores/modules/tagsView.ts`):
- [ ] `visitedViews`: 已访问标签页列表
- [ ] `cachedViews`: keep-alive 缓存列表
- [ ] Actions: `addView`, `delView`, `delOthersViews`, `delAllViews`, `delRightTags`, `delLeftTags`
**settings store** (`stores/modules/settings.ts`):
- [ ] `theme`, `sideTheme`, `showSettings`
- [ ] `topNav`, `tagsView`, `fixedHeader`, `sidebarLogo`, `dynamicTitle`
- [ ] 持久化到 localStorage键名 `layout-setting`
### Step 2.4: 配置 Axios 调用层
`src/utils/request.ts`:
- [ ] 创建 Axios 实例,`baseURL = import.meta.env.VITE_APP_BASE_API`
- [ ] 设置 `timeout: 60000`
- [ ] 请求拦截器:注入 `Authorization: Bearer {token}`(支持 `isToken: false` 跳过)
- [ ] 请求拦截器GET 参数序列化(复用 `tansParams` 逻辑)
- [ ] 响应拦截器:解析 `res.data.code`,处理 401/500/其他
- [ ] 401 处理:使用 `ElMessageBox.confirm` → 退出登录 → `router.replace('/')`(修复原 `location.href` 硬跳转问题)
- [ ] 响应拦截器HTTP 错误处理Network Error / Timeout / 4xx / 5xx
- [ ] 导出 `download(url, params, filename)` 方法blob 下载 + 登录态处理)
### Step 2.5: 配置权限控制
- [ ] **路由守卫** (`src/permission.ts`):
- 白名单检查(`/login`, `/register`, 等)
- Token 验证(从 Cookie 读取 `Admin-Token`
- 用户信息加载roles + permissions
- 动态路由注册(`router.addRoute`
- 页面标题设置
- [ ] **指令权限** (`src/directives/permission.ts`):
- `v-hasPermi` 指令:检查权限标识
- `v-hasRole` 指令:检查角色
- 支持超级权限 `*:*:*`
- 支持超级角色 `admin`
- [ ] **全局方法** (`src/plugins/auth.ts`):
- `$auth.hasPermi(permission)` 方法
- `$auth.hasRole(role)` 方法
### Step 2.6: 配置插件系统
重构 `ruoyi-ui/src/plugins/` 下的插件:
- [ ] `modal.ts` — 全局弹窗封装ElMessage, ElMessageBox, ElNotification
- [ ] `cache.ts` — 缓存封装localStorage + sessionStorage带过期支持
- [ ] `tab.ts` — 标签页操作(切换、关闭、刷新)
- [ ] `auth.ts` — 权限校验方法
- [ ] `index.ts` — 统一注册为 Vue 全局属性
**验收标准:**
- [ ] Pinia DevTools 可查看 5 个 store 状态
- [ ] 访问 `/login` 页面可正常渲染
- [ ] Axios 请求拦截器正确注入 Token
- [ ] 401 响应触发确认弹窗而非硬跳转
- [ ] `v-hasPermi` 指令在元素上正确隐藏/显示
- [ ] 路由守卫阻止未登录访问受保护页面
- [ ] TypeScript 编译零错误
---
## Task 3: 深色主题和 UI 框架
**目标**: 安装配置 Element Plus实现深色主题系统配置 CSS 变量
**Files:**
- Create: `ruoyi-ui-next/src/assets/styles/variables.scss`
- Create: `ruoyi-ui-next/src/assets/styles/dark-theme.scss`
- Create: `ruoyi-ui-next/src/assets/styles/index.scss`
- Create: `ruoyi-ui-next/src/assets/styles/element-override.scss`
- Create: `ruoyi-ui-next/src/assets/styles/sidebar.scss`
- Create: `ruoyi-ui-next/src/assets/styles/transition.scss`
- Create: `ruoyi-ui-next/src/assets/styles/btn.scss`
- Reference: `ruoyi-ui/src/assets/styles/` 目录
- Reference: `data/demo-dashboard-heatmap.html`(深色主题配色来源)
**Dependencies:** Task 1, Task 2 完成
### Step 3.1: 安装 Element Plus
- [ ] 确认 `element-plus` 已安装Task 1 package.json
- [ ] 配置按需导入(通过 `unplugin-vue-components` 自动按需导入组件)
- [ ] 安装 `@element-plus/icons-vue` 图标库
- [ ] 配置 Element Plus 中文语言包
### Step 3.2: 实现深色主题 CSS 变量
基于 demo-dashboard-heatmap.html 的配色方案:
```scss
// src/assets/styles/variables.scss
// 源自 demo-dashboard-heatmap.html 的深色主题
:root {
// 背景色
--bg-primary: #0d1117; // 最深背景 (--bg0)
--bg-secondary: #161b22; // 侧边栏/顶栏 (--bg1)
--bg-tertiary: #1c2333; // 卡片背景 (--bg2)
--bg-elevated: #2a3040; // 悬浮背景 (--bg3)
// 边框
--border-color: #30363d; // 边框色 (--bd)
// 文字
--text-primary: #e6edf3; // 主文字 (--t1)
--text-secondary: #8b949e; // 次要文字 (--t2)
--text-muted: #6e7681; // 弱化文字 (--tm)
// 功能色
--color-danger: #f85149; // 红色/下跌 (--red)
--color-success: #3fb950; // 绿色/上涨 (--grn)
--color-warning: #d29922; // 橙色 (--org)
--color-info: #58a6ff; // 蓝色 (--blu)
// 热力图排名色阶
--heat-1: #ff4444;
--heat-2: #ff6b6b;
--heat-3: #ff9a76;
--heat-4: #ffa502;
--heat-5: #e3b341;
--heat-6: #e6d34a;
--heat-7: #8bda5e;
--heat-8: #4dd08b;
--heat-9: #36b37e;
--heat-10: #2d9cdb;
// 布局
--sidebar-width: 200px;
--navbar-height: 50px;
--tags-view-height: 34px;
}
```
- [ ] 创建 `variables.scss` 定义所有 CSS 变量
- [ ] 创建 `dark-theme.scss` 包含热力图排名色阶样式
- [ ] 创建 `element-override.scss` 覆盖 Element Plus 默认样式为深色
- [ ] 创建 `index.scss` 整合所有样式文件
- [ ] 创建 `sidebar.scss` 侧边栏专属样式
- [ ] 创建 `transition.scss` 过渡动画样式
- [ ] 创建 `btn.scss` 按钮工具类
### Step 3.3: 覆盖 Element Plus 深色主题
- [ ] 覆盖 `el-menu` 侧边栏菜单背景色和文字色
- [ ] 覆盖 `el-table` 表格背景色和边框色
- [ ] 覆盖 `el-dialog` 弹窗背景色
- [ ] 覆盖 `el-input` / `el-select` / `el-date-picker` 表单组件
- [ ] 覆盖 `el-pagination` 分页组件
- [ ] 覆盖 `el-message-box` / `el-notification`
- [ ] 覆盖 `el-tag` 标签组件
### Step 3.4: 实现主题切换功能
- [ ] 创建 ThemePicker 组件(支持深色/浅色切换)
- [ ] 使用 CSS `data-theme` 属性切换主题
- [ ] 在 settings store 中保存主题偏好
- [ ] 默认主题为深色
- [ ] 主题切换时无闪烁过渡
### Step 3.5: 配置全局样式
- [ ] 重置默认样式margin/padding/box-sizing
- [ ] 配置滚动条样式(`::-webkit-scrollbar`
- [ ] 配置通用工具类(`.mt-10`, `.mb-10`, `.flex`, `.text-center` 等)
- [ ] 配置 NProgress 进度条深色样式
**验收标准:**
- [ ] 深色主题默认渲染正确
- [ ] Element Plus 组件在深色主题下视觉一致
- [ ] 主题切换功能正常工作
- [ ] CSS 变量在 DevTools 中可查看
- [ ] 热力图排名色阶样式正确1-10 级 + >10 级)
- [ ] 与 demo-dashboard-heatmap.html 配色方案一致
---
## Task 4: 核心组件迁移
**目标**: 迁移布局组件和通用组件,重构为 Composition API + TypeScript
**Files:**
- Create: `ruoyi-ui-next/src/layout/index.vue`
- Create: `ruoyi-ui-next/src/layout/components/AppMain.vue`
- Create: `ruoyi-ui-next/src/layout/components/Navbar.vue`
- Create: `ruoyi-ui-next/src/layout/components/Sidebar/index.vue`
- Create: `ruoyi-ui-next/src/layout/components/Sidebar/SidebarItem.vue`
- Create: `ruoyi-ui-next/src/layout/components/Sidebar/Item.vue`
- Create: `ruoyi-ui-next/src/layout/components/Sidebar/Link.vue`
- Create: `ruoyi-ui-next/src/layout/components/Sidebar/Logo.vue`
- Create: `ruoyi-ui-next/src/layout/components/TagsView/index.vue`
- Create: `ruoyi-ui-next/src/layout/components/TagsView/ScrollPane.vue`
- Create: `ruoyi-ui-next/src/layout/components/Settings/index.vue`
- Create: `ruoyi-ui-next/src/layout/components/InnerLink/index.vue`
- Create: `ruoyi-ui-next/src/components/Breadcrumb/index.vue`
- Create: `ruoyi-ui-next/src/components/Pagination/index.vue`
- Create: `ruoyi-ui-next/src/components/DictTag/index.vue`
- Create: `ruoyi-ui-next/src/components/SvgIcon/index.vue`
- Create: `ruoyi-ui-next/src/components/Hamburger/index.vue`
- Create: `ruoyi-ui-next/src/components/Screenfull/index.vue`
- Create: `ruoyi-ui-next/src/components/SizeSelect/index.vue`
- Create: `ruoyi-ui-next/src/components/ThemePicker/index.vue`
- Create: `ruoyi-ui-next/src/components/TopNav/index.vue`
- Create: `ruoyi-ui-next/src/components/RightToolbar/index.vue`
- Create: `ruoyi-ui-next/src/components/Editor/index.vue`
- Create: `ruoyi-ui-next/src/components/FileUpload/index.vue`
- Create: `ruoyi-ui-next/src/components/ImageUpload/index.vue`
- Create: `ruoyi-ui-next/src/components/ImagePreview/index.vue`
- Create: `ruoyi-ui-next/src/components/IconSelect/index.vue`
- Create: `ruoyi-ui-next/src/components/HeaderSearch/index.vue`
- Create: `ruoyi-ui-next/src/components/Crontab/index.vue`
- Create: `ruoyi-ui-next/src/components/Crontab/day.vue`
- Create: `ruoyi-ui-next/src/components/Crontab/hour.vue`
- Create: `ruoyi-ui-next/src/components/Crontab/min.vue`
- Create: `ruoyi-ui-next/src/components/Crontab/month.vue`
- Create: `ruoyi-ui-next/src/components/Crontab/week.vue`
- Create: `ruoyi-ui-next/src/components/Crontab/year.vue`
- Create: `ruoyi-ui-next/src/components/Crontab/second.vue`
- Create: `ruoyi-ui-next/src/components/Crontab/result.vue`
- Create: `ruoyi-ui-next/src/components/ParentView/index.vue`
- Create: `ruoyi-ui-next/src/components/iFrame/index.vue`
- Reference: `ruoyi-ui/src/layout/` 目录所有文件
- Reference: `ruoyi-ui/src/components/` 目录所有文件
**Dependencies:** Task 1, Task 2, Task 3 完成
### Step 4.1: 迁移布局组件
- [ ] **Layout** (`layout/index.vue`): 主布局容器,使用 `<script setup lang="ts">`,使用 `useAppStore()` 获取侧边栏状态
- [ ] **AppMain**: 主内容区,包含 `<router-view>` + `<keep-alive>` + `<transition>`
- [ ] **Navbar**: 顶部导航栏,包含 Hamburger、Breadcrumb、搜索、全屏、通知、用户下拉菜单
- [ ] **Sidebar**: 侧边栏,包含 Logo + Menu + SidebarItem 递归渲染
- [ ] **SidebarItem**: 递归菜单项,支持嵌套路由
- [ ] **TagsView**: 标签页视图,支持右键菜单(刷新、关闭其他、关闭全部)
- [ ] **Settings**: 系统设置面板(主题、顶栏、标签页等开关)
- [ ] 移除 `ResizeHandler.js` Mixin改用 `useResize()` Composition API 函数
### Step 4.2: 迁移高复用通用组件
每个组件迁移要求:
- 使用 `<script setup lang="ts">` 语法
- Props 使用 `defineProps<T>()` 类型标注
- Emits 使用 `defineEmits<T>()` 类型标注
- 移除 Vue 2 Mixin改用 Composition API
- 保持原有功能不变
**高优先级组件(按复用程度)**:
- [ ] **Pagination**: 分页组件Props 包括 `total`, `page`, `limit`Emits `update:page`, `update:limit`, `pagination`
- [ ] **DictTag**: 字典标签渲染Props 包括 `options`, `value`
- [ ] **RightToolbar**: 右侧工具栏搜索、显隐列Props 包括 `showSearch`, `columns`
- [ ] **Breadcrumb**: 面包屑导航,读取 `useRoute()` 生成面包屑
- [ ] **SvgIcon**: SVG 图标封装,使用 `vite-plugin-svg-icons`
- [ ] **Hamburger**: 汉堡菜单按钮
**中优先级组件**:
- [ ] **Screenfull**: 全屏切换
- [ ] **SizeSelect**: 组件尺寸选择
- [ ] **ThemePicker**: 主题选择器(与 Task 3 整合)
- [ ] **TopNav**: 顶部导航菜单
- [ ] **Editor**: 富文本编辑器Quill确认依赖兼容性
- [ ] **FileUpload**: 文件上传
- [ ] **ImageUpload**: 图片上传
- [ ] **ImagePreview**: 图片预览
**低优先级组件**:
- [ ] **HeaderSearch**: 头部搜索Fuse.js 全文搜索)
- [ ] **IconSelect**: SVG 图标选择器
- [ ] **Crontab**: Cron 表达式生成器9 个子组件)
- [ ] **ParentView**: 父级视图占位
- [ ] **iFrame**: 内嵌 iframe
### Step 4.3: 迁移业务组件
将业务组件从 `src/components/` 移至 `src/components-business/`
- [ ] **Kdialog**: K 线对话框 → `components-business/Kdialog/`
- [ ] **NegativeDialog**: 涨跌停对话框 → `components-business/NegativeDialog/`
- [ ] **TrendStocksDialog**: 板块趋势对话框 → `components-business/TrendStocksDialog/`
### Step 4.4: 全局注册策略
- [ ] 高复用组件Pagination, DictTag, SvgIcon, RightToolbar自动全局注册
- [ ] 布局组件按需导入
- [ ] 业务组件按需导入(仅在其所属模块页面中引用)
- [ ] 移除 `ruoyi-ui/src/main.js` 中的 `Vue.component()` 全局注册方式
### Step 4.5: 字典系统重构
- [ ] 重构 `DictData` 模块为 TypeScript
- [ ] 创建 `utils/dict/index.ts` 统一管理字典加载
- [ ] 字典缓存使用 Pinia store 而非全局变量
- [ ] 保持 `getDicts(dictType)` API 调用兼容
**验收标准:**
- [ ] 所有组件使用 `<script setup lang="ts">` 语法
- [ ] Props 和 Emits 有完整 TypeScript 类型定义
- [ ] 无 Vue 2 特有 API 使用(`this.$xxx`, `Vue.extend`, Mixin
- [ ] Layout 布局渲染正确Sidebar + Navbar + TagsView + AppMain
- [ ] Pagination 组件在列表中正常工作
- [ ] DictTag 正确渲染字典标签
- [ ] SvgIcon 正确渲染 SVG 图标
- [ ] 业务组件不再污染全局组件注册表
- [ ] 组件无 ESLint 报错
---
## Task 5: 数据可视化实现
**目标**: 安装配置 ECharts 5实现行情总览和趋势热力图页面
**Files:**
- Create: `ruoyi-ui-next/src/views/dashboard/index.vue` — 行情总览页面
- Create: `ruoyi-ui-next/src/views/dashboard/components/StatsCards.vue` — 统计卡片
- Create: `ruoyi-ui-next/src/views/dashboard/components/SentimentRow.vue` — 情绪指标行
- Create: `ruoyi-ui-next/src/views/dashboard/components/UpDownChart.vue` — 涨跌分布图
- [ ] Create: `ruoyi-ui-next/src/views/dashboard/components/TopTrends.vue` — 趋势板块 TOP 10
- Create: `ruoyi-ui-next/src/views/dashboard/components/TrendChanges.vue` — 趋势变化榜
- Create: `ruoyi-ui-next/src/views/dashboard/components/ConsecutiveStats.vue` — 连续统计
- Create: `ruoyi-ui-next/src/views/heatmap/index.vue` — 趋势热力图页面
- Create: `ruoyi-ui-next/src/views/heatmap/components/HeatmapMatrix.vue` — 热力矩阵
- Create: `ruoyi-ui-next/src/views/heatmap/components/EnhancedTable.vue` — 增强表格
- Create: `ruoyi-ui-next/src/views/heatmap/components/DetailModal.vue` — 详情弹窗
- Create: `ruoyi-ui-next/src/utils/echarts.ts` — ECharts 封装工具
- Reference: `data/demo-dashboard-heatmap.html`UI 参考)
- Reference: `ruoyi-ui/src/views/dashboard/` 目录(旧图表组件)
**Dependencies:** Task 1, Task 2, Task 3, Task 4 完成
### Step 5.1: 配置 ECharts 5
- [ ] 确认 `echarts@^5.5.0` 已安装
- [ ] 创建 `utils/echarts.ts` 封装工具:
- `initChart(dom, theme)` — 初始化图表
- `resizeChart(chart)` — 响应式调整
- 自定义深色主题配置(与全局 CSS 变量一致)
- [ ] 创建 `useECharts` Composition API Hook
- 自动处理 resize
- 自动处理组件卸载时的 dispose
- 支持响应式 option 更新
### Step 5.2: 实现行情总览页面
参考 demo-dashboard-heatmap.html 中 `#pg-dashboard` 的布局:
```
行情总览布局:
├── 市场范围筛选器全A / 上证 / 深证 / 创业板 / 科创板 / 中证1000
├── 迷你统计卡片4 格)
│ ├── 上涨家数
│ ├── 下跌家数
│ ├── 涨停家数
│ └── 跌停家数
├── 情绪指标行4 格)
│ ├── 全A情绪含进度条 + 元信息)
│ ├── 中证1000情绪
│ ├── 创业板情绪
│ └── 科创板情绪
├── 双列布局
│ ├── 左涨跌分布柱状图ECharts
│ └── 右:趋势板块 TOP 10表格
└── 双列布局
├── 左:趋势变化榜(上升/下降列表)
└── 右:强势板块连续统计(表格)
```
- [ ] **StatsCards**: 4 格迷你统计卡片组件
- [ ] **SentimentRow**: 情绪指标行,含热力色阶进度条
- [ ] **UpDownChart**: ECharts 涨跌分布柱状图
- [ ] **TopTrends**: 趋势板块 TOP 10 表格(含排名徽章、动量信号、详情按钮)
- [ ] **TrendChanges**: 趋势变化榜(上升红色/下降绿色箭头)
- [ ] **ConsecutiveStats**: 连续统计表格
### Step 5.3: 实现趋势热力图页面
参考 demo-dashboard-heatmap.html 中 `#pg-heatmap` 的布局:
```
趋势热力图布局:
├── 筛选栏动量周期1日/3日/5日/10日/15日/20日/30日 + 日期选择 + 刷新)
├── 视图切换(热力矩阵 / 增强表格)
├── 热力矩阵视图
│ └── 2级行业 × 交易日排名矩阵
│ └── 点击单元格 → 详情弹窗
└── 增强表格视图
└── 行业 × 日期排名 + 趋势变化
```
- [ ] **HeatmapMatrix**: 热力矩阵组件
- 2 级行业为行,交易日为列
- 排名 1-10 使用色阶渐变(源自 CSS 变量 `--heat-1``--heat-10`
- 排名 >10 使用默认灰色
- 点击单元格触发详情弹窗
- [ ] **EnhancedTable**: 增强表格视图
- 行业 × 日期排名
- 趋势变化列(上升/下降)
- [ ] **DetailModal**: 详情弹窗
- 统计卡片当前动量、近5日趋势、涨停家数、创新高、连续天数、平均排名
- ECharts K 线图
- ECharts 排名趋势折线图
- Tab 切换(概览 / 排名趋势 / 成分股 / 历史数据)
### Step 5.4: 数据卡片和统计组件
- [ ] 创建通用 `StatCard` 组件label + value + sub
- [ ] 创建通用 `MiniBarChart` 组件(小型柱状图)
- [ ] 创建通用 `RankBadge` 组件排名徽章1-3 金色/2 银色/3 铜色/其他普通)
- [ ] 创建通用 `SignalTag` 组件(动量信号标签)
### Step 5.5: 数据适配层
- [ ] 创建 Mock 数据(开发阶段使用)
- [ ] 创建 API 调用函数(连接后端实际接口)
- [ ] 确保数据格式与现有后端 API 兼容
**验收标准:**
- [ ] 行情总览页面渲染正确,布局与 demo 一致
- [ ] 趋势热力图页面渲染正确,热力矩阵色阶正确
- [ ] ECharts 图表在窗口缩放时自适应
- [ ] 点击热力图单元格可打开详情弹窗
- [ ] 详情弹窗中 K 线图和排名趋势图正确渲染
- [ ] 视图切换(热力矩阵/增强表格)正常工作
- [ ] 所有图表使用深色主题
- [ ] 组件使用 Composition API + TypeScript
---
## Task 6: 业务模块迁移
**目标**: 迁移所有业务模块页面,保持与后端 API 兼容
**Files:**
- Create: `ruoyi-ui-next/src/views/system/user/index.vue` — 用户管理
- Create: `ruoyi-ui-next/src/views/system/role/index.vue` — 角色管理
- Create: `ruoyi-ui-next/src/views/system/menu/index.vue` — 菜单管理
- Create: `ruoyi-ui-next/src/views/system/dept/index.vue` — 部门管理
- Create: `ruoyi-ui-next/src/views/system/config/index.vue` — 参数配置
- Create: `ruoyi-ui-next/src/views/system/notice/index.vue` — 通知公告
- Create: `ruoyi-ui-next/src/views/system/post/index.vue` — 岗位管理
- Create: `ruoyi-ui-next/src/views/system/dict/index.vue` — 字典管理
- Create: `ruoyi-ui-next/src/views/system/dict/data.vue` — 字典数据
- Create: `ruoyi-ui-next/src/views/system/user/profile/index.vue` — 个人中心
- Create: `ruoyi-ui-next/src/views/system/role/authUser.vue` — 角色分配用户
- Create: `ruoyi-ui-next/src/views/books/account/index.vue` — 交易账户
- Create: `ruoyi-ui-next/src/views/books/book/index.vue` — 账本管理
- Create: `ruoyi-ui-next/src/views/books/operations/index.vue` — 操作记录
- Create: `ruoyi-ui-next/src/views/books/statistics/index.vue` — 统计分析
- Create: `ruoyi-ui-next/src/views/books/statistictotal/index.vue` — 统计汇总
- Create: `ruoyi-ui-next/src/views/books/statisticremain/index.vue` — 统计结余
- Create: `ruoyi-ui-next/src/views/stocks/stocks/index.vue` — 股票列表
- Create: `ruoyi-ui-next/src/views/stocks/stockbasic/index.vue` — 股票基础信息
- Create: `ruoyi-ui-next/src/views/stocks/stockindex/index.vue` — 指数信息
- Create: `ruoyi-ui-next/src/views/stocks/financial/index.vue` — 财务数据
- Create: `ruoyi-ui-next/src/views/stocks/trends/index.vue` — 趋势数据
- Create: `ruoyi-ui-next/src/views/stocks/trendStocks/index.vue` — 板块趋势
- Create: `ruoyi-ui-next/src/views/stocks/newrecord/index.vue` — 新增记录
- Create: `ruoyi-ui-next/src/views/stocks/stockslimit/index.vue` — 涨跌停
- Create: `ruoyi-ui-next/src/views/stocks/book/index.vue` — 股票账本
- Create: `ruoyi-ui-next/src/views/monitor/cache/index.vue` — 缓存监控
- Create: `ruoyi-ui-next/src/views/monitor/job/index.vue` — 定时任务
- Create: `ruoyi-ui-next/src/views/monitor/job/log.vue` — 调度日志
- Create: `ruoyi-ui-next/src/views/monitor/logininfor/index.vue` — 登录日志
- Create: `ruoyi-ui-next/src/views/monitor/online/index.vue` — 在线用户
- Create: `ruoyi-ui-next/src/views/monitor/operlog/index.vue` — 操作日志
- Create: `ruoyi-ui-next/src/views/monitor/server/index.vue` — 服务器监控
- Create: `ruoyi-ui-next/src/views/tool/gen/index.vue` — 代码生成
- Create: `ruoyi-ui-next/src/views/login.vue` — 登录页
- Create: `ruoyi-ui-next/src/views/register.vue` — 注册页
- Create: `ruoyi-ui-next/src/views/error/401.vue` — 401 错误页
- Create: `ruoyi-ui-next/src/views/error/404.vue` — 404 错误页
- Create: `ruoyi-ui-next/src/api/*.ts` — 所有 API 模块TypeScript
- Reference: `ruoyi-ui/src/views/` 目录所有页面文件
- Reference: `ruoyi-ui/src/api/` 目录所有 API 文件
**Dependencies:** Task 1, Task 2, Task 3, Task 4 完成
### Step 6.1: 迁移 API 调用层
`ruoyi-ui/src/api/` 下所有 `.js` 文件重构为 `.ts` 文件:
- [ ] **API 类型定义** — 为每个 API 模块定义请求参数和响应类型
- [ ] **system API** (8 文件) — user, role, menu, dept, config, notice, post, dict(data+type)
- [ ] **monitor API** (6 文件) — cache, job, jobLog, logininfor, online, operlog, server
- [ ] **booksystem API** (6 文件) — account, book, operations, statistics, statistictotal, statisticremain
- [ ] **stocksystem API** (9 文件) — stocks, stockbasic, stockindex, financial, trends, trendStocks, newrecord, stockslimit, book
- [ ] 保持 API 路径完全一致(如 `/system/user/list` 不变)
- [ ] 使用 `request.get<T>()` / `request.post<T>()` 泛型方法
### Step 6.2: 迁移系统管理模块10+ 页面)
每个页面迁移要求:
- 使用 `<script setup lang="ts">` 语法
- 使用 Composition API 替代 Vue 2 Options API
- API 调用使用 TypeScript 类型
- 使用 Element Plus 组件替代 Element UI
- 保持搜索表单 + 表格 + 分页 + 弹窗的 CRUD 布局
- 使用 `v-hasPermi` 指令控制按钮权限
- 使用 `DictTag` 组件展示字典
- 使用 `Pagination` 组件分页
页面清单:
- [ ] **用户管理** — 用户列表、新增/编辑弹窗、分配角色、导入/导出
- [ ] **角色管理** — 角色列表、权限分配、数据权限、分配用户
- [ ] **菜单管理** — 菜单树、新增/编辑弹窗、图标选择
- [ ] **部门管理** — 部门树、新增/编辑弹窗
- [ ] **参数配置** — 配置列表、新增/编辑弹窗
- [ ] **通知公告** — 公告列表、富文本编辑
- [ ] **岗位管理** — 岗位列表、新增/编辑弹窗
- [ ] **字典管理** — 字典类型列表、字典数据子页面
- [ ] **个人中心** — 用户信息修改、头像上传、密码修改
### Step 6.3: 迁移账本系统模块6 页面)
- [ ] **交易账户** — 账户列表、CRUD
- [ ] **账本管理** — 账本列表、CRUD
- [ ] **操作记录** — 操作日志列表
- [ ] **统计分析** — 统计图表 + 列表
- [ ] **统计汇总** — 汇总数据展示
- [ ] **统计结余** — 结余数据展示
### Step 6.4: 迁移股票系统模块8+ 页面)
- [ ] **股票列表** — 股票信息列表、CRUD
- [ ] **股票基础信息** — 基础数据列表
- [ ] **指数信息** — 指数列表
- [ ] **财务数据** — 财务信息列表
- [ ] **趋势数据** — 趋势数据列表
- [ ] **板块趋势** — 板块趋势列表(含业务组件 TrendStocksDialog
- [ ] **新增记录** — 新增记录列表
- [ ] **涨跌停** — 涨跌停列表(含业务组件 NegativeDialog
- [ ] **股票账本** — 股票相关账本
### Step 6.5: 迁移监控模块7 页面)
- [ ] **缓存监控** — Redis 缓存信息
- [ ] **定时任务** — 任务列表、执行、日志
- [ ] **调度日志** — 任务执行日志
- [ ] **登录日志** — 登录记录列表
- [ ] **在线用户** — 在线用户列表、强退
- [ ] **操作日志** — 操作记录列表
- [ ] **服务器监控** — CPU/内存/磁盘信息
### Step 6.6: 迁移认证和错误页面
- [ ] **登录页** — 用户名/密码/验证码登录RSA 加密
- [ ] **注册页** — 用户注册
- [ ] **401 错误页** — 无权限页面
- [ ] **404 错误页** — 页面不存在
### Step 6.7: 优化 API 调用层
- [ ] 添加请求重试机制(可配置重试次数和间隔)
- [ ] 添加请求取消机制(页面切换时取消未完成的请求)
- [ ] 统一错误码处理逻辑
- [ ] 添加 API 请求 loading 状态管理
**验收标准:**
- [ ] 所有页面可正常访问(路由配置正确)
- [ ] CRUD 操作功能正常(增删改查)
- [ ] 分页功能正常
- [ ] 搜索/过滤功能正常
- [ ] 权限按钮正确显示/隐藏
- [ ] 字典标签正确渲染
- [ ] 文件上传功能正常
- [ ] 富文本编辑器正常工作
- [ ] 与后端 API 完全兼容(无需后端改动)
- [ ] TypeScript 编译零错误
- [ ] 无 ESLint 报错
---
## Task 7: 测试和优化
**目标**: 功能测试、性能优化、响应式适配、浏览器兼容性测试
**Files:**
- Modify: `ruoyi-ui-next/vite.config.ts`(构建优化配置)
- Modify: `ruoyi-ui-next/src/utils/request.ts`(性能优化)
- Create: `ruoyi-ui-next/.browserslistrc`
- Reference: 所有已创建的页面和组件文件
**Dependencies:** Task 1-6 全部完成
### Step 7.1: 功能测试
- [ ] 登录/注册流程测试
- [ ] 动态路由加载测试(不同角色看到不同菜单)
- [ ] 权限控制测试(无权限按钮隐藏)
- [ ] 所有系统管理页面 CRUD 测试
- [ ] 所有业务模块页面功能测试
- [ ] 监控模块页面功能测试
- [ ] 数据可视化页面测试(行情总览 + 趋势热力图)
- [ ] 标签页操作测试(切换、关闭、刷新、关闭其他)
- [ ] 主题切换测试(深色/浅色)
- [ ] 文件上传/下载测试
- [ ] 富文本编辑器测试
- [ ] 401 处理测试Token 过期 → 确认弹窗 → 退出登录)
### Step 7.2: 性能优化
**构建优化**:
- [ ] 配置 Vite 代码分割(`manualChunks`
- `element-plus` 独立 chunk
- `echarts` 独立 chunk
- `vendor` 第三方库 chunk
- [ ] 启用 gzip/brotli 压缩
- [ ] 图片资源优化(自动 webp 转换)
- [ ] 移除开发环境的 source map
- [ ] 配置预加载/预取策略
**运行时优化**:
- [ ] 路由懒加载验证(所有路由页面按需加载)
- [ ] ECharts 按需引入(仅使用需要的图表类型)
- [ ] 大表格虚拟滚动(如数据量超过 100 行)
- [ ] 图片懒加载
- [ ] keep-alive 缓存策略优化
- [ ] 移除未使用的依赖和代码
**加载速度目标**:
- [ ] 首屏加载 < 2 秒(3G 网络模拟)
- [ ] 构建产物 < 2 MBgzip 后)
- [ ] 构建时间 < 30
### Step 7.3: 响应式适配
- [ ] 桌面端(1920px / 1440px / 1280px
- [ ] 平板端(1024px / 768px
- [ ] 移动端(375px)— 侧边栏自动折叠
- [ ] 热力图矩阵在小屏幕下可横向滚动
- [ ] 表格在小屏幕下可横向滚动
- [ ] 弹窗在小屏幕下自适应宽度
### Step 7.4: 浏览器兼容性
- [ ] Chrome 90+
- [ ] Firefox 90+
- [ ] Safari 14+
- [ ] Edge 90+
- [ ] 配置 `.browserslistrc`
- [ ] 验证无浏览器特有 API 使用(或添加 polyfill
### Step 7.5: 代码质量检查
- [ ] ESLint 全量检查通过(零警告)
- [ ] TypeScript 严格模式编译通过(零错误)
- [ ] Prettier 格式化全部代码
- [ ] `any` 类型滥用(最多 5 `any`
- [ ] Vue 2 残留代码(`this.` 引用、MixinOptions API
- [ ] 组件命名规范检查
- [ ] 导入路径规范检查(使用 `@/` 别名)
**验收标准:**
- [ ] 所有页面功能测试通过
- [ ] 首屏加载 < 2
- [ ] 构建产物 < 2 MBgzip 后)
- [ ] 响应式适配桌面/平板/移动端
- [ ] 主流浏览器兼容
- [ ] ESLint + TypeScript 零错误
- [ ] Vue 2 残留代码
- [ ] 项目可独立运行(`npm run dev` + `npm run build`
---
## 任务依赖关系图
```
Task 1 (项目初始化)
Task 2 (核心架构搭建)
Task 3 (深色主题和 UI 框架) ──┐
│ │
▼ ▼
Task 4 (核心组件迁移) Task 5 (数据可视化实现)
│ │
└──────────┬───────────────┘
Task 6 (业务模块迁移)
Task 7 (测试和优化)
```
## 里程碑
| 里程碑 | 包含任务 | 预期产出 |
|--------|---------|---------|
| **M1: 项目骨架** | Task 1 | 可运行的 Vite + Vue3 + TS 空项目 |
| **M2: 架构就绪** | Task 2 | 路由、状态、权限、API 层完成 |
| **M3: 主题完成** | Task 3 | 深色主题 + Element Plus 配置完成 |
| **M4: 组件迁移** | Task 4 | 所有布局组件和通用组件迁移完成 |
| **M5: 可视化完成** | Task 5 | 行情总览和趋势热力图页面完成 |
| **M6: 全量迁移** | Task 6 | 所有业务页面迁移完成 |
| **M7: 发布就绪** | Task 7 | 测试通过、性能达标、可部署 |
## 风险提示
| 风险 | 影响 | 应对策略 |
|------|------|---------|
| ECharts 5 API 变化 | 图表组件需适配 | 对比 4.x/5.x 文档,逐项验证 |
| Element Plus 组件 API 差异 | 部分组件属性变更 | 查阅 Element Plus 文档,逐个适配 |
| Quill 富文本编辑器兼容性 | 可能需替换方案 | 准备 wangEditor 作为备选 |
| 后端 API 未适配 Vue3 | 前端需额外处理 | 保持 API 调用层兼容,无需后端改动 |
| 大项目 TypeScript 类型定义工作量 | 开发周期延长 | 优先核心类型,逐步补充 |
| 旧组件逻辑复杂难以重构 | 重构风险高 | 先复制功能,再逐步优化 |
## 自检查
### 1. 规范覆盖
- [x] Task 1: 项目初始化
- [x] Task 2: 核心架构搭建
- [x] Task 3: 深色主题和 UI 框架
- [x] Task 4: 核心组件迁移
- [x] Task 5: 数据可视化实现
- [x] Task 6: 业务模块迁移
- [x] Task 7: 测试和优化
### 2. Placeholder 扫描
- [x] "TBD"、"TODO"
- [x] "implement later"
- [x] 所有步骤包含具体文件路径和实施内容
- [x] "Similar to Task N"
### 3. 类型一致性
- [x] 所有路径使用 `ruoyi-ui-next/` 前缀
- [x] 文件名命名一致(kebab-case / PascalCase
- [x] API 路径与现有后端一致
- [x] CSS 变量命名与 demo 一致
### 4. 最小单元验证
- [x] 每个任务可独立执行(按依赖顺序)
- [x] 每个任务有明确输出和验收标准
- [x] 每个任务可独立验证
- [x] 依赖关系清晰
---
## 执行选择
计划完成!共 7 个任务,约 150+ 步骤,产出完整的 Vue3 前端项目。
**执行选项**
1. **Subagent-Driven推荐** 每个任务分发到独立子 agent 执行,任务间审查,快速迭代
2. **Inline Execution** 在当前会话中顺序执行所有任务
3. **按里程碑分批执行** M1-M7 分批,每个里程碑完成后审查再继续
您希望采用哪种执行方式?