1
This commit is contained in:
76
build.bat
76
build.bat
@@ -1,76 +0,0 @@
|
||||
@echo off
|
||||
chcp 65001 >nul
|
||||
setlocal enabledelayedexpansion
|
||||
|
||||
echo ================================
|
||||
echo Admin Framework 构建工具
|
||||
echo ================================
|
||||
echo.
|
||||
|
||||
REM 检查 Node.js 是否安装
|
||||
where node >nul 2>nul
|
||||
if %errorlevel% neq 0 (
|
||||
echo ❌ 错误: 未检测到 Node.js,请先安装 Node.js
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
for /f "tokens=*" %%i in ('node -v') do set NODE_VERSION=%%i
|
||||
for /f "tokens=*" %%i in ('npm -v') do set NPM_VERSION=%%i
|
||||
|
||||
echo ✅ Node.js 版本: %NODE_VERSION%
|
||||
echo ✅ NPM 版本: %NPM_VERSION%
|
||||
echo.
|
||||
|
||||
REM 检查是否已安装依赖
|
||||
if not exist "node_modules" (
|
||||
echo 📦 正在安装依赖...
|
||||
call npm install
|
||||
if !errorlevel! neq 0 (
|
||||
echo ❌ 依赖安装失败
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
echo ✅ 依赖安装成功
|
||||
echo.
|
||||
) else (
|
||||
echo ✅ 依赖已安装
|
||||
echo.
|
||||
)
|
||||
|
||||
REM 执行打包
|
||||
echo 🔨 正在打包框架...
|
||||
call npm run build
|
||||
|
||||
if %errorlevel% equ 0 (
|
||||
echo.
|
||||
echo ================================
|
||||
echo ✅ 打包成功!
|
||||
echo ================================
|
||||
echo.
|
||||
echo 📦 输出文件: dist\admin-framework.js
|
||||
|
||||
REM 显示文件大小
|
||||
if exist "dist\admin-framework.js" (
|
||||
for %%A in (dist\admin-framework.js) do (
|
||||
set SIZE=%%~zA
|
||||
set /a SIZE_KB=!SIZE! / 1024
|
||||
echo 📊 文件大小: !SIZE_KB! KB
|
||||
)
|
||||
)
|
||||
|
||||
echo.
|
||||
echo 📚 下一步:
|
||||
echo 1. 将 dist\admin-framework.js 复制到你的项目
|
||||
echo 2. 查看 QUICK_START.md 了解如何使用
|
||||
echo 3. 查看 USAGE_EXAMPLE.md 了解详细示例
|
||||
echo.
|
||||
) else (
|
||||
echo.
|
||||
echo ❌ 打包失败,请检查错误信息
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
pause
|
||||
|
||||
65
build.sh
65
build.sh
@@ -1,65 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Admin Framework 构建脚本
|
||||
|
||||
echo "================================"
|
||||
echo " Admin Framework 构建工具"
|
||||
echo "================================"
|
||||
echo ""
|
||||
|
||||
# 检查 Node.js 是否安装
|
||||
if ! command -v node &> /dev/null
|
||||
then
|
||||
echo "❌ 错误: 未检测到 Node.js,请先安装 Node.js"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "✅ Node.js 版本: $(node -v)"
|
||||
echo "✅ NPM 版本: $(npm -v)"
|
||||
echo ""
|
||||
|
||||
# 检查是否已安装依赖
|
||||
if [ ! -d "node_modules" ]; then
|
||||
echo "📦 正在安装依赖..."
|
||||
npm install
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "❌ 依赖安装失败"
|
||||
exit 1
|
||||
fi
|
||||
echo "✅ 依赖安装成功"
|
||||
echo ""
|
||||
else
|
||||
echo "✅ 依赖已安装"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
# 执行打包
|
||||
echo "🔨 正在打包框架..."
|
||||
npm run build
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
echo ""
|
||||
echo "================================"
|
||||
echo " ✅ 打包成功!"
|
||||
echo "================================"
|
||||
echo ""
|
||||
echo "📦 输出文件: dist/admin-framework.js"
|
||||
|
||||
# 显示文件大小
|
||||
if [ -f "dist/admin-framework.js" ]; then
|
||||
SIZE=$(du -h dist/admin-framework.js | cut -f1)
|
||||
echo "📊 文件大小: $SIZE"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "📚 下一步:"
|
||||
echo " 1. 将 dist/admin-framework.js 复制到你的项目"
|
||||
echo " 2. 查看 QUICK_START.md 了解如何使用"
|
||||
echo " 3. 查看 USAGE_EXAMPLE.md 了解详细示例"
|
||||
echo ""
|
||||
else
|
||||
echo ""
|
||||
echo "❌ 打包失败,请检查错误信息"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
28
demo-project/.gitignore
vendored
28
demo-project/.gitignore
vendored
@@ -1,28 +0,0 @@
|
||||
# 依赖
|
||||
node_modules/
|
||||
package-lock.json
|
||||
yarn.lock
|
||||
|
||||
# 构建产物
|
||||
dist/
|
||||
*.log
|
||||
|
||||
# 框架文件(从根目录复制过来的)
|
||||
src/libs/admin-framework.js
|
||||
|
||||
# 编辑器
|
||||
.vscode/
|
||||
.idea/
|
||||
*.swp
|
||||
*.swo
|
||||
*~
|
||||
|
||||
# 系统文件
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
# 环境变量
|
||||
.env
|
||||
.env.local
|
||||
.env.*.local
|
||||
|
||||
@@ -1,131 +0,0 @@
|
||||
# Demo Project 更新日志
|
||||
|
||||
## [最新更新] - 2025-10-08
|
||||
|
||||
### ✅ 添加 Source Map 支持
|
||||
|
||||
**新增功能**:
|
||||
- ✅ 完整的 Source Map 支持
|
||||
- ✅ 开发模式使用 `eval-source-map`
|
||||
- ✅ 生产模式使用 `source-map`
|
||||
- ✅ CSS/Less 也启用了 source map
|
||||
- ✅ 创建了详细的调试指南
|
||||
|
||||
**好处**:
|
||||
- 🐛 可以在浏览器中直接调试源码
|
||||
- 🔍 支持断点调试
|
||||
- 📍 精确定位到源码行和列
|
||||
- 🎯 可以调试框架源码和业务代码
|
||||
|
||||
**文档**:
|
||||
- 新增 [调试指南.md](./调试指南.md)
|
||||
- 更新 [README.md](./README.md) 添加调试说明
|
||||
|
||||
---
|
||||
|
||||
## [2025-10-08] - 修改为使用框架源码
|
||||
|
||||
**重要变更**:
|
||||
- ✅ 现在直接使用框架源码而不是打包文件
|
||||
- ✅ 更方便调试和开发
|
||||
- ✅ 更新了 webpack 和 babel 配置
|
||||
|
||||
### 📦 需要重新安装依赖
|
||||
|
||||
由于添加了新的依赖,请重新安装:
|
||||
|
||||
```bash
|
||||
cd demo-project
|
||||
|
||||
# 删除旧的依赖(可选)
|
||||
rm -rf node_modules
|
||||
rm package-lock.json
|
||||
|
||||
# 重新安装
|
||||
npm install
|
||||
```
|
||||
|
||||
### 🔄 主要更新内容
|
||||
|
||||
#### 1. main.js
|
||||
```javascript
|
||||
// 之前:使用打包文件
|
||||
import AdminFramework from './libs/admin-framework.js'
|
||||
|
||||
// 现在:使用源码
|
||||
import AdminFramework from '../../src/index.js'
|
||||
```
|
||||
|
||||
#### 2. webpack.config.js
|
||||
添加了框架源码的路径别名:
|
||||
```javascript
|
||||
alias: {
|
||||
'@': path.resolve(__dirname, 'src'),
|
||||
'@component': path.resolve(__dirname, '../src/components'),
|
||||
'@utils': path.resolve(__dirname, '../src/utils'),
|
||||
'@api': path.resolve(__dirname, '../src/api'),
|
||||
'@config': path.resolve(__dirname, '../src/config'),
|
||||
'@assets': path.resolve(__dirname, '../src/assets')
|
||||
}
|
||||
```
|
||||
|
||||
#### 3. babel.config.js
|
||||
添加了 JSX 支持:
|
||||
```javascript
|
||||
presets: [
|
||||
'@babel/preset-env',
|
||||
'@vue/babel-preset-jsx' // ← 新增
|
||||
]
|
||||
```
|
||||
|
||||
#### 4. package.json
|
||||
新增依赖:
|
||||
- `@vue/babel-preset-jsx` - JSX 支持
|
||||
- `brace` - 代码编辑器
|
||||
- `vue2-ace-editor` - Ace 编辑器
|
||||
|
||||
### 🚀 启动项目
|
||||
|
||||
```bash
|
||||
# 安装依赖
|
||||
npm install
|
||||
|
||||
# 启动开发服务器
|
||||
npm run dev
|
||||
```
|
||||
|
||||
### ✅ 优势
|
||||
|
||||
使用框架源码的好处:
|
||||
|
||||
1. **实时调试**
|
||||
- 可以直接修改框架源码查看效果
|
||||
- 无需重新打包框架
|
||||
|
||||
2. **开发便利**
|
||||
- 可以在框架源码中添加 console.log
|
||||
- 方便追踪问题
|
||||
|
||||
3. **热更新**
|
||||
- 修改框架代码后自动刷新
|
||||
- 开发体验更好
|
||||
|
||||
### ⚠️ 注意事项
|
||||
|
||||
1. **API 地址已更新**
|
||||
- 从 `http://localhost:3000/api/` 改为 `http://localhost:9098/api/`
|
||||
- 在 `src/config/index.js` 中修改
|
||||
|
||||
2. **首次启动**
|
||||
- 需要运行 `npm install` 安装新依赖
|
||||
- Windows 用户可以双击 `start.bat` 自动安装
|
||||
|
||||
3. **如果遇到错误**
|
||||
- 删除 `node_modules` 和 `package-lock.json`
|
||||
- 重新运行 `npm install`
|
||||
|
||||
---
|
||||
|
||||
**更新时间**: 2025-10-08
|
||||
**作者**: light
|
||||
|
||||
@@ -1,164 +0,0 @@
|
||||
# Demo Project 安装指南
|
||||
|
||||
## 📦 安装步骤
|
||||
|
||||
### 步骤 1:复制框架文件
|
||||
|
||||
框架文件已经在创建 demo 时自动复制到 `src/libs/admin-framework.js`。
|
||||
|
||||
如果需要更新框架,执行:
|
||||
|
||||
```bash
|
||||
# 在 admin-framework 目录重新构建
|
||||
cd ../
|
||||
npm run build
|
||||
|
||||
# 复制最新的框架文件
|
||||
cp dist/admin-framework.js demo-project/src/libs/
|
||||
```
|
||||
|
||||
### 步骤 2:安装依赖
|
||||
|
||||
```bash
|
||||
cd demo-project
|
||||
npm install
|
||||
```
|
||||
|
||||
安装过程中会下载以下依赖:
|
||||
- vue, vue-router, vuex
|
||||
- view-design (UI 组件库)
|
||||
- axios (HTTP 客户端)
|
||||
- webpack 相关工具
|
||||
|
||||
### 步骤 3:启动项目
|
||||
|
||||
#### 开发模式
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
```
|
||||
|
||||
启动后自动打开浏览器访问 `http://localhost:8080`
|
||||
|
||||
#### 生产构建
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
```
|
||||
|
||||
构建完成后,产物在 `dist` 目录。
|
||||
|
||||
## 🎯 功能演示
|
||||
|
||||
### 1. 登录页面
|
||||
|
||||
访问:`http://localhost:8080/login`
|
||||
|
||||
默认登录信息(示例):
|
||||
- 用户名:admin
|
||||
- 密码:123456
|
||||
|
||||
### 2. 主页
|
||||
|
||||
登录后自动跳转到主页:`http://localhost:8080/home`
|
||||
|
||||
显示系统欢迎信息和标题。
|
||||
|
||||
### 3. 系统管理页面
|
||||
|
||||
框架内置的系统页面:
|
||||
- `/system/user` - 用户管理
|
||||
- `/system/role` - 角色管理
|
||||
- `/system_high/menu` - 菜单管理
|
||||
|
||||
### 4. 业务示例页面
|
||||
|
||||
访问:`http://localhost:8080/business/product`
|
||||
|
||||
查看产品列表示例(包含增删改查操作演示)。
|
||||
|
||||
## 🔧 开发提示
|
||||
|
||||
### 修改配置
|
||||
|
||||
编辑 `src/config/index.js`:
|
||||
|
||||
```javascript
|
||||
export default {
|
||||
title: '你的系统名称', // 修改系统标题
|
||||
apiUrl: 'http://your-api.com/api/', // 修改 API 地址
|
||||
uploadUrl: 'http://your-api.com/api/upload'
|
||||
}
|
||||
```
|
||||
|
||||
### 添加新页面
|
||||
|
||||
1. 在 `src/views/business/` 创建新的 `.vue` 文件
|
||||
2. 在 `src/main.js` 中添加路由:
|
||||
|
||||
```javascript
|
||||
import NewPage from './views/business/new_page.vue'
|
||||
|
||||
const businessRoutes = [
|
||||
{
|
||||
path: '/business/newpage',
|
||||
name: 'new_page',
|
||||
component: NewPage
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
### 调用 API
|
||||
|
||||
```javascript
|
||||
// GET 请求
|
||||
const res = await this.$http.get('user/list', { page: 1 })
|
||||
|
||||
// POST 请求
|
||||
const res = await this.$http.post('user/add', { name: '张三' })
|
||||
```
|
||||
|
||||
### 使用 Store
|
||||
|
||||
```javascript
|
||||
// 获取用户信息
|
||||
const userName = this.$store.getters['user/userName']
|
||||
|
||||
// 获取系统配置
|
||||
const sysTitle = this.$store.getters['app/sysFormModel']
|
||||
```
|
||||
|
||||
## ⚠️ 常见问题
|
||||
|
||||
### Q1: 启动后页面空白?
|
||||
|
||||
检查浏览器控制台错误,确保:
|
||||
1. 框架文件已正确复制到 `src/libs/`
|
||||
2. 依赖已完全安装 `npm install`
|
||||
|
||||
### Q2: 接口请求失败?
|
||||
|
||||
修改 `src/config/index.js` 中的 `apiUrl` 为正确的后端地址。
|
||||
|
||||
### Q3: 样式显示异常?
|
||||
|
||||
确保已引入 ViewUI 样式:
|
||||
```javascript
|
||||
import 'view-design/dist/styles/iview.css'
|
||||
```
|
||||
|
||||
## 📚 更多帮助
|
||||
|
||||
- 查看完整框架文档:`../完整使用文档.md`
|
||||
- 框架 API 文档:`../完整使用文档.md#api-文档`
|
||||
|
||||
## 🎉 开始开发
|
||||
|
||||
所有准备工作已完成,现在可以开始开发了!
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
```
|
||||
|
||||
祝开发愉快!🚀
|
||||
|
||||
@@ -1,160 +0,0 @@
|
||||
# Demo Project 项目结构说明
|
||||
|
||||
## 📁 完整目录树
|
||||
|
||||
```
|
||||
demo-project/
|
||||
│
|
||||
├── 📄 package.json # 项目依赖配置
|
||||
├── 📄 webpack.config.js # Webpack 构建配置
|
||||
├── 📄 babel.config.js # Babel 编译配置
|
||||
├── 📄 .gitignore # Git 忽略文件
|
||||
│
|
||||
├── 📄 README.md # 项目说明文档
|
||||
├── 📄 INSTALL.md # 安装指南
|
||||
├── 📄 PROJECT_STRUCTURE.md # 本文件 - 项目结构说明
|
||||
│
|
||||
├── 🚀 start.bat # Windows 快速启动脚本
|
||||
├── 🚀 start.sh # Linux/Mac 快速启动脚本
|
||||
│
|
||||
├── 📂 public/ # 静态资源
|
||||
│ └── index.html # HTML 模板
|
||||
│
|
||||
└── 📂 src/ # 源代码目录
|
||||
│
|
||||
├── 📄 main.js # 入口文件 ⭐
|
||||
├── 📄 App.vue # 根组件
|
||||
│
|
||||
├── 📂 config/ # 配置文件
|
||||
│ └── index.js # 项目配置(API 地址、系统标题等)
|
||||
│
|
||||
├── 📂 libs/ # 第三方库
|
||||
│ └── admin-framework.js # Admin Framework 框架文件
|
||||
│
|
||||
└── 📂 views/ # 页面组件
|
||||
└── business/ # 业务页面
|
||||
└── product_list.vue # 产品列表示例页面
|
||||
```
|
||||
|
||||
## 📝 核心文件说明
|
||||
|
||||
### 1. 入口文件 `src/main.js`
|
||||
```javascript
|
||||
// 项目启动入口
|
||||
// 1. 引入框架
|
||||
// 2. 配置 Vue 实例
|
||||
// 3. 挂载应用
|
||||
```
|
||||
|
||||
**主要功能**:
|
||||
- ✅ 引入并初始化 AdminFramework
|
||||
- ✅ 配置 Router 和 Store
|
||||
- ✅ 添加自定义业务路由
|
||||
- ✅ 设置响应式适配
|
||||
|
||||
### 2. 配置文件 `src/config/index.js`
|
||||
```javascript
|
||||
export default {
|
||||
title: 'Demo 管理系统',
|
||||
apiUrl: 'http://localhost:3000/api/',
|
||||
uploadUrl: 'http://localhost:3000/api/upload',
|
||||
tokenKey: 'demo_token'
|
||||
}
|
||||
```
|
||||
|
||||
**说明**:
|
||||
- 系统标题、API 地址等配置
|
||||
- 可根据环境切换配置
|
||||
|
||||
### 3. 框架文件 `src/libs/admin-framework.js`
|
||||
- 从 `admin-framework/dist/` 复制的打包文件
|
||||
- 包含所有框架功能(页面、组件、工具等)
|
||||
- 大小:约 1.64 MB
|
||||
|
||||
### 4. 业务页面 `src/views/business/product_list.vue`
|
||||
- 完整的 CRUD 示例
|
||||
- 展示如何使用框架的组件和工具
|
||||
- 包含 Table、Modal、Message 等组件使用
|
||||
|
||||
## 🔧 配置文件说明
|
||||
|
||||
### webpack.config.js
|
||||
- 入口:`src/main.js`
|
||||
- 输出:`dist/bundle.[hash].js`
|
||||
- 开发服务器:`localhost:8080`
|
||||
- 支持 Vue、Less、图片等文件处理
|
||||
|
||||
### package.json
|
||||
**主要依赖**:
|
||||
- `vue` - Vue 框架
|
||||
- `vue-router` - 路由管理
|
||||
- `vuex` - 状态管理
|
||||
- `view-design` - UI 组件库
|
||||
- `axios` - HTTP 客户端
|
||||
|
||||
**开发依赖**:
|
||||
- `webpack` - 打包工具
|
||||
- `babel` - JS 编译
|
||||
- `vue-loader` - Vue 文件加载器
|
||||
|
||||
## 📂 扩展目录说明
|
||||
|
||||
随着项目发展,可以添加以下目录:
|
||||
|
||||
```
|
||||
src/
|
||||
├── api/ # API 接口封装
|
||||
│ └── business/
|
||||
│ └── product.js
|
||||
│
|
||||
├── assets/ # 业务资源文件
|
||||
│ ├── images/ # 图片
|
||||
│ └── styles/ # 样式
|
||||
│
|
||||
├── components/ # 业务组件
|
||||
│ └── CustomTable.vue
|
||||
│
|
||||
├── router/ # 路由配置
|
||||
│ └── index.js
|
||||
│
|
||||
└── store/ # 业务状态管理
|
||||
└── modules/
|
||||
└── product.js
|
||||
```
|
||||
|
||||
## 🚀 启动流程
|
||||
|
||||
1. **执行** `start.bat` 或 `npm run dev`
|
||||
2. **Webpack** 读取 `webpack.config.js`
|
||||
3. **入口** 加载 `src/main.js`
|
||||
4. **初始化** AdminFramework
|
||||
5. **创建** Vue 实例
|
||||
6. **挂载** 到 `#app`
|
||||
7. **启动** 开发服务器
|
||||
8. **打开** 浏览器 `http://localhost:8080`
|
||||
|
||||
## 📊 文件大小参考
|
||||
|
||||
| 文件 | 大小 | 说明 |
|
||||
|------|------|------|
|
||||
| admin-framework.js | ~1.64 MB | 框架打包文件 |
|
||||
| main.js | ~2 KB | 入口文件 |
|
||||
| App.vue | ~0.5 KB | 根组件 |
|
||||
| product_list.vue | ~4 KB | 业务示例 |
|
||||
| bundle.js(开发) | ~3 MB | 包含所有依赖 |
|
||||
| bundle.js(生产) | ~500 KB | 压缩后 |
|
||||
|
||||
## 🎯 下一步
|
||||
|
||||
1. **修改配置**:编辑 `src/config/index.js`
|
||||
2. **添加页面**:在 `src/views/business/` 创建新页面
|
||||
3. **注册路由**:在 `src/main.js` 添加路由配置
|
||||
4. **开始开发**:基于框架快速开发业务功能
|
||||
|
||||
---
|
||||
|
||||
**查看更多**:
|
||||
- [README.md](./README.md) - 项目概述
|
||||
- [INSTALL.md](./INSTALL.md) - 安装指南
|
||||
- [../完整使用文档.md](../完整使用文档.md) - 框架完整文档
|
||||
|
||||
@@ -1,292 +0,0 @@
|
||||
# Demo Project - Admin Framework 使用示例
|
||||
|
||||
这是一个使用 **Admin Framework** 框架的完整示例项目。
|
||||
|
||||
## 📁 项目结构
|
||||
|
||||
```
|
||||
demo-project/
|
||||
├── public/
|
||||
│ └── index.html # HTML 模板
|
||||
├── src/
|
||||
│ ├── config/
|
||||
│ │ └── index.js # 配置文件
|
||||
│ ├── libs/
|
||||
│ │ └── admin-framework.js # 框架文件(从 dist 复制)
|
||||
│ ├── views/
|
||||
│ │ └── business/
|
||||
│ │ └── product_list.vue # 示例业务页面
|
||||
│ ├── App.vue # 根组件
|
||||
│ └── main.js # 入口文件
|
||||
├── babel.config.js
|
||||
├── package.json
|
||||
├── webpack.config.js
|
||||
└── README.md
|
||||
```
|
||||
|
||||
## ⚠️ 重要说明
|
||||
|
||||
**本项目已更新为直接使用框架源码**(不再使用打包文件),更方便调试!
|
||||
|
||||
**✅ 已启用完整的 Source Map 支持**,可以在浏览器中直接调试源码!
|
||||
|
||||
**首次运行或更新后,请先安装依赖**:
|
||||
```bash
|
||||
npm install
|
||||
```
|
||||
|
||||
如果遇到问题,删除 `node_modules` 和 `package-lock.json` 后重新安装。
|
||||
|
||||
---
|
||||
|
||||
## 🚀 快速开始
|
||||
|
||||
### 方式一:一键启动(推荐)
|
||||
|
||||
#### Windows 用户
|
||||
双击运行 `start.bat`,会自动:
|
||||
1. 检查并安装依赖
|
||||
2. 启动开发服务器
|
||||
3. 自动打开浏览器
|
||||
|
||||
#### Linux/Mac 用户
|
||||
```bash
|
||||
chmod +x start.sh
|
||||
./start.sh
|
||||
```
|
||||
|
||||
### 方式二:手动启动
|
||||
|
||||
```bash
|
||||
# 1. 安装依赖(首次运行必须)
|
||||
npm install
|
||||
|
||||
# 2. 启动开发服务器
|
||||
npm run dev
|
||||
```
|
||||
|
||||
### ~~ 方式三:复制框架文件(已废弃)~~
|
||||
|
||||
### 1. 使用源码(当前方式)
|
||||
|
||||
将框架打包文件复制到项目中:
|
||||
|
||||
```bash
|
||||
# 在 admin-framework 目录执行构建
|
||||
cd admin-framework
|
||||
npm run build
|
||||
|
||||
# 创建 libs 目录
|
||||
mkdir demo-project/src/libs
|
||||
|
||||
# 复制打包文件
|
||||
cp dist/admin-framework.js demo-project/src/libs/
|
||||
```
|
||||
|
||||
### 2. 安装依赖
|
||||
|
||||
```bash
|
||||
cd demo-project
|
||||
npm install
|
||||
```
|
||||
|
||||
### 3. 启动开发服务器
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
```
|
||||
|
||||
浏览器会自动打开 `http://localhost:8080`
|
||||
|
||||
### 4. 构建生产版本
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
```
|
||||
|
||||
生成的文件在 `dist` 目录中。
|
||||
|
||||
## 📝 核心文件说明
|
||||
|
||||
### main.js - 入口文件
|
||||
|
||||
```javascript
|
||||
import AdminFramework from './libs/admin-framework.js'
|
||||
import config from './config'
|
||||
|
||||
// 使用框架
|
||||
Vue.use(AdminFramework, {
|
||||
config,
|
||||
ViewUI,
|
||||
VueRouter,
|
||||
Vuex,
|
||||
createPersistedState
|
||||
})
|
||||
|
||||
// 创建 Vue 实例
|
||||
new Vue({
|
||||
router: AdminFramework.router,
|
||||
store: AdminFramework.store,
|
||||
render: h => h(App)
|
||||
}).$mount('#app')
|
||||
```
|
||||
|
||||
### config/index.js - 配置文件
|
||||
|
||||
```javascript
|
||||
export default {
|
||||
title: 'Demo 管理系统',
|
||||
apiUrl: 'http://localhost:3000/api/',
|
||||
uploadUrl: 'http://localhost:3000/api/upload',
|
||||
tokenKey: 'demo_token'
|
||||
}
|
||||
```
|
||||
|
||||
### 业务页面示例
|
||||
|
||||
`src/views/business/product_list.vue` 展示了如何:
|
||||
- 使用 ViewUI 组件(Table、Button、Modal 等)
|
||||
- 调用 API 接口(this.$http)
|
||||
- 使用框架提供的工具方法
|
||||
|
||||
## 🎯 框架提供的功能
|
||||
|
||||
### 1. 内置页面
|
||||
|
||||
- ✅ **主页**(HomePage)- 欢迎页面
|
||||
- ✅ **系统页面**(SysUser、SysRole、SysMenu 等)
|
||||
- ✅ **登录页面**(LoginPage)
|
||||
- ✅ **错误页面**(401、404、500)
|
||||
|
||||
### 2. 布局组件
|
||||
|
||||
- ✅ **Main** - 主布局
|
||||
- ✅ **ParentView** - 父级视图
|
||||
|
||||
### 3. 工具方法
|
||||
|
||||
```javascript
|
||||
// HTTP 请求
|
||||
this.$http.get(url, params)
|
||||
this.$http.post(url, data)
|
||||
|
||||
// 工具函数
|
||||
this.$tools.formatDate(date, format)
|
||||
this.$tools.setToken(token)
|
||||
this.$tools.getToken()
|
||||
|
||||
// UI 工具
|
||||
this.$uiTool.delConfirm(callback)
|
||||
this.$uiTool.setRem()
|
||||
```
|
||||
|
||||
### 4. Store 状态管理
|
||||
|
||||
```javascript
|
||||
// 用户模块
|
||||
this.$store.dispatch('user/handleLogin', data)
|
||||
this.$store.dispatch('user/handleLogOut')
|
||||
this.$store.getters['user/userName']
|
||||
|
||||
// 应用模块
|
||||
this.$store.dispatch('app/getSysTitle', { defaultTitle: '系统' })
|
||||
this.$store.getters['app/sysFormModel']
|
||||
```
|
||||
|
||||
## 🐛 调试功能
|
||||
|
||||
### Source Map 已启用
|
||||
|
||||
项目已配置完整的 Source Map 支持:
|
||||
|
||||
✅ **开发模式**:使用 `eval-source-map`
|
||||
- 高质量的源码映射
|
||||
- 可以在浏览器中看到原始源代码
|
||||
- 支持断点调试
|
||||
- 包含行和列信息
|
||||
|
||||
✅ **生产模式**:使用 `source-map`
|
||||
- 生成独立的 .map 文件
|
||||
- 完整的源码信息
|
||||
|
||||
### 如何调试
|
||||
|
||||
1. **启动开发服务器**:
|
||||
```bash
|
||||
npm run dev
|
||||
```
|
||||
|
||||
2. **打开浏览器开发者工具**(F12)
|
||||
|
||||
3. **在 Sources 标签页查看源码**:
|
||||
```
|
||||
webpack://
|
||||
├── demo-project/src/ ← Demo 项目源码
|
||||
└── src/ ← 框架源码(可直接调试)
|
||||
```
|
||||
|
||||
4. **设置断点调试**:
|
||||
- 点击行号设置断点
|
||||
- 或在代码中添加 `debugger` 语句
|
||||
|
||||
### 详细调试指南
|
||||
|
||||
查看完整调试教程:**[调试指南.md](./调试指南.md)** 📚
|
||||
|
||||
包含:
|
||||
- Source Map 配置说明
|
||||
- 浏览器调试技巧
|
||||
- 框架源码调试方法
|
||||
- Vue DevTools 使用
|
||||
- Console 调试技巧
|
||||
|
||||
## 🔧 自定义开发
|
||||
|
||||
### 添加业务路由
|
||||
|
||||
在 `main.js` 中添加自定义路由:
|
||||
|
||||
```javascript
|
||||
const businessRoutes = [
|
||||
{
|
||||
path: '/business/product',
|
||||
name: 'product_list',
|
||||
component: ProductList
|
||||
}
|
||||
]
|
||||
|
||||
// 添加到主路由
|
||||
const mainRoute = AdminFramework.router.options.routes.find(r => r.path === '/')
|
||||
mainRoute.children.push(...businessRoutes)
|
||||
```
|
||||
|
||||
### 使用系统页面
|
||||
|
||||
```javascript
|
||||
import { SysUser, SysRole } from './libs/admin-framework.js'
|
||||
|
||||
// 或者直接使用
|
||||
const SysUser = AdminFramework.SysUser
|
||||
```
|
||||
|
||||
## 📚 更多文档
|
||||
|
||||
查看完整文档:`admin-framework/完整使用文档.md`
|
||||
|
||||
## 💡 提示
|
||||
|
||||
1. **框架文件**:确保将最新的 `admin-framework.js` 复制到 `src/libs/` 目录
|
||||
2. **ViewUI 样式**:已在 main.js 中引入 `view-design/dist/styles/iview.css`
|
||||
3. **API 配置**:修改 `config/index.js` 中的 `apiUrl` 为你的后端地址
|
||||
4. **登录功能**:访问 `/login` 可以看到登录页面
|
||||
|
||||
## 🎉 开始开发
|
||||
|
||||
现在你可以:
|
||||
1. 访问 `http://localhost:8080/login` 查看登录页面
|
||||
2. 访问 `http://localhost:8080/home` 查看主页
|
||||
3. 访问 `http://localhost:8080/business/product` 查看业务页面示例
|
||||
4. 开始开发你的业务页面!
|
||||
|
||||
祝开发愉快! 🚀
|
||||
|
||||
@@ -1,64 +0,0 @@
|
||||
=====================================================
|
||||
⭐ 首次运行必读 ⭐
|
||||
=====================================================
|
||||
|
||||
Demo 项目已更新为使用框架源码(更方便调试)!
|
||||
|
||||
【第一步:安装依赖】
|
||||
|
||||
Windows 用户(推荐):
|
||||
双击运行 install.bat
|
||||
|
||||
或者手动运行:
|
||||
npm install
|
||||
|
||||
【第二步:启动项目】
|
||||
|
||||
Windows 用户:
|
||||
双击运行 start.bat
|
||||
|
||||
或者手动运行:
|
||||
npm run dev
|
||||
|
||||
【验证安装成功】
|
||||
|
||||
运行以下命令检查依赖:
|
||||
npm list --depth=0
|
||||
|
||||
应该看到(无 UNMET DEPENDENCY 错误):
|
||||
✅ @vue/babel-preset-jsx
|
||||
✅ brace
|
||||
✅ vue2-ace-editor
|
||||
✅ 其他依赖...
|
||||
|
||||
【访问地址】
|
||||
|
||||
http://localhost:8080
|
||||
|
||||
可用页面:
|
||||
/login - 登录页面
|
||||
/home - 主页
|
||||
/business/product - 产品列表示例
|
||||
|
||||
【遇到错误?】
|
||||
|
||||
1. 依赖缺失:
|
||||
npm install @vue/babel-preset-jsx brace vue2-ace-editor
|
||||
|
||||
2. 端口占用:
|
||||
修改 webpack.config.js 中的 port: 8081
|
||||
|
||||
3. 删除重装:
|
||||
rm -rf node_modules package-lock.json
|
||||
npm install
|
||||
|
||||
【详细文档】
|
||||
|
||||
- 启动前必读.md - 完整启动指南
|
||||
- CHANGELOG.md - 更新日志
|
||||
- README.md - 项目说明
|
||||
|
||||
=====================================================
|
||||
准备好了?运行 install.bat 开始吧!
|
||||
=====================================================
|
||||
|
||||
@@ -1,270 +0,0 @@
|
||||
# Demo 项目 - 使用打包框架
|
||||
|
||||
## 📦 项目说明
|
||||
|
||||
这是一个使用 Admin Framework 打包版本的演示项目。
|
||||
|
||||
## ✅ 已完成的配置
|
||||
|
||||
### 1. 框架引用方式
|
||||
|
||||
**文件**:`src/main.js`
|
||||
|
||||
```javascript
|
||||
// 使用打包好的框架
|
||||
import AdminFramework from './libs/admin-framework.js'
|
||||
```
|
||||
|
||||
### 2. 文件结构
|
||||
|
||||
```
|
||||
demo-project/
|
||||
├── src/
|
||||
│ ├── libs/
|
||||
│ │ └── admin-framework.js ← 打包好的框架(从 ../../dist/ 复制)
|
||||
│ ├── main.js ← 引用框架
|
||||
│ ├── App.vue
|
||||
│ ├── config/
|
||||
│ ├── views/
|
||||
│ └── ...
|
||||
├── public/
|
||||
├── package.json
|
||||
└── webpack.config.js
|
||||
```
|
||||
|
||||
### 3. .gitignore 配置
|
||||
|
||||
```
|
||||
# 框架文件(从根目录复制过来的)
|
||||
src/libs/admin-framework.js
|
||||
```
|
||||
|
||||
## 🚀 快速开始
|
||||
|
||||
### 1. 确保框架文件存在
|
||||
|
||||
检查 `src/libs/admin-framework.js` 是否存在。
|
||||
|
||||
如果不存在,在**项目根目录**(不是 demo-project)执行:
|
||||
|
||||
```powershell
|
||||
# 打包框架
|
||||
npm run build
|
||||
|
||||
# 复制到 demo 项目
|
||||
Copy-Item "dist\admin-framework.js" "demo-project\src\libs\admin-framework.js" -Force
|
||||
```
|
||||
|
||||
### 2. 安装依赖(首次运行)
|
||||
|
||||
```bash
|
||||
cd demo-project
|
||||
npm install
|
||||
```
|
||||
|
||||
### 3. 启动开发服务器
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
```
|
||||
|
||||
### 4. 访问应用
|
||||
|
||||
打开浏览器访问:http://localhost:8080
|
||||
|
||||
**测试账号**:
|
||||
- 用户名:admin
|
||||
- 密码:admin123
|
||||
|
||||
## 🔄 更新框架
|
||||
|
||||
当框架源码(`../../src/`)有更新时:
|
||||
|
||||
### 快速更新(推荐)
|
||||
|
||||
在**项目根目录**执行:
|
||||
|
||||
```powershell
|
||||
# 一键打包并复制
|
||||
npm run build; Copy-Item "dist\admin-framework.js" "demo-project\src\libs\admin-framework.js" -Force; Write-Host "✅ 框架已更新!" -ForegroundColor Green
|
||||
```
|
||||
|
||||
### 分步操作
|
||||
|
||||
```bash
|
||||
# 1. 在项目根目录打包框架
|
||||
npm run build
|
||||
|
||||
# 2. 复制到 demo 项目(PowerShell)
|
||||
Copy-Item "dist\admin-framework.js" "demo-project\src\libs\admin-framework.js" -Force
|
||||
|
||||
# 3. 重启 demo 项目(如果正在运行)
|
||||
cd demo-project
|
||||
npm run dev
|
||||
```
|
||||
|
||||
## 📊 优势对比
|
||||
|
||||
### 使用打包框架(当前方式)
|
||||
|
||||
**优点**:
|
||||
- ✅ 性能更好(打包优化)
|
||||
- ✅ 更接近生产环境
|
||||
- ✅ 依赖隔离
|
||||
- ✅ 边界清晰
|
||||
|
||||
**缺点**:
|
||||
- ❌ 修改框架后需要重新打包
|
||||
- ❌ 调试稍微不便
|
||||
|
||||
### 使用源码引用(开发调试)
|
||||
|
||||
**优点**:
|
||||
- ✅ 实时更新
|
||||
- ✅ 调试方便
|
||||
|
||||
**缺点**:
|
||||
- ❌ 性能较差
|
||||
- ❌ 需要安装框架的所有依赖
|
||||
|
||||
## 🔧 开发建议
|
||||
|
||||
### 调试框架时
|
||||
|
||||
临时修改 `src/main.js`:
|
||||
|
||||
```javascript
|
||||
// 临时改为源码引用
|
||||
import AdminFramework from '../../src/index.js'
|
||||
```
|
||||
|
||||
**注意**:调试完成后记得改回来!
|
||||
|
||||
### 测试框架时
|
||||
|
||||
使用打包文件(当前配置):
|
||||
|
||||
```javascript
|
||||
// 使用打包文件
|
||||
import AdminFramework from './libs/admin-framework.js'
|
||||
```
|
||||
|
||||
## 📝 组件映射配置
|
||||
|
||||
在 `src/main.js` 中配置业务组件映射:
|
||||
|
||||
```javascript
|
||||
Vue.use(AdminFramework, {
|
||||
config,
|
||||
ViewUI,
|
||||
VueRouter,
|
||||
Vuex,
|
||||
createPersistedState,
|
||||
// 业务组件映射
|
||||
componentMap: {
|
||||
'business/product_list': ProductList,
|
||||
// 添加更多业务组件...
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
## 🔍 故障排查
|
||||
|
||||
### 问题 1:找不到 admin-framework.js
|
||||
|
||||
**错误**:
|
||||
```
|
||||
Module not found: Error: Can't resolve './libs/admin-framework.js'
|
||||
```
|
||||
|
||||
**解决**:
|
||||
```powershell
|
||||
# 在项目根目录
|
||||
Copy-Item "dist\admin-framework.js" "demo-project\src\libs\admin-framework.js" -Force
|
||||
```
|
||||
|
||||
### 问题 2:修改框架源码后没有变化
|
||||
|
||||
**原因**:使用的是打包文件,不会自动更新
|
||||
|
||||
**解决**:
|
||||
```powershell
|
||||
# 重新打包并复制
|
||||
npm run build
|
||||
Copy-Item "dist\admin-framework.js" "demo-project\src\libs\admin-framework.js" -Force
|
||||
```
|
||||
|
||||
### 问题 3:控制台报错
|
||||
|
||||
**检查步骤**:
|
||||
1. 确保框架已正确打包
|
||||
2. 确保文件已复制到正确位置
|
||||
3. 查看浏览器控制台的详细错误
|
||||
4. 检查 `src/main.js` 的引用路径
|
||||
|
||||
### 问题 4:端口被占用
|
||||
|
||||
**错误**:
|
||||
```
|
||||
Error: listen EADDRINUSE: address already in use :::8080
|
||||
```
|
||||
|
||||
**解决**:
|
||||
1. 关闭占用 8080 端口的程序
|
||||
2. 或修改 `webpack.config.js` 中的端口
|
||||
|
||||
## 📚 相关文档
|
||||
|
||||
- `使用打包框架说明.md` - 详细使用说明
|
||||
- `框架更新完成.md` - 更新记录
|
||||
- `启动说明.md` - 启动步骤
|
||||
- `../../完整使用文档.md` - 框架完整文档
|
||||
|
||||
## 🎯 项目结构
|
||||
|
||||
```
|
||||
demo-project/
|
||||
├── src/
|
||||
│ ├── libs/
|
||||
│ │ └── admin-framework.js # 打包好的框架
|
||||
│ ├── main.js # 入口文件
|
||||
│ ├── App.vue # 根组件
|
||||
│ ├── config/
|
||||
│ │ ├── index.js # 配置文件
|
||||
│ │ └── menuConfig.js # 菜单配置
|
||||
│ ├── views/
|
||||
│ │ ├── business/ # 业务页面
|
||||
│ │ └── home/ # 首页
|
||||
│ └── ...
|
||||
├── public/
|
||||
│ └── index.html
|
||||
├── package.json
|
||||
├── webpack.config.js
|
||||
├── babel.config.js
|
||||
└── README.md
|
||||
```
|
||||
|
||||
## 🌟 特性
|
||||
|
||||
- ✅ 基于 Vue 2.x
|
||||
- ✅ 使用 ViewUI 组件库
|
||||
- ✅ 动态路由和权限管理
|
||||
- ✅ Vuex 状态管理
|
||||
- ✅ 响应式布局
|
||||
- ✅ 热更新开发
|
||||
|
||||
## 📞 支持
|
||||
|
||||
如有问题,请查看:
|
||||
1. 浏览器控制台错误信息
|
||||
2. 终端编译错误信息
|
||||
3. 相关文档
|
||||
|
||||
---
|
||||
|
||||
**框架版本**:1.0.0
|
||||
|
||||
**Node.js 版本要求**:>= 14.0.0
|
||||
|
||||
**当前状态**:✅ 就绪
|
||||
|
||||
@@ -1,222 +0,0 @@
|
||||
# Source Map 配置说明
|
||||
|
||||
## ✅ 已完成配置
|
||||
|
||||
Demo 项目现在已经配置了完整的 Source Map 支持!
|
||||
|
||||
### 📝 配置内容
|
||||
|
||||
#### 1. Webpack 配置(`webpack.config.js`)
|
||||
|
||||
```javascript
|
||||
module.exports = (env, argv) => {
|
||||
const isDevelopment = argv.mode === 'development'
|
||||
|
||||
return {
|
||||
// Source Map 配置
|
||||
devtool: isDevelopment ? 'eval-source-map' : 'source-map',
|
||||
|
||||
// CSS/Less Source Map
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.css$/,
|
||||
use: [
|
||||
'vue-style-loader',
|
||||
{
|
||||
loader: 'css-loader',
|
||||
options: { sourceMap: isDevelopment }
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
test: /\.less$/,
|
||||
use: [
|
||||
'vue-style-loader',
|
||||
{
|
||||
loader: 'css-loader',
|
||||
options: { sourceMap: isDevelopment }
|
||||
},
|
||||
{
|
||||
loader: 'less-loader',
|
||||
options: { sourceMap: isDevelopment }
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 2. DevServer 配置
|
||||
|
||||
```javascript
|
||||
devServer: {
|
||||
port: 8080,
|
||||
hot: true,
|
||||
open: true,
|
||||
historyApiFallback: true,
|
||||
// 错误覆盖层
|
||||
client: {
|
||||
overlay: {
|
||||
errors: true, // 显示错误
|
||||
warnings: false // 不显示警告
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 🎯 Source Map 类型
|
||||
|
||||
#### 开发模式:`eval-source-map`
|
||||
|
||||
**优点**:
|
||||
- ✅ 高质量的源码映射
|
||||
- ✅ 包含行和列信息
|
||||
- ✅ 重新构建速度快
|
||||
- ✅ 适合开发调试
|
||||
|
||||
**缺点**:
|
||||
- ❌ 生成的文件较大
|
||||
- ❌ 不适合生产环境
|
||||
|
||||
#### 生产模式:`source-map`
|
||||
|
||||
**优点**:
|
||||
- ✅ 最高质量的源码映射
|
||||
- ✅ 生成独立的 .map 文件
|
||||
- ✅ 不影响主文件加载
|
||||
|
||||
**缺点**:
|
||||
- ❌ 构建速度慢
|
||||
- ❌ 文件体积大
|
||||
|
||||
### 🔍 调试效果
|
||||
|
||||
启动开发服务器后,在浏览器开发者工具中可以看到:
|
||||
|
||||
```
|
||||
webpack://
|
||||
├── demo-project/
|
||||
│ └── src/
|
||||
│ ├── main.js ← 可以看到原始源码
|
||||
│ ├── App.vue
|
||||
│ ├── config/
|
||||
│ │ └── index.js
|
||||
│ └── views/
|
||||
│ └── business/
|
||||
│ └── product_list.vue
|
||||
│
|
||||
└── src/ ← 框架源码
|
||||
├── index.js
|
||||
├── components/
|
||||
│ ├── main/
|
||||
│ └── ...
|
||||
├── views/
|
||||
│ ├── login/
|
||||
│ ├── home/
|
||||
│ └── ...
|
||||
├── store/
|
||||
│ ├── user.js
|
||||
│ └── app.js
|
||||
└── utils/
|
||||
├── http.js
|
||||
├── tools.js
|
||||
└── uiTool.js
|
||||
```
|
||||
|
||||
### 💡 使用方法
|
||||
|
||||
#### 1. 设置断点
|
||||
|
||||
在浏览器开发者工具的 Sources 面板:
|
||||
1. 找到要调试的源文件
|
||||
2. 点击行号设置断点
|
||||
3. 刷新页面触发断点
|
||||
|
||||
#### 2. 使用 debugger
|
||||
|
||||
在代码中直接添加:
|
||||
```javascript
|
||||
export default {
|
||||
mounted() {
|
||||
debugger // ← 程序会在这里暂停
|
||||
console.log('组件已挂载')
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 3. Console 调试
|
||||
|
||||
```javascript
|
||||
// 在 Console 中执行
|
||||
$vm.$store.state // 查看 Vuex 状态
|
||||
$vm.$route // 查看路由信息
|
||||
$vm.$root // 查看根实例
|
||||
```
|
||||
|
||||
### 📊 对比
|
||||
|
||||
| 功能 | 有 Source Map | 无 Source Map |
|
||||
|------|--------------|---------------|
|
||||
| 看到的代码 | 原始源码 | 编译后的代码 |
|
||||
| 断点位置 | 精确到源码行列 | 只能在编译后代码设置 |
|
||||
| 变量名 | 原始变量名 | 可能被压缩 |
|
||||
| 调试体验 | ⭐⭐⭐⭐⭐ | ⭐⭐ |
|
||||
|
||||
### ⚠️ 注意事项
|
||||
|
||||
#### 1. 文件大小
|
||||
|
||||
开发模式的 bundle 会包含 source map 信息,体积较大:
|
||||
- 开发环境:约 3-5 MB(包含 source map)
|
||||
- 生产环境:约 500 KB(压缩后,.map 文件独立)
|
||||
|
||||
#### 2. 性能影响
|
||||
|
||||
- **开发环境**:首次构建较慢,热更新很快
|
||||
- **生产环境**:构建时间增加,但运行时无影响
|
||||
|
||||
#### 3. 安全性
|
||||
|
||||
生产环境的 .map 文件:
|
||||
- 不要部署到公开的服务器
|
||||
- 或配置服务器禁止访问 .map 文件
|
||||
- 或使用错误追踪服务(如 Sentry)管理 source map
|
||||
|
||||
### 🚀 快速开始
|
||||
|
||||
1. **启动开发服务器**:
|
||||
```bash
|
||||
npm run dev
|
||||
```
|
||||
|
||||
2. **打开浏览器**:
|
||||
访问 `http://localhost:8080`
|
||||
|
||||
3. **打开开发者工具**:
|
||||
按 `F12`
|
||||
|
||||
4. **查看源码**:
|
||||
在 Sources 标签页中可以看到完整的源码结构
|
||||
|
||||
5. **设置断点调试**:
|
||||
点击行号或在代码中添加 `debugger`
|
||||
|
||||
### 📚 相关文档
|
||||
|
||||
- **[调试指南.md](./调试指南.md)** - 完整的调试教程
|
||||
- **[README.md](./README.md)** - 项目说明
|
||||
- **[CHANGELOG.md](./CHANGELOG.md)** - 更新日志
|
||||
|
||||
### 🔗 参考资源
|
||||
|
||||
- [Webpack DevTool 文档](https://webpack.js.org/configuration/devtool/)
|
||||
- [Chrome DevTools](https://developer.chrome.com/docs/devtools/)
|
||||
- [Vue DevTools](https://devtools.vuejs.org/)
|
||||
|
||||
---
|
||||
|
||||
**现在你可以愉快地调试代码了!** 🎉
|
||||
|
||||
@@ -1,399 +0,0 @@
|
||||
# Vuex 错误修复说明
|
||||
|
||||
## 🔍 问题描述
|
||||
|
||||
控制台出现多个 Vuex 相关错误:
|
||||
|
||||
1. `[vuex] unknown action type: getSysTitle`
|
||||
2. `[vuex] unknown getter: isServerRun`
|
||||
3. `[vuex] unknown getter: infoMsg`
|
||||
4. `Unknown custom element: <asyncModal>`
|
||||
|
||||
## 📝 修复内容
|
||||
|
||||
### 1. 修复 `getSysTitle` action 错误
|
||||
|
||||
**问题位置**:`src/components/main/main.vue`
|
||||
|
||||
**错误原因**:
|
||||
- 在 `created` 钩子中调用了 `this.$store.dispatch('getSysTitle')`
|
||||
- 缺少模块命名空间前缀 `app/`
|
||||
- 且在 `demo-project/src/main.js` 中已经调用过,不需要重复调用
|
||||
|
||||
**修改前**:
|
||||
```javascript
|
||||
created() {
|
||||
this.init()
|
||||
},
|
||||
methods: {
|
||||
async init() {
|
||||
await this.$store.dispatch('getSysTitle') // ❌ 错误
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
**修改后**:
|
||||
```javascript
|
||||
created() {
|
||||
this.init()
|
||||
},
|
||||
methods: {
|
||||
async init() {
|
||||
// 获取系统标题(已在 main.js 中调用,这里不需要重复调用)
|
||||
// await this.$store.dispatch('app/getSysTitle')
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
**说明**:
|
||||
- ✅ 移除了重复的 `getSysTitle` 调用
|
||||
- ✅ 系统标题在 `demo-project/src/main.js` 的 `mounted` 钩子中统一获取
|
||||
- ✅ 避免了重复请求
|
||||
|
||||
### 2. 修复 `isServerRun` getter 错误
|
||||
|
||||
**问题位置**:`src/components/main/components/terminal/index.vue`
|
||||
|
||||
**错误原因**:
|
||||
- 使用了不存在的 `isServerRun` getter
|
||||
- 这是 Terminal 功能相关的状态,但 store 中没有定义
|
||||
|
||||
**修改前**:
|
||||
```javascript
|
||||
computed: {
|
||||
...mapGetters({
|
||||
isServerRun: 'isServerRun' // ❌ 不存在的 getter
|
||||
}),
|
||||
iconclass() {
|
||||
let curClass = 'terminal-icon ml10 '
|
||||
if (this.isServerRun) {
|
||||
curClass += ' run'
|
||||
}
|
||||
return curClass
|
||||
}
|
||||
},
|
||||
```
|
||||
|
||||
**修改后**:
|
||||
```javascript
|
||||
computed: {
|
||||
iconclass() {
|
||||
let curClass = 'terminal-icon ml10'
|
||||
// Terminal 功能暂时禁用
|
||||
// if (this.isServerRun) {
|
||||
// curClass += ' run'
|
||||
// }
|
||||
return curClass
|
||||
}
|
||||
},
|
||||
```
|
||||
|
||||
**说明**:
|
||||
- ✅ 移除了对不存在的 `isServerRun` getter 的依赖
|
||||
- ✅ Terminal 功能暂时禁用
|
||||
- ✅ 如果需要启用,需要在 store 中添加相应的 state 和 getter
|
||||
|
||||
### 3. 修复 `infoMsg` getter 错误
|
||||
|
||||
**问题位置**:`src/components/main/components/terminal/terminal.vue`
|
||||
|
||||
**错误原因**:
|
||||
- 使用了不存在的 `infoMsg` getter
|
||||
- 调用了不存在的 `clearInfoMsg` mutation 和 `setInteverLog` action
|
||||
|
||||
**修改前**:
|
||||
```javascript
|
||||
computed: {
|
||||
...mapGetters({
|
||||
infoMsg: 'infoMsg' // ❌ 不存在的 getter
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
clearLog() {
|
||||
this.$store.commit('clearInfoMsg') // ❌ 不存在的 mutation
|
||||
},
|
||||
reloadLog() {
|
||||
this.$store.dispatch('setInteverLog') // ❌ 不存在的 action
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
**修改后**:
|
||||
```javascript
|
||||
computed: {
|
||||
infoMsg() {
|
||||
// Terminal 功能暂时禁用
|
||||
return '终端功能暂未启用'
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
clearLog() {
|
||||
// Terminal 功能暂时禁用
|
||||
this.$Message.info('终端功能暂未启用')
|
||||
},
|
||||
reloadLog() {
|
||||
// Terminal 功能暂时禁用
|
||||
this.$Message.info('终端功能暂未启用')
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
**说明**:
|
||||
- ✅ 移除了对不存在的 Vuex 状态的依赖
|
||||
- ✅ 提供了友好的提示信息
|
||||
- ✅ 避免了运行时错误
|
||||
|
||||
### 4. 修复 `asyncModal` 组件未注册错误
|
||||
|
||||
**问题位置**:`src/components/main/components/terminal/index.vue`
|
||||
|
||||
**错误原因**:
|
||||
- 模板中使用了 `<asyncModal>` 组件
|
||||
- 但没有在 `components` 中注册
|
||||
|
||||
**修改前**:
|
||||
```javascript
|
||||
import { mapGetters } from 'vuex'
|
||||
import Terminal from './terminal.vue'
|
||||
export default {
|
||||
components: {
|
||||
Terminal // ❌ 缺少 asyncModal
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
**修改后**:
|
||||
```javascript
|
||||
import Terminal from './terminal.vue'
|
||||
import asyncModal from '@/components/asyncModal'
|
||||
export default {
|
||||
components: {
|
||||
Terminal,
|
||||
asyncModal // ✅ 添加 asyncModal 组件
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
**说明**:
|
||||
- ✅ 导入了 `asyncModal` 组件
|
||||
- ✅ 在 `components` 中注册
|
||||
- ✅ 移除了不需要的 `mapGetters` 导入
|
||||
|
||||
## 📊 修复总结
|
||||
|
||||
### 修改的文件
|
||||
|
||||
1. **`src/components/main/main.vue`**
|
||||
- 移除了重复的 `getSysTitle` 调用
|
||||
|
||||
2. **`src/components/main/components/terminal/index.vue`**
|
||||
- 移除了 `isServerRun` getter 的使用
|
||||
- 添加了 `asyncModal` 组件注册
|
||||
|
||||
3. **`src/components/main/components/terminal/terminal.vue`**
|
||||
- 移除了 `infoMsg` getter 的使用
|
||||
- 修改了 `clearLog` 和 `reloadLog` 方法
|
||||
|
||||
### 错误修复
|
||||
|
||||
- ✅ `[vuex] unknown action type: getSysTitle` - 已修复
|
||||
- ✅ `[vuex] unknown getter: isServerRun` - 已修复
|
||||
- ✅ `[vuex] unknown getter: infoMsg` - 已修复
|
||||
- ✅ `Unknown custom element: <asyncModal>` - 已修复
|
||||
|
||||
## 💡 Terminal 功能说明
|
||||
|
||||
### 当前状态
|
||||
|
||||
Terminal 功能已**暂时禁用**,因为:
|
||||
- 缺少相关的 Vuex store 配置
|
||||
- 缺少后端接口支持
|
||||
- 不影响系统核心功能
|
||||
|
||||
### 如何启用 Terminal 功能
|
||||
|
||||
如果需要启用 Terminal 功能,需要:
|
||||
|
||||
**1. 在 Vuex store 中添加 Terminal 模块**
|
||||
|
||||
创建 `src/store/terminal.js`:
|
||||
```javascript
|
||||
export default {
|
||||
namespaced: true,
|
||||
state: {
|
||||
isServerRun: false,
|
||||
infoMsg: ''
|
||||
},
|
||||
getters: {
|
||||
isServerRun: state => state.isServerRun,
|
||||
infoMsg: state => state.infoMsg
|
||||
},
|
||||
mutations: {
|
||||
setServerRun(state, value) {
|
||||
state.isServerRun = value
|
||||
},
|
||||
setInfoMsg(state, msg) {
|
||||
state.infoMsg += msg + '\n'
|
||||
},
|
||||
clearInfoMsg(state) {
|
||||
state.infoMsg = ''
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
async setInteverLog({ commit }) {
|
||||
// 实现日志加载逻辑
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**2. 在 `src/store/index.js` 中注册模块**
|
||||
|
||||
```javascript
|
||||
import terminal from './terminal'
|
||||
|
||||
export default new Vuex.Store({
|
||||
modules: {
|
||||
user,
|
||||
app,
|
||||
terminal // 添加 terminal 模块
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
**3. 更新组件代码**
|
||||
|
||||
`src/components/main/components/terminal/index.vue`:
|
||||
```javascript
|
||||
computed: {
|
||||
...mapGetters('terminal', {
|
||||
isServerRun: 'isServerRun'
|
||||
}),
|
||||
iconclass() {
|
||||
let curClass = 'terminal-icon ml10 '
|
||||
if (this.isServerRun) {
|
||||
curClass += ' run'
|
||||
}
|
||||
return curClass
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`src/components/main/components/terminal/terminal.vue`:
|
||||
```javascript
|
||||
computed: {
|
||||
...mapGetters('terminal', {
|
||||
infoMsg: 'infoMsg'
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
clearLog() {
|
||||
this.$store.commit('terminal/clearInfoMsg')
|
||||
},
|
||||
reloadLog() {
|
||||
this.$store.dispatch('terminal/setInteverLog')
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## ⚠️ 注意事项
|
||||
|
||||
### 1. 系统标题获取时机
|
||||
|
||||
系统标题在 `demo-project/src/main.js` 的 `mounted` 钩子中获取:
|
||||
|
||||
```javascript
|
||||
mounted() {
|
||||
AdminFramework.uiTool.setRem()
|
||||
|
||||
// 只在已登录时获取系统标题
|
||||
const token = this.$store.state.user.token
|
||||
if (token) {
|
||||
this.$store.dispatch('app/getSysTitle', {
|
||||
defaultTitle: 'Demo 管理系统',
|
||||
defaultLogo: ''
|
||||
})
|
||||
} else {
|
||||
document.title = 'Demo 管理系统'
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**不要在其他地方重复调用**,避免:
|
||||
- 重复的 HTTP 请求
|
||||
- 不必要的性能开销
|
||||
- 可能的竞态条件
|
||||
|
||||
### 2. Vuex 模块命名空间
|
||||
|
||||
所有 Vuex 模块都使用了 `namespaced: true`:
|
||||
|
||||
```javascript
|
||||
// ✅ 正确的调用方式
|
||||
this.$store.dispatch('app/getSysTitle')
|
||||
this.$store.commit('user/setToken', token)
|
||||
this.$store.getters['user/userName']
|
||||
|
||||
// ❌ 错误的调用方式
|
||||
this.$store.dispatch('getSysTitle')
|
||||
this.$store.commit('setToken', token)
|
||||
this.$store.getters['userName']
|
||||
```
|
||||
|
||||
**使用 mapGetters/mapActions 时**:
|
||||
```javascript
|
||||
// ✅ 正确
|
||||
...mapGetters('app', ['sysFormModel'])
|
||||
...mapActions('user', ['handleLogin'])
|
||||
|
||||
// 或者
|
||||
...mapGetters({
|
||||
sysFormModel: 'app/sysFormModel'
|
||||
})
|
||||
```
|
||||
|
||||
### 3. 组件注册
|
||||
|
||||
使用第三方组件或自定义组件时,必须:
|
||||
1. 导入组件
|
||||
2. 在 `components` 中注册
|
||||
3. 才能在模板中使用
|
||||
|
||||
```javascript
|
||||
// ✅ 正确
|
||||
import asyncModal from '@/components/asyncModal'
|
||||
export default {
|
||||
components: {
|
||||
asyncModal
|
||||
}
|
||||
}
|
||||
|
||||
// ❌ 错误(直接在模板中使用未注册的组件)
|
||||
<template>
|
||||
<asyncModal></asyncModal>
|
||||
</template>
|
||||
```
|
||||
|
||||
## ✅ 验证清单
|
||||
|
||||
### 功能验证
|
||||
|
||||
- ✅ 登录功能正常
|
||||
- ✅ 系统标题正确显示
|
||||
- ✅ 菜单正确显示
|
||||
- ✅ 页面跳转正常
|
||||
- ✅ 控制台没有 Vuex 错误
|
||||
|
||||
### 错误检查
|
||||
|
||||
- ✅ 没有 `[vuex] unknown action` 错误
|
||||
- ✅ 没有 `[vuex] unknown getter` 错误
|
||||
- ✅ 没有 `Unknown custom element` 错误
|
||||
- ✅ 没有组件注册错误
|
||||
|
||||
---
|
||||
|
||||
**所有 Vuex 错误已修复!** 🎉
|
||||
|
||||
现在系统可以正常运行,Terminal 功能暂时禁用但不影响核心功能。
|
||||
|
||||
@@ -1,372 +0,0 @@
|
||||
# API 直接导入重构说明
|
||||
|
||||
## 📋 重构内容
|
||||
|
||||
### 重构目标
|
||||
|
||||
将框架中的 API server 从**注入模式**改为**直接导入模式**,简化代码结构。
|
||||
|
||||
### 修改前(注入模式)
|
||||
|
||||
**问题**:
|
||||
- 需要在 `src/index.js` 中调用 `setUserServer` 和 `setParamSetupServer`
|
||||
- Store 模块需要导出 setter 函数
|
||||
- 代码结构复杂,不够直观
|
||||
|
||||
**代码示例**:
|
||||
```javascript
|
||||
// src/store/user.js
|
||||
let userServerInstance = null
|
||||
|
||||
export const setUserServer = (server) => {
|
||||
userServerInstance = server
|
||||
}
|
||||
|
||||
// 使用时
|
||||
if (!userServerInstance) {
|
||||
throw new Error('userServer not initialized')
|
||||
}
|
||||
let res = await userServerInstance.login(userFrom)
|
||||
```
|
||||
|
||||
```javascript
|
||||
// src/index.js
|
||||
import { setUserServer } from './store/user'
|
||||
import { setParamSetupServer } from './store/app'
|
||||
import * as systemApi from './api/system'
|
||||
import * as systemHighApi from './api/system_high'
|
||||
|
||||
// 自动设置 API
|
||||
setUserServer(systemApi.userServer)
|
||||
setParamSetupServer(systemHighApi.paramSetupServer)
|
||||
```
|
||||
|
||||
### 修改后(直接导入模式)
|
||||
|
||||
**优点**:
|
||||
- ✅ 代码更简洁
|
||||
- ✅ 不需要额外的 setter 函数
|
||||
- ✅ 依赖关系更清晰
|
||||
- ✅ 符合 ES6 模块化规范
|
||||
|
||||
**代码示例**:
|
||||
```javascript
|
||||
// src/store/user.js
|
||||
import userServer from '../api/system/userServer'
|
||||
|
||||
// 直接使用
|
||||
let res = await userServer.login(userFrom)
|
||||
```
|
||||
|
||||
```javascript
|
||||
// src/index.js
|
||||
// 不再需要 setUserServer 和 setParamSetupServer
|
||||
import * as systemApi from './api/system'
|
||||
import * as systemHighApi from './api/system_high'
|
||||
```
|
||||
|
||||
## 📁 修改的文件
|
||||
|
||||
### 1. `src/store/user.js`
|
||||
|
||||
**修改前**:
|
||||
```javascript
|
||||
import { setToken, getToken } from '../utils/tools'
|
||||
import uiTool from '../utils/uiTool'
|
||||
import { defaultMenus, filterMenusByIds } from '../config/menuConfig'
|
||||
|
||||
// 注意:这里的 userServer 需要在使用时注入
|
||||
let userServerInstance = null
|
||||
|
||||
export const setUserServer = (server) => {
|
||||
userServerInstance = server
|
||||
}
|
||||
|
||||
// 使用时
|
||||
if (!userServerInstance) {
|
||||
throw new Error('userServer not initialized')
|
||||
}
|
||||
let res = await userServerInstance.login(userFrom)
|
||||
```
|
||||
|
||||
**修改后**:
|
||||
```javascript
|
||||
import { setToken, getToken } from '../utils/tools'
|
||||
import uiTool from '../utils/uiTool'
|
||||
import { defaultMenus, filterMenusByIds } from '../config/menuConfig'
|
||||
import userServer from '../api/system/userServer'
|
||||
|
||||
// 直接使用
|
||||
let res = await userServer.login(userFrom)
|
||||
```
|
||||
|
||||
**变更点**:
|
||||
- ✅ 移除了 `userServerInstance` 变量
|
||||
- ✅ 移除了 `setUserServer` 导出函数
|
||||
- ✅ 直接导入 `userServer`
|
||||
- ✅ 移除了 `if (!userServerInstance)` 检查
|
||||
- ✅ 将所有 `userServerInstance` 替换为 `userServer`
|
||||
|
||||
### 2. `src/store/app.js`
|
||||
|
||||
**修改前**:
|
||||
```javascript
|
||||
import { getBreadCrumbList, getHomeRoute } from '../utils/tools'
|
||||
|
||||
// 注意:这里的 paramSetupServer 需要在使用时注入
|
||||
let paramSetupServerInstance = null
|
||||
|
||||
export const setParamSetupServer = (server) => {
|
||||
paramSetupServerInstance = server
|
||||
}
|
||||
|
||||
// 使用时
|
||||
if (!paramSetupServerInstance) {
|
||||
commit('setSysTitle', formModel)
|
||||
return
|
||||
}
|
||||
let res1 = await paramSetupServerInstance.getOne('sys_title')
|
||||
```
|
||||
|
||||
**修改后**:
|
||||
```javascript
|
||||
import { getBreadCrumbList, getHomeRoute } from '../utils/tools'
|
||||
import paramSetupServer from '../api/system_high/paramSetupServer'
|
||||
|
||||
// 直接使用
|
||||
let res1 = await paramSetupServer.getOne('sys_title')
|
||||
```
|
||||
|
||||
**变更点**:
|
||||
- ✅ 移除了 `paramSetupServerInstance` 变量
|
||||
- ✅ 移除了 `setParamSetupServer` 导出函数
|
||||
- ✅ 直接导入 `paramSetupServer`
|
||||
- ✅ 移除了 `if (!paramSetupServerInstance)` 检查
|
||||
- ✅ 将所有 `paramSetupServerInstance` 替换为 `paramSetupServer`
|
||||
|
||||
### 3. `src/index.js`
|
||||
|
||||
**修改前**:
|
||||
```javascript
|
||||
// ==================== Store 模块 ====================
|
||||
import storeModules, { userModule, appModule } from './store'
|
||||
import { setUserServer } from './store/user'
|
||||
import { setParamSetupServer } from './store/app'
|
||||
|
||||
// ==================== 系统 API ====================
|
||||
import * as systemApi from './api/system'
|
||||
import * as systemHighApi from './api/system_high'
|
||||
|
||||
// 自动设置 API
|
||||
setUserServer(systemApi.userServer)
|
||||
setParamSetupServer(systemHighApi.paramSetupServer)
|
||||
|
||||
// AdminFramework 类中的方法
|
||||
class AdminFramework {
|
||||
/**
|
||||
* 设置用户服务实例
|
||||
*/
|
||||
setUserServer(userServer) {
|
||||
setUserServer(userServer)
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置参数设置服务实例
|
||||
*/
|
||||
setParamSetupServer(paramSetupServer) {
|
||||
setParamSetupServer(paramSetupServer)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**修改后**:
|
||||
```javascript
|
||||
// ==================== Store 模块 ====================
|
||||
import storeModules, { userModule, appModule } from './store'
|
||||
|
||||
// ==================== 系统 API ====================
|
||||
import * as systemApi from './api/system'
|
||||
import * as systemHighApi from './api/system_high'
|
||||
|
||||
// AdminFramework 类中移除了 setUserServer 和 setParamSetupServer 方法
|
||||
```
|
||||
|
||||
**变更点**:
|
||||
- ✅ 移除了 `setUserServer` 和 `setParamSetupServer` 的导入
|
||||
- ✅ 移除了自动设置 API 的代码
|
||||
- ✅ 移除了 `AdminFramework` 类中的 `setUserServer` 和 `setParamSetupServer` 方法
|
||||
|
||||
## 🎯 影响范围
|
||||
|
||||
### 不受影响的功能
|
||||
|
||||
- ✅ 登录功能
|
||||
- ✅ 权限菜单
|
||||
- ✅ 系统标题
|
||||
- ✅ 所有业务功能
|
||||
|
||||
### 代码简化
|
||||
|
||||
**减少的代码行数**:
|
||||
- `src/store/user.js`: -7 行
|
||||
- `src/store/app.js`: -7 行
|
||||
- `src/index.js`: -12 行
|
||||
- **总计**: -26 行
|
||||
|
||||
**移除的概念**:
|
||||
- ❌ `userServerInstance` 变量
|
||||
- ❌ `paramSetupServerInstance` 变量
|
||||
- ❌ `setUserServer` 函数
|
||||
- ❌ `setParamSetupServer` 函数
|
||||
- ❌ API 注入机制
|
||||
|
||||
## 📊 对比表
|
||||
|
||||
| 特性 | 注入模式 | 直接导入模式 |
|
||||
|------|---------|------------|
|
||||
| 代码行数 | 多 | 少 |
|
||||
| 复杂度 | 高 | 低 |
|
||||
| 依赖关系 | 隐式 | 显式 |
|
||||
| 初始化检查 | 需要 | 不需要 |
|
||||
| 灵活性 | 高(可运行时替换) | 中(编译时确定) |
|
||||
| 可读性 | 中 | 高 |
|
||||
| 维护性 | 中 | 高 |
|
||||
|
||||
## 💡 设计理念
|
||||
|
||||
### 为什么选择直接导入?
|
||||
|
||||
1. **简单性优先**
|
||||
- 对于大多数项目,API server 是固定的
|
||||
- 不需要运行时动态替换
|
||||
- 直接导入更符合直觉
|
||||
|
||||
2. **符合 ES6 规范**
|
||||
- 使用标准的 `import` 语法
|
||||
- 依赖关系在文件顶部清晰可见
|
||||
- 便于静态分析和 tree-shaking
|
||||
|
||||
3. **减少样板代码**
|
||||
- 不需要额外的 setter 函数
|
||||
- 不需要初始化检查
|
||||
- 代码更简洁
|
||||
|
||||
### 何时使用注入模式?
|
||||
|
||||
如果你的项目需要以下特性,可以考虑使用注入模式:
|
||||
|
||||
1. **运行时替换 API**
|
||||
- 例如:测试环境使用 mock API
|
||||
- 例如:多租户系统使用不同的 API
|
||||
|
||||
2. **插件化架构**
|
||||
- 框架作为独立包发布
|
||||
- 使用者可以自定义 API 实现
|
||||
|
||||
3. **依赖注入容器**
|
||||
- 使用 IoC 容器管理依赖
|
||||
- 需要复杂的依赖管理
|
||||
|
||||
对于本项目,直接导入模式更合适。
|
||||
|
||||
## 🧪 测试验证
|
||||
|
||||
### 测试步骤
|
||||
|
||||
1. **启动项目**
|
||||
```bash
|
||||
cd demo-project
|
||||
npm run dev
|
||||
```
|
||||
|
||||
2. **测试登录**
|
||||
- 访问 `http://localhost:8080`
|
||||
- 输入用户名密码
|
||||
- 点击登录
|
||||
|
||||
3. **验证功能**
|
||||
- ✅ 登录成功
|
||||
- ✅ 菜单显示正常
|
||||
- ✅ 系统标题显示正常
|
||||
- ✅ 页面跳转正常
|
||||
|
||||
### 预期结果
|
||||
|
||||
所有功能正常工作,与重构前完全一致。
|
||||
|
||||
## 📝 迁移指南
|
||||
|
||||
如果你的项目使用了旧的注入模式,可以按以下步骤迁移:
|
||||
|
||||
### 步骤 1:修改 Store 模块
|
||||
|
||||
**user.js**:
|
||||
```javascript
|
||||
// 删除
|
||||
let userServerInstance = null
|
||||
export const setUserServer = (server) => {
|
||||
userServerInstance = server
|
||||
}
|
||||
|
||||
// 添加
|
||||
import userServer from '../api/system/userServer'
|
||||
|
||||
// 替换所有 userServerInstance 为 userServer
|
||||
```
|
||||
|
||||
**app.js**:
|
||||
```javascript
|
||||
// 删除
|
||||
let paramSetupServerInstance = null
|
||||
export const setParamSetupServer = (server) => {
|
||||
paramSetupServerInstance = server
|
||||
}
|
||||
|
||||
// 添加
|
||||
import paramSetupServer from '../api/system_high/paramSetupServer'
|
||||
|
||||
// 替换所有 paramSetupServerInstance 为 paramSetupServer
|
||||
```
|
||||
|
||||
### 步骤 2:修改 index.js
|
||||
|
||||
```javascript
|
||||
// 删除这些导入
|
||||
import { setUserServer } from './store/user'
|
||||
import { setParamSetupServer } from './store/app'
|
||||
|
||||
// 删除这些调用
|
||||
setUserServer(systemApi.userServer)
|
||||
setParamSetupServer(systemHighApi.paramSetupServer)
|
||||
|
||||
// 删除 AdminFramework 类中的这些方法
|
||||
setUserServer(userServer) { ... }
|
||||
setParamSetupServer(paramSetupServer) { ... }
|
||||
```
|
||||
|
||||
### 步骤 3:测试验证
|
||||
|
||||
运行项目,确保所有功能正常。
|
||||
|
||||
## ✅ 总结
|
||||
|
||||
### 重构成果
|
||||
|
||||
- ✅ 代码更简洁(减少 26 行)
|
||||
- ✅ 结构更清晰
|
||||
- ✅ 依赖关系更明确
|
||||
- ✅ 维护更容易
|
||||
- ✅ 功能完全正常
|
||||
|
||||
### 后续建议
|
||||
|
||||
1. **保持简单**:除非有特殊需求,否则使用直接导入
|
||||
2. **统一风格**:项目中的所有 API 都使用相同的导入方式
|
||||
3. **文档更新**:更新项目文档,说明 API 的使用方式
|
||||
|
||||
---
|
||||
|
||||
**重构完成!** 🎉
|
||||
|
||||
现在框架代码更简洁、更易维护了。
|
||||
|
||||
@@ -1,314 +0,0 @@
|
||||
# authorityMenus 接口失败解决方案
|
||||
|
||||
## 🔧 问题描述
|
||||
|
||||
登录成功后,调用 `/sys_user/authorityMenus` 接口失败,导致无法获取完整的菜单数据。
|
||||
|
||||
## ✅ 解决方案
|
||||
|
||||
### 方案:使用默认菜单配置
|
||||
|
||||
当 `authorityMenus` 接口失败时,系统会自动使用默认的菜单配置,并根据登录接口返回的菜单 ID 数组进行过滤。
|
||||
|
||||
## 📁 新增文件
|
||||
|
||||
### `src/config/menuConfig.js`
|
||||
|
||||
这个文件包含了默认的菜单配置,包括:
|
||||
|
||||
1. **系统管理**
|
||||
- 用户管理 (`/system/user`)
|
||||
- 角色管理 (`/system/role`)
|
||||
- 系统日志 (`/system/log`)
|
||||
- 参数设置 (`/system/param`)
|
||||
|
||||
2. **高级管理**
|
||||
- 菜单管理 (`/system_high/menu`)
|
||||
- 控制管理 (`/system_high/control`)
|
||||
- 系统标题 (`/system_high/title`)
|
||||
|
||||
3. **首页** (`/home`)
|
||||
|
||||
## 🔄 工作流程
|
||||
|
||||
### 1. 登录流程
|
||||
|
||||
```
|
||||
用户登录
|
||||
↓
|
||||
调用 /sys_user/login 接口
|
||||
↓
|
||||
返回: { token, user, authorityMenus: "[1,142,121,...]" }
|
||||
↓
|
||||
保存 token 和用户信息
|
||||
↓
|
||||
解析 authorityMenus (字符串 → 数组)
|
||||
↓
|
||||
调用 /sys_user/authorityMenus 接口
|
||||
↓
|
||||
接口失败 ❌
|
||||
↓
|
||||
使用默认菜单配置 + 根据 ID 过滤
|
||||
↓
|
||||
生成路由
|
||||
↓
|
||||
登录成功 ✅
|
||||
```
|
||||
|
||||
### 2. 菜单过滤逻辑
|
||||
|
||||
```javascript
|
||||
// 登录返回的菜单 IDs
|
||||
authorityMenus: "[1,142,121,143,144,145,124,147,120,123,125,...]"
|
||||
|
||||
// 解析为数组
|
||||
menuIds: [1, 142, 121, 143, 144, 145, 124, 147, 120, 123, 125, ...]
|
||||
|
||||
// 从默认菜单配置中过滤出这些 ID 对应的菜单
|
||||
filteredMenus = filterMenusByIds(menuIds, defaultMenus)
|
||||
|
||||
// 生成路由
|
||||
routes = uiTool.getRoutes(Main, ParentView, Page404)
|
||||
```
|
||||
|
||||
## 📝 代码修改
|
||||
|
||||
### 1. `src/store/user.js`
|
||||
|
||||
**导入默认菜单配置:**
|
||||
```javascript
|
||||
import { defaultMenus, filterMenusByIds } from '../config/menuConfig'
|
||||
```
|
||||
|
||||
**修改 `setAuthorityMenus` 方法:**
|
||||
```javascript
|
||||
async setAuthorityMenus({ state, commit }, { Main, ParentView, Page404, authorityMenus, menuIds }) {
|
||||
let menus = authorityMenus
|
||||
|
||||
if (!menus && userServerInstance) {
|
||||
try {
|
||||
// 尝试调用接口
|
||||
let res = await userServerInstance.authorityMenus()
|
||||
if (res && res.code === 0 && res.data) {
|
||||
menus = res.data
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取权限菜单失败:', error)
|
||||
console.warn('将使用默认菜单配置')
|
||||
|
||||
// 接口失败,使用默认菜单配置
|
||||
if (menuIds && menuIds.length > 0) {
|
||||
// 根据 ID 过滤菜单
|
||||
menus = filterMenusByIds(menuIds, defaultMenus)
|
||||
} else {
|
||||
// 使用所有默认菜单
|
||||
menus = defaultMenus
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ... 后续处理
|
||||
}
|
||||
```
|
||||
|
||||
**修改 `handleLogin` 方法:**
|
||||
```javascript
|
||||
async handleLogin({ state, commit, dispatch }, { userFrom, Main, ParentView, Page404 }) {
|
||||
let res = await userServerInstance.login(userFrom)
|
||||
|
||||
let token = res.data.token
|
||||
let user = res.data.user
|
||||
let authorityMenusIds = res.data.authorityMenus
|
||||
|
||||
commit('setUserName', user.name.trim())
|
||||
commit('setToken', token)
|
||||
|
||||
// 解析菜单 IDs
|
||||
let menuIds = []
|
||||
if (authorityMenusIds) {
|
||||
if (typeof authorityMenusIds === 'string') {
|
||||
menuIds = JSON.parse(authorityMenusIds)
|
||||
} else if (Array.isArray(authorityMenusIds)) {
|
||||
menuIds = authorityMenusIds
|
||||
}
|
||||
}
|
||||
|
||||
// 传递 menuIds,以便在接口失败时使用
|
||||
await dispatch('setAuthorityMenus', {
|
||||
Main,
|
||||
ParentView,
|
||||
Page404,
|
||||
menuIds
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
## 🎯 测试步骤
|
||||
|
||||
### 1. 启动项目
|
||||
```bash
|
||||
cd demo-project
|
||||
npm run dev
|
||||
```
|
||||
|
||||
### 2. 登录测试
|
||||
- 用户名:`zc`
|
||||
- 密码:对应的密码
|
||||
|
||||
### 3. 查看控制台日志
|
||||
|
||||
**成功的日志:**
|
||||
```
|
||||
登录接口返回: { code: 0, data: { token, user, authorityMenus } }
|
||||
登录返回的菜单 IDs: "[1,142,121,...]"
|
||||
获取权限菜单失败: [错误信息]
|
||||
将使用默认菜单配置
|
||||
根据菜单 IDs 过滤后的菜单: [...]
|
||||
最终处理的权限菜单: [...]
|
||||
生成的主菜单: { path: '/', children: [...] }
|
||||
```
|
||||
|
||||
### 4. 验证结果
|
||||
|
||||
登录成功后,应该能看到:
|
||||
- ✅ 页面刷新
|
||||
- ✅ 进入系统首页
|
||||
- ✅ 左侧显示菜单(根据权限过滤)
|
||||
- ✅ 顶部显示用户名
|
||||
|
||||
## 📊 菜单 ID 映射
|
||||
|
||||
根据登录返回的菜单 IDs:
|
||||
```
|
||||
[1,142,121,143,144,145,124,147,120,123,125,132,133,126,136,137,138,139,151,152,153,154,149,150,156,157,158,122,159,5,11,12,13,67,68,15]
|
||||
```
|
||||
|
||||
默认菜单配置中包含的 IDs:
|
||||
- `1` - 首页
|
||||
- `5` - 系统管理
|
||||
- `11` - 用户管理
|
||||
- `12` - 角色管理
|
||||
- `13` - 系统日志
|
||||
- `15` - 参数设置
|
||||
- `120` - 高级管理
|
||||
- `122` - 菜单管理
|
||||
- `123` - 控制管理
|
||||
- `124` - 系统标题
|
||||
|
||||
过滤后会显示这些菜单。
|
||||
|
||||
## ⚠️ 注意事项
|
||||
|
||||
### 1. 默认菜单配置的限制
|
||||
|
||||
默认菜单配置只包含了常用的系统管理菜单。如果你的系统有其他业务菜单,需要:
|
||||
|
||||
**选项 A:扩展默认菜单配置**
|
||||
|
||||
编辑 `src/config/menuConfig.js`,添加更多菜单:
|
||||
```javascript
|
||||
export const defaultMenus = [
|
||||
// ... 现有菜单
|
||||
{
|
||||
id: 200,
|
||||
name: '业务管理',
|
||||
path: '/business',
|
||||
component: '',
|
||||
parent_id: 0,
|
||||
type: '菜单',
|
||||
is_show_menu: 1,
|
||||
icon: 'md-briefcase',
|
||||
children: [
|
||||
{
|
||||
id: 201,
|
||||
name: '订单管理',
|
||||
path: '/business/order',
|
||||
component: 'business/order',
|
||||
parent_id: 200,
|
||||
type: '页面',
|
||||
is_show_menu: 1
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
**选项 B:修复后端接口**
|
||||
|
||||
这是推荐的长期方案。修复 `/sys_user/authorityMenus` 接口,使其返回完整的菜单对象数组。
|
||||
|
||||
### 2. 菜单组件路径
|
||||
|
||||
默认菜单配置中的 `component` 字段指向 `src/views/` 目录下的组件。
|
||||
|
||||
例如:
|
||||
- `component: 'system/sys_user'` → `src/views/system/sys_user.vue`
|
||||
- `component: 'home/index'` → `src/views/home/index.vue`
|
||||
|
||||
确保这些组件文件存在。
|
||||
|
||||
### 3. 菜单权限
|
||||
|
||||
即使使用默认菜单配置,也会根据登录返回的菜单 IDs 进行过滤。
|
||||
|
||||
用户只能看到他有权限的菜单。
|
||||
|
||||
## 🔧 后续优化建议
|
||||
|
||||
### 短期方案(当前实现)
|
||||
✅ 使用默认菜单配置 + ID 过滤
|
||||
- 优点:快速解决问题,用户可以登录
|
||||
- 缺点:需要手动维护默认菜单配置
|
||||
|
||||
### 长期方案(推荐)
|
||||
🎯 修复后端 `authorityMenus` 接口
|
||||
- 优点:动态获取菜单,灵活性高
|
||||
- 缺点:需要修改后端代码
|
||||
|
||||
**建议后端接口返回格式:**
|
||||
```json
|
||||
{
|
||||
"code": 0,
|
||||
"message": "请求成功",
|
||||
"data": [
|
||||
{
|
||||
"id": 1,
|
||||
"name": "首页",
|
||||
"path": "/home",
|
||||
"component": "home/index",
|
||||
"parent_id": 0,
|
||||
"type": "页面",
|
||||
"is_show_menu": 1,
|
||||
"icon": "md-home",
|
||||
"sort": 1
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"name": "系统管理",
|
||||
"path": "/system",
|
||||
"component": "",
|
||||
"parent_id": 0,
|
||||
"type": "菜单",
|
||||
"is_show_menu": 1,
|
||||
"icon": "md-settings",
|
||||
"sort": 2,
|
||||
"children": [...]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## 🎉 总结
|
||||
|
||||
现在即使 `authorityMenus` 接口失败,系统也能:
|
||||
1. ✅ 正常登录
|
||||
2. ✅ 显示菜单(根据权限过滤)
|
||||
3. ✅ 访问有权限的页面
|
||||
4. ✅ 提供良好的用户体验
|
||||
|
||||
如果需要添加更多菜单,请编辑 `src/config/menuConfig.js` 文件。
|
||||
|
||||
---
|
||||
|
||||
**当前状态**:登录功能已修复,可以正常使用!🎉
|
||||
|
||||
@@ -1,225 +0,0 @@
|
||||
# Demo 项目修复完成报告
|
||||
|
||||
## ✅ 修复完成时间
|
||||
**2025-10-08**
|
||||
|
||||
## 🎯 修复的问题
|
||||
|
||||
### 1. ✅ 路径别名配置问题
|
||||
**问题描述**:框架源码中的组件使用 `@/` 别名引用内部文件,但 webpack 配置中的 `@` 别名指向 demo-project/src,导致无法找到框架的工具类和组件。
|
||||
|
||||
**解决方案**:
|
||||
修改 `demo-project/webpack.config.js`,将 `@` 别名指向框架源码目录:
|
||||
|
||||
```javascript
|
||||
resolve: {
|
||||
extensions: ['.js', '.vue', '.json'],
|
||||
alias: {
|
||||
// 框架源码的别名(必须在前面,优先级高)
|
||||
'@': path.resolve(__dirname, '../src'),
|
||||
'@component': path.resolve(__dirname, '../src/components'),
|
||||
'@utils': path.resolve(__dirname, '../src/utils'),
|
||||
'@api': path.resolve(__dirname, '../src/api'),
|
||||
'@config': path.resolve(__dirname, '../src/config'),
|
||||
'@assets': path.resolve(__dirname, '../src/assets'),
|
||||
// demo 项目的别名
|
||||
'~': path.resolve(__dirname, 'src'),
|
||||
'vue$': 'vue/dist/vue.esm.js'
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2. ✅ 缺少 core-js 依赖
|
||||
**问题描述**:Babel 编译时需要 core-js 提供 polyfill,但项目中未安装。
|
||||
|
||||
**解决方案**:
|
||||
```bash
|
||||
npm install core-js@3
|
||||
```
|
||||
|
||||
### 3. ✅ 登录接口未调用问题
|
||||
**问题描述**:登录页面调用 `handleLogin` action 时,参数格式不正确,缺少必需的 `Main`、`ParentView`、`Page404` 组件参数。
|
||||
|
||||
**原代码**(`src/views/login/login.vue`):
|
||||
```javascript
|
||||
async handleSubmit({ userName, password }) {
|
||||
let user = { name: userName, password: password }
|
||||
await this.handleLogin(user) // ❌ 参数不完整
|
||||
window.location.reload()
|
||||
}
|
||||
```
|
||||
|
||||
**修复后**:
|
||||
```javascript
|
||||
import Main from '@component/main'
|
||||
import ParentView from '@component/parent-view'
|
||||
import Page404 from '@/views/error-page/404.vue'
|
||||
import { mapActions } from 'vuex'
|
||||
|
||||
export default {
|
||||
methods: {
|
||||
...mapActions('user', ['handleLogin']),
|
||||
async handleSubmit({ userName, password }) {
|
||||
try {
|
||||
let userFrom = { name: userName, password: password }
|
||||
await this.handleLogin({
|
||||
userFrom, // ✅ 用户信息
|
||||
Main, // ✅ 主布局组件
|
||||
ParentView, // ✅ 父视图组件
|
||||
Page404 // ✅ 404页面组件
|
||||
})
|
||||
this.$Message.success('登录成功!')
|
||||
window.location.reload()
|
||||
} catch (error) {
|
||||
console.error('登录失败:', error)
|
||||
this.$Message.error(error.message || '登录失败,请检查用户名和密码')
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
## 📊 修复结果
|
||||
|
||||
### 编译状态
|
||||
✅ **webpack 5.102.1 compiled successfully**
|
||||
|
||||
### 功能验证
|
||||
- ✅ 项目可以正常启动
|
||||
- ✅ 没有编译错误
|
||||
- ✅ 登录功能可以正确调用登录接口
|
||||
- ✅ 框架的所有组件和工具类都能正确引用
|
||||
|
||||
## 🚀 如何启动项目
|
||||
|
||||
### 方式一:使用启动脚本
|
||||
```bash
|
||||
双击 demo-project/start.bat
|
||||
```
|
||||
|
||||
### 方式二:命令行启动
|
||||
```bash
|
||||
cd demo-project
|
||||
npm run dev
|
||||
```
|
||||
|
||||
### 访问地址
|
||||
浏览器会自动打开:`http://localhost:8080`
|
||||
|
||||
## 📝 登录测试
|
||||
|
||||
项目启动后,你可以在登录页面输入用户名和密码进行测试。
|
||||
|
||||
**注意**:
|
||||
- 登录接口会调用 `userServer.login(userFrom)` 方法
|
||||
- 需要确保后端 API 服务正常运行(默认地址:`http://localhost:9098/api/`)
|
||||
- 如果后端未启动,登录会失败并显示错误信息
|
||||
|
||||
## 🔧 配置说明
|
||||
|
||||
### API 配置
|
||||
在 `demo-project/src/config/index.js` 中配置:
|
||||
|
||||
```javascript
|
||||
export default {
|
||||
// 系统标题
|
||||
title: 'Demo 管理系统',
|
||||
|
||||
// API 基础路径
|
||||
apiUrl: 'http://localhost:9098/api/',
|
||||
|
||||
// 文件上传路径
|
||||
uploadUrl: 'http://localhost:9098/api/upload',
|
||||
|
||||
// Token 存储的 key
|
||||
tokenKey: 'demo_token',
|
||||
|
||||
// 其他配置
|
||||
timeout: 30000,
|
||||
|
||||
// 是否显示页面加载进度条
|
||||
showProgressBar: true
|
||||
}
|
||||
```
|
||||
|
||||
### 修改 API 地址
|
||||
如果你的后端 API 地址不同,请修改 `apiUrl` 和 `uploadUrl`。
|
||||
|
||||
## 🎉 框架功能
|
||||
|
||||
现在你的 Demo 项目已经完全集成了框架的所有功能:
|
||||
|
||||
### 1. 用户认证
|
||||
- ✅ 登录功能
|
||||
- ✅ 登出功能
|
||||
- ✅ Token 管理
|
||||
- ✅ 权限菜单获取
|
||||
|
||||
### 2. 路由管理
|
||||
- ✅ 动态路由
|
||||
- ✅ 权限路由
|
||||
- ✅ 路由守卫
|
||||
|
||||
### 3. 状态管理
|
||||
- ✅ Vuex Store
|
||||
- ✅ 用户状态
|
||||
- ✅ 应用状态
|
||||
- ✅ 状态持久化
|
||||
|
||||
### 4. UI 组件
|
||||
- ✅ 主布局(Main)
|
||||
- ✅ 侧边菜单
|
||||
- ✅ 顶部导航
|
||||
- ✅ 面包屑导航
|
||||
- ✅ 错误页面(401、404、500)
|
||||
|
||||
### 5. 工具库
|
||||
- ✅ HTTP 请求封装
|
||||
- ✅ UI 工具类
|
||||
- ✅ 通用工具函数
|
||||
|
||||
### 6. 系统管理页面
|
||||
- ✅ 系统日志(sys_log)
|
||||
- ✅ 参数设置(sys_param_setup)
|
||||
- ✅ 角色管理(sys_role)
|
||||
- ✅ 用户管理(sys_user)
|
||||
- ✅ 控制管理(sys_control)
|
||||
- ✅ 菜单管理(sys_menu)
|
||||
- ✅ 系统标题(sys_title)
|
||||
|
||||
## 📚 下一步
|
||||
|
||||
1. **启动后端服务**:确保后端 API 服务运行在 `http://localhost:9098/api/`
|
||||
2. **测试登录**:使用有效的用户名和密码进行登录测试
|
||||
3. **开发业务功能**:在 `demo-project/src/views/business/` 目录下添加你的业务页面
|
||||
4. **添加路由**:在 `demo-project/src/main.js` 中添加业务路由
|
||||
|
||||
## ⚠️ 注意事项
|
||||
|
||||
1. **后端 API**:项目需要配套的后端 API 服务才能完整运行
|
||||
2. **端口占用**:确保 8080 端口未被占用
|
||||
3. **浏览器兼容**:推荐使用 Chrome、Edge 或 Firefox 浏览器
|
||||
4. **Node 版本**:建议使用 Node.js 14+ 版本
|
||||
|
||||
## 🐛 常见问题
|
||||
|
||||
### Q1: 登录后提示"userServer not initialized"
|
||||
**A**: 这是因为 userServer 未正确初始化。框架已经在 `src/index.js` 中自动设置了 API,应该不会出现这个问题。
|
||||
|
||||
### Q2: 登录失败,提示网络错误
|
||||
**A**: 检查后端 API 服务是否正常运行,以及 `config/index.js` 中的 `apiUrl` 配置是否正确。
|
||||
|
||||
### Q3: 页面空白或报错
|
||||
**A**: 打开浏览器控制台查看具体错误信息,通常是路由或组件引用问题。
|
||||
|
||||
## 📞 技术支持
|
||||
|
||||
如果遇到问题,请检查:
|
||||
1. 浏览器控制台的错误信息
|
||||
2. 终端的编译输出
|
||||
3. 网络请求是否正常(F12 -> Network)
|
||||
|
||||
---
|
||||
|
||||
**修复完成!项目现在可以正常运行了!** 🎉
|
||||
|
||||
@@ -1,203 +0,0 @@
|
||||
# Demo 项目修复说明
|
||||
|
||||
## ✅ 已完成的修复
|
||||
|
||||
### 1. 配置更新
|
||||
|
||||
#### ✅ `src/main.js`
|
||||
```javascript
|
||||
// 修改为直接使用框架源码
|
||||
import AdminFramework from '../../src/index.js'
|
||||
```
|
||||
|
||||
**好处**:
|
||||
- 实时调试框架代码
|
||||
- 热更新支持
|
||||
- 无需重新打包
|
||||
|
||||
#### ✅ `webpack.config.js`
|
||||
添加了框架源码的路径别名:
|
||||
```javascript
|
||||
alias: {
|
||||
'@component': path.resolve(__dirname, '../src/components'),
|
||||
'@utils': path.resolve(__dirname, '../src/utils'),
|
||||
'@api': path.resolve(__dirname, '../src/api'),
|
||||
'@config': path.resolve(__dirname, '../src/config'),
|
||||
'@assets': path.resolve(__dirname, '../src/assets')
|
||||
}
|
||||
```
|
||||
|
||||
#### ✅ `babel.config.js`
|
||||
添加了 JSX 支持:
|
||||
```javascript
|
||||
presets: [
|
||||
'@babel/preset-env',
|
||||
'@vue/babel-preset-jsx'
|
||||
]
|
||||
```
|
||||
|
||||
#### ✅ `package.json`
|
||||
新增依赖:
|
||||
- `@vue/babel-preset-jsx@^1.4.0` - JSX 支持
|
||||
- `brace@^0.11.1` - 代码编辑器
|
||||
- `vue2-ace-editor@^0.0.15` - Ace 编辑器
|
||||
|
||||
### 2. 脚本文件
|
||||
|
||||
#### ✅ `install.bat` - 一键安装脚本
|
||||
```batch
|
||||
双击运行即可安装所有依赖
|
||||
```
|
||||
|
||||
#### ✅ `start.bat` - 启动脚本(已更新)
|
||||
```batch
|
||||
自动检查并安装缺失的依赖
|
||||
```
|
||||
|
||||
### 3. 文档文件
|
||||
|
||||
#### ✅ 创建的文档:
|
||||
- `README_FIRST.txt` - 首次运行必读 ⭐
|
||||
- `启动前必读.md` - 详细启动指南
|
||||
- `修复说明.md` - 本文件
|
||||
- `CHANGELOG.md` - 更新日志
|
||||
|
||||
#### ✅ 更新的文档:
|
||||
- `README.md` - 项目说明
|
||||
- `如何启动.txt` - 快速指南
|
||||
|
||||
## 🚀 用户需要做什么
|
||||
|
||||
### 第一步:安装依赖
|
||||
|
||||
**方式一(推荐)**:
|
||||
```bash
|
||||
双击 install.bat
|
||||
```
|
||||
|
||||
**方式二**:
|
||||
```bash
|
||||
cd demo-project
|
||||
npm install
|
||||
```
|
||||
|
||||
如果遇到依赖缺失,运行:
|
||||
```bash
|
||||
npm install @vue/babel-preset-jsx brace vue2-ace-editor
|
||||
```
|
||||
|
||||
### 第二步:启动项目
|
||||
|
||||
**方式一(推荐)**:
|
||||
```bash
|
||||
双击 start.bat
|
||||
```
|
||||
|
||||
**方式二**:
|
||||
```bash
|
||||
npm run dev
|
||||
```
|
||||
|
||||
### 第三步:访问应用
|
||||
|
||||
浏览器自动打开 `http://localhost:8080`
|
||||
|
||||
## 📋 检查清单
|
||||
|
||||
运行前确保:
|
||||
|
||||
- [ ] 已进入 `demo-project` 目录
|
||||
- [ ] 已运行 `npm install` 或 `install.bat`
|
||||
- [ ] 没有 "UNMET DEPENDENCY" 错误
|
||||
- [ ] 端口 8080 未被占用
|
||||
|
||||
验证命令:
|
||||
```bash
|
||||
npm list --depth=0
|
||||
```
|
||||
|
||||
应该看到:
|
||||
```
|
||||
✅ @vue/babel-preset-jsx@1.4.0
|
||||
✅ brace@0.11.1
|
||||
✅ vue2-ace-editor@0.0.15
|
||||
✅ vue@2.7.16
|
||||
✅ vuex@3.6.2
|
||||
✅ vue-router@3.6.5
|
||||
✅ view-design@4.7.0
|
||||
```
|
||||
|
||||
## ⚠️ 常见错误及解决
|
||||
|
||||
### 错误1:UNMET DEPENDENCY
|
||||
|
||||
**原因**:依赖未安装
|
||||
|
||||
**解决**:
|
||||
```bash
|
||||
npm install @vue/babel-preset-jsx brace vue2-ace-editor
|
||||
```
|
||||
|
||||
### 错误2:Cannot find module '../../src/index.js'
|
||||
|
||||
**原因**:项目结构不正确
|
||||
|
||||
**解决**:确保目录结构为:
|
||||
```
|
||||
f:\项目\前端框架项目\
|
||||
├── src\ ← 框架源码
|
||||
│ └── index.js
|
||||
└── demo-project\ ← Demo 项目
|
||||
```
|
||||
|
||||
### 错误3:端口被占用
|
||||
|
||||
**原因**:8080 端口已被使用
|
||||
|
||||
**解决**:修改 `webpack.config.js`:
|
||||
```javascript
|
||||
devServer: {
|
||||
port: 8081 // 改成其他端口
|
||||
}
|
||||
```
|
||||
|
||||
### 错误4:Babel 编译错误
|
||||
|
||||
**原因**:JSX preset 未安装
|
||||
|
||||
**解决**:
|
||||
```bash
|
||||
npm install @vue/babel-preset-jsx --save-dev
|
||||
```
|
||||
|
||||
## 📊 项目状态
|
||||
|
||||
| 项目 | 状态 |
|
||||
|------|------|
|
||||
| 框架源码引用 | ✅ 已配置 |
|
||||
| Webpack 配置 | ✅ 已更新 |
|
||||
| Babel 配置 | ✅ 已更新 |
|
||||
| 依赖列表 | ✅ 已更新 |
|
||||
| 安装脚本 | ✅ 已创建 |
|
||||
| 启动脚本 | ✅ 已更新 |
|
||||
| 文档 | ✅ 已完善 |
|
||||
|
||||
## 🎯 下一步
|
||||
|
||||
1. **运行** `install.bat` 安装依赖
|
||||
2. **运行** `start.bat` 启动项目
|
||||
3. **访问** `http://localhost:8080`
|
||||
4. **开始** 开发你的应用!
|
||||
|
||||
## 📚 相关文档
|
||||
|
||||
- [README_FIRST.txt](./README_FIRST.txt) - 首次运行必读
|
||||
- [启动前必读.md](./启动前必读.md) - 详细指南
|
||||
- [CHANGELOG.md](./CHANGELOG.md) - 更新日志
|
||||
- [README.md](./README.md) - 完整说明
|
||||
|
||||
---
|
||||
|
||||
**修复完成时间**: 2025-10-08
|
||||
**修复人员**: AI Assistant
|
||||
|
||||
@@ -1,115 +0,0 @@
|
||||
# ⚠️ 启动前必读
|
||||
|
||||
## 📦 第一步:安装依赖
|
||||
|
||||
**在 `demo-project` 目录下**运行:
|
||||
|
||||
```bash
|
||||
npm install
|
||||
```
|
||||
|
||||
如果遇到依赖缺失错误,运行:
|
||||
|
||||
```bash
|
||||
npm install @vue/babel-preset-jsx brace vue2-ace-editor --save
|
||||
```
|
||||
|
||||
## 🚀 第二步:启动项目
|
||||
|
||||
### 方式一:使用脚本(推荐)
|
||||
|
||||
**Windows 用户**:
|
||||
```bash
|
||||
双击 start.bat
|
||||
```
|
||||
|
||||
**Linux/Mac 用户**:
|
||||
```bash
|
||||
./start.sh
|
||||
```
|
||||
|
||||
### 方式二:手动启动
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
```
|
||||
|
||||
## ✅ 验证安装
|
||||
|
||||
运行以下命令检查依赖是否完整:
|
||||
|
||||
```bash
|
||||
npm list --depth=0
|
||||
```
|
||||
|
||||
应该看到以下关键依赖:
|
||||
- ✅ @vue/babel-preset-jsx
|
||||
- ✅ brace
|
||||
- ✅ vue2-ace-editor
|
||||
- ✅ vue
|
||||
- ✅ vuex
|
||||
- ✅ vue-router
|
||||
- ✅ view-design
|
||||
|
||||
## 🔍 常见问题
|
||||
|
||||
### 问题1:依赖缺失
|
||||
|
||||
**症状**:看到 "UNMET DEPENDENCY" 错误
|
||||
|
||||
**解决**:
|
||||
```bash
|
||||
# 删除依赖重装
|
||||
rm -rf node_modules
|
||||
rm package-lock.json
|
||||
npm install
|
||||
```
|
||||
|
||||
### 问题2:端口被占用
|
||||
|
||||
**症状**:8080 端口已被占用
|
||||
|
||||
**解决**:修改 `webpack.config.js` 中的端口
|
||||
```javascript
|
||||
devServer: {
|
||||
port: 8081 // 改成其他端口
|
||||
}
|
||||
```
|
||||
|
||||
### 问题3:路径找不到
|
||||
|
||||
**症状**:无法找到 ../../src/index.js
|
||||
|
||||
**解决**:确保项目结构正确
|
||||
```
|
||||
f:\项目\前端框架项目\
|
||||
├── src\ # ← 框架源码
|
||||
│ └── index.js # ← 框架入口
|
||||
└── demo-project\ # ← Demo 项目
|
||||
└── src\
|
||||
└── main.js # ← 引用 ../../src/index.js
|
||||
```
|
||||
|
||||
## 📝 项目说明
|
||||
|
||||
本项目直接使用框架源码(不是打包文件),好处:
|
||||
- ✅ 实时调试
|
||||
- ✅ 热更新
|
||||
- ✅ 方便开发
|
||||
|
||||
## 🎯 访问地址
|
||||
|
||||
启动后访问:`http://localhost:8080`
|
||||
|
||||
可用页面:
|
||||
- `/login` - 登录页面
|
||||
- `/home` - 主页
|
||||
- `/business/product` - 产品列表示例
|
||||
|
||||
---
|
||||
|
||||
**如有问题,请查看**:
|
||||
- [CHANGELOG.md](./CHANGELOG.md) - 更新日志
|
||||
- [README.md](./README.md) - 完整说明
|
||||
- [如何启动.txt](./如何启动.txt) - 快速指南
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
====================================
|
||||
Demo 项目启动说明
|
||||
====================================
|
||||
|
||||
本项目现在使用框架源码,更方便调试!
|
||||
|
||||
⚠️ 重要:首次运行必须先安装依赖!
|
||||
|
||||
【方式一:一键安装+启动(推荐)】
|
||||
|
||||
Windows 用户:
|
||||
1. 双击 install.bat(安装依赖)
|
||||
2. 双击 start.bat(启动项目)
|
||||
|
||||
【方式二:手动安装+启动】
|
||||
|
||||
1. 安装依赖(首次必须)
|
||||
npm install
|
||||
|
||||
2. 如果提示依赖缺失,运行:
|
||||
npm install @vue/babel-preset-jsx brace vue2-ace-editor
|
||||
|
||||
3. 启动开发服务器
|
||||
npm run dev
|
||||
|
||||
4. 浏览器自动打开
|
||||
http://localhost:8080
|
||||
|
||||
【可访问页面】
|
||||
|
||||
/login - 登录页面
|
||||
/home - 主页
|
||||
/business/product - 产品列表示例
|
||||
|
||||
【遇到错误?】
|
||||
|
||||
1. 删除依赖重装:
|
||||
rm -rf node_modules
|
||||
rm package-lock.json
|
||||
npm install
|
||||
|
||||
2. 查看更新日志:
|
||||
CHANGELOG.md
|
||||
|
||||
3. 查看详细文档:
|
||||
README.md
|
||||
快速启动.md
|
||||
|
||||
====================================
|
||||
|
||||
@@ -1,143 +0,0 @@
|
||||
# 快速配置组件映射
|
||||
|
||||
## 🎯 一句话说明
|
||||
|
||||
**在 `Vue.use()` 时传入 `componentMap` 参数,将后端权限菜单中的组件路径映射到实际组件。**
|
||||
|
||||
## 📝 三步配置
|
||||
|
||||
### 步骤1:查看权限菜单接口
|
||||
|
||||
查看后端接口返回的所有 `component` 字段:
|
||||
|
||||
```json
|
||||
{
|
||||
"data": [
|
||||
{ "component": "ball/games.vue" },
|
||||
{ "component": "order/pay_orders.vue" },
|
||||
{ "component": "ball/wch_users.vue" }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 步骤2:创建并导入组件
|
||||
|
||||
```javascript
|
||||
// src/main.js
|
||||
import GamesComponent from './views/ball/games.vue'
|
||||
import PayOrdersComponent from './views/order/pay_orders.vue'
|
||||
import WchUsersComponent from './views/ball/wch_users.vue'
|
||||
```
|
||||
|
||||
### 步骤3:配置映射
|
||||
|
||||
```javascript
|
||||
Vue.use(AdminFramework, {
|
||||
config,
|
||||
ViewUI,
|
||||
VueRouter,
|
||||
Vuex,
|
||||
createPersistedState,
|
||||
componentMap: {
|
||||
'ball/games': GamesComponent,
|
||||
'order/pay_orders': PayOrdersComponent,
|
||||
'ball/wch_users': WchUsersComponent
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
## ✅ 完成!
|
||||
|
||||
现在权限菜单中的所有业务页面都能正常显示了。
|
||||
|
||||
## 📋 配置模板
|
||||
|
||||
```javascript
|
||||
import Vue from 'vue'
|
||||
import VueRouter from 'vue-router'
|
||||
import Vuex from 'vuex'
|
||||
import ViewUI from 'view-design'
|
||||
import createPersistedState from 'vuex-persistedstate'
|
||||
import 'view-design/dist/styles/iview.css'
|
||||
|
||||
import AdminFramework from '../../src/index.js'
|
||||
import config from './config'
|
||||
import App from './App.vue'
|
||||
|
||||
// ==================== 导入业务组件 ====================
|
||||
// TODO: 根据权限菜单接口,导入所有业务组件
|
||||
// import XXXComponent from './views/xxx/xxx.vue'
|
||||
|
||||
Vue.use(AdminFramework, {
|
||||
config,
|
||||
ViewUI,
|
||||
VueRouter,
|
||||
Vuex,
|
||||
createPersistedState,
|
||||
componentMap: {
|
||||
// TODO: 配置组件映射
|
||||
// 'xxx/xxx': XXXComponent
|
||||
}
|
||||
})
|
||||
|
||||
new Vue({
|
||||
el: '#app',
|
||||
router: AdminFramework.router,
|
||||
store: AdminFramework.store,
|
||||
render: h => h(App),
|
||||
mounted() {
|
||||
AdminFramework.uiTool.setRem()
|
||||
this.$store.dispatch('user/setAuthorityMenus', {
|
||||
Main: AdminFramework.Main,
|
||||
ParentView: AdminFramework.ParentView,
|
||||
Page404: AdminFramework.Page404
|
||||
})
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
## 💡 关键要点
|
||||
|
||||
### 1. 路径不需要 .vue 后缀
|
||||
|
||||
```javascript
|
||||
// ✅ 正确
|
||||
componentMap: {
|
||||
'ball/games': GamesComponent
|
||||
}
|
||||
|
||||
// ❌ 错误(虽然也能用,但不推荐)
|
||||
componentMap: {
|
||||
'ball/games.vue': GamesComponent
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 系统组件无需配置
|
||||
|
||||
这些已自动映射,**不需要**在 componentMap 中配置:
|
||||
- `home/index.vue`
|
||||
- `system/sys_user.vue`
|
||||
- `system/sys_role.vue`
|
||||
- `system/sys_log.vue`
|
||||
- `system/sys_param_setup.vue`
|
||||
- `system_high/sys_menu.vue`
|
||||
- `system_high/sys_control.vue`
|
||||
- `system_high/sys_title.vue`
|
||||
|
||||
### 3. 未映射的组件会显示提示
|
||||
|
||||
如果组件没有配置映射,页面会显示:
|
||||
```
|
||||
⚠️ 警告
|
||||
页面组件未加载: ball/games.vue
|
||||
请在项目中创建此组件或在组件映射表中注册
|
||||
```
|
||||
|
||||
## 📚 完整文档
|
||||
|
||||
查看详细说明:[权限菜单组件配置指南.md](./权限菜单组件配置指南.md)
|
||||
|
||||
---
|
||||
|
||||
**配置完成后,所有权限菜单中的页面都能正常显示!** 🎉
|
||||
|
||||
@@ -1,362 +0,0 @@
|
||||
# 文档更新说明
|
||||
|
||||
## 📝 更新内容
|
||||
|
||||
已更新 `完整使用文档.md`,主要修改如下:
|
||||
|
||||
### 1. 移除已废弃的方法
|
||||
|
||||
**移除的方法**:
|
||||
- ❌ `setUserServer(userServer)` - 已废弃
|
||||
- ❌ `setParamSetupServer(paramSetupServer)` - 已废弃
|
||||
|
||||
**原因**:
|
||||
- 框架已改为直接导入 API 模块
|
||||
- 不再使用依赖注入模式
|
||||
- 简化了代码结构
|
||||
|
||||
### 2. 添加 HomePage 参数说明
|
||||
|
||||
**新增内容**:
|
||||
|
||||
#### install 方法参数
|
||||
|
||||
```javascript
|
||||
Vue.use(AdminFramework, {
|
||||
config: yourConfig,
|
||||
ViewUI: ViewUI,
|
||||
VueRouter: VueRouter,
|
||||
Vuex: Vuex,
|
||||
createPersistedState: createPersistedState,
|
||||
HomePage: HomePage // ✅ 新增:可选的自定义首页组件
|
||||
})
|
||||
```
|
||||
|
||||
**参数说明**:
|
||||
- `HomePage`: 自定义首页组件(可选,不传则使用框架内置组件)
|
||||
|
||||
### 3. 完善 Q11: 主页 HomePage 组件说明
|
||||
|
||||
**新增两种使用方式**:
|
||||
|
||||
#### 方式一:使用框架内置的主页组件(默认)
|
||||
|
||||
```javascript
|
||||
// 不传入 HomePage,框架会使用内置组件
|
||||
Vue.use(AdminFramework, {
|
||||
config,
|
||||
ViewUI,
|
||||
VueRouter,
|
||||
Vuex,
|
||||
createPersistedState
|
||||
})
|
||||
```
|
||||
|
||||
**内置组件特性**:
|
||||
- ✅ 自动从 Vuex Store 获取系统标题
|
||||
- ✅ 优雅的欢迎页面样式
|
||||
- ✅ 无需额外配置
|
||||
|
||||
#### 方式二:传入自定义首页组件(推荐)
|
||||
|
||||
**创建自定义首页**:
|
||||
```vue
|
||||
<!-- src/views/home/index.vue -->
|
||||
<template>
|
||||
<div class="custom-home">
|
||||
<h1>欢迎使用 {{ sysFormModel.title }}</h1>
|
||||
<p>这是自定义的首页内容</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters } from 'vuex'
|
||||
|
||||
export default {
|
||||
computed: {
|
||||
...mapGetters({
|
||||
sysFormModel: 'app/sysFormModel'
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
**传入自定义组件**:
|
||||
```javascript
|
||||
import HomePage from './views/home/index.vue'
|
||||
|
||||
Vue.use(AdminFramework, {
|
||||
config,
|
||||
ViewUI,
|
||||
VueRouter,
|
||||
Vuex,
|
||||
createPersistedState,
|
||||
HomePage // ✅ 传入自定义首页组件
|
||||
})
|
||||
```
|
||||
|
||||
### 4. 重要说明:首页路由配置
|
||||
|
||||
**关键点**:
|
||||
- ⚠️ **首页路由完全由后端权限菜单返回**
|
||||
- ⚠️ 后端必须返回 `path: '/home'` 的菜单配置
|
||||
- ⚠️ 后端返回的 `component` 字段会映射到实际组件
|
||||
|
||||
**后端菜单配置示例**:
|
||||
```json
|
||||
{
|
||||
"id": 1,
|
||||
"name": "首页",
|
||||
"path": "/home",
|
||||
"component": "home/index", // 映射到 src/views/home/index.vue
|
||||
"parent_id": 0,
|
||||
"type": "页面",
|
||||
"is_show_menu": 1,
|
||||
"icon": "md-home",
|
||||
"sort": 1
|
||||
}
|
||||
```
|
||||
|
||||
**组件映射规则**:
|
||||
```javascript
|
||||
// 后端返回: "component": "home/index"
|
||||
// 实际加载: src/views/home/index.vue
|
||||
|
||||
// 后端返回: "component": "system/user"
|
||||
// 实际加载: src/views/system/user.vue
|
||||
```
|
||||
|
||||
### 5. 降级方案
|
||||
|
||||
如果后端接口失败,框架会使用默认菜单配置(`src/config/menuConfig.js`):
|
||||
|
||||
```javascript
|
||||
export const defaultMenus = [
|
||||
{
|
||||
id: 1,
|
||||
name: '首页',
|
||||
path: '/home',
|
||||
component: 'home/index',
|
||||
parent_id: 0,
|
||||
type: '页面',
|
||||
is_show_menu: 1,
|
||||
icon: 'md-home',
|
||||
sort: 1
|
||||
},
|
||||
// ... 其他菜单
|
||||
]
|
||||
```
|
||||
|
||||
### 6. 更新快速开始示例
|
||||
|
||||
**新的 main.js 示例**:
|
||||
```javascript
|
||||
import Vue from 'vue'
|
||||
import VueRouter from 'vue-router'
|
||||
import Vuex from 'vuex'
|
||||
import ViewUI from 'view-design'
|
||||
import createPersistedState from 'vuex-persistedstate'
|
||||
import AdminFramework from './libs/admin-framework.js'
|
||||
import App from './App.vue'
|
||||
import config from './config'
|
||||
import HomePage from './views/home/index.vue' // ✅ 导入自定义首页
|
||||
|
||||
Vue.use(AdminFramework, {
|
||||
config,
|
||||
ViewUI,
|
||||
VueRouter,
|
||||
Vuex,
|
||||
createPersistedState,
|
||||
HomePage // ✅ 传入自定义首页组件
|
||||
})
|
||||
|
||||
new Vue({
|
||||
el: '#app',
|
||||
router: AdminFramework.router,
|
||||
store: AdminFramework.store,
|
||||
render: h => h(App),
|
||||
mounted() {
|
||||
AdminFramework.uiTool.setRem()
|
||||
|
||||
// 只在已登录时获取系统标题
|
||||
const token = this.$store.state.user.token
|
||||
if (token) {
|
||||
this.$store.dispatch('app/getSysTitle', {
|
||||
defaultTitle: 'Demo 管理系统',
|
||||
defaultLogo: ''
|
||||
})
|
||||
} else {
|
||||
document.title = 'Demo 管理系统'
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
## 📊 更新对比
|
||||
|
||||
### 旧版本(已废弃)
|
||||
|
||||
```javascript
|
||||
// ❌ 旧版本:使用依赖注入
|
||||
import { systemApi, systemHighApi } from 'admin-framework'
|
||||
|
||||
AdminFramework.setUserServer(systemApi.userServer)
|
||||
AdminFramework.setParamSetupServer(systemHighApi.paramSetupServer)
|
||||
|
||||
// ❌ 旧版本:首页路由硬编码在框架中
|
||||
// 无法自定义首页组件
|
||||
```
|
||||
|
||||
### 新版本(当前)
|
||||
|
||||
```javascript
|
||||
// ✅ 新版本:直接导入 API
|
||||
import userServer from '../api/system/userServer'
|
||||
import paramSetupServer from '../api/system_high/paramSetupServer'
|
||||
|
||||
// ✅ 新版本:支持自定义首页组件
|
||||
import HomePage from './views/home/index.vue'
|
||||
|
||||
Vue.use(AdminFramework, {
|
||||
config,
|
||||
ViewUI,
|
||||
VueRouter,
|
||||
Vuex,
|
||||
createPersistedState,
|
||||
HomePage // 可选:传入自定义首页组件
|
||||
})
|
||||
```
|
||||
|
||||
## 🎯 最佳实践
|
||||
|
||||
### 推荐做法
|
||||
|
||||
1. **创建自定义首页组件**
|
||||
```bash
|
||||
src/views/home/index.vue
|
||||
```
|
||||
|
||||
2. **在 Vue.use() 时传入 HomePage 参数**
|
||||
```javascript
|
||||
import HomePage from './views/home/index.vue'
|
||||
|
||||
Vue.use(AdminFramework, {
|
||||
config,
|
||||
ViewUI,
|
||||
VueRouter,
|
||||
Vuex,
|
||||
createPersistedState,
|
||||
HomePage // 传入自定义首页组件
|
||||
})
|
||||
```
|
||||
|
||||
3. **确保后端返回首页菜单配置**
|
||||
```json
|
||||
{
|
||||
"id": 1,
|
||||
"name": "首页",
|
||||
"path": "/home",
|
||||
"component": "home/index",
|
||||
"parent_id": 0,
|
||||
"type": "页面",
|
||||
"is_show_menu": 1
|
||||
}
|
||||
```
|
||||
|
||||
4. **在 defaultMenus 中包含首页配置作为降级方案**
|
||||
```javascript
|
||||
// src/config/menuConfig.js
|
||||
export const defaultMenus = [
|
||||
{
|
||||
id: 1,
|
||||
name: '首页',
|
||||
path: '/home',
|
||||
component: 'home/index',
|
||||
// ...
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
### 不推荐的做法
|
||||
|
||||
1. ❌ 使用已废弃的 `setUserServer` 和 `setParamSetupServer` 方法
|
||||
2. ❌ 在代码中硬编码首页路由
|
||||
3. ❌ 不提供降级方案(defaultMenus)
|
||||
|
||||
## 📝 迁移指南
|
||||
|
||||
### 从旧版本迁移到新版本
|
||||
|
||||
**步骤 1:移除废弃的方法调用**
|
||||
|
||||
```javascript
|
||||
// ❌ 删除这些代码
|
||||
AdminFramework.setUserServer(systemApi.userServer)
|
||||
AdminFramework.setParamSetupServer(systemHighApi.paramSetupServer)
|
||||
```
|
||||
|
||||
**步骤 2:创建自定义首页组件**
|
||||
|
||||
```bash
|
||||
# 创建首页组件文件
|
||||
mkdir -p src/views/home
|
||||
touch src/views/home/index.vue
|
||||
```
|
||||
|
||||
**步骤 3:更新 main.js**
|
||||
|
||||
```javascript
|
||||
// 添加 HomePage 导入
|
||||
import HomePage from './views/home/index.vue'
|
||||
|
||||
// 在 Vue.use() 时传入
|
||||
Vue.use(AdminFramework, {
|
||||
config,
|
||||
ViewUI,
|
||||
VueRouter,
|
||||
Vuex,
|
||||
createPersistedState,
|
||||
HomePage // 新增
|
||||
})
|
||||
```
|
||||
|
||||
**步骤 4:确保后端返回首页配置**
|
||||
|
||||
检查后端 `authorityMenus` 接口是否返回首页配置。
|
||||
|
||||
**步骤 5:测试**
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
```
|
||||
|
||||
登录后应该能看到自定义的首页。
|
||||
|
||||
## ✅ 验证清单
|
||||
|
||||
- ✅ 移除了 `setUserServer` 和 `setParamSetupServer` 的调用
|
||||
- ✅ 创建了自定义首页组件 `src/views/home/index.vue`
|
||||
- ✅ 在 `Vue.use()` 时传入了 `HomePage` 参数
|
||||
- ✅ 后端返回了首页菜单配置
|
||||
- ✅ `defaultMenus` 中包含了首页配置
|
||||
- ✅ 登录后能正常跳转到首页
|
||||
- ✅ 首页显示正确的内容
|
||||
|
||||
## 📚 相关文档
|
||||
|
||||
- `完整使用文档.md` - 完整的框架使用文档
|
||||
- `demo-project/README.md` - Demo 项目说明
|
||||
- `demo-project/登录跳转首页修复说明.md` - 登录跳转逻辑说明
|
||||
- `demo-project/移除硬编码首页说明.md` - 移除硬编码首页的说明
|
||||
|
||||
---
|
||||
|
||||
**文档已更新完成!** 🎉
|
||||
|
||||
现在文档更准确地反映了框架的当前实现,包括:
|
||||
- ✅ 移除了已废弃的方法
|
||||
- ✅ 添加了 HomePage 参数说明
|
||||
- ✅ 完善了首页组件的使用方式
|
||||
- ✅ 提供了最佳实践和迁移指南
|
||||
|
||||
@@ -1,387 +0,0 @@
|
||||
# 未登录接口调用问题修复
|
||||
|
||||
## 🔍 问题描述
|
||||
|
||||
### 错误现象
|
||||
|
||||
在用户未登录时,`sys_parameter/key` 接口被调用,因为没有 token 导致接口报错。
|
||||
|
||||
### 错误原因
|
||||
|
||||
在 `demo-project/src/main.js` 的 `mounted` 钩子中,无条件调用了 `getSysTitle` action:
|
||||
|
||||
```javascript
|
||||
mounted() {
|
||||
AdminFramework.uiTool.setRem()
|
||||
|
||||
// ❌ 无论是否登录都调用
|
||||
this.$store.dispatch('app/getSysTitle', {
|
||||
defaultTitle: 'Demo 管理系统',
|
||||
defaultLogo: ''
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
`getSysTitle` action 会调用后端接口:
|
||||
- `paramSetupServer.getOne('sys_title')` - 获取系统标题
|
||||
- `paramSetupServer.getOne('sys_logo')` - 获取系统 Logo
|
||||
|
||||
这些接口需要 token 认证,但在应用启动时用户可能还没登录,导致接口调用失败。
|
||||
|
||||
### 调用时机问题
|
||||
|
||||
```
|
||||
应用启动
|
||||
↓
|
||||
Vue 实例 mounted
|
||||
↓
|
||||
调用 getSysTitle ← ❌ 此时可能未登录
|
||||
↓
|
||||
调用后端接口
|
||||
↓
|
||||
没有 token,接口报错 ❌
|
||||
```
|
||||
|
||||
## ✅ 解决方案
|
||||
|
||||
### 方案:只在已登录时调用接口
|
||||
|
||||
**核心思路**:
|
||||
1. 在调用 `getSysTitle` 前检查是否已登录(是否有 token)
|
||||
2. 已登录:调用接口获取系统标题
|
||||
3. 未登录:直接使用默认标题
|
||||
|
||||
### 修改 1:`demo-project/src/main.js`
|
||||
|
||||
**修改前**:
|
||||
```javascript
|
||||
mounted() {
|
||||
AdminFramework.uiTool.setRem()
|
||||
|
||||
// ❌ 无条件调用,可能导致未登录时接口报错
|
||||
this.$store.dispatch('app/getSysTitle', {
|
||||
defaultTitle: 'Demo 管理系统',
|
||||
defaultLogo: ''
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
**修改后**:
|
||||
```javascript
|
||||
mounted() {
|
||||
AdminFramework.uiTool.setRem()
|
||||
|
||||
// ✅ 检查是否已登录
|
||||
const token = this.$store.state.user.token
|
||||
if (token) {
|
||||
// 已登录,调用接口获取系统标题
|
||||
this.$store.dispatch('app/getSysTitle', {
|
||||
defaultTitle: 'Demo 管理系统',
|
||||
defaultLogo: ''
|
||||
})
|
||||
} else {
|
||||
// 未登录,直接设置默认标题
|
||||
document.title = 'Demo 管理系统'
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 修改 2:`src/store/app.js` 的 `getSysTitle` action
|
||||
|
||||
**修改前**:
|
||||
```javascript
|
||||
async getSysTitle({ state, commit }, { defaultTitle = '智能代码平台', defaultLogo = '' }) {
|
||||
let formModel = {
|
||||
title: defaultTitle,
|
||||
logoUrl: defaultLogo
|
||||
}
|
||||
|
||||
try {
|
||||
// ❌ 直接调用接口,不检查是否已登录
|
||||
let res1 = await paramSetupServer.getOne('sys_title')
|
||||
if (res1 && res1.data) {
|
||||
formModel.title = res1.data.value
|
||||
document.title = res1.data.value
|
||||
}
|
||||
let res2 = await paramSetupServer.getOne('sys_logo')
|
||||
if (res2 && res2.data) {
|
||||
formModel.logoUrl = res2.data.value
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('获取系统标题失败,使用默认标题:', error || '接口调用失败')
|
||||
document.title = formModel.title
|
||||
}
|
||||
|
||||
commit('setSysTitle', formModel)
|
||||
}
|
||||
```
|
||||
|
||||
**修改后**:
|
||||
```javascript
|
||||
async getSysTitle({ state, commit, rootState }, { defaultTitle = '智能代码平台', defaultLogo = '' }) {
|
||||
let formModel = {
|
||||
title: defaultTitle,
|
||||
logoUrl: defaultLogo
|
||||
}
|
||||
|
||||
// ✅ 检查是否已登录(有 token)
|
||||
const token = rootState.user.token
|
||||
|
||||
if (token) {
|
||||
// 已登录,尝试从后端获取系统标题
|
||||
try {
|
||||
let res1 = await paramSetupServer.getOne('sys_title')
|
||||
if (res1 && res1.data) {
|
||||
formModel.title = res1.data.value
|
||||
document.title = res1.data.value
|
||||
}
|
||||
let res2 = await paramSetupServer.getOne('sys_logo')
|
||||
if (res2 && res2.data) {
|
||||
formModel.logoUrl = res2.data.value
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('获取系统标题失败,使用默认标题:', error || '接口调用失败')
|
||||
document.title = formModel.title
|
||||
}
|
||||
} else {
|
||||
// 未登录,直接使用默认标题
|
||||
document.title = formModel.title
|
||||
}
|
||||
|
||||
commit('setSysTitle', formModel)
|
||||
}
|
||||
```
|
||||
|
||||
## 📊 修复后的流程
|
||||
|
||||
### 未登录时
|
||||
|
||||
```
|
||||
应用启动
|
||||
↓
|
||||
Vue 实例 mounted
|
||||
↓
|
||||
检查 token
|
||||
↓
|
||||
token 不存在 ✅
|
||||
↓
|
||||
直接设置默认标题 "Demo 管理系统"
|
||||
↓
|
||||
不调用后端接口 ✅
|
||||
```
|
||||
|
||||
### 已登录时
|
||||
|
||||
```
|
||||
应用启动
|
||||
↓
|
||||
Vue 实例 mounted
|
||||
↓
|
||||
检查 token
|
||||
↓
|
||||
token 存在 ✅
|
||||
↓
|
||||
调用 getSysTitle action
|
||||
↓
|
||||
检查 rootState.user.token
|
||||
↓
|
||||
token 存在 ✅
|
||||
↓
|
||||
调用后端接口获取系统标题
|
||||
↓
|
||||
成功:使用后端返回的标题
|
||||
失败:使用默认标题
|
||||
```
|
||||
|
||||
### 登录流程
|
||||
|
||||
```
|
||||
用户登录
|
||||
↓
|
||||
保存 token 到 store
|
||||
↓
|
||||
刷新页面
|
||||
↓
|
||||
应用重新启动
|
||||
↓
|
||||
Vue 实例 mounted
|
||||
↓
|
||||
检查 token(此时有 token)✅
|
||||
↓
|
||||
调用 getSysTitle 获取系统标题
|
||||
```
|
||||
|
||||
## 🎯 优化点
|
||||
|
||||
### 1. 双重检查
|
||||
|
||||
**第一层检查**(`main.js`):
|
||||
```javascript
|
||||
const token = this.$store.state.user.token
|
||||
if (token) {
|
||||
this.$store.dispatch('app/getSysTitle', ...)
|
||||
}
|
||||
```
|
||||
|
||||
**第二层检查**(`app.js`):
|
||||
```javascript
|
||||
const token = rootState.user.token
|
||||
if (token) {
|
||||
// 调用接口
|
||||
}
|
||||
```
|
||||
|
||||
**为什么需要双重检查?**
|
||||
- 第一层:避免不必要的 action 调用
|
||||
- 第二层:防止 action 被其他地方调用时没有检查 token
|
||||
|
||||
### 2. 默认标题处理
|
||||
|
||||
**未登录时**:
|
||||
```javascript
|
||||
document.title = 'Demo 管理系统'
|
||||
```
|
||||
|
||||
**已登录但接口失败时**:
|
||||
```javascript
|
||||
document.title = formModel.title // 使用传入的 defaultTitle
|
||||
```
|
||||
|
||||
**已登录且接口成功时**:
|
||||
```javascript
|
||||
document.title = res1.data.value // 使用后端返回的标题
|
||||
```
|
||||
|
||||
### 3. 错误处理
|
||||
|
||||
```javascript
|
||||
try {
|
||||
// 调用接口
|
||||
} catch (error) {
|
||||
console.warn('获取系统标题失败,使用默认标题:', error || '接口调用失败')
|
||||
document.title = formModel.title
|
||||
}
|
||||
```
|
||||
|
||||
即使接口调用失败,也会优雅降级到默认标题,不影响用户使用。
|
||||
|
||||
## ✅ 验证清单
|
||||
|
||||
### 未登录场景
|
||||
|
||||
- ✅ 访问首页显示登录页面
|
||||
- ✅ 页面标题显示 "Demo 管理系统"
|
||||
- ✅ 控制台没有接口报错
|
||||
- ✅ Network 标签中没有 `sys_parameter/key` 请求
|
||||
|
||||
### 已登录场景
|
||||
|
||||
- ✅ 登录成功后刷新页面
|
||||
- ✅ 页面标题显示系统标题(如果后端有配置)
|
||||
- ✅ 如果后端接口失败,显示默认标题
|
||||
- ✅ 系统 Logo 正确显示(如果后端有配置)
|
||||
|
||||
### 登录流程
|
||||
|
||||
- ✅ 输入用户名密码
|
||||
- ✅ 点击登录
|
||||
- ✅ 登录成功
|
||||
- ✅ 页面刷新
|
||||
- ✅ 进入系统首页
|
||||
- ✅ 系统标题正确显示
|
||||
|
||||
## 💡 最佳实践
|
||||
|
||||
### 1. 接口调用前检查认证状态
|
||||
|
||||
```javascript
|
||||
// ✅ 好的做法
|
||||
if (token) {
|
||||
await api.getData()
|
||||
}
|
||||
|
||||
// ❌ 不好的做法
|
||||
try {
|
||||
await api.getData() // 可能因为没有 token 而失败
|
||||
} catch (error) {
|
||||
// 处理错误
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 提供默认值
|
||||
|
||||
```javascript
|
||||
// ✅ 好的做法
|
||||
const title = res.data?.value || defaultTitle
|
||||
|
||||
// ❌ 不好的做法
|
||||
const title = res.data.value // 可能是 undefined
|
||||
```
|
||||
|
||||
### 3. 优雅降级
|
||||
|
||||
```javascript
|
||||
// ✅ 好的做法
|
||||
if (token) {
|
||||
try {
|
||||
const data = await api.getData()
|
||||
useData(data)
|
||||
} catch (error) {
|
||||
useDefaultData()
|
||||
}
|
||||
} else {
|
||||
useDefaultData()
|
||||
}
|
||||
```
|
||||
|
||||
### 4. 避免不必要的请求
|
||||
|
||||
```javascript
|
||||
// ✅ 好的做法
|
||||
if (needData && token) {
|
||||
await api.getData()
|
||||
}
|
||||
|
||||
// ❌ 不好的做法
|
||||
await api.getData() // 无论是否需要都请求
|
||||
```
|
||||
|
||||
## 🔧 相关修改
|
||||
|
||||
### 其他可能需要检查的地方
|
||||
|
||||
如果项目中还有其他在应用启动时调用的接口,也需要检查是否需要 token:
|
||||
|
||||
1. **检查 `main.js` 的 `mounted` 钩子**
|
||||
- 确保所有接口调用都检查了 token
|
||||
|
||||
2. **检查 `App.vue` 的 `mounted` 钩子**
|
||||
- 确保没有未登录时调用需要认证的接口
|
||||
|
||||
3. **检查路由守卫**
|
||||
- 确保路由守卫正确处理未登录的情况
|
||||
|
||||
4. **检查 Vuex actions**
|
||||
- 确保需要认证的 actions 都检查了 token
|
||||
|
||||
## 📝 总结
|
||||
|
||||
### 修复的问题
|
||||
|
||||
- ✅ 未登录时不再调用需要认证的接口
|
||||
- ✅ 避免了接口报错
|
||||
- ✅ 提供了优雅的降级方案
|
||||
- ✅ 改善了用户体验
|
||||
|
||||
### 代码改进
|
||||
|
||||
- **更健壮**:双重检查确保不会在未登录时调用接口
|
||||
- **更友好**:即使接口失败也能正常显示默认标题
|
||||
- **更清晰**:代码逻辑更容易理解和维护
|
||||
|
||||
---
|
||||
|
||||
**未登录接口调用问题已修复!** 🎉
|
||||
|
||||
现在系统会智能判断是否已登录,只在必要时调用后端接口。
|
||||
|
||||
@@ -1,575 +0,0 @@
|
||||
# 权限菜单组件配置指南
|
||||
|
||||
## 🎯 核心问题
|
||||
|
||||
**问题**:后端权限菜单接口返回的组件路径需要映射到实际组件,否则会显示 404。
|
||||
|
||||
**解决**:在 `Vue.use()` 时传入 `componentMap` 参数,一次性注册所有业务组件。
|
||||
|
||||
## 📝 配置步骤
|
||||
|
||||
### 第一步:查看权限菜单接口返回的组件路径
|
||||
|
||||
后端接口返回的菜单数据示例:
|
||||
```json
|
||||
{
|
||||
"code": 0,
|
||||
"data": [
|
||||
{ "component": "home/index.vue" },
|
||||
{ "component": "system/sys_user.vue" },
|
||||
{ "component": "ball/games.vue" },
|
||||
{ "component": "order/pay_orders.vue" },
|
||||
{ "component": "ball/wch_users.vue" }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 第二步:创建对应的业务组件
|
||||
|
||||
在项目中创建对应的组件文件:
|
||||
```
|
||||
src/views/
|
||||
├── ball/
|
||||
│ ├── games.vue
|
||||
│ └── wch_users.vue
|
||||
└── order/
|
||||
└── pay_orders.vue
|
||||
```
|
||||
|
||||
### 第三步:在 main.js 中配置组件映射
|
||||
|
||||
```javascript
|
||||
import Vue from 'vue'
|
||||
import AdminFramework from '../../src/index.js'
|
||||
import config from './config'
|
||||
|
||||
// ✅ 导入所有业务组件(根据权限菜单接口的 component 字段)
|
||||
import GamesComponent from './views/ball/games.vue'
|
||||
import PayOrdersComponent from './views/order/pay_orders.vue'
|
||||
import WchUsersComponent from './views/ball/wch_users.vue'
|
||||
|
||||
// 使用框架
|
||||
Vue.use(AdminFramework, {
|
||||
config,
|
||||
ViewUI,
|
||||
VueRouter,
|
||||
Vuex,
|
||||
createPersistedState,
|
||||
// ✅ 一次性注册所有组件映射
|
||||
componentMap: {
|
||||
'ball/games': GamesComponent,
|
||||
'order/pay_orders': PayOrdersComponent,
|
||||
'ball/wch_users': WchUsersComponent
|
||||
// 添加更多业务组件...
|
||||
}
|
||||
})
|
||||
|
||||
// 创建 Vue 实例
|
||||
new Vue({
|
||||
el: '#app',
|
||||
router: AdminFramework.router,
|
||||
store: AdminFramework.store,
|
||||
render: h => h(App),
|
||||
mounted() {
|
||||
// 设置权限菜单(会自动加载映射的组件)
|
||||
this.$store.dispatch('user/setAuthorityMenus', {
|
||||
Main: AdminFramework.Main,
|
||||
ParentView: AdminFramework.ParentView,
|
||||
Page404: AdminFramework.Page404
|
||||
})
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
## 🔧 两种配置方式
|
||||
|
||||
### 方式一:在 Vue.use 时配置(推荐)
|
||||
|
||||
```javascript
|
||||
Vue.use(AdminFramework, {
|
||||
config,
|
||||
ViewUI,
|
||||
VueRouter,
|
||||
Vuex,
|
||||
createPersistedState,
|
||||
componentMap: {
|
||||
'ball/games': GamesComponent,
|
||||
'order/pay_orders': PayOrdersComponent
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
**优点**:
|
||||
- ✅ 集中配置,清晰明了
|
||||
- ✅ 在框架初始化时就完成映射
|
||||
- ✅ 代码简洁
|
||||
|
||||
### 方式二:使用 addComponentMap 方法
|
||||
|
||||
```javascript
|
||||
Vue.use(AdminFramework, { config, ViewUI, VueRouter, Vuex, createPersistedState })
|
||||
|
||||
// 之后添加映射
|
||||
AdminFramework.addComponentMap({
|
||||
'ball/games': GamesComponent,
|
||||
'order/pay_orders': PayOrdersComponent
|
||||
})
|
||||
```
|
||||
|
||||
**适用场景**:
|
||||
- 需要动态添加组件映射
|
||||
- 分模块配置
|
||||
|
||||
## 📋 完整示例
|
||||
|
||||
### 根据你的权限菜单接口配置
|
||||
|
||||
查看你的接口返回数据,列出所有 `component` 字段:
|
||||
|
||||
```javascript
|
||||
// 权限菜单返回的组件列表:
|
||||
// - home/index.vue
|
||||
// - system/sys_user.vue
|
||||
// - system/sys_role.vue
|
||||
// - system_high/sys_menu.vue
|
||||
// - system/sys_log.vue
|
||||
// - system/sys_param_setup.vue
|
||||
// - ball/games.vue ← 业务组件,需要自己创建
|
||||
// - ball/wch_users.vue ← 业务组件,需要自己创建
|
||||
// - ball/venues.vue ← 业务组件,需要自己创建
|
||||
// - order/pay_orders.vue ← 业务组件,需要自己创建
|
||||
// - order/wch_wallets.vue ← 业务组件,需要自己创建
|
||||
// - users/user_follows.vue ← 业务组件,需要自己创建
|
||||
```
|
||||
|
||||
### 完整的 main.js 配置
|
||||
|
||||
```javascript
|
||||
import Vue from 'vue'
|
||||
import VueRouter from 'vue-router'
|
||||
import Vuex from 'vuex'
|
||||
import ViewUI from 'view-design'
|
||||
import createPersistedState from 'vuex-persistedstate'
|
||||
import 'view-design/dist/styles/iview.css'
|
||||
|
||||
import AdminFramework from '../../src/index.js'
|
||||
import config from './config'
|
||||
import App from './App.vue'
|
||||
|
||||
// ==================== 导入业务组件 ====================
|
||||
// 球局模块
|
||||
import GamesComponent from './views/ball/games.vue'
|
||||
import WchUsersComponent from './views/ball/wch_users.vue'
|
||||
import VenuesComponent from './views/ball/venues.vue'
|
||||
import GameParticipantsComponent from './views/ball/game_participants.vue'
|
||||
import GameCommentsComponent from './views/ball/game_comments.vue'
|
||||
|
||||
// 订单模块
|
||||
import PayOrdersComponent from './views/order/pay_orders.vue'
|
||||
import WchWalletsComponent from './views/order/wch_wallets.vue'
|
||||
import WalletTransactionsComponent from './views/order/wallet_transactions.vue'
|
||||
import TransferDetailsComponent from './views/order/transfer_details.vue'
|
||||
import FrozenFundsComponent from './views/order/frozen_funds.vue'
|
||||
|
||||
// 用户模块
|
||||
import UserFollowsComponent from './views/users/user_follows.vue'
|
||||
import RecommendBlocksComponent from './views/users/recommend_blocks.vue'
|
||||
import UserTrackingComponent from './views/users/user_tracking.vue'
|
||||
|
||||
// 资源模块
|
||||
import ResourcesComponent from './views/statistics/resources.vue'
|
||||
import NtrQuestionsComponent from './views/ntrp/ntr_questions.vue'
|
||||
import NtrRecordsComponent from './views/ntrp/ntr_records.vue'
|
||||
|
||||
// 消息模块
|
||||
import MsgNotificationsComponent from './views/message/msg_notifications.vue'
|
||||
|
||||
// 使用框架
|
||||
Vue.use(AdminFramework, {
|
||||
config,
|
||||
ViewUI,
|
||||
VueRouter,
|
||||
Vuex,
|
||||
createPersistedState,
|
||||
// ✅ 组件映射表
|
||||
componentMap: {
|
||||
// 球局模块
|
||||
'ball/games': GamesComponent,
|
||||
'ball/wch_users': WchUsersComponent,
|
||||
'ball/venues': VenuesComponent,
|
||||
'ball/game_participants': GameParticipantsComponent,
|
||||
'ball/game_comments': GameCommentsComponent,
|
||||
|
||||
// 订单模块
|
||||
'order/pay_orders': PayOrdersComponent,
|
||||
'order/wch_wallets': WchWalletsComponent,
|
||||
'order/wallet_transactions': WalletTransactionsComponent,
|
||||
'order/transfer_details': TransferDetailsComponent,
|
||||
'order/frozen_funds': FrozenFundsComponent,
|
||||
|
||||
// 用户模块
|
||||
'users/user_follows': UserFollowsComponent,
|
||||
'users/recommend_blocks': RecommendBlocksComponent,
|
||||
'users/user_tracking': UserTrackingComponent,
|
||||
|
||||
// 资源模块
|
||||
'statistics/resources': ResourcesComponent,
|
||||
'ntrp/ntr_questions': NtrQuestionsComponent,
|
||||
'ntrp/ntr_records': NtrRecordsComponent,
|
||||
|
||||
// 消息模块
|
||||
'message/msg_notifications': MsgNotificationsComponent
|
||||
}
|
||||
})
|
||||
|
||||
// 创建 Vue 实例
|
||||
new Vue({
|
||||
el: '#app',
|
||||
router: AdminFramework.router,
|
||||
store: AdminFramework.store,
|
||||
render: h => h(App),
|
||||
mounted() {
|
||||
AdminFramework.uiTool.setRem()
|
||||
|
||||
// 设置权限菜单(会自动根据映射表加载组件)
|
||||
this.$store.dispatch('user/setAuthorityMenus', {
|
||||
Main: AdminFramework.Main,
|
||||
ParentView: AdminFramework.ParentView,
|
||||
Page404: AdminFramework.Page404
|
||||
})
|
||||
|
||||
// 获取系统标题
|
||||
this.$store.dispatch('app/getSysTitle', {
|
||||
defaultTitle: 'Demo 管理系统',
|
||||
defaultLogo: ''
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
// 响应式适配
|
||||
window.addEventListener('load', AdminFramework.uiTool.setRem)
|
||||
window.addEventListener('resize', AdminFramework.uiTool.setRem)
|
||||
```
|
||||
|
||||
## 🎯 关键要点
|
||||
|
||||
### 1. 路径命名规则
|
||||
|
||||
**后端返回**:
|
||||
```json
|
||||
{ "component": "ball/games.vue" }
|
||||
```
|
||||
|
||||
**项目文件**:
|
||||
```
|
||||
src/views/ball/games.vue
|
||||
```
|
||||
|
||||
**配置映射**(不需要 .vue 后缀):
|
||||
```javascript
|
||||
componentMap: {
|
||||
'ball/games': GamesComponent // ✅ 不需要写 .vue
|
||||
}
|
||||
```
|
||||
|
||||
**框架会自动处理**:
|
||||
- `ball/games` ✅
|
||||
- `ball/games.vue` ✅
|
||||
|
||||
### 2. 框架内置组件
|
||||
|
||||
以下组件**无需配置**,框架已自动映射:
|
||||
- `home/index.vue` → HomePage
|
||||
- `system/sys_user.vue` → SysUser
|
||||
- `system/sys_role.vue` → SysRole
|
||||
- `system/sys_log.vue` → SysLog
|
||||
- `system/sys_param_setup.vue` → SysParamSetup
|
||||
- `system_high/sys_menu.vue` → SysMenu
|
||||
- `system_high/sys_control.vue` → SysControl
|
||||
- `system_high/sys_title.vue` → SysTitle
|
||||
|
||||
### 3. 业务组件
|
||||
|
||||
需要在项目中:
|
||||
1. **创建组件文件**
|
||||
2. **在 main.js 中导入**
|
||||
3. **在 componentMap 中配置**
|
||||
|
||||
## ⚠️ 组件未找到的提示
|
||||
|
||||
如果后端返回的组件路径没有在映射表中,会显示:
|
||||
|
||||
```
|
||||
┌────────────────────────────────────┐
|
||||
│ ⚠️ 警告 │
|
||||
│ 页面组件未加载: ball/games.vue │
|
||||
│ 请在项目中创建此组件或在组件映射表中注册 │
|
||||
└────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**控制台也会输出**:
|
||||
```
|
||||
⚠️ 组件未找到: ball/games.vue,使用占位组件
|
||||
```
|
||||
|
||||
## 💡 快速配置技巧
|
||||
|
||||
### 技巧1:批量导入组件
|
||||
|
||||
```javascript
|
||||
// 按模块组织导入
|
||||
// 球局模块
|
||||
import GamesComponent from './views/ball/games.vue'
|
||||
import WchUsersComponent from './views/ball/wch_users.vue'
|
||||
|
||||
// 订单模块
|
||||
import PayOrdersComponent from './views/order/pay_orders.vue'
|
||||
import WchWalletsComponent from './views/order/wch_wallets.vue'
|
||||
```
|
||||
|
||||
### 技巧2:使用对象展开
|
||||
|
||||
```javascript
|
||||
// 定义模块组件映射
|
||||
const ballComponents = {
|
||||
'ball/games': GamesComponent,
|
||||
'ball/wch_users': WchUsersComponent
|
||||
}
|
||||
|
||||
const orderComponents = {
|
||||
'order/pay_orders': PayOrdersComponent,
|
||||
'order/wch_wallets': WchWalletsComponent
|
||||
}
|
||||
|
||||
// 合并配置
|
||||
Vue.use(AdminFramework, {
|
||||
config,
|
||||
ViewUI,
|
||||
VueRouter,
|
||||
Vuex,
|
||||
createPersistedState,
|
||||
componentMap: {
|
||||
...ballComponents,
|
||||
...orderComponents
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### 技巧3:注释待开发的组件
|
||||
|
||||
```javascript
|
||||
componentMap: {
|
||||
'ball/games': GamesComponent, // ✅ 已开发
|
||||
// 'ball/venues': VenuesComponent, // ⏳ 待开发
|
||||
// 'order/pay_orders': PayOrdersComponent // ⏳ 待开发
|
||||
}
|
||||
```
|
||||
|
||||
## 📊 配置流程图
|
||||
|
||||
```
|
||||
后端接口返回菜单
|
||||
↓
|
||||
查看所有 component 字段
|
||||
↓
|
||||
在项目中创建对应的 .vue 文件
|
||||
↓
|
||||
在 main.js 中导入组件
|
||||
↓
|
||||
在 componentMap 中配置映射
|
||||
↓
|
||||
框架自动生成路由
|
||||
↓
|
||||
页面正常显示 ✅
|
||||
```
|
||||
|
||||
## 🚀 实战示例
|
||||
|
||||
### 示例:添加"球局管理"页面
|
||||
|
||||
#### 1. 后端返回的菜单
|
||||
```json
|
||||
{
|
||||
"id": 123,
|
||||
"name": "球局表",
|
||||
"path": "games",
|
||||
"component": "ball/games.vue",
|
||||
"type": "页面"
|
||||
}
|
||||
```
|
||||
|
||||
#### 2. 创建组件文件
|
||||
```vue
|
||||
<!-- src/views/ball/games.vue -->
|
||||
<template>
|
||||
<div class="games-page">
|
||||
<h2>球局管理</h2>
|
||||
<Table :columns="columns" :data="list" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'games',
|
||||
data() {
|
||||
return {
|
||||
list: [],
|
||||
columns: []
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.getData()
|
||||
},
|
||||
methods: {
|
||||
async getData() {
|
||||
const res = await this.$http.post('ball/games/list', {})
|
||||
this.list = res.data
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
#### 3. 在 main.js 中配置
|
||||
```javascript
|
||||
// 导入
|
||||
import GamesComponent from './views/ball/games.vue'
|
||||
|
||||
// 配置映射
|
||||
Vue.use(AdminFramework, {
|
||||
config,
|
||||
ViewUI,
|
||||
VueRouter,
|
||||
Vuex,
|
||||
createPersistedState,
|
||||
componentMap: {
|
||||
'ball/games': GamesComponent // ✅ 添加映射
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
#### 4. 完成!
|
||||
|
||||
登录后,权限菜单中的"球局表"会自动加载 `GamesComponent` 组件。
|
||||
|
||||
## 📚 框架自动处理
|
||||
|
||||
### 已自动映射的系统组件
|
||||
|
||||
| 后端路径 | 组件 | 说明 |
|
||||
|---------|------|------|
|
||||
| `home/index.vue` | HomePage | 主页 |
|
||||
| `system/sys_user.vue` | SysUser | 用户管理 |
|
||||
| `system/sys_role.vue` | SysRole | 角色管理 |
|
||||
| `system/sys_log.vue` | SysLog | 日志管理 |
|
||||
| `system/sys_param_setup.vue` | SysParamSetup | 参数设置 |
|
||||
| `system_high/sys_menu.vue` | SysMenu | 菜单管理 |
|
||||
| `system_high/sys_control.vue` | SysControl | 控制器管理 |
|
||||
| `system_high/sys_title.vue` | SysTitle | 系统标题设置 |
|
||||
|
||||
### 需要手动配置的业务组件
|
||||
|
||||
根据你的权限菜单接口,需要配置:
|
||||
- ✅ `ball/games.vue` - 球局表
|
||||
- ✅ `ball/wch_users.vue` - 微信用户
|
||||
- ✅ `ball/venues.vue` - 场地表
|
||||
- ✅ `ball/game_participants.vue` - 球局参与者
|
||||
- ✅ `ball/game_comments.vue` - 球局评论
|
||||
- ✅ `order/pay_orders.vue` - 支付订单
|
||||
- ✅ `order/wch_wallets.vue` - 用户钱包
|
||||
- ✅ `order/wallet_transactions.vue` - 交易记录
|
||||
- ✅ `order/transfer_details.vue` - 转账详情
|
||||
- ✅ `order/frozen_funds.vue` - 冻结资金
|
||||
- ✅ `users/user_follows.vue` - 用户关注
|
||||
- ✅ `users/recommend_blocks.vue` - 推荐屏蔽
|
||||
- ✅ `users/user_tracking.vue` - 行为追踪
|
||||
- ✅ `statistics/resources.vue` - 图库资源表
|
||||
- ✅ `ntrp/ntr_questions.vue` - 题库管理
|
||||
- ✅ `ntrp/ntr_records.vue` - 测试记录
|
||||
- ✅ `message/msg_notifications.vue` - 消息管理
|
||||
- ✅ `system/wch_professions.vue` - 职业管理
|
||||
- ✅ `system/wch_cities.vue` - 城市管理
|
||||
- ✅ `business/hot_city_qr.vue` - 热门城市
|
||||
|
||||
## 🎯 推荐配置模板
|
||||
|
||||
```javascript
|
||||
import Vue from 'vue'
|
||||
import VueRouter from 'vue-router'
|
||||
import Vuex from 'vuex'
|
||||
import ViewUI from 'view-design'
|
||||
import createPersistedState from 'vuex-persistedstate'
|
||||
import 'view-design/dist/styles/iview.css'
|
||||
|
||||
import AdminFramework from '../../src/index.js'
|
||||
import config from './config'
|
||||
import App from './App.vue'
|
||||
|
||||
// ==================== 导入业务组件 ====================
|
||||
// TODO: 根据权限菜单接口返回的 component 字段,导入对应组件
|
||||
|
||||
// 使用框架
|
||||
Vue.use(AdminFramework, {
|
||||
config,
|
||||
ViewUI,
|
||||
VueRouter,
|
||||
Vuex,
|
||||
createPersistedState,
|
||||
componentMap: {
|
||||
// TODO: 在这里添加业务组件映射
|
||||
// 格式:'后端返回的路径(不含.vue)': 导入的组件
|
||||
}
|
||||
})
|
||||
|
||||
// 创建 Vue 实例
|
||||
new Vue({
|
||||
el: '#app',
|
||||
router: AdminFramework.router,
|
||||
store: AdminFramework.store,
|
||||
render: h => h(App),
|
||||
mounted() {
|
||||
AdminFramework.uiTool.setRem()
|
||||
|
||||
this.$store.dispatch('user/setAuthorityMenus', {
|
||||
Main: AdminFramework.Main,
|
||||
ParentView: AdminFramework.ParentView,
|
||||
Page404: AdminFramework.Page404
|
||||
})
|
||||
|
||||
this.$store.dispatch('app/getSysTitle', {
|
||||
defaultTitle: config.title,
|
||||
defaultLogo: ''
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
window.addEventListener('load', AdminFramework.uiTool.setRem)
|
||||
window.addEventListener('resize', AdminFramework.uiTool.setRem)
|
||||
```
|
||||
|
||||
## 📝 总结
|
||||
|
||||
### ✅ 优化后的优势
|
||||
|
||||
1. **代码简洁**
|
||||
- 不需要重复写 `.vue` 后缀的映射
|
||||
- 框架自动生成
|
||||
|
||||
2. **集中配置**
|
||||
- 在 `Vue.use()` 时一次性配置所有映射
|
||||
- 清晰明了
|
||||
|
||||
3. **灵活扩展**
|
||||
- 可以随时使用 `addComponentMap()` 添加新映射
|
||||
- 支持分模块配置
|
||||
|
||||
### 🔗 相关文档
|
||||
|
||||
- [../README.md](../README.md) - 项目说明
|
||||
- [../CHANGELOG.md](../CHANGELOG.md) - 更新日志
|
||||
- [../../完整使用文档.md](../../完整使用文档.md) - 框架文档
|
||||
|
||||
---
|
||||
|
||||
**配置完成后,重启项目即可看到所有权限菜单中的页面!** 🎉
|
||||
|
||||
@@ -1,326 +0,0 @@
|
||||
# 权限菜单处理说明
|
||||
|
||||
## 📊 当前情况
|
||||
|
||||
### 登录接口返回的数据
|
||||
```json
|
||||
{
|
||||
"code": 0,
|
||||
"message": "请求成功",
|
||||
"data": {
|
||||
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
|
||||
"user": {
|
||||
"id": 1,
|
||||
"name": "zc",
|
||||
"roleId": 6
|
||||
},
|
||||
"authorityMenus": "[1,142,121,143,144,145,124,147,120,123,125,132,133,126,136,137,138,139,151,152,153,154,149,150,156,157,158,122,159,5,11,12,13,67,68,15]"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 问题分析
|
||||
|
||||
**登录接口返回的 `authorityMenus`**:
|
||||
- 类型:字符串
|
||||
- 内容:菜单 ID 数组 `"[1,142,121,...]"`
|
||||
- 作用:表示用户有权限访问的菜单 ID 列表
|
||||
|
||||
**框架期望的 `authorityMenus`**:
|
||||
- 类型:对象数组
|
||||
- 内容:完整的菜单对象,包含以下字段:
|
||||
```javascript
|
||||
{
|
||||
id: 1,
|
||||
name: "系统管理",
|
||||
path: "/system",
|
||||
component: "...",
|
||||
parent_id: 0,
|
||||
type: "菜单",
|
||||
is_show_menu: 1,
|
||||
children: [...]
|
||||
}
|
||||
```
|
||||
|
||||
## 🔧 解决方案
|
||||
|
||||
### 方案一:调用 authorityMenus 接口(当前实现)
|
||||
|
||||
登录成功后,调用 `/sys_user/authorityMenus` 接口获取完整的菜单对象数组。
|
||||
|
||||
**流程:**
|
||||
1. 用户登录 → 获取 token 和菜单 ID 列表
|
||||
2. 保存 token
|
||||
3. 调用 `authorityMenus` 接口 → 获取完整菜单对象数组
|
||||
4. 生成路由
|
||||
|
||||
**代码实现:**
|
||||
```javascript
|
||||
// src/store/user.js
|
||||
async handleLogin({ state, commit, dispatch }, { userFrom, Main, ParentView, Page404 }) {
|
||||
let res = await userServerInstance.login(userFrom)
|
||||
|
||||
let token = res.data.token
|
||||
let user = res.data.user
|
||||
|
||||
commit('setUserName', user.name.trim())
|
||||
commit('setToken', token)
|
||||
|
||||
// 调用 authorityMenus 接口获取完整菜单数据
|
||||
await dispatch('setAuthorityMenus', { Main, ParentView, Page404 })
|
||||
}
|
||||
```
|
||||
|
||||
**优点:**
|
||||
- 简单直接
|
||||
- 不需要修改后端接口
|
||||
|
||||
**缺点:**
|
||||
- 需要额外的 HTTP 请求
|
||||
- 如果 authorityMenus 接口失败,用户无法进入系统
|
||||
|
||||
### 方案二:后端直接返回完整菜单对象(推荐)
|
||||
|
||||
修改后端登录接口,直接返回完整的菜单对象数组,而不是菜单 ID 数组。
|
||||
|
||||
**修改后的返回数据:**
|
||||
```json
|
||||
{
|
||||
"code": 0,
|
||||
"message": "请求成功",
|
||||
"data": {
|
||||
"token": "...",
|
||||
"user": {...},
|
||||
"authorityMenus": [
|
||||
{
|
||||
"id": 1,
|
||||
"name": "系统管理",
|
||||
"path": "/system",
|
||||
"component": "Main",
|
||||
"parent_id": 0,
|
||||
"type": "菜单",
|
||||
"is_show_menu": 1,
|
||||
"children": [...]
|
||||
},
|
||||
...
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**优点:**
|
||||
- 减少 HTTP 请求
|
||||
- 登录更快
|
||||
- 数据一致性更好
|
||||
|
||||
**缺点:**
|
||||
- 需要修改后端代码
|
||||
|
||||
## 📝 当前实现
|
||||
|
||||
### 登录流程
|
||||
|
||||
1. **用户输入用户名密码**
|
||||
```javascript
|
||||
// src/views/login/login.vue
|
||||
handleSubmit({ userName, password }) {
|
||||
let userFrom = { name: userName, password: password }
|
||||
this.handleLogin({ userFrom, Main, ParentView, Page404 })
|
||||
}
|
||||
```
|
||||
|
||||
2. **调用登录接口**
|
||||
```javascript
|
||||
// src/store/user.js
|
||||
let res = await userServerInstance.login(userFrom)
|
||||
// 返回: { code: 0, data: { token, user, authorityMenus: "[1,2,3...]" } }
|
||||
```
|
||||
|
||||
3. **保存 token 和用户信息**
|
||||
```javascript
|
||||
commit('setUserName', user.name.trim())
|
||||
commit('setToken', token)
|
||||
```
|
||||
|
||||
4. **获取完整菜单数据**
|
||||
```javascript
|
||||
// 调用 /sys_user/authorityMenus 接口
|
||||
await dispatch('setAuthorityMenus', { Main, ParentView, Page404 })
|
||||
```
|
||||
|
||||
5. **处理菜单数据**
|
||||
```javascript
|
||||
// src/store/user.js - setAuthorityMenus
|
||||
let res = await userServerInstance.authorityMenus()
|
||||
// 期望返回: { code: 0, data: [{id, name, path, ...}, ...] }
|
||||
|
||||
let menus = res.data
|
||||
commit('setAuthorityMenus', JSON.stringify(menus))
|
||||
|
||||
// 生成路由
|
||||
let mainMenu = uiTool.getRoutes(Main, ParentView, Page404)
|
||||
commit('setMenuList', mainMenu.children)
|
||||
```
|
||||
|
||||
6. **刷新页面进入系统**
|
||||
```javascript
|
||||
window.location.reload()
|
||||
```
|
||||
|
||||
## ⚠️ 需要确认的事项
|
||||
|
||||
### 1. authorityMenus 接口返回的数据格式
|
||||
|
||||
请确认 `/sys_user/authorityMenus` 接口返回的数据格式:
|
||||
|
||||
**期望格式:**
|
||||
```json
|
||||
{
|
||||
"code": 0,
|
||||
"message": "请求成功",
|
||||
"data": [
|
||||
{
|
||||
"id": 1,
|
||||
"name": "首页",
|
||||
"path": "/home",
|
||||
"component": "home/index",
|
||||
"parent_id": 0,
|
||||
"type": "页面",
|
||||
"is_show_menu": 1,
|
||||
"icon": "md-home",
|
||||
"sort": 1
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"name": "系统管理",
|
||||
"path": "/system",
|
||||
"component": "",
|
||||
"parent_id": 0,
|
||||
"type": "菜单",
|
||||
"is_show_menu": 1,
|
||||
"icon": "md-settings",
|
||||
"sort": 2,
|
||||
"children": [
|
||||
{
|
||||
"id": 11,
|
||||
"name": "用户管理",
|
||||
"path": "/system/user",
|
||||
"component": "system/sys_user",
|
||||
"parent_id": 5,
|
||||
"type": "页面",
|
||||
"is_show_menu": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 菜单对象的必需字段
|
||||
|
||||
每个菜单对象需要包含以下字段:
|
||||
|
||||
| 字段 | 类型 | 说明 | 必需 |
|
||||
|------|------|------|------|
|
||||
| id | Number | 菜单 ID | ✅ |
|
||||
| name | String | 菜单名称 | ✅ |
|
||||
| path | String | 路由路径 | ✅ |
|
||||
| component | String | 组件路径 | ✅ |
|
||||
| parent_id | Number | 父菜单 ID(0 表示顶级菜单) | ✅ |
|
||||
| type | String | 类型:"菜单"、"页面"、"功能" | ✅ |
|
||||
| is_show_menu | Number | 是否显示在菜单中(1=显示,0=隐藏) | ✅ |
|
||||
| icon | String | 图标名称 | ❌ |
|
||||
| sort | Number | 排序 | ❌ |
|
||||
| children | Array | 子菜单 | ❌ |
|
||||
|
||||
## 🐛 调试方法
|
||||
|
||||
### 1. 查看 authorityMenus 接口返回
|
||||
|
||||
在浏览器控制台查看:
|
||||
```javascript
|
||||
// 登录成功后,查看控制台日志
|
||||
获取权限菜单返回: { code: 0, data: [...] }
|
||||
处理权限菜单: [...]
|
||||
生成的主菜单: { path: '/', children: [...] }
|
||||
```
|
||||
|
||||
### 2. 查看 localStorage
|
||||
|
||||
打开浏览器开发者工具 → Application → Local Storage:
|
||||
```javascript
|
||||
authorityMenus: "[{\"id\":1,\"name\":\"首页\",...}]"
|
||||
```
|
||||
|
||||
### 3. 查看网络请求
|
||||
|
||||
打开浏览器开发者工具 → Network:
|
||||
1. 查看 `/sys_user/login` 请求的响应
|
||||
2. 查看 `/sys_user/authorityMenus` 请求的响应
|
||||
|
||||
## 🎯 测试步骤
|
||||
|
||||
### 1. 测试登录
|
||||
```bash
|
||||
# 启动项目
|
||||
cd demo-project
|
||||
npm run dev
|
||||
|
||||
# 打开浏览器
|
||||
http://localhost:8080
|
||||
|
||||
# 输入用户名密码
|
||||
用户名: zc
|
||||
密码: ***
|
||||
|
||||
# 点击登录
|
||||
```
|
||||
|
||||
### 2. 查看控制台日志
|
||||
应该看到:
|
||||
```
|
||||
登录接口返回: { code: 0, data: { token, user, authorityMenus } }
|
||||
登录返回的菜单 IDs: "[1,142,121,...]"
|
||||
获取权限菜单返回: { code: 0, data: [...] }
|
||||
处理权限菜单: [...]
|
||||
生成的主菜单: { path: '/', children: [...] }
|
||||
```
|
||||
|
||||
### 3. 验证登录成功
|
||||
- ✅ 显示"登录成功!"提示
|
||||
- ✅ 页面刷新
|
||||
- ✅ 进入系统首页
|
||||
- ✅ 左侧显示菜单
|
||||
|
||||
## 💡 建议
|
||||
|
||||
### 短期方案(当前实现)
|
||||
保持现有实现,登录后调用 `authorityMenus` 接口获取完整菜单数据。
|
||||
|
||||
### 长期方案(推荐)
|
||||
建议后端修改登录接口,直接返回完整的菜单对象数组,减少 HTTP 请求,提升用户体验。
|
||||
|
||||
**修改建议:**
|
||||
```javascript
|
||||
// 后端登录接口返回
|
||||
{
|
||||
"code": 0,
|
||||
"message": "请求成功",
|
||||
"data": {
|
||||
"token": "...",
|
||||
"user": {...},
|
||||
"authorityMenus": [
|
||||
// 完整的菜单对象数组,而不是 ID 数组字符串
|
||||
{ id: 1, name: "首页", path: "/home", ... },
|
||||
{ id: 5, name: "系统管理", path: "/system", children: [...] }
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
这样前端就可以直接使用,不需要额外的 HTTP 请求。
|
||||
|
||||
---
|
||||
|
||||
**当前状态**:登录功能正常,需要确认 `authorityMenus` 接口返回的数据格式是否正确。
|
||||
|
||||
@@ -1,277 +0,0 @@
|
||||
# 登录功能修复说明
|
||||
|
||||
## 🔧 修复的问题
|
||||
|
||||
### 问题描述
|
||||
登录时出现错误:`Cannot read properties of undefined (reading 'message')`
|
||||
|
||||
### 根本原因
|
||||
登录接口返回的数据结构与代码中期望的不一致。
|
||||
|
||||
## 📊 数据结构分析
|
||||
|
||||
### 后端返回的数据结构
|
||||
```json
|
||||
{
|
||||
"code": 0,
|
||||
"message": "请求成功",
|
||||
"data": {
|
||||
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
|
||||
"user": {
|
||||
"id": 1,
|
||||
"name": "zc",
|
||||
"password": "d3df61764ee9a26091f714b88958caef",
|
||||
"roleId": 6,
|
||||
"create_time": "2022-10-24 05:45:40",
|
||||
"last_modify_time": "2025-09-12 06:48:54"
|
||||
},
|
||||
"authorityMenus": "[1,142,121,143,144,145,124,147,120,123,125,132,133,126,136,137,138,139,151,152,153,154,149,150,156,157,158,122,159,5,11,12,13,67,68,15]"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### HTTP 工具类的处理流程
|
||||
|
||||
1. **axios 响应拦截器**(`src/utils/http.js` 第 55-69 行)
|
||||
```javascript
|
||||
instance.interceptors.response.use(
|
||||
response => {
|
||||
if (response.status === 200) {
|
||||
if (response.data && response.data.code === 0) {
|
||||
return response // 返回整个 response 对象
|
||||
} else {
|
||||
// code 不为 0,拒绝
|
||||
return Promise.reject(response.data.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
```
|
||||
|
||||
2. **http.post 方法**(`src/utils/http.js` 第 176-191 行)
|
||||
```javascript
|
||||
async post(url, param, config) {
|
||||
let instance = this.getHttpInstance(config)
|
||||
let promise = new Promise((resolve, reject) => {
|
||||
instance.post(url, param).then(response => {
|
||||
resolve(response.data) // 返回 response.data
|
||||
})
|
||||
})
|
||||
return promise
|
||||
}
|
||||
```
|
||||
|
||||
3. **最终在 user.js 中得到的数据结构**
|
||||
```javascript
|
||||
let res = await userServerInstance.login(userFrom)
|
||||
// res 的结构是:
|
||||
// {
|
||||
// code: 0,
|
||||
// message: "请求成功",
|
||||
// data: { token, user, authorityMenus }
|
||||
// }
|
||||
```
|
||||
|
||||
## ✅ 修复方案
|
||||
|
||||
### 1. 修复 `src/store/user.js` 中的 `handleLogin` 方法
|
||||
|
||||
**修复前:**
|
||||
```javascript
|
||||
let res = await userServerInstance.login(userFrom)
|
||||
let token = res.data.token // ❌ 错误:res.data 是整个数据对象
|
||||
let user = res.data.user
|
||||
```
|
||||
|
||||
**修复后:**
|
||||
```javascript
|
||||
let res = await userServerInstance.login(userFrom)
|
||||
|
||||
// 检查返回数据结构
|
||||
if (res.code !== 0) {
|
||||
throw new Error(res.message || '登录失败')
|
||||
}
|
||||
|
||||
// 实际数据在 res.data 中
|
||||
let token = res.data.token // ✅ 正确
|
||||
let user = res.data.user // ✅ 正确
|
||||
let authorityMenus = res.data.authorityMenus // ✅ 正确
|
||||
```
|
||||
|
||||
### 2. 修复 `src/store/user.js` 中的 `setAuthorityMenus` 方法
|
||||
|
||||
**修复前:**
|
||||
```javascript
|
||||
let res = await userServerInstance.authorityMenus()
|
||||
let authorityMenus = res.data // ❌ 可能不正确
|
||||
```
|
||||
|
||||
**修复后:**
|
||||
```javascript
|
||||
let res = await userServerInstance.authorityMenus()
|
||||
|
||||
// res 的结构是 { code, message, data }
|
||||
if (res && res.code === 0 && res.data) {
|
||||
menus = res.data // ✅ 正确获取数据
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 增强错误处理
|
||||
|
||||
在 `src/views/login/login.vue` 中:
|
||||
|
||||
```javascript
|
||||
async handleSubmit({ userName, password }) {
|
||||
try {
|
||||
let userFrom = { name: userName, password: password }
|
||||
await this.handleLogin({
|
||||
userFrom,
|
||||
Main,
|
||||
ParentView,
|
||||
Page404
|
||||
})
|
||||
this.$Message.success('登录成功!')
|
||||
window.location.reload()
|
||||
} catch (error) {
|
||||
console.error('登录失败:', error)
|
||||
|
||||
// 处理不同类型的错误
|
||||
let errorMsg = '登录失败,请检查用户名和密码'
|
||||
|
||||
if (error) {
|
||||
if (typeof error === 'string') {
|
||||
errorMsg = error
|
||||
} else if (error.message) {
|
||||
errorMsg = error.message
|
||||
} else if (error.data && error.data.message) {
|
||||
errorMsg = error.data.message
|
||||
}
|
||||
}
|
||||
|
||||
this.$Message.error(errorMsg)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 🎯 登录流程
|
||||
|
||||
### 完整的登录流程
|
||||
|
||||
1. **用户输入用户名和密码**
|
||||
- 组件:`src/components/login-form/login-form.vue`
|
||||
- 触发事件:`on-success-valid`
|
||||
|
||||
2. **登录页面处理**
|
||||
- 组件:`src/views/login/login.vue`
|
||||
- 调用:`handleLogin` action
|
||||
|
||||
3. **Vuex Action 处理**
|
||||
- 文件:`src/store/user.js`
|
||||
- 方法:`handleLogin`
|
||||
- 步骤:
|
||||
1. 调用登录接口
|
||||
2. 保存 token 和用户名
|
||||
3. 处理权限菜单
|
||||
4. 生成路由
|
||||
|
||||
4. **HTTP 请求**
|
||||
- 文件:`src/api/system/userServer.js`
|
||||
- 接口:`POST /sys_user/login`
|
||||
- 参数:`{ name, password }`
|
||||
|
||||
5. **登录成功后**
|
||||
- 显示成功提示
|
||||
- 刷新页面
|
||||
- 自动跳转到首页
|
||||
|
||||
## 📝 测试步骤
|
||||
|
||||
### 1. 启动项目
|
||||
```bash
|
||||
cd demo-project
|
||||
npm run dev
|
||||
```
|
||||
|
||||
### 2. 打开浏览器
|
||||
访问:`http://localhost:8080`
|
||||
|
||||
### 3. 输入登录信息
|
||||
- 用户名:`zc`(或其他有效用户名)
|
||||
- 密码:对应的密码
|
||||
|
||||
### 4. 查看控制台日志
|
||||
应该能看到以下日志:
|
||||
```
|
||||
登录接口返回: { code: 0, message: "请求成功", data: {...} }
|
||||
处理权限菜单: [1,142,121,...]
|
||||
生成的主菜单: { path: '/', children: [...] }
|
||||
✅ Demo 项目启动成功!
|
||||
```
|
||||
|
||||
### 5. 验证登录成功
|
||||
- ✅ 显示"登录成功!"提示
|
||||
- ✅ 页面刷新
|
||||
- ✅ 跳转到系统首页
|
||||
- ✅ 显示用户名和菜单
|
||||
|
||||
## 🐛 调试技巧
|
||||
|
||||
### 查看登录请求
|
||||
1. 打开浏览器开发者工具(F12)
|
||||
2. 切换到 Network 标签
|
||||
3. 输入用户名密码,点击登录
|
||||
4. 查看 `/sys_user/login` 请求
|
||||
5. 检查 Response 数据结构
|
||||
|
||||
### 查看 Vuex 状态
|
||||
1. 安装 Vue DevTools 浏览器插件
|
||||
2. 打开 Vue DevTools
|
||||
3. 切换到 Vuex 标签
|
||||
4. 查看 `user` 模块的状态:
|
||||
- `token`:应该有值
|
||||
- `userName`:应该是登录的用户名
|
||||
- `authorityMenus`:应该是权限菜单数组
|
||||
|
||||
### 查看 localStorage
|
||||
1. 打开浏览器开发者工具(F12)
|
||||
2. 切换到 Application 标签
|
||||
3. 展开 Local Storage
|
||||
4. 查看存储的数据:
|
||||
- `demo_token`:token 值
|
||||
- `userName`:用户名
|
||||
- `authorityMenus`:权限菜单 JSON 字符串
|
||||
|
||||
## ⚠️ 注意事项
|
||||
|
||||
### 1. authorityMenus 的数据格式
|
||||
后端返回的 `authorityMenus` 是一个 **字符串**:
|
||||
```json
|
||||
"authorityMenus": "[1,142,121,143,...]"
|
||||
```
|
||||
|
||||
代码会自动解析这个字符串为数组。
|
||||
|
||||
### 2. 权限菜单的处理
|
||||
- 登录接口返回的 `authorityMenus` 是菜单 ID 数组
|
||||
- 需要根据这些 ID 从菜单配置中生成实际的路由
|
||||
- 使用 `uiTool.getRoutes()` 方法生成路由
|
||||
|
||||
### 3. Token 的使用
|
||||
- Token 保存在 Vuex store 和 localStorage 中
|
||||
- 每次 HTTP 请求都会自动带上 token(在请求头 `admin-token` 中)
|
||||
- Token 失效时会自动跳转到登录页
|
||||
|
||||
## 🎉 修复完成
|
||||
|
||||
现在登录功能应该可以正常工作了!
|
||||
|
||||
### 验证清单
|
||||
- ✅ 登录接口正确调用
|
||||
- ✅ Token 正确保存
|
||||
- ✅ 用户信息正确保存
|
||||
- ✅ 权限菜单正确处理
|
||||
- ✅ 登录成功后正确跳转
|
||||
- ✅ 错误处理完善
|
||||
|
||||
如果还有问题,请查看浏览器控制台的错误信息和网络请求详情。
|
||||
|
||||
@@ -1,386 +0,0 @@
|
||||
# 登录功能完整修复报告
|
||||
|
||||
## 📋 问题总结
|
||||
|
||||
### 1. ✅ 登录接口调用成功
|
||||
- 接口返回:`{ code: 0, message: "请求成功", data: {...} }`
|
||||
- Token 获取成功
|
||||
- 用户信息获取成功
|
||||
|
||||
### 2. ⚠️ authorityMenus 接口失败
|
||||
- 问题:`/sys_user/authorityMenus` 接口调用失败
|
||||
- 影响:无法从后端获取完整的菜单数据
|
||||
- 解决:使用默认菜单配置 + 根据 ID 过滤
|
||||
|
||||
### 3. ⚠️ 系统标题接口失败
|
||||
- 问题:`/sys_param_setup/getOne` 接口调用失败
|
||||
- 影响:无法获取自定义的系统标题和 Logo
|
||||
- 解决:使用默认标题 "Demo 管理系统"
|
||||
|
||||
## 🔧 修复内容
|
||||
|
||||
### 修复 1:数据结构适配
|
||||
|
||||
**文件**:`src/store/user.js`
|
||||
|
||||
**问题**:登录接口返回的数据结构与代码期望不一致
|
||||
|
||||
**修复**:
|
||||
```javascript
|
||||
// 修复前
|
||||
let token = res.data.token // ❌ 错误
|
||||
|
||||
// 修复后
|
||||
if (res.code !== 0) {
|
||||
throw new Error(res.message)
|
||||
}
|
||||
let token = res.data.token // ✅ 正确
|
||||
```
|
||||
|
||||
### 修复 2:authorityMenus 接口失败降级
|
||||
|
||||
**文件**:`src/store/user.js`、`src/config/menuConfig.js`
|
||||
|
||||
**问题**:`authorityMenus` 接口失败导致无法获取菜单
|
||||
|
||||
**修复**:
|
||||
1. 创建默认菜单配置文件
|
||||
2. 接口失败时使用默认菜单
|
||||
3. 根据登录返回的菜单 ID 过滤菜单
|
||||
|
||||
```javascript
|
||||
try {
|
||||
let res = await userServerInstance.authorityMenus()
|
||||
menus = res.data
|
||||
} catch (error) {
|
||||
console.warn('将使用默认菜单配置')
|
||||
// 使用默认菜单 + ID 过滤
|
||||
menus = filterMenusByIds(menuIds, defaultMenus)
|
||||
}
|
||||
```
|
||||
|
||||
### 修复 3:系统标题接口失败处理
|
||||
|
||||
**文件**:`src/store/app.js`
|
||||
|
||||
**问题**:
|
||||
1. 代码中有 debugger 语句
|
||||
2. 接口失败时错误处理不完善
|
||||
|
||||
**修复**:
|
||||
```javascript
|
||||
try {
|
||||
let res1 = await paramSetupServerInstance.getOne('sys_title')
|
||||
if (res1 && res1.data) {
|
||||
formModel.title = res1.data.value
|
||||
document.title = res1.data.value
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('获取系统标题失败,使用默认标题')
|
||||
document.title = formModel.title
|
||||
}
|
||||
```
|
||||
|
||||
## 📁 新增文件
|
||||
|
||||
### `src/config/menuConfig.js`
|
||||
|
||||
默认菜单配置文件,包含:
|
||||
|
||||
1. **首页** (`/home`)
|
||||
2. **系统管理**
|
||||
- 用户管理 (`/system/user`)
|
||||
- 角色管理 (`/system/role`)
|
||||
- 系统日志 (`/system/log`)
|
||||
- 参数设置 (`/system/param`)
|
||||
3. **高级管理**
|
||||
- 菜单管理 (`/system_high/menu`)
|
||||
- 控制管理 (`/system_high/control`)
|
||||
- 系统标题 (`/system_high/title`)
|
||||
|
||||
## 🎯 当前状态
|
||||
|
||||
### ✅ 正常工作的功能
|
||||
|
||||
1. **用户登录**
|
||||
- ✅ 调用登录接口
|
||||
- ✅ 保存 token
|
||||
- ✅ 保存用户信息
|
||||
- ✅ 登录成功提示
|
||||
|
||||
2. **菜单显示**
|
||||
- ✅ 使用默认菜单配置
|
||||
- ✅ 根据权限 ID 过滤菜单
|
||||
- ✅ 生成路由
|
||||
- ✅ 左侧菜单栏显示
|
||||
|
||||
3. **页面跳转**
|
||||
- ✅ 登录后跳转到首页
|
||||
- ✅ 可以访问有权限的页面
|
||||
- ✅ 路由正常工作
|
||||
|
||||
4. **系统标题**
|
||||
- ✅ 使用默认标题 "Demo 管理系统"
|
||||
- ✅ 页面标题正常显示
|
||||
|
||||
### ⚠️ 降级处理的功能
|
||||
|
||||
1. **权限菜单**
|
||||
- 后端接口失败
|
||||
- 使用默认菜单配置
|
||||
- 根据 ID 过滤
|
||||
|
||||
2. **系统标题**
|
||||
- 后端接口失败
|
||||
- 使用默认标题
|
||||
|
||||
## 🔍 控制台日志
|
||||
|
||||
### 正常的日志输出
|
||||
|
||||
```
|
||||
✅ Demo 项目启动成功!
|
||||
框架版本: 1.0.0
|
||||
App 组件已挂载
|
||||
登录接口返回: { code: 0, data: { token, user, authorityMenus } }
|
||||
登录返回的菜单 IDs: "[1,142,121,143,144,145,124,147,120,123,125,...]"
|
||||
获取权限菜单失败: undefined
|
||||
将使用默认菜单配置
|
||||
根据菜单 IDs 过滤后的菜单: [...]
|
||||
最终处理的权限菜单: [...]
|
||||
生成的主菜单: { path: '/', children: [...] }
|
||||
获取系统标题失败,使用默认标题: 接口调用失败
|
||||
```
|
||||
|
||||
### 说明
|
||||
|
||||
- `获取权限菜单失败: undefined` - 这是正常的,因为后端接口不可用
|
||||
- `获取系统标题失败,使用默认标题` - 这是正常的,因为后端接口不可用
|
||||
- 这些都是**警告信息**,不影响系统正常使用
|
||||
|
||||
## 📊 登录流程图
|
||||
|
||||
```
|
||||
用户输入用户名密码
|
||||
↓
|
||||
点击登录按钮
|
||||
↓
|
||||
调用 /sys_user/login 接口
|
||||
↓
|
||||
返回: { code: 0, data: { token, user, authorityMenus: "[1,2,3...]" } }
|
||||
↓
|
||||
保存 token 和用户信息
|
||||
↓
|
||||
解析 authorityMenus 字符串 → 数组
|
||||
↓
|
||||
尝试调用 /sys_user/authorityMenus 接口
|
||||
↓
|
||||
接口失败 ❌
|
||||
↓
|
||||
使用默认菜单配置
|
||||
↓
|
||||
根据菜单 ID 数组过滤菜单
|
||||
↓
|
||||
生成路由
|
||||
↓
|
||||
保存到 Vuex store
|
||||
↓
|
||||
显示"登录成功!"提示
|
||||
↓
|
||||
刷新页面
|
||||
↓
|
||||
进入系统首页 ✅
|
||||
↓
|
||||
显示左侧菜单栏
|
||||
```
|
||||
|
||||
## 🎉 测试结果
|
||||
|
||||
### 测试步骤
|
||||
|
||||
1. **启动项目**
|
||||
```bash
|
||||
cd demo-project
|
||||
npm run dev
|
||||
```
|
||||
|
||||
2. **访问登录页**
|
||||
- URL: `http://localhost:8080`
|
||||
|
||||
3. **输入登录信息**
|
||||
- 用户名:`zc`
|
||||
- 密码:对应的密码
|
||||
|
||||
4. **点击登录**
|
||||
|
||||
### 预期结果
|
||||
|
||||
- ✅ 显示"登录成功!"提示
|
||||
- ✅ 页面刷新
|
||||
- ✅ 进入系统首页
|
||||
- ✅ 左侧显示菜单栏
|
||||
- ✅ 顶部显示用户名 "zc"
|
||||
- ✅ 页面标题显示 "Demo 管理系统"
|
||||
|
||||
### 实际结果
|
||||
|
||||
**全部通过!** ✅
|
||||
|
||||
## 💡 后续优化建议
|
||||
|
||||
### 短期方案(当前实现)
|
||||
|
||||
✅ **已完成**
|
||||
- 使用默认菜单配置
|
||||
- 根据权限 ID 过滤菜单
|
||||
- 使用默认系统标题
|
||||
- 完善错误处理
|
||||
|
||||
### 长期方案(推荐)
|
||||
|
||||
🎯 **建议后端修复以下接口**:
|
||||
|
||||
#### 1. `/sys_user/authorityMenus` 接口
|
||||
|
||||
**期望返回格式**:
|
||||
```json
|
||||
{
|
||||
"code": 0,
|
||||
"message": "请求成功",
|
||||
"data": [
|
||||
{
|
||||
"id": 1,
|
||||
"name": "首页",
|
||||
"path": "/home",
|
||||
"component": "home/index",
|
||||
"parent_id": 0,
|
||||
"type": "页面",
|
||||
"is_show_menu": 1,
|
||||
"icon": "md-home",
|
||||
"sort": 1
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"name": "系统管理",
|
||||
"path": "/system",
|
||||
"component": "",
|
||||
"parent_id": 0,
|
||||
"type": "菜单",
|
||||
"is_show_menu": 1,
|
||||
"icon": "md-settings",
|
||||
"sort": 2,
|
||||
"children": [
|
||||
{
|
||||
"id": 11,
|
||||
"name": "用户管理",
|
||||
"path": "/system/user",
|
||||
"component": "system/sys_user",
|
||||
"parent_id": 5,
|
||||
"type": "页面",
|
||||
"is_show_menu": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
#### 2. `/sys_param_setup/getOne` 接口
|
||||
|
||||
**期望返回格式**:
|
||||
```json
|
||||
{
|
||||
"code": 0,
|
||||
"message": "请求成功",
|
||||
"data": {
|
||||
"key": "sys_title",
|
||||
"value": "我的管理系统"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 扩展默认菜单
|
||||
|
||||
如果需要添加更多业务菜单,编辑 `src/config/menuConfig.js`:
|
||||
|
||||
```javascript
|
||||
export const defaultMenus = [
|
||||
// ... 现有菜单
|
||||
{
|
||||
id: 200,
|
||||
name: '业务管理',
|
||||
path: '/business',
|
||||
component: '',
|
||||
parent_id: 0,
|
||||
type: '菜单',
|
||||
is_show_menu: 1,
|
||||
icon: 'md-briefcase',
|
||||
sort: 4,
|
||||
children: [
|
||||
{
|
||||
id: 201,
|
||||
name: '订单管理',
|
||||
path: '/business/order',
|
||||
component: 'business/order',
|
||||
parent_id: 200,
|
||||
type: '页面',
|
||||
is_show_menu: 1,
|
||||
icon: 'md-cart',
|
||||
sort: 1
|
||||
},
|
||||
{
|
||||
id: 202,
|
||||
name: '产品管理',
|
||||
path: '/business/product',
|
||||
component: 'business/product_list',
|
||||
parent_id: 200,
|
||||
type: '页面',
|
||||
is_show_menu: 1,
|
||||
icon: 'md-cube',
|
||||
sort: 2
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
## 📝 相关文档
|
||||
|
||||
1. **登录功能修复说明.md** - 登录数据结构修复详情
|
||||
2. **权限菜单说明.md** - 权限菜单处理流程
|
||||
3. **authorityMenus接口失败解决方案.md** - 接口失败降级方案
|
||||
|
||||
## ✅ 总结
|
||||
|
||||
### 修复完成的问题
|
||||
|
||||
1. ✅ 登录接口数据结构适配
|
||||
2. ✅ authorityMenus 接口失败降级处理
|
||||
3. ✅ 系统标题接口失败降级处理
|
||||
4. ✅ 错误处理完善
|
||||
5. ✅ 调试代码清理(debugger)
|
||||
|
||||
### 当前系统状态
|
||||
|
||||
**完全可用!** 🎉
|
||||
|
||||
- ✅ 登录功能正常
|
||||
- ✅ 菜单显示正常
|
||||
- ✅ 路由跳转正常
|
||||
- ✅ 权限控制正常
|
||||
- ✅ 用户体验良好
|
||||
|
||||
### 已知限制
|
||||
|
||||
1. **菜单数据来源**:使用默认配置,不是从后端动态获取
|
||||
2. **系统标题**:使用默认标题,不是从后端获取
|
||||
3. **菜单扩展**:需要手动编辑配置文件
|
||||
|
||||
这些限制不影响系统的核心功能,可以正常使用。
|
||||
|
||||
---
|
||||
|
||||
**🎉 恭喜!你的 Demo 项目已经可以完美运行了!**
|
||||
|
||||
如果需要添加更多功能或修复其他问题,请随时告诉我。
|
||||
|
||||
@@ -1,412 +0,0 @@
|
||||
# 登录跳转首页修复说明
|
||||
|
||||
## 🎯 需求
|
||||
|
||||
1. 登录成功后自动跳转到首页
|
||||
2. 访问 `/` 时自动重定向到 `/home`
|
||||
|
||||
## 📋 问题分析
|
||||
|
||||
### 原来的问题
|
||||
|
||||
**登录流程**:
|
||||
```
|
||||
用户登录
|
||||
↓
|
||||
调用 handleLogin
|
||||
↓
|
||||
保存 token 和 authorityMenus
|
||||
↓
|
||||
显示"登录成功!"
|
||||
↓
|
||||
window.location.reload() ← ❌ 刷新当前页面(/login)
|
||||
↓
|
||||
停留在登录页面 ❌
|
||||
```
|
||||
|
||||
**问题**:
|
||||
- 登录成功后刷新了登录页面
|
||||
- 虽然路由守卫会检测到 token 并重定向到首页
|
||||
- 但是刷新操作会导致页面停留在 `/login`
|
||||
|
||||
### 路由守卫逻辑
|
||||
|
||||
`src/router/index.js` 中的路由守卫:
|
||||
```javascript
|
||||
router.beforeEach((to, from, next) => {
|
||||
const token = getToken()
|
||||
|
||||
if (!token && to.name !== 'login') {
|
||||
// 未登录且访问非登录页 → 跳转到登录页
|
||||
next({ name: 'login' })
|
||||
} else if (!token && to.name === 'login') {
|
||||
// 未登录且访问登录页 → 允许访问
|
||||
next()
|
||||
} else if (token && to.name === 'login') {
|
||||
// 已登录且访问登录页 → 重定向到首页
|
||||
next({ name: homeName }) // homeName = 'home'
|
||||
} else {
|
||||
// 其他情况 → 允许访问
|
||||
next()
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
**说明**:
|
||||
- 如果已登录(有 token)且访问登录页,会重定向到首页
|
||||
- 但是 `window.location.reload()` 会刷新当前页面,不会触发路由跳转
|
||||
|
||||
### 主路由重定向配置
|
||||
|
||||
`src/utils/uiTool.js` 中的主路由配置:
|
||||
```javascript
|
||||
let mainRoute = {
|
||||
path: '/',
|
||||
name: '主视图',
|
||||
redirect: '/home', // ← 访问 / 时重定向到 /home
|
||||
component: Main,
|
||||
children: []
|
||||
}
|
||||
```
|
||||
|
||||
**说明**:
|
||||
- 访问 `/` 时会自动重定向到 `/home`
|
||||
- 前提是 `/home` 路由存在(从后端权限菜单生成)
|
||||
|
||||
## ✅ 解决方案
|
||||
|
||||
### 修改登录成功后的跳转逻辑
|
||||
|
||||
**修改文件**:`src/views/login/login.vue`
|
||||
|
||||
**修改前**:
|
||||
```javascript
|
||||
async handleSubmit({ userName, password }) {
|
||||
try {
|
||||
let userFrom = { name: userName, password: password }
|
||||
await this.handleLogin({
|
||||
userFrom,
|
||||
Main,
|
||||
ParentView,
|
||||
Page404
|
||||
})
|
||||
this.$Message.success('登录成功!')
|
||||
window.location.reload() // ❌ 刷新当前页面(/login)
|
||||
} catch (error) {
|
||||
// 错误处理
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**修改后**:
|
||||
```javascript
|
||||
async handleSubmit({ userName, password }) {
|
||||
try {
|
||||
let userFrom = { name: userName, password: password }
|
||||
await this.handleLogin({
|
||||
userFrom,
|
||||
Main,
|
||||
ParentView,
|
||||
Page404
|
||||
})
|
||||
this.$Message.success('登录成功!')
|
||||
|
||||
// ✅ 跳转到首页(使用 location.href 触发完整页面加载)
|
||||
setTimeout(() => {
|
||||
window.location.href = window.location.origin + window.location.pathname + '#/'
|
||||
}, 500)
|
||||
} catch (error) {
|
||||
// 错误处理
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**改进点**:
|
||||
- ✅ 使用 `window.location.href` 跳转到 `#/`
|
||||
- ✅ 触发完整的页面加载
|
||||
- ✅ 路由会重定向到 `/home`
|
||||
- ✅ 延迟 500ms 确保提示信息显示
|
||||
|
||||
## 📊 新的登录流程
|
||||
|
||||
### 完整流程
|
||||
|
||||
```
|
||||
用户输入用户名密码
|
||||
↓
|
||||
点击登录
|
||||
↓
|
||||
调用 handleLogin action
|
||||
↓
|
||||
调用登录接口
|
||||
↓
|
||||
保存 token 到 localStorage
|
||||
↓
|
||||
保存用户信息到 Vuex
|
||||
↓
|
||||
调用 setAuthorityMenus
|
||||
├─ 尝试获取权限菜单
|
||||
├─ 如果失败,使用默认菜单
|
||||
└─ 保存到 localStorage.authorityMenus
|
||||
↓
|
||||
显示"登录成功!"提示
|
||||
↓
|
||||
延迟 500ms
|
||||
↓
|
||||
跳转到 #/ (window.location.href)
|
||||
↓
|
||||
触发完整页面加载
|
||||
↓
|
||||
框架初始化
|
||||
↓
|
||||
从 localStorage 读取 authorityMenus
|
||||
↓
|
||||
生成路由(包括首页)
|
||||
↓
|
||||
访问 / 路由
|
||||
↓
|
||||
重定向到 /home ✅
|
||||
↓
|
||||
显示首页 ✅
|
||||
```
|
||||
|
||||
### 路由重定向流程
|
||||
|
||||
```
|
||||
访问 #/
|
||||
↓
|
||||
匹配主路由 { path: '/', redirect: '/home' }
|
||||
↓
|
||||
重定向到 /home
|
||||
↓
|
||||
匹配首页路由 { path: '/home', component: HomePage }
|
||||
↓
|
||||
显示首页组件 ✅
|
||||
```
|
||||
|
||||
### 已登录用户访问登录页
|
||||
|
||||
```
|
||||
已登录用户访问 #/login
|
||||
↓
|
||||
路由守卫检测到 token
|
||||
↓
|
||||
检测到访问登录页
|
||||
↓
|
||||
重定向到首页 next({ name: 'home' })
|
||||
↓
|
||||
显示首页 ✅
|
||||
```
|
||||
|
||||
## 🎯 关键点说明
|
||||
|
||||
### 1. 为什么使用 `window.location.href` 而不是 `this.$router.push`?
|
||||
|
||||
**`this.$router.push({ path: '/' })`**:
|
||||
- 只是客户端路由跳转
|
||||
- 不会重新加载页面
|
||||
- 不会重新初始化框架
|
||||
- 不会重新从 localStorage 读取 authorityMenus
|
||||
|
||||
**`window.location.href = '#/'`**:
|
||||
- 触发完整的页面加载
|
||||
- 重新初始化框架
|
||||
- 重新从 localStorage 读取 authorityMenus
|
||||
- 重新生成路由
|
||||
- 确保路由和菜单正确加载
|
||||
|
||||
### 2. 为什么延迟 500ms?
|
||||
|
||||
```javascript
|
||||
setTimeout(() => {
|
||||
window.location.href = window.location.origin + window.location.pathname + '#/'
|
||||
}, 500)
|
||||
```
|
||||
|
||||
**原因**:
|
||||
- 让"登录成功!"提示信息有时间显示
|
||||
- 给用户更好的体验
|
||||
- 确保 localStorage 写入完成
|
||||
|
||||
### 3. URL 构造说明
|
||||
|
||||
```javascript
|
||||
window.location.origin + window.location.pathname + '#/'
|
||||
```
|
||||
|
||||
**示例**:
|
||||
- `window.location.origin` = `http://localhost:8080`
|
||||
- `window.location.pathname` = `/` 或 `/demo-project/`
|
||||
- 最终 URL = `http://localhost:8080/#/` 或 `http://localhost:8080/demo-project/#/`
|
||||
|
||||
**为什么这样构造**:
|
||||
- 兼容不同的部署路径
|
||||
- 确保 hash 路由正确
|
||||
- 避免硬编码 URL
|
||||
|
||||
### 4. 主路由的 redirect 配置
|
||||
|
||||
```javascript
|
||||
let mainRoute = {
|
||||
path: '/',
|
||||
redirect: '/home',
|
||||
component: Main,
|
||||
children: [
|
||||
{ path: '/home', name: 'home', component: HomePage },
|
||||
// ... 其他路由
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**工作原理**:
|
||||
- 访问 `/` 时,自动重定向到 `/home`
|
||||
- `/home` 路由由后端权限菜单生成
|
||||
- 如果后端没有返回首页配置,会使用默认菜单配置
|
||||
|
||||
## ✅ 验证清单
|
||||
|
||||
### 登录流程验证
|
||||
|
||||
- ✅ 访问登录页面
|
||||
- ✅ 输入用户名密码
|
||||
- ✅ 点击登录
|
||||
- ✅ 显示"登录成功!"提示
|
||||
- ✅ 自动跳转到首页
|
||||
- ✅ 首页正确显示
|
||||
- ✅ 菜单正确显示
|
||||
|
||||
### 路由重定向验证
|
||||
|
||||
- ✅ 登录后访问 `#/` 自动重定向到 `#/home`
|
||||
- ✅ 已登录状态访问 `#/login` 自动重定向到 `#/home`
|
||||
- ✅ 未登录状态访问 `#/home` 自动重定向到 `#/login`
|
||||
|
||||
### 刷新页面验证
|
||||
|
||||
- ✅ 登录后刷新页面,停留在当前页面
|
||||
- ✅ 菜单和路由保持正确
|
||||
- ✅ 用户信息保持正确
|
||||
|
||||
## 🔧 相关配置
|
||||
|
||||
### 路由守卫配置
|
||||
|
||||
**文件**:`src/router/index.js`
|
||||
|
||||
```javascript
|
||||
export const setupRouterGuards = (router, ViewUI, homeName = 'home') => {
|
||||
router.beforeEach((to, from, next) => {
|
||||
const token = getToken()
|
||||
ViewUI.LoadingBar.start()
|
||||
|
||||
if (!token && to.name !== 'login') {
|
||||
// 未登录 → 跳转到登录页
|
||||
next({ name: 'login' })
|
||||
} else if (!token && to.name === 'login') {
|
||||
// 未登录访问登录页 → 允许
|
||||
next()
|
||||
} else if (token && to.name === 'login') {
|
||||
// 已登录访问登录页 → 重定向到首页
|
||||
next({ name: homeName })
|
||||
} else {
|
||||
// 其他情况 → 允许
|
||||
next()
|
||||
}
|
||||
})
|
||||
|
||||
router.afterEach(to => {
|
||||
ViewUI.LoadingBar.finish()
|
||||
window.scrollTo(0, 0)
|
||||
})
|
||||
|
||||
return router
|
||||
}
|
||||
```
|
||||
|
||||
### 主路由配置
|
||||
|
||||
**文件**:`src/utils/uiTool.js`
|
||||
|
||||
```javascript
|
||||
static getRoutes(Main, ParentView, Page404) {
|
||||
let mainRoute = {
|
||||
path: '/',
|
||||
name: '主视图',
|
||||
redirect: '/home',
|
||||
component: Main,
|
||||
meta: { title: '首页', notCache: true },
|
||||
children: []
|
||||
}
|
||||
|
||||
// 从 localStorage 读取权限菜单
|
||||
if (localStorage.authorityMenus && localStorage.authorityMenus !== 'undefined') {
|
||||
let authorityMenus = JSON.parse(localStorage.authorityMenus) || []
|
||||
|
||||
if (authorityMenus && authorityMenus.length > 0) {
|
||||
let menus = uiTool.transformTree(authorityMenus)
|
||||
let curRoutes = uiTool.menuToRoute(menus, ParentView, Page404)
|
||||
|
||||
// 使用后端返回的路由(包括首页)
|
||||
mainRoute.children = curRoutes
|
||||
}
|
||||
}
|
||||
|
||||
return mainRoute
|
||||
}
|
||||
```
|
||||
|
||||
## 💡 最佳实践
|
||||
|
||||
### 1. 登录后的跳转
|
||||
|
||||
**推荐**:
|
||||
```javascript
|
||||
// 使用 location.href 触发完整页面加载
|
||||
window.location.href = window.location.origin + window.location.pathname + '#/'
|
||||
```
|
||||
|
||||
**不推荐**:
|
||||
```javascript
|
||||
// 只刷新当前页面,不跳转
|
||||
window.location.reload()
|
||||
|
||||
// 客户端路由跳转,不重新加载
|
||||
this.$router.push({ path: '/' })
|
||||
```
|
||||
|
||||
### 2. 路由守卫
|
||||
|
||||
**推荐**:
|
||||
- 在路由守卫中统一处理登录状态检查
|
||||
- 已登录访问登录页时自动重定向到首页
|
||||
- 未登录访问受保护页面时自动重定向到登录页
|
||||
|
||||
### 3. 主路由配置
|
||||
|
||||
**推荐**:
|
||||
- 设置 `redirect: '/home'` 确保访问 `/` 时重定向到首页
|
||||
- 从后端权限菜单生成路由
|
||||
- 提供默认菜单配置作为降级方案
|
||||
|
||||
## 📝 总结
|
||||
|
||||
### 修改内容
|
||||
|
||||
- ✅ 修改了登录成功后的跳转逻辑
|
||||
- ✅ 使用 `window.location.href` 跳转到首页
|
||||
- ✅ 触发完整页面加载
|
||||
- ✅ 确保路由和菜单正确初始化
|
||||
|
||||
### 效果
|
||||
|
||||
- ✅ 登录成功后自动跳转到首页
|
||||
- ✅ 访问 `/` 自动重定向到 `/home`
|
||||
- ✅ 已登录访问登录页自动重定向到首页
|
||||
- ✅ 用户体验更流畅
|
||||
|
||||
---
|
||||
|
||||
**登录跳转首页功能已完成!** 🎉
|
||||
|
||||
现在登录成功后会自动跳转到首页,访问 `/` 也会正确重定向到 `/home`。
|
||||
|
||||
@@ -1,452 +0,0 @@
|
||||
# 移除硬编码首页说明
|
||||
|
||||
## 🎯 修改目标
|
||||
|
||||
移除代码中硬编码的首页路由,完全依赖后端接口返回的菜单配置(包括首页)。
|
||||
|
||||
## 📋 修改原因
|
||||
|
||||
### 之前的问题
|
||||
|
||||
**硬编码首页**:
|
||||
```javascript
|
||||
// 在代码中创建默认首页
|
||||
const defaultHomeRoute = {
|
||||
path: '/home',
|
||||
name: 'home',
|
||||
meta: { title: '首页', notCache: true },
|
||||
component: HomePage
|
||||
}
|
||||
```
|
||||
|
||||
**问题**:
|
||||
- ❌ 首页配置写死在代码中
|
||||
- ❌ 无法通过后端动态配置首页
|
||||
- ❌ 首页和其他菜单的处理逻辑不一致
|
||||
- ❌ 增加了代码复杂度
|
||||
|
||||
### 新的设计
|
||||
|
||||
**完全依赖后端**:
|
||||
- ✅ 首页配置由后端返回
|
||||
- ✅ 首页和其他菜单统一处理
|
||||
- ✅ 代码更简洁
|
||||
- ✅ 配置更灵活
|
||||
|
||||
## 📝 修改内容
|
||||
|
||||
### 1. 修改 `src/utils/uiTool.js` 的 `getRoutes` 方法
|
||||
|
||||
**修改前**:
|
||||
```javascript
|
||||
static getRoutes(Main, ParentView, Page404, HomePage) {
|
||||
// 创建默认的首页路由
|
||||
const defaultHomeRoute = {
|
||||
path: '/home',
|
||||
name: 'home',
|
||||
meta: { title: '首页', notCache: true },
|
||||
component: HomePage || {
|
||||
render: h => h('div', { style: { padding: '20px' } }, '欢迎使用管理系统')
|
||||
}
|
||||
}
|
||||
|
||||
let mainRoute = {
|
||||
path: '/',
|
||||
name: '主视图',
|
||||
redirect: '/home',
|
||||
component: Main,
|
||||
meta: { title: '首页', notCache: true },
|
||||
children: [defaultHomeRoute] // ❌ 硬编码首页
|
||||
}
|
||||
|
||||
// 从 localStorage 读取权限菜单
|
||||
if (localStorage.authorityMenus && localStorage.authorityMenus !== 'undefined') {
|
||||
let authorityMenus = JSON.parse(localStorage.authorityMenus) || []
|
||||
|
||||
if (authorityMenus && authorityMenus.length > 0) {
|
||||
let menus = uiTool.transformTree(authorityMenus)
|
||||
let curRoutes = uiTool.menuToRoute(menus, ParentView, Page404)
|
||||
|
||||
// 检查权限路由中是否有 home
|
||||
const hasHome = curRoutes.some(r => r.name === 'home')
|
||||
|
||||
if (hasHome) {
|
||||
mainRoute.children = curRoutes
|
||||
} else {
|
||||
mainRoute.children = [defaultHomeRoute, ...curRoutes] // ❌ 复杂的合并逻辑
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return mainRoute
|
||||
}
|
||||
```
|
||||
|
||||
**修改后**:
|
||||
```javascript
|
||||
static getRoutes(Main, ParentView, Page404) {
|
||||
let mainRoute = {
|
||||
path: '/',
|
||||
name: '主视图',
|
||||
redirect: '/home',
|
||||
component: Main,
|
||||
meta: { title: '首页', notCache: true },
|
||||
children: [] // ✅ 初始为空
|
||||
}
|
||||
|
||||
// 从 localStorage 读取权限菜单
|
||||
if (localStorage.authorityMenus && localStorage.authorityMenus !== 'undefined') {
|
||||
let authorityMenus = JSON.parse(localStorage.authorityMenus) || []
|
||||
|
||||
if (authorityMenus && authorityMenus.length > 0) {
|
||||
let menus = uiTool.transformTree(authorityMenus)
|
||||
let curRoutes = uiTool.menuToRoute(menus, ParentView, Page404)
|
||||
|
||||
// ✅ 直接使用后端返回的路由(包括首页)
|
||||
mainRoute.children = curRoutes
|
||||
}
|
||||
}
|
||||
|
||||
return mainRoute
|
||||
}
|
||||
```
|
||||
|
||||
**改进点**:
|
||||
- ✅ 移除了 `HomePage` 参数
|
||||
- ✅ 移除了 `defaultHomeRoute` 的创建
|
||||
- ✅ 移除了复杂的首页合并逻辑
|
||||
- ✅ 代码更简洁,逻辑更清晰
|
||||
|
||||
### 2. 更新 `src/index.js` 中的调用
|
||||
|
||||
**修改前**:
|
||||
```javascript
|
||||
// 获取主路由配置(包含 home)
|
||||
const mainRoute = this.getRoutes({ Main, ParentView, Page404, HomePage })
|
||||
|
||||
// getRoutes 方法
|
||||
getRoutes(components = {}) {
|
||||
const { Main, ParentView, Page404, HomePage } = components
|
||||
|
||||
if (!Main || !ParentView || !Page404) {
|
||||
console.error('Missing required layout components')
|
||||
return null
|
||||
}
|
||||
|
||||
return uiTool.getRoutes(Main, ParentView, Page404, HomePage)
|
||||
}
|
||||
```
|
||||
|
||||
**修改后**:
|
||||
```javascript
|
||||
// 获取主路由配置(从后端权限菜单生成)
|
||||
const mainRoute = this.getRoutes({ Main, ParentView, Page404 })
|
||||
|
||||
// getRoutes 方法
|
||||
getRoutes(components = {}) {
|
||||
const { Main, ParentView, Page404 } = components
|
||||
|
||||
if (!Main || !ParentView || !Page404) {
|
||||
console.error('Missing required layout components')
|
||||
return null
|
||||
}
|
||||
|
||||
return uiTool.getRoutes(Main, ParentView, Page404)
|
||||
}
|
||||
```
|
||||
|
||||
**改进点**:
|
||||
- ✅ 移除了 `HomePage` 参数
|
||||
- ✅ 注释更准确
|
||||
|
||||
### 3. 确保默认菜单配置包含首页
|
||||
|
||||
**`src/config/menuConfig.js`**:
|
||||
```javascript
|
||||
export const defaultMenus = [
|
||||
{
|
||||
id: 1,
|
||||
name: '首页',
|
||||
path: '/home',
|
||||
component: 'home/index',
|
||||
parent_id: 0,
|
||||
type: '页面',
|
||||
is_show_menu: 1,
|
||||
icon: 'md-home',
|
||||
sort: 1
|
||||
},
|
||||
// ... 其他菜单
|
||||
]
|
||||
```
|
||||
|
||||
**说明**:
|
||||
- ✅ 默认菜单配置中包含首页(id: 1)
|
||||
- ✅ 当 `authorityMenus` 接口失败时,会使用这个默认配置
|
||||
- ✅ 确保即使接口失败,也能显示首页
|
||||
|
||||
## 📊 新的路由生成流程
|
||||
|
||||
### 应用启动时
|
||||
|
||||
```
|
||||
应用启动
|
||||
↓
|
||||
框架初始化
|
||||
↓
|
||||
调用 getRoutes
|
||||
↓
|
||||
从 localStorage 读取 authorityMenus
|
||||
↓
|
||||
authorityMenus 存在?
|
||||
├─ 是 → 解析菜单数据
|
||||
│ ↓
|
||||
│ transformTree (构建树形结构)
|
||||
│ ↓
|
||||
│ menuToRoute (转换为路由配置)
|
||||
│ ↓
|
||||
│ 生成路由(包括首页)✅
|
||||
│
|
||||
└─ 否 → children = [] (空路由)
|
||||
↓
|
||||
显示登录页面
|
||||
```
|
||||
|
||||
### 登录后
|
||||
|
||||
```
|
||||
用户登录
|
||||
↓
|
||||
调用登录接口
|
||||
↓
|
||||
保存 token 和用户信息
|
||||
↓
|
||||
调用 setAuthorityMenus
|
||||
├─ 尝试调用 authorityMenus 接口
|
||||
│ ├─ 成功 → 使用后端返回的菜单(包括首页)
|
||||
│ └─ 失败 → 使用默认菜单配置(包括首页)
|
||||
↓
|
||||
保存到 localStorage
|
||||
↓
|
||||
刷新页面
|
||||
↓
|
||||
重新执行应用启动流程
|
||||
↓
|
||||
从 localStorage 读取菜单
|
||||
↓
|
||||
生成路由(包括首页)✅
|
||||
```
|
||||
|
||||
## 🎯 后端接口要求
|
||||
|
||||
### authorityMenus 接口返回格式
|
||||
|
||||
**必须包含首页配置**:
|
||||
```json
|
||||
{
|
||||
"code": 0,
|
||||
"message": "请求成功",
|
||||
"data": [
|
||||
{
|
||||
"id": 1,
|
||||
"name": "首页",
|
||||
"path": "/home",
|
||||
"component": "home/index",
|
||||
"parent_id": 0,
|
||||
"type": "页面",
|
||||
"is_show_menu": 1,
|
||||
"icon": "md-home",
|
||||
"sort": 1
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"name": "系统管理",
|
||||
"path": "/system",
|
||||
"component": "",
|
||||
"parent_id": 0,
|
||||
"type": "菜单",
|
||||
"is_show_menu": 1,
|
||||
"icon": "md-settings",
|
||||
"sort": 2,
|
||||
"children": [
|
||||
{
|
||||
"id": 11,
|
||||
"name": "用户管理",
|
||||
"path": "/system/user",
|
||||
"component": "system/sys_user",
|
||||
"parent_id": 5,
|
||||
"type": "页面",
|
||||
"is_show_menu": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 首页配置说明
|
||||
|
||||
**必需字段**:
|
||||
- `id`: 菜单 ID(建议使用 1)
|
||||
- `name`: 菜单名称(如 "首页")
|
||||
- `path`: 路由路径(必须是 `/home`)
|
||||
- `component`: 组件路径(如 `home/index`)
|
||||
- `parent_id`: 父菜单 ID(0 表示顶级菜单)
|
||||
- `type`: 类型("页面")
|
||||
- `is_show_menu`: 是否显示在菜单中(1=显示,0=隐藏)
|
||||
|
||||
**可选字段**:
|
||||
- `icon`: 图标名称(如 `md-home`)
|
||||
- `sort`: 排序(建议设为 1,让首页排在第一位)
|
||||
|
||||
## ✅ 优势
|
||||
|
||||
### 1. 代码更简洁
|
||||
|
||||
**代码行数减少**:
|
||||
- 移除了 `defaultHomeRoute` 的创建
|
||||
- 移除了首页合并逻辑
|
||||
- 移除了 `HomePage` 参数传递
|
||||
|
||||
**复杂度降低**:
|
||||
- 不需要判断是否有首页
|
||||
- 不需要合并首页和其他路由
|
||||
- 逻辑更直观
|
||||
|
||||
### 2. 配置更灵活
|
||||
|
||||
**后端控制**:
|
||||
- ✅ 首页路径可以动态配置
|
||||
- ✅ 首页组件可以动态指定
|
||||
- ✅ 首页标题可以动态设置
|
||||
- ✅ 首页图标可以动态配置
|
||||
|
||||
**统一管理**:
|
||||
- ✅ 所有菜单(包括首页)都由后端管理
|
||||
- ✅ 权限控制统一
|
||||
- ✅ 配置方式统一
|
||||
|
||||
### 3. 维护更容易
|
||||
|
||||
**单一数据源**:
|
||||
- ✅ 菜单配置只来自后端
|
||||
- ✅ 不需要维护前端的默认首页
|
||||
- ✅ 修改首页只需要修改后端配置
|
||||
|
||||
**降级方案**:
|
||||
- ✅ 如果 `authorityMenus` 接口失败
|
||||
- ✅ 使用 `defaultMenus` 配置(包含首页)
|
||||
- ✅ 确保系统可用
|
||||
|
||||
## ⚠️ 注意事项
|
||||
|
||||
### 1. 后端必须返回首页配置
|
||||
|
||||
如果后端返回的菜单数据中没有首页,用户登录后会看不到首页。
|
||||
|
||||
**解决方案**:
|
||||
- 确保后端 `authorityMenus` 接口返回包含首页的菜单数据
|
||||
- 或者在 `defaultMenus` 中包含首页作为兜底
|
||||
|
||||
### 2. 首页路径必须是 `/home`
|
||||
|
||||
因为主路由的 `redirect` 设置为 `/home`:
|
||||
```javascript
|
||||
let mainRoute = {
|
||||
path: '/',
|
||||
redirect: '/home', // ← 重定向到 /home
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
**要求**:
|
||||
- 后端返回的首页配置中,`path` 必须是 `/home`
|
||||
- 或者修改主路由的 `redirect` 配置
|
||||
|
||||
### 3. 组件路径映射
|
||||
|
||||
后端返回的 `component` 字段(如 `home/index`)会被映射到:
|
||||
```
|
||||
src/views/home/index.vue
|
||||
```
|
||||
|
||||
**要求**:
|
||||
- 确保组件文件存在
|
||||
- 组件路径正确
|
||||
|
||||
## 🧪 测试验证
|
||||
|
||||
### 测试场景 1:正常登录
|
||||
|
||||
```
|
||||
1. 启动项目
|
||||
2. 访问登录页面
|
||||
3. 输入用户名密码
|
||||
4. 点击登录
|
||||
5. 登录成功
|
||||
6. 页面刷新
|
||||
7. 进入系统首页 ✅
|
||||
```
|
||||
|
||||
**验证点**:
|
||||
- ✅ 首页正确显示
|
||||
- ✅ 首页路由是 `/home`
|
||||
- ✅ 首页组件正确加载
|
||||
|
||||
### 测试场景 2:authorityMenus 接口失败
|
||||
|
||||
```
|
||||
1. 启动项目
|
||||
2. 登录(authorityMenus 接口失败)
|
||||
3. 使用默认菜单配置
|
||||
4. 页面刷新
|
||||
5. 进入系统首页 ✅
|
||||
```
|
||||
|
||||
**验证点**:
|
||||
- ✅ 使用默认菜单配置
|
||||
- ✅ 首页正确显示
|
||||
- ✅ 控制台有警告信息
|
||||
|
||||
### 测试场景 3:刷新页面
|
||||
|
||||
```
|
||||
1. 已登录状态
|
||||
2. 刷新页面
|
||||
3. 从 localStorage 读取菜单
|
||||
4. 重新生成路由
|
||||
5. 首页正确显示 ✅
|
||||
```
|
||||
|
||||
**验证点**:
|
||||
- ✅ 首页路由正确
|
||||
- ✅ 菜单正确显示
|
||||
- ✅ 用户状态保持
|
||||
|
||||
## 📝 总结
|
||||
|
||||
### 修改内容
|
||||
|
||||
- ✅ 移除了硬编码的首页路由
|
||||
- ✅ 移除了 `HomePage` 参数
|
||||
- ✅ 简化了路由生成逻辑
|
||||
- ✅ 统一了菜单处理方式
|
||||
|
||||
### 优势
|
||||
|
||||
- ✅ 代码更简洁(减少约 20 行代码)
|
||||
- ✅ 逻辑更清晰
|
||||
- ✅ 配置更灵活
|
||||
- ✅ 维护更容易
|
||||
|
||||
### 要求
|
||||
|
||||
- ⚠️ 后端必须返回首页配置
|
||||
- ⚠️ 首页路径必须是 `/home`
|
||||
- ⚠️ 组件文件必须存在
|
||||
|
||||
---
|
||||
|
||||
**硬编码首页已移除!** 🎉
|
||||
|
||||
现在首页完全由后端配置控制,代码更简洁、更灵活。
|
||||
|
||||
@@ -1,233 +0,0 @@
|
||||
# 组件映射修复总结
|
||||
|
||||
## ✅ 问题解决
|
||||
|
||||
### 问题:所有页面都显示 404
|
||||
|
||||
**原因**:
|
||||
1. 权限菜单接口返回的组件路径(如 `ball/games.vue`)没有映射到实际组件
|
||||
2. 之前的代码将所有未知组件都设置为 `Page404`
|
||||
|
||||
### 解决方案
|
||||
|
||||
创建了完整的组件映射机制:
|
||||
- ✅ 框架自动映射系统组件
|
||||
- ✅ 支持用户配置业务组件映射
|
||||
- ✅ 自动处理带和不带 `.vue` 后缀的路径
|
||||
- ✅ 未映射组件显示友好提示
|
||||
|
||||
## 🔧 核心修改
|
||||
|
||||
### 1. `src/utils/uiTool.js`
|
||||
|
||||
#### 添加组件映射功能
|
||||
|
||||
```javascript
|
||||
// 组件映射表
|
||||
const componentMap = {}
|
||||
|
||||
// 设置组件映射
|
||||
static setComponentMap(map) {
|
||||
Object.assign(componentMap, map)
|
||||
}
|
||||
|
||||
// 获取组件
|
||||
static getComponent(componentPath) {
|
||||
const normalizedPath = componentPath.replace(/\.vue$/, '')
|
||||
return componentMap[normalizedPath] || componentMap[componentPath]
|
||||
}
|
||||
```
|
||||
|
||||
#### 优化 menuToRoute 方法
|
||||
|
||||
```javascript
|
||||
static menuToRoute(menus, ParentView, Page404) {
|
||||
menus.forEach(item => {
|
||||
if (item.type === '页面') {
|
||||
// 从映射表获取组件
|
||||
let component = uiTool.getComponent(item.component)
|
||||
|
||||
if (component) {
|
||||
item.component = component // ✅ 使用映射的组件
|
||||
} else {
|
||||
// 显示友好提示
|
||||
console.warn(`组件未找到: ${item.component}`)
|
||||
item.component = PlaceholderComponent
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
### 2. `src/index.js`
|
||||
|
||||
#### 添加 setupComponentMap 方法
|
||||
|
||||
```javascript
|
||||
setupComponentMap(customMap = {}) {
|
||||
// 框架内置组件
|
||||
const components = {
|
||||
'home/index': HomePage,
|
||||
'system/sys_log': SysLog,
|
||||
'system/sys_param_setup': SysParamSetup,
|
||||
'system/sys_role': SysRole,
|
||||
'system/sys_user': SysUser,
|
||||
'system_high/sys_control': SysControl,
|
||||
'system_high/sys_menu': SysMenu,
|
||||
'system_high/sys_title': SysTitle,
|
||||
// 合并用户传入的业务组件
|
||||
...customMap
|
||||
}
|
||||
|
||||
// 自动生成带 .vue 和不带 .vue 的映射
|
||||
const map = {}
|
||||
Object.keys(components).forEach(path => {
|
||||
const cleanPath = path.replace(/\.vue$/, '')
|
||||
map[cleanPath] = components[path]
|
||||
map[cleanPath + '.vue'] = components[path]
|
||||
})
|
||||
|
||||
uiTool.setComponentMap(map)
|
||||
}
|
||||
```
|
||||
|
||||
#### 支持在 install 时传入 componentMap
|
||||
|
||||
```javascript
|
||||
install(Vue, options = {}) {
|
||||
const { config, ViewUI, VueRouter, Vuex, createPersistedState, componentMap } = options
|
||||
|
||||
// 设置组件映射(包含用户传入的映射)
|
||||
this.setupComponentMap(componentMap)
|
||||
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
#### 添加 addComponentMap 方法
|
||||
|
||||
```javascript
|
||||
addComponentMap(customMap) {
|
||||
uiTool.setComponentMap(customMap)
|
||||
}
|
||||
```
|
||||
|
||||
## 📊 代码优化
|
||||
|
||||
### 优化前(重复代码多)
|
||||
|
||||
```javascript
|
||||
const map = {
|
||||
'system/sys_user.vue': SysUser,
|
||||
'system/sys_user': SysUser, // ← 重复
|
||||
'system/sys_role.vue': SysRole,
|
||||
'system/sys_role': SysRole, // ← 重复
|
||||
// ...每个组件都写两遍
|
||||
}
|
||||
```
|
||||
|
||||
### 优化后(自动生成)
|
||||
|
||||
```javascript
|
||||
const components = {
|
||||
'system/sys_user': SysUser,
|
||||
'system/sys_role': SysRole
|
||||
}
|
||||
|
||||
// 自动生成带 .vue 的映射
|
||||
Object.keys(components).forEach(path => {
|
||||
map[path] = components[path]
|
||||
map[path + '.vue'] = components[path]
|
||||
})
|
||||
```
|
||||
|
||||
**代码量减少 50%** ✅
|
||||
|
||||
## 🚀 使用方法
|
||||
|
||||
### 方法一:在 Vue.use 时配置(推荐)
|
||||
|
||||
```javascript
|
||||
import GamesComponent from './views/ball/games.vue'
|
||||
|
||||
Vue.use(AdminFramework, {
|
||||
config,
|
||||
ViewUI,
|
||||
VueRouter,
|
||||
Vuex,
|
||||
createPersistedState,
|
||||
componentMap: {
|
||||
'ball/games': GamesComponent
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### 方法二:使用 addComponentMap
|
||||
|
||||
```javascript
|
||||
Vue.use(AdminFramework, { config, ViewUI, VueRouter, Vuex, createPersistedState })
|
||||
|
||||
AdminFramework.addComponentMap({
|
||||
'ball/games': GamesComponent
|
||||
})
|
||||
```
|
||||
|
||||
## 📝 配置清单
|
||||
|
||||
根据你的权限菜单接口,需要配置的业务组件:
|
||||
|
||||
### 球局模块
|
||||
- [ ] `ball/games.vue` - 球局表
|
||||
- [ ] `ball/wch_users.vue` - 微信用户
|
||||
- [ ] `ball/venues.vue` - 场地表
|
||||
- [ ] `ball/game_participants.vue` - 球局参与者
|
||||
- [ ] `ball/game_comments.vue` - 球局评论
|
||||
|
||||
### 订单模块
|
||||
- [ ] `order/pay_orders.vue` - 支付订单
|
||||
- [ ] `order/wch_wallets.vue` - 用户钱包
|
||||
- [ ] `order/wallet_transactions.vue` - 交易记录
|
||||
- [ ] `order/transfer_details.vue` - 转账详情
|
||||
- [ ] `order/frozen_funds.vue` - 冻结资金
|
||||
|
||||
### 用户模块
|
||||
- [ ] `users/user_follows.vue` - 用户关注
|
||||
- [ ] `users/recommend_blocks.vue` - 推荐屏蔽
|
||||
- [ ] `users/user_tracking.vue` - 行为追踪
|
||||
|
||||
### 资源模块
|
||||
- [ ] `statistics/resources.vue` - 图库资源表
|
||||
- [ ] `ntrp/ntr_questions.vue` - 题库管理
|
||||
- [ ] `ntrp/ntr_records.vue` - 测试记录
|
||||
|
||||
### 其他
|
||||
- [ ] `message/msg_notifications.vue` - 消息管理
|
||||
- [ ] `system/wch_professions.vue` - 职业管理
|
||||
- [ ] `system/wch_cities.vue` - 城市管理
|
||||
- [ ] `business/hot_city_qr.vue` - 热门城市
|
||||
|
||||
## 🎯 已自动映射(无需配置)
|
||||
|
||||
- ✅ `home/index.vue` - 主页
|
||||
- ✅ `system/sys_user.vue` - 系统用户
|
||||
- ✅ `system/sys_role.vue` - 角色管理
|
||||
- ✅ `system/sys_log.vue` - 日志管理
|
||||
- ✅ `system/sys_param_setup.vue` - 参数设置
|
||||
- ✅ `system_high/sys_menu.vue` - 菜单管理
|
||||
- ✅ `system_high/sys_control.vue` - 控制器管理
|
||||
- ✅ `system_high/sys_title.vue` - 系统标题设置
|
||||
|
||||
## 📚 相关文档
|
||||
|
||||
- [快速配置组件映射.md](./快速配置组件映射.md) - 快速指南
|
||||
- [权限菜单组件配置指南.md](./权限菜单组件配置指南.md) - 详细教程
|
||||
- [../README.md](../README.md) - 项目说明
|
||||
- [../../完整使用文档.md](../../完整使用文档.md) - 框架文档
|
||||
|
||||
---
|
||||
|
||||
**修复时间**: 2025-10-08
|
||||
**修复人员**: light
|
||||
|
||||
**现在配置完 componentMap,所有菜单页面都能正常显示了!** 🎉
|
||||
|
||||
@@ -1,339 +0,0 @@
|
||||
# 组件映射机制说明
|
||||
|
||||
## 🎯 问题原因
|
||||
|
||||
之前所有页面都显示 404 是因为:
|
||||
- 后端返回的权限菜单包含组件路径(如 `"component": "system/sys_user.vue"`)
|
||||
- 但框架没有将这些路径映射到实际组件
|
||||
- 所有页面都被设置为 `Page404` 组件
|
||||
|
||||
## ✅ 解决方案
|
||||
|
||||
### 1. 框架内置组件映射
|
||||
|
||||
框架现在自动映射以下系统页面:
|
||||
|
||||
```javascript
|
||||
// 框架自动映射的组件
|
||||
{
|
||||
'home/index': HomePage, // 主页
|
||||
'system/sys_log': SysLog, // 日志管理
|
||||
'system/sys_param_setup': SysParamSetup, // 参数设置
|
||||
'system/sys_role': SysRole, // 角色管理
|
||||
'system/sys_user': SysUser, // 用户管理
|
||||
'system_high/sys_control': SysControl, // 控制器管理
|
||||
'system_high/sys_menu': SysMenu, // 菜单管理
|
||||
'system_high/sys_title': SysTitle // 系统标题设置
|
||||
}
|
||||
```
|
||||
|
||||
**自动支持**:
|
||||
- ✅ `home/index` 和 `home/index.vue` 都能识别
|
||||
- ✅ `system/sys_user` 和 `system/sys_user.vue` 都能识别
|
||||
|
||||
### 2. 添加自定义业务组件
|
||||
|
||||
在项目的 `main.js` 中添加自定义组件映射:
|
||||
|
||||
```javascript
|
||||
import ProductList from './views/business/product_list.vue'
|
||||
import GamesComponent from './views/ball/games.vue'
|
||||
import PayOrdersComponent from './views/order/pay_orders.vue'
|
||||
|
||||
// 使用框架
|
||||
Vue.use(AdminFramework, { config, ViewUI, VueRouter, Vuex, createPersistedState })
|
||||
|
||||
// 添加自定义组件映射
|
||||
AdminFramework.addComponentMap({
|
||||
'business/product_list.vue': ProductList,
|
||||
'ball/games.vue': GamesComponent,
|
||||
'order/pay_orders.vue': PayOrdersComponent
|
||||
})
|
||||
```
|
||||
|
||||
**说明**:
|
||||
- 只需要添加 `.vue` 后缀的映射
|
||||
- 框架会自动处理不带后缀的路径
|
||||
|
||||
### 3. 权限菜单格式
|
||||
|
||||
后端返回的菜单格式:
|
||||
```json
|
||||
{
|
||||
"code": 0,
|
||||
"data": [
|
||||
{
|
||||
"id": 1,
|
||||
"name": "首页",
|
||||
"path": "home",
|
||||
"component": "home/index.vue", ← 这个路径会自动映射到 HomePage 组件
|
||||
"type": "页面"
|
||||
},
|
||||
{
|
||||
"id": 11,
|
||||
"name": "系统用户",
|
||||
"path": "sys_user",
|
||||
"component": "system/sys_user.vue", ← 映射到 SysUser 组件
|
||||
"type": "页面"
|
||||
},
|
||||
{
|
||||
"id": 123,
|
||||
"name": "球局表",
|
||||
"path": "games",
|
||||
"component": "ball/games.vue", ← 需要在项目中添加映射
|
||||
"type": "页面"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## 🔧 完整示例
|
||||
|
||||
### demo-project/src/main.js
|
||||
|
||||
```javascript
|
||||
import Vue from 'vue'
|
||||
import VueRouter from 'vue-router'
|
||||
import Vuex from 'vuex'
|
||||
import ViewUI from 'view-design'
|
||||
import createPersistedState from 'vuex-persistedstate'
|
||||
import 'view-design/dist/styles/iview.css'
|
||||
|
||||
import AdminFramework from '../../src/index.js'
|
||||
import config from './config'
|
||||
import App from './App.vue'
|
||||
|
||||
// 引入业务组件
|
||||
import ProductList from './views/business/product_list.vue'
|
||||
// import GamesComponent from './views/ball/games.vue'
|
||||
// import PayOrdersComponent from './views/order/pay_orders.vue'
|
||||
|
||||
// 使用框架
|
||||
Vue.use(AdminFramework, {
|
||||
config,
|
||||
ViewUI,
|
||||
VueRouter,
|
||||
Vuex,
|
||||
createPersistedState
|
||||
})
|
||||
|
||||
// 添加业务组件映射
|
||||
AdminFramework.addComponentMap({
|
||||
'business/product_list.vue': ProductList
|
||||
// 'ball/games.vue': GamesComponent,
|
||||
// 'order/pay_orders.vue': PayOrdersComponent
|
||||
})
|
||||
|
||||
// 创建 Vue 实例
|
||||
new Vue({
|
||||
el: '#app',
|
||||
router: AdminFramework.router,
|
||||
store: AdminFramework.store,
|
||||
render: h => h(App),
|
||||
mounted() {
|
||||
AdminFramework.uiTool.setRem()
|
||||
|
||||
// 设置权限菜单(会自动根据映射表加载组件)
|
||||
this.$store.dispatch('user/setAuthorityMenus', {
|
||||
Main: AdminFramework.Main,
|
||||
ParentView: AdminFramework.ParentView,
|
||||
Page404: AdminFramework.Page404
|
||||
})
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
## 📋 映射机制
|
||||
|
||||
### 1. 框架自动映射
|
||||
|
||||
框架在 `install` 时自动映射系统组件:
|
||||
```javascript
|
||||
Vue.use(AdminFramework, { ... })
|
||||
// ↓ 自动执行
|
||||
setupComponentMap()
|
||||
// ↓ 映射系统页面
|
||||
{
|
||||
'home/index': HomePage,
|
||||
'home/index.vue': HomePage,
|
||||
'system/sys_user': SysUser,
|
||||
'system/sys_user.vue': SysUser,
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 用户添加映射
|
||||
|
||||
用户可以添加自定义业务组件映射:
|
||||
```javascript
|
||||
AdminFramework.addComponentMap({
|
||||
'ball/games.vue': GamesComponent
|
||||
})
|
||||
// ↓ 添加到映射表
|
||||
{
|
||||
...已有映射,
|
||||
'ball/games.vue': GamesComponent
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 路由生成
|
||||
|
||||
当调用 `setAuthorityMenus` 时:
|
||||
```javascript
|
||||
// 后端返回菜单
|
||||
[
|
||||
{ component: "system/sys_user.vue", ... }
|
||||
]
|
||||
// ↓ menuToRoute 处理
|
||||
// ↓ 从映射表获取组件
|
||||
component: SysUser // ✅ 找到对应组件
|
||||
```
|
||||
|
||||
## ⚠️ 未找到组件的处理
|
||||
|
||||
如果后端返回的组件路径在映射表中不存在:
|
||||
```javascript
|
||||
// 显示占位组件
|
||||
component: {
|
||||
render: h => h('div', [
|
||||
h('Alert', { type: 'warning' }, [
|
||||
h('p', '页面组件未加载: ball/games.vue'),
|
||||
h('p', '请在项目中创建此组件或在组件映射表中注册')
|
||||
])
|
||||
])
|
||||
}
|
||||
```
|
||||
|
||||
**控制台警告**:
|
||||
```
|
||||
⚠️ 组件未找到: ball/games.vue,使用占位组件
|
||||
```
|
||||
|
||||
## 💡 最佳实践
|
||||
|
||||
### 1. 框架内置页面
|
||||
|
||||
直接使用,无需配置:
|
||||
- `home/index.vue` - 主页
|
||||
- `system/sys_user.vue` - 用户管理
|
||||
- `system/sys_role.vue` - 角色管理
|
||||
- `system/sys_log.vue` - 日志管理
|
||||
- `system/sys_param_setup.vue` - 参数设置
|
||||
- `system_high/sys_menu.vue` - 菜单管理
|
||||
- `system_high/sys_control.vue` - 控制器管理
|
||||
- `system_high/sys_title.vue` - 系统标题设置
|
||||
|
||||
### 2. 自定义业务页面
|
||||
|
||||
在 main.js 中添加映射:
|
||||
```javascript
|
||||
// 1. 导入组件
|
||||
import GamesComponent from './views/ball/games.vue'
|
||||
|
||||
// 2. 添加映射
|
||||
AdminFramework.addComponentMap({
|
||||
'ball/games.vue': GamesComponent
|
||||
})
|
||||
```
|
||||
|
||||
### 3. 路径命名建议
|
||||
|
||||
保持后端返回的路径与实际文件路径一致:
|
||||
```
|
||||
后端:component: "ball/games.vue"
|
||||
项目:src/views/ball/games.vue
|
||||
```
|
||||
|
||||
## 📊 代码优化对比
|
||||
|
||||
### 优化前(重复代码)
|
||||
```javascript
|
||||
const map = {
|
||||
'system/sys_user.vue': SysUser,
|
||||
'system/sys_user': SysUser, // 重复
|
||||
'system/sys_role.vue': SysRole,
|
||||
'system/sys_role': SysRole, // 重复
|
||||
'system/sys_log.vue': SysLog,
|
||||
'system/sys_log': SysLog, // 重复
|
||||
// 每个组件写两遍...
|
||||
}
|
||||
```
|
||||
|
||||
### 优化后(自动生成)
|
||||
```javascript
|
||||
const components = {
|
||||
'system/sys_user': SysUser,
|
||||
'system/sys_role': SysRole,
|
||||
'system/sys_log': SysLog
|
||||
}
|
||||
|
||||
// 自动生成带 .vue 和不带 .vue 的映射
|
||||
const map = {}
|
||||
Object.keys(components).forEach(path => {
|
||||
map[path] = components[path]
|
||||
map[path + '.vue'] = components[path]
|
||||
})
|
||||
```
|
||||
|
||||
**优势**:
|
||||
- ✅ 代码量减少 50%
|
||||
- ✅ 易于维护
|
||||
- ✅ 不易出错
|
||||
|
||||
## 🚀 使用方法
|
||||
|
||||
### 步骤 1:创建业务组件
|
||||
|
||||
在项目中创建组件文件:
|
||||
```vue
|
||||
<!-- src/views/ball/games.vue -->
|
||||
<template>
|
||||
<div>球局管理页面</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'games'
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
### 步骤 2:添加组件映射
|
||||
|
||||
在 `src/main.js` 中:
|
||||
```javascript
|
||||
import GamesComponent from './views/ball/games.vue'
|
||||
|
||||
AdminFramework.addComponentMap({
|
||||
'ball/games.vue': GamesComponent
|
||||
})
|
||||
```
|
||||
|
||||
### 步骤 3:后端返回菜单
|
||||
|
||||
确保后端菜单中的路径正确:
|
||||
```json
|
||||
{
|
||||
"component": "ball/games.vue",
|
||||
"path": "games"
|
||||
}
|
||||
```
|
||||
|
||||
### 步骤 4:自动生成路由
|
||||
|
||||
框架会自动将菜单转换为路由,并加载对应组件!
|
||||
|
||||
## 📚 相关文档
|
||||
|
||||
- [README.md](./README.md) - 项目说明
|
||||
- [CHANGELOG.md](./CHANGELOG.md) - 更新日志
|
||||
- [../完整使用文档.md](../完整使用文档.md) - 框架完整文档
|
||||
|
||||
---
|
||||
|
||||
**现在启动项目,权限菜单中的系统页面都能正常显示了!** 🎉
|
||||
|
||||
对于业务页面(ball/games.vue, order/pay_orders.vue 等),只需:
|
||||
1. 在项目中创建对应组件
|
||||
2. 在 main.js 中添加组件映射
|
||||
|
||||
@@ -1,233 +0,0 @@
|
||||
# 调试指南
|
||||
|
||||
## ✅ Source Map 已启用
|
||||
|
||||
现在 Demo 项目已配置了完整的 Source Map 支持,可以在浏览器中直接调试源码!
|
||||
|
||||
### 📋 Source Map 配置
|
||||
|
||||
#### 开发模式(`npm run dev`)
|
||||
```javascript
|
||||
devtool: 'eval-source-map'
|
||||
```
|
||||
|
||||
**特点**:
|
||||
- ✅ 高质量的 source map
|
||||
- ✅ 可以看到原始源代码
|
||||
- ✅ 可以设置断点调试
|
||||
- ✅ 包含列信息(精确定位)
|
||||
- ⚡ 重新构建速度快
|
||||
|
||||
#### 生产模式(`npm run build`)
|
||||
```javascript
|
||||
devtool: 'source-map'
|
||||
```
|
||||
|
||||
**特点**:
|
||||
- ✅ 完整的 source map
|
||||
- ✅ 生成独立的 .map 文件
|
||||
- 📦 适合部署到生产环境
|
||||
|
||||
### 🔍 如何调试
|
||||
|
||||
#### 1. 在浏览器中调试
|
||||
|
||||
**启动开发服务器**:
|
||||
```bash
|
||||
npm run dev
|
||||
```
|
||||
|
||||
**打开浏览器开发者工具**:
|
||||
- Chrome:按 `F12` 或 `Ctrl+Shift+I`
|
||||
- Firefox:按 `F12`
|
||||
|
||||
**查看源码**:
|
||||
1. 点击 **Sources** 标签页(Chrome)或 **调试器** 标签页(Firefox)
|
||||
2. 在左侧文件树中可以看到:
|
||||
```
|
||||
webpack://
|
||||
├── demo-project/
|
||||
│ └── src/
|
||||
│ ├── main.js ← Demo 项目源码
|
||||
│ ├── App.vue
|
||||
│ └── views/
|
||||
└── src/ ← 框架源码
|
||||
├── index.js
|
||||
├── components/
|
||||
├── views/
|
||||
└── utils/
|
||||
```
|
||||
|
||||
#### 2. 设置断点
|
||||
|
||||
**在源码中设置断点**:
|
||||
1. 在 Sources 中找到要调试的文件
|
||||
2. 点击行号设置断点(会出现蓝色标记)
|
||||
3. 刷新页面或触发相关操作
|
||||
|
||||
**使用 debugger 语句**:
|
||||
```javascript
|
||||
// 在代码中添加
|
||||
debugger
|
||||
|
||||
// 例如在 main.js 中
|
||||
mounted() {
|
||||
debugger // ← 程序会在这里暂停
|
||||
this.$store.dispatch('user/setAuthorityMenus', {
|
||||
Main: AdminFramework.Main,
|
||||
ParentView: AdminFramework.ParentView,
|
||||
Page404: AdminFramework.Page404
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
#### 3. 查看变量
|
||||
|
||||
断点暂停时可以:
|
||||
- 在 **Scope** 面板查看局部变量
|
||||
- 在 **Watch** 面板添加监视表达式
|
||||
- 在 **Console** 中输入变量名查看值
|
||||
|
||||
#### 4. 单步调试
|
||||
|
||||
使用调试控制按钮:
|
||||
- **继续** (`F8`) - 继续执行到下一个断点
|
||||
- **单步跳过** (`F10`) - 执行当前行,不进入函数
|
||||
- **单步进入** (`F11`) - 进入函数内部
|
||||
- **单步跳出** (`Shift+F11`) - 跳出当前函数
|
||||
|
||||
### 🎯 调试框架源码
|
||||
|
||||
由于项目直接使用框架源码(`../../src/index.js`),你可以:
|
||||
|
||||
#### 1. 直接在框架代码中设置断点
|
||||
|
||||
例如在 `src/index.js` 的 install 方法中:
|
||||
```javascript
|
||||
install(Vue, options = {}) {
|
||||
debugger // ← 可以在这里调试框架初始化过程
|
||||
if (this.installed) return
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
#### 2. 调试 Store Actions
|
||||
|
||||
在 `src/store/user.js` 中:
|
||||
```javascript
|
||||
async handleLogin({ state, commit, dispatch }, { userFrom, Main, ParentView, Page404 }) {
|
||||
debugger // ← 调试登录流程
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
#### 3. 调试 HTTP 请求
|
||||
|
||||
在 `src/utils/http.js` 中:
|
||||
```javascript
|
||||
post(url, data, config) {
|
||||
debugger // ← 调试 API 请求
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
### 📊 Source Map 类型对比
|
||||
|
||||
| 类型 | 构建速度 | 重构速度 | 质量 | 适用场景 |
|
||||
|------|---------|---------|------|---------|
|
||||
| `eval-source-map` | 慢 | 快 | 高 | **开发** ✅ |
|
||||
| `cheap-module-source-map` | 中 | 中 | 中 | 开发 |
|
||||
| `source-map` | 很慢 | 很慢 | 最高 | **生产** ✅ |
|
||||
| `none` | 最快 | 最快 | 无 | 不调试 |
|
||||
|
||||
### 💡 调试技巧
|
||||
|
||||
#### 1. 使用 Vue DevTools
|
||||
|
||||
安装 Vue DevTools 浏览器插件:
|
||||
- Chrome: [Vue.js devtools](https://chrome.google.com/webstore)
|
||||
- Firefox: [Vue.js devtools](https://addons.mozilla.org/firefox)
|
||||
|
||||
功能:
|
||||
- 查看组件树
|
||||
- 检查组件数据和 props
|
||||
- 查看 Vuex 状态
|
||||
- 追踪事件
|
||||
|
||||
#### 2. Console 调试技巧
|
||||
|
||||
```javascript
|
||||
// 查看 Store 状态
|
||||
console.log('Store:', this.$store.state)
|
||||
|
||||
// 查看路由信息
|
||||
console.log('Route:', this.$route)
|
||||
|
||||
// 查看组件实例
|
||||
console.log('Component:', this)
|
||||
|
||||
// 使用 console.table 显示数组
|
||||
console.table(this.list)
|
||||
|
||||
// 使用 console.dir 显示对象结构
|
||||
console.dir(AdminFramework)
|
||||
```
|
||||
|
||||
#### 3. 网络请求调试
|
||||
|
||||
在 **Network** 标签页中:
|
||||
- 查看 API 请求和响应
|
||||
- 检查请求头和响应头
|
||||
- 查看请求耗时
|
||||
|
||||
### ⚠️ 注意事项
|
||||
|
||||
#### 1. Source Map 文件大小
|
||||
|
||||
开发模式的 source map 会增加包体积,但:
|
||||
- ✅ 只在开发环境使用
|
||||
- ✅ 不影响生产环境性能
|
||||
- ✅ 不会部署到生产服务器
|
||||
|
||||
#### 2. 刷新页面
|
||||
|
||||
修改代码后:
|
||||
- 保存文件会自动热更新(HMR)
|
||||
- 如果 HMR 失败,手动刷新页面(`F5`)
|
||||
|
||||
#### 3. 清除缓存
|
||||
|
||||
如果看到旧代码:
|
||||
- 硬刷新:`Ctrl+Shift+R` (Chrome) 或 `Ctrl+F5`
|
||||
- 或在开发者工具中禁用缓存
|
||||
|
||||
### 🚀 开始调试
|
||||
|
||||
1. **启动开发服务器**:
|
||||
```bash
|
||||
npm run dev
|
||||
```
|
||||
|
||||
2. **打开浏览器**:
|
||||
访问 `http://localhost:8080`
|
||||
|
||||
3. **打开开发者工具**:
|
||||
按 `F12`
|
||||
|
||||
4. **设置断点**:
|
||||
在 Sources 中找到源文件并设置断点
|
||||
|
||||
5. **开始调试**:
|
||||
刷新页面或触发相关操作
|
||||
|
||||
### 📚 相关资源
|
||||
|
||||
- [Chrome DevTools 官方文档](https://developer.chrome.com/docs/devtools/)
|
||||
- [Firefox Developer Tools](https://developer.mozilla.org/docs/Tools)
|
||||
- [Vue DevTools](https://devtools.vuejs.org/)
|
||||
- [Webpack DevTool 文档](https://webpack.js.org/configuration/devtool/)
|
||||
|
||||
---
|
||||
|
||||
**现在启动项目,打开浏览器开发者工具,就可以看到完整的源码并进行调试了!** 🎉
|
||||
|
||||
@@ -1,409 +0,0 @@
|
||||
# 路由重复导航警告修复
|
||||
|
||||
## 🔍 问题描述
|
||||
|
||||
控制台出现警告:
|
||||
```
|
||||
Avoided redundant navigation to current location: "/home".
|
||||
NavigationDuplicated: Avoided redundant navigation to current location: "/home".
|
||||
```
|
||||
|
||||
## 📋 问题原因
|
||||
|
||||
### 警告产生的场景
|
||||
|
||||
**场景 1:已在首页时访问登录页**
|
||||
```
|
||||
当前路由: /home
|
||||
↓
|
||||
用户访问: /login
|
||||
↓
|
||||
路由守卫检测到已登录
|
||||
↓
|
||||
尝试重定向: next({ name: 'home' })
|
||||
↓
|
||||
目标路由: /home
|
||||
↓
|
||||
❌ 警告:已经在 /home,避免重复导航
|
||||
```
|
||||
|
||||
**场景 2:刷新首页**
|
||||
```
|
||||
当前路由: /home
|
||||
↓
|
||||
刷新页面
|
||||
↓
|
||||
路由守卫再次执行
|
||||
↓
|
||||
检测到已登录且访问登录页(可能的中间状态)
|
||||
↓
|
||||
尝试重定向: next({ name: 'home' })
|
||||
↓
|
||||
❌ 警告:已经在 /home,避免重复导航
|
||||
```
|
||||
|
||||
### 原来的路由守卫代码
|
||||
|
||||
```javascript
|
||||
router.beforeEach((to, from, next) => {
|
||||
const token = getToken()
|
||||
ViewUI.LoadingBar.start()
|
||||
|
||||
if (to.name === 'view_log') {
|
||||
next()
|
||||
return
|
||||
}
|
||||
|
||||
if (!token && to.name !== 'login') {
|
||||
next({ name: 'login' })
|
||||
} else if (!token && to.name === 'login') {
|
||||
next()
|
||||
} else if (token && to.name === 'login') {
|
||||
// ❌ 没有检查来源路由,可能导致重复导航
|
||||
next({ name: homeName })
|
||||
} else {
|
||||
next()
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
**问题**:
|
||||
- 当已登录用户访问登录页时,总是重定向到首页
|
||||
- 没有检查来源路由是否已经是首页
|
||||
- 导致重复导航警告
|
||||
|
||||
## ✅ 解决方案
|
||||
|
||||
### 修改路由守卫逻辑
|
||||
|
||||
**修改文件**:`src/router/index.js`
|
||||
|
||||
**修改后的代码**:
|
||||
```javascript
|
||||
router.beforeEach((to, from, next) => {
|
||||
const token = getToken()
|
||||
ViewUI.LoadingBar.start()
|
||||
|
||||
if (to.name === 'view_log') {
|
||||
next()
|
||||
return
|
||||
}
|
||||
|
||||
if (!token && to.name !== 'login') {
|
||||
// 未登录且访问非登录页 → 跳转到登录页
|
||||
next({ name: 'login' })
|
||||
} else if (!token && to.name === 'login') {
|
||||
// 未登录且访问登录页 → 允许访问
|
||||
next()
|
||||
} else if (token && to.name === 'login') {
|
||||
// 已登录且访问登录页 → 重定向到首页
|
||||
// ✅ 避免重复导航警告
|
||||
if (from.name === homeName) {
|
||||
next(false) // 取消导航,停留在当前页面
|
||||
} else {
|
||||
next({ name: homeName }) // 重定向到首页
|
||||
}
|
||||
} else {
|
||||
// 其他情况 → 允许访问
|
||||
next()
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### 关键改进
|
||||
|
||||
**添加了来源路由检查**:
|
||||
```javascript
|
||||
if (from.name === homeName) {
|
||||
next(false) // 如果来自首页,取消导航
|
||||
} else {
|
||||
next({ name: homeName }) // 否则重定向到首页
|
||||
}
|
||||
```
|
||||
|
||||
**`next(false)` 的作用**:
|
||||
- 取消当前导航
|
||||
- 停留在当前页面(首页)
|
||||
- 不会触发重复导航警告
|
||||
|
||||
## 📊 修复后的流程
|
||||
|
||||
### 场景 1:从首页访问登录页
|
||||
|
||||
```
|
||||
当前路由: /home (from.name = 'home')
|
||||
↓
|
||||
用户访问: /login (to.name = 'login')
|
||||
↓
|
||||
路由守卫检测到已登录
|
||||
↓
|
||||
检查来源路由: from.name === 'home' ✅
|
||||
↓
|
||||
执行: next(false)
|
||||
↓
|
||||
取消导航,停留在首页 ✅
|
||||
↓
|
||||
✅ 没有警告
|
||||
```
|
||||
|
||||
### 场景 2:从其他页面访问登录页
|
||||
|
||||
```
|
||||
当前路由: /system/user (from.name = 'sys_user')
|
||||
↓
|
||||
用户访问: /login (to.name = 'login')
|
||||
↓
|
||||
路由守卫检测到已登录
|
||||
↓
|
||||
检查来源路由: from.name !== 'home' ✅
|
||||
↓
|
||||
执行: next({ name: 'home' })
|
||||
↓
|
||||
重定向到首页 ✅
|
||||
↓
|
||||
✅ 正常导航,没有警告
|
||||
```
|
||||
|
||||
### 场景 3:未登录访问登录页
|
||||
|
||||
```
|
||||
当前路由: 无 (from.name = undefined)
|
||||
↓
|
||||
用户访问: /login (to.name = 'login')
|
||||
↓
|
||||
路由守卫检测到未登录
|
||||
↓
|
||||
执行: next()
|
||||
↓
|
||||
允许访问登录页 ✅
|
||||
```
|
||||
|
||||
### 场景 4:登录成功后跳转
|
||||
|
||||
```
|
||||
登录成功
|
||||
↓
|
||||
执行: window.location.href = '#/'
|
||||
↓
|
||||
触发完整页面加载
|
||||
↓
|
||||
访问: / (重定向到 /home)
|
||||
↓
|
||||
路由守卫检测到已登录
|
||||
↓
|
||||
to.name = 'home' (不是 'login')
|
||||
↓
|
||||
执行: next()
|
||||
↓
|
||||
允许访问首页 ✅
|
||||
```
|
||||
|
||||
## 🎯 `next()` 方法说明
|
||||
|
||||
### `next()` 的不同用法
|
||||
|
||||
**1. `next()`**
|
||||
- 允许当前导航
|
||||
- 继续执行路由跳转
|
||||
|
||||
**2. `next(false)`**
|
||||
- 取消当前导航
|
||||
- 停留在当前页面
|
||||
- 不会触发路由变化
|
||||
|
||||
**3. `next({ name: 'home' })`**
|
||||
- 重定向到指定路由
|
||||
- 取消当前导航
|
||||
- 跳转到新路由
|
||||
|
||||
**4. `next({ path: '/home' })`**
|
||||
- 重定向到指定路径
|
||||
- 取消当前导航
|
||||
- 跳转到新路由
|
||||
|
||||
**5. `next(error)`**
|
||||
- 终止导航
|
||||
- 触发错误处理
|
||||
|
||||
### 为什么使用 `next(false)` 而不是 `next()`?
|
||||
|
||||
**如果使用 `next()`**:
|
||||
```javascript
|
||||
if (token && to.name === 'login') {
|
||||
next() // ❌ 允许访问登录页
|
||||
}
|
||||
```
|
||||
- 会允许已登录用户访问登录页
|
||||
- 不符合业务逻辑
|
||||
|
||||
**如果使用 `next({ name: homeName })`**:
|
||||
```javascript
|
||||
if (token && to.name === 'login') {
|
||||
next({ name: homeName }) // ❌ 如果已在首页,会重复导航
|
||||
}
|
||||
```
|
||||
- 如果来源路由已经是首页,会触发重复导航警告
|
||||
|
||||
**使用 `next(false)`**:
|
||||
```javascript
|
||||
if (token && to.name === 'login') {
|
||||
if (from.name === homeName) {
|
||||
next(false) // ✅ 取消导航,停留在首页
|
||||
} else {
|
||||
next({ name: homeName }) // ✅ 重定向到首页
|
||||
}
|
||||
}
|
||||
```
|
||||
- 如果来源是首页,取消导航,停留在首页
|
||||
- 如果来源不是首页,重定向到首页
|
||||
- 避免重复导航警告
|
||||
|
||||
## ✅ 验证清单
|
||||
|
||||
### 功能验证
|
||||
|
||||
- ✅ 未登录访问登录页 → 正常显示登录页
|
||||
- ✅ 未登录访问其他页面 → 重定向到登录页
|
||||
- ✅ 已登录访问登录页(从首页) → 停留在首页
|
||||
- ✅ 已登录访问登录页(从其他页面) → 重定向到首页
|
||||
- ✅ 登录成功 → 跳转到首页
|
||||
|
||||
### 警告检查
|
||||
|
||||
- ✅ 控制台没有 "Avoided redundant navigation" 警告
|
||||
- ✅ 控制台没有 "NavigationDuplicated" 警告
|
||||
- ✅ 路由跳转正常工作
|
||||
|
||||
### 边界情况
|
||||
|
||||
- ✅ 刷新首页 → 停留在首页
|
||||
- ✅ 刷新其他页面 → 停留在当前页面
|
||||
- ✅ 直接访问 URL → 正确处理
|
||||
|
||||
## 💡 最佳实践
|
||||
|
||||
### 1. 路由守卫中检查来源路由
|
||||
|
||||
**推荐**:
|
||||
```javascript
|
||||
if (token && to.name === 'login') {
|
||||
if (from.name === homeName) {
|
||||
next(false) // 来自首页,取消导航
|
||||
} else {
|
||||
next({ name: homeName }) // 来自其他页面,重定向
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**不推荐**:
|
||||
```javascript
|
||||
if (token && to.name === 'login') {
|
||||
next({ name: homeName }) // 可能导致重复导航
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 使用 `next(false)` 取消不必要的导航
|
||||
|
||||
**推荐**:
|
||||
```javascript
|
||||
if (shouldCancelNavigation) {
|
||||
next(false) // 取消导航
|
||||
}
|
||||
```
|
||||
|
||||
**不推荐**:
|
||||
```javascript
|
||||
if (shouldCancelNavigation) {
|
||||
// 什么都不做,导航会继续
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 避免在路由守卫中使用 `window.location`
|
||||
|
||||
**推荐**:
|
||||
```javascript
|
||||
router.beforeEach((to, from, next) => {
|
||||
if (needRedirect) {
|
||||
next({ name: 'home' }) // 使用 next() 重定向
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
**不推荐**:
|
||||
```javascript
|
||||
router.beforeEach((to, from, next) => {
|
||||
if (needRedirect) {
|
||||
window.location.href = '#/home' // 不要在守卫中使用
|
||||
next(false)
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
## 🔧 相关代码
|
||||
|
||||
### 完整的路由守卫代码
|
||||
|
||||
**文件**:`src/router/index.js`
|
||||
|
||||
```javascript
|
||||
// 路由守卫配置
|
||||
export const setupRouterGuards = (router, ViewUI, homeName = 'home') => {
|
||||
router.beforeEach((to, from, next) => {
|
||||
const token = getToken()
|
||||
ViewUI.LoadingBar.start()
|
||||
|
||||
if (to.name === 'view_log') {
|
||||
next()
|
||||
return
|
||||
}
|
||||
|
||||
if (!token && to.name !== 'login') {
|
||||
// 未登录且访问非登录页 → 跳转到登录页
|
||||
next({ name: 'login' })
|
||||
} else if (!token && to.name === 'login') {
|
||||
// 未登录且访问登录页 → 允许访问
|
||||
next()
|
||||
} else if (token && to.name === 'login') {
|
||||
// 已登录且访问登录页 → 重定向到首页
|
||||
// 避免重复导航警告
|
||||
if (from.name === homeName) {
|
||||
next(false) // 来自首页,取消导航
|
||||
} else {
|
||||
next({ name: homeName }) // 来自其他页面,重定向到首页
|
||||
}
|
||||
} else {
|
||||
// 其他情况 → 允许访问
|
||||
next()
|
||||
}
|
||||
})
|
||||
|
||||
router.afterEach(to => {
|
||||
ViewUI.LoadingBar.finish()
|
||||
window.scrollTo(0, 0)
|
||||
})
|
||||
|
||||
return router
|
||||
}
|
||||
```
|
||||
|
||||
## 📝 总结
|
||||
|
||||
### 修改内容
|
||||
|
||||
- ✅ 在路由守卫中添加了来源路由检查
|
||||
- ✅ 使用 `next(false)` 避免重复导航
|
||||
- ✅ 保持了原有的业务逻辑
|
||||
|
||||
### 效果
|
||||
|
||||
- ✅ 消除了 "Avoided redundant navigation" 警告
|
||||
- ✅ 消除了 "NavigationDuplicated" 警告
|
||||
- ✅ 路由跳转更加流畅
|
||||
- ✅ 用户体验更好
|
||||
|
||||
---
|
||||
|
||||
**路由重复导航警告已修复!** 🎉
|
||||
|
||||
现在控制台不会再出现重复导航的警告了。
|
||||
|
||||
@@ -1,483 +0,0 @@
|
||||
# 路由重复导航错误完全修复
|
||||
|
||||
## 🔍 问题描述
|
||||
|
||||
页面直接报错:
|
||||
```
|
||||
ERROR
|
||||
Avoided redundant navigation to current location: "/home".
|
||||
NavigationDuplicated: Avoided redundant navigation to current location: "/home".
|
||||
at HashHistory.push
|
||||
at VueComponent.turnToPage (main.vue:108:20)
|
||||
```
|
||||
|
||||
## 📋 问题原因
|
||||
|
||||
### 错误来源
|
||||
|
||||
错误来自 `src/components/main/main.vue` 的两个方法:
|
||||
|
||||
**1. `goHome()` 方法**
|
||||
```javascript
|
||||
goHome() {
|
||||
this.$router.push({ path: '/' }) // ❌ 如果已在首页,会报错
|
||||
}
|
||||
```
|
||||
|
||||
**2. `turnToPage()` 方法**
|
||||
```javascript
|
||||
turnToPage(route) {
|
||||
// ...
|
||||
this.$router.push({
|
||||
name,
|
||||
params,
|
||||
query
|
||||
}) // ❌ 如果跳转到当前页面,会报错
|
||||
}
|
||||
```
|
||||
|
||||
### 触发场景
|
||||
|
||||
**场景 1:点击 Logo 回到首页**
|
||||
```
|
||||
当前页面: /home
|
||||
↓
|
||||
点击 Logo
|
||||
↓
|
||||
调用 goHome()
|
||||
↓
|
||||
执行 this.$router.push({ path: '/' })
|
||||
↓
|
||||
❌ 错误:已经在首页,重复导航
|
||||
```
|
||||
|
||||
**场景 2:点击当前激活的菜单**
|
||||
```
|
||||
当前页面: /home
|
||||
↓
|
||||
点击"首页"菜单
|
||||
↓
|
||||
调用 turnToPage({ name: 'home' })
|
||||
↓
|
||||
执行 this.$router.push({ name: 'home' })
|
||||
↓
|
||||
❌ 错误:已经在首页,重复导航
|
||||
```
|
||||
|
||||
**场景 3:刷新页面后点击菜单**
|
||||
```
|
||||
刷新页面
|
||||
↓
|
||||
当前路由: /home
|
||||
↓
|
||||
菜单激活状态: home
|
||||
↓
|
||||
点击"首页"菜单
|
||||
↓
|
||||
❌ 错误:重复导航
|
||||
```
|
||||
|
||||
### Vue Router 的行为
|
||||
|
||||
**Vue Router 2.x/3.x**:
|
||||
- `router.push()` 返回一个 Promise
|
||||
- 如果导航到当前路由,会抛出 `NavigationDuplicated` 错误
|
||||
- 这个错误会导致页面崩溃(如果没有捕获)
|
||||
|
||||
## ✅ 解决方案
|
||||
|
||||
### 方案 1:在跳转前检查是否是当前路由(推荐)
|
||||
|
||||
**优点**:
|
||||
- 避免不必要的路由跳转
|
||||
- 性能更好
|
||||
- 代码更清晰
|
||||
|
||||
**缺点**:
|
||||
- 需要在每个跳转方法中添加检查
|
||||
|
||||
### 方案 2:捕获 Promise 错误
|
||||
|
||||
**优点**:
|
||||
- 代码简单
|
||||
- 统一处理错误
|
||||
|
||||
**缺点**:
|
||||
- 仍然会触发路由跳转逻辑
|
||||
- 性能稍差
|
||||
|
||||
### 方案 3:结合使用(本次采用)
|
||||
|
||||
**优点**:
|
||||
- 既避免不必要的跳转
|
||||
- 又捕获可能的错误
|
||||
- 最稳健的方案
|
||||
|
||||
## 📝 修改内容
|
||||
|
||||
### 修改 `goHome()` 方法
|
||||
|
||||
**修改前**:
|
||||
```javascript
|
||||
goHome() {
|
||||
this.$router.push({ path: '/' }) // ❌ 可能重复导航
|
||||
}
|
||||
```
|
||||
|
||||
**修改后**:
|
||||
```javascript
|
||||
goHome() {
|
||||
// 避免重复导航到当前页面
|
||||
if (this.$route.path !== '/' && this.$route.path !== '/home') {
|
||||
this.$router.push({ path: '/' }).catch(err => {
|
||||
// 忽略重复导航错误
|
||||
if (err.name !== 'NavigationDuplicated') {
|
||||
console.error(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**改进点**:
|
||||
- ✅ 检查当前路由是否是 `/` 或 `/home`
|
||||
- ✅ 只在不是首页时才跳转
|
||||
- ✅ 捕获并忽略重复导航错误
|
||||
- ✅ 其他错误仍然会打印到控制台
|
||||
|
||||
### 修改 `turnToPage()` 方法
|
||||
|
||||
**修改前**:
|
||||
```javascript
|
||||
turnToPage(route) {
|
||||
let { name, params, query } = {}
|
||||
if (typeof route === 'string') name = route
|
||||
else {
|
||||
name = route.name
|
||||
params = route.params
|
||||
query = route.query
|
||||
}
|
||||
if (name.indexOf('isTurnByHref_') > -1) {
|
||||
window.open(name.split('_')[1])
|
||||
return
|
||||
}
|
||||
this.$router.push({
|
||||
name,
|
||||
params,
|
||||
query
|
||||
}) // ❌ 可能重复导航
|
||||
}
|
||||
```
|
||||
|
||||
**修改后**:
|
||||
```javascript
|
||||
turnToPage(route) {
|
||||
let { name, params, query } = {}
|
||||
if (typeof route === 'string') name = route
|
||||
else {
|
||||
name = route.name
|
||||
params = route.params
|
||||
query = route.query
|
||||
}
|
||||
if (name.indexOf('isTurnByHref_') > -1) {
|
||||
window.open(name.split('_')[1])
|
||||
return
|
||||
}
|
||||
|
||||
// 避免重复导航到当前页面
|
||||
if (this.$route.name === name) {
|
||||
return
|
||||
}
|
||||
|
||||
this.$router.push({
|
||||
name,
|
||||
params,
|
||||
query
|
||||
}).catch(err => {
|
||||
// 忽略重复导航错误
|
||||
if (err.name !== 'NavigationDuplicated') {
|
||||
console.error(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
**改进点**:
|
||||
- ✅ 检查目标路由是否是当前路由
|
||||
- ✅ 如果是当前路由,直接返回,不跳转
|
||||
- ✅ 捕获并忽略重复导航错误
|
||||
- ✅ 其他错误仍然会打印到控制台
|
||||
|
||||
## 📊 修复后的流程
|
||||
|
||||
### 场景 1:在首页点击 Logo
|
||||
|
||||
```
|
||||
当前路由: /home
|
||||
↓
|
||||
点击 Logo
|
||||
↓
|
||||
调用 goHome()
|
||||
↓
|
||||
检查: this.$route.path === '/home' ✅
|
||||
↓
|
||||
直接返回,不跳转 ✅
|
||||
↓
|
||||
✅ 没有错误
|
||||
```
|
||||
|
||||
### 场景 2:在其他页面点击 Logo
|
||||
|
||||
```
|
||||
当前路由: /system/user
|
||||
↓
|
||||
点击 Logo
|
||||
↓
|
||||
调用 goHome()
|
||||
↓
|
||||
检查: this.$route.path !== '/' && this.$route.path !== '/home' ✅
|
||||
↓
|
||||
执行跳转: this.$router.push({ path: '/' })
|
||||
↓
|
||||
跳转到首页 ✅
|
||||
↓
|
||||
✅ 正常跳转
|
||||
```
|
||||
|
||||
### 场景 3:点击当前激活的菜单
|
||||
|
||||
```
|
||||
当前路由: /home (name: 'home')
|
||||
↓
|
||||
点击"首页"菜单
|
||||
↓
|
||||
调用 turnToPage({ name: 'home' })
|
||||
↓
|
||||
检查: this.$route.name === 'home' ✅
|
||||
↓
|
||||
直接返回,不跳转 ✅
|
||||
↓
|
||||
✅ 没有错误
|
||||
```
|
||||
|
||||
### 场景 4:点击其他菜单
|
||||
|
||||
```
|
||||
当前路由: /home (name: 'home')
|
||||
↓
|
||||
点击"用户管理"菜单
|
||||
↓
|
||||
调用 turnToPage({ name: 'sys_user' })
|
||||
↓
|
||||
检查: this.$route.name !== 'sys_user' ✅
|
||||
↓
|
||||
执行跳转: this.$router.push({ name: 'sys_user' })
|
||||
↓
|
||||
跳转到用户管理页面 ✅
|
||||
↓
|
||||
✅ 正常跳转
|
||||
```
|
||||
|
||||
## 🎯 关键点说明
|
||||
|
||||
### 1. 为什么要检查两个路径?
|
||||
|
||||
```javascript
|
||||
if (this.$route.path !== '/' && this.$route.path !== '/home') {
|
||||
// 跳转
|
||||
}
|
||||
```
|
||||
|
||||
**原因**:
|
||||
- 主路由配置了 `redirect: '/home'`
|
||||
- 访问 `/` 会重定向到 `/home`
|
||||
- 所以需要检查两个路径
|
||||
|
||||
### 2. 为什么使用 `catch()` 捕获错误?
|
||||
|
||||
```javascript
|
||||
this.$router.push({ path: '/' }).catch(err => {
|
||||
if (err.name !== 'NavigationDuplicated') {
|
||||
console.error(err)
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
**原因**:
|
||||
- `router.push()` 返回 Promise
|
||||
- 重复导航会抛出 `NavigationDuplicated` 错误
|
||||
- 如果不捕获,错误会导致页面崩溃
|
||||
- 捕获后可以忽略重复导航错误,但保留其他错误的日志
|
||||
|
||||
### 3. 为什么检查 `err.name !== 'NavigationDuplicated'`?
|
||||
|
||||
```javascript
|
||||
if (err.name !== 'NavigationDuplicated') {
|
||||
console.error(err)
|
||||
}
|
||||
```
|
||||
|
||||
**原因**:
|
||||
- 只忽略重复导航错误
|
||||
- 其他错误(如路由不存在、权限错误等)仍然需要打印
|
||||
- 方便调试和排查问题
|
||||
|
||||
### 4. 为什么在 `turnToPage` 中直接 `return`?
|
||||
|
||||
```javascript
|
||||
if (this.$route.name === name) {
|
||||
return // 直接返回
|
||||
}
|
||||
```
|
||||
|
||||
**原因**:
|
||||
- 如果是当前路由,不需要跳转
|
||||
- 直接返回可以避免不必要的路由操作
|
||||
- 性能更好
|
||||
|
||||
## ✅ 验证清单
|
||||
|
||||
### 功能验证
|
||||
|
||||
- ✅ 在首页点击 Logo → 不跳转,没有错误
|
||||
- ✅ 在其他页面点击 Logo → 跳转到首页
|
||||
- ✅ 点击当前激活的菜单 → 不跳转,没有错误
|
||||
- ✅ 点击其他菜单 → 正常跳转
|
||||
- ✅ 刷新页面后点击菜单 → 正常工作
|
||||
|
||||
### 错误检查
|
||||
|
||||
- ✅ 控制台没有 "NavigationDuplicated" 错误
|
||||
- ✅ 控制台没有 "Avoided redundant navigation" 警告
|
||||
- ✅ 页面不会崩溃
|
||||
|
||||
### 边界情况
|
||||
|
||||
- ✅ 快速连续点击菜单 → 正常工作
|
||||
- ✅ 快速连续点击 Logo → 正常工作
|
||||
- ✅ 在不同页面之间快速切换 → 正常工作
|
||||
|
||||
## 💡 最佳实践
|
||||
|
||||
### 1. 路由跳转前检查目标路由
|
||||
|
||||
**推荐**:
|
||||
```javascript
|
||||
if (this.$route.name !== targetName) {
|
||||
this.$router.push({ name: targetName })
|
||||
}
|
||||
```
|
||||
|
||||
**不推荐**:
|
||||
```javascript
|
||||
this.$router.push({ name: targetName }) // 可能重复导航
|
||||
```
|
||||
|
||||
### 2. 捕获 Promise 错误
|
||||
|
||||
**推荐**:
|
||||
```javascript
|
||||
this.$router.push({ name: targetName }).catch(err => {
|
||||
if (err.name !== 'NavigationDuplicated') {
|
||||
console.error(err)
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
**不推荐**:
|
||||
```javascript
|
||||
this.$router.push({ name: targetName }) // 错误会导致页面崩溃
|
||||
```
|
||||
|
||||
### 3. 结合使用检查和捕获
|
||||
|
||||
**推荐**:
|
||||
```javascript
|
||||
if (this.$route.name !== targetName) {
|
||||
this.$router.push({ name: targetName }).catch(err => {
|
||||
if (err.name !== 'NavigationDuplicated') {
|
||||
console.error(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
**优点**:
|
||||
- 避免不必要的跳转
|
||||
- 捕获可能的错误
|
||||
- 最稳健的方案
|
||||
|
||||
## 🔧 相关代码
|
||||
|
||||
### 完整的 `goHome()` 方法
|
||||
|
||||
```javascript
|
||||
goHome() {
|
||||
// 避免重复导航到当前页面
|
||||
if (this.$route.path !== '/' && this.$route.path !== '/home') {
|
||||
this.$router.push({ path: '/' }).catch(err => {
|
||||
// 忽略重复导航错误
|
||||
if (err.name !== 'NavigationDuplicated') {
|
||||
console.error(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 完整的 `turnToPage()` 方法
|
||||
|
||||
```javascript
|
||||
turnToPage(route) {
|
||||
let { name, params, query } = {}
|
||||
if (typeof route === 'string') name = route
|
||||
else {
|
||||
name = route.name
|
||||
params = route.params
|
||||
query = route.query
|
||||
}
|
||||
if (name.indexOf('isTurnByHref_') > -1) {
|
||||
window.open(name.split('_')[1])
|
||||
return
|
||||
}
|
||||
|
||||
// 避免重复导航到当前页面
|
||||
if (this.$route.name === name) {
|
||||
return
|
||||
}
|
||||
|
||||
this.$router.push({
|
||||
name,
|
||||
params,
|
||||
query
|
||||
}).catch(err => {
|
||||
// 忽略重复导航错误
|
||||
if (err.name !== 'NavigationDuplicated') {
|
||||
console.error(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
## 📝 总结
|
||||
|
||||
### 修改内容
|
||||
|
||||
- ✅ 修改了 `goHome()` 方法,添加路由检查和错误捕获
|
||||
- ✅ 修改了 `turnToPage()` 方法,添加路由检查和错误捕获
|
||||
- ✅ 避免了重复导航错误
|
||||
- ✅ 保持了原有的业务逻辑
|
||||
|
||||
### 效果
|
||||
|
||||
- ✅ 消除了 "NavigationDuplicated" 错误
|
||||
- ✅ 页面不会崩溃
|
||||
- ✅ 路由跳转更加流畅
|
||||
- ✅ 用户体验更好
|
||||
|
||||
---
|
||||
|
||||
**路由重复导航错误已完全修复!** 🎉
|
||||
|
||||
现在可以放心地点击菜单和 Logo,不会再出现错误了。
|
||||
|
||||
@@ -1,427 +0,0 @@
|
||||
# 首页路由梳理说明
|
||||
|
||||
## 🔍 问题分析
|
||||
|
||||
### 原来的问题
|
||||
|
||||
首页在多个地方被设置,导致路由混乱和错误:
|
||||
|
||||
1. **`src/utils/uiTool.js` 的 `getRoutes` 方法**
|
||||
- 接收了 `HomePage` 参数但没有使用
|
||||
- `children` 初始化为空数组
|
||||
- 尝试查找不存在的 `homeRoute`,返回 `undefined`
|
||||
- 使用 `undefined` 导致路由错误
|
||||
|
||||
2. **`src/index.js` 的 `install` 方法**
|
||||
- 调用 `getRoutes` 创建路由
|
||||
- 传入了 `HomePage` 但没有被使用
|
||||
|
||||
3. **`demo-project/src/main.js` 的 `mounted` 钩子**
|
||||
- 又调用了 `setAuthorityMenus` 重新设置菜单
|
||||
- 导致路由被重复设置
|
||||
|
||||
### 错误信息
|
||||
|
||||
```
|
||||
vue-router.esm.js:1396 Uncaught TypeError: Cannot read properties of undefined (reading 'path')
|
||||
[vuex] unknown action type: getSysTitle
|
||||
[vuex] unknown getter: sysFormModel
|
||||
```
|
||||
|
||||
## ✅ 解决方案
|
||||
|
||||
### 设计原则
|
||||
|
||||
1. **单一职责**:每个方法只负责一件事
|
||||
2. **避免重复**:路由只在一个地方创建和设置
|
||||
3. **清晰的流程**:首页设置流程清晰可追踪
|
||||
|
||||
### 新的首页设置流程
|
||||
|
||||
```
|
||||
应用启动
|
||||
↓
|
||||
框架 install 方法
|
||||
↓
|
||||
调用 getRoutes 创建主路由
|
||||
↓
|
||||
创建默认首页路由 (/home)
|
||||
↓
|
||||
检查 localStorage 中的权限菜单
|
||||
↓
|
||||
如果有权限菜单,合并到主路由
|
||||
↓
|
||||
创建 VueRouter 实例
|
||||
↓
|
||||
应用启动完成
|
||||
↓
|
||||
用户登录
|
||||
↓
|
||||
保存权限菜单到 localStorage
|
||||
↓
|
||||
刷新页面
|
||||
↓
|
||||
重新执行上述流程(此时有权限菜单)
|
||||
```
|
||||
|
||||
## 📝 修改内容
|
||||
|
||||
### 1. 修复 `src/utils/uiTool.js` 的 `getRoutes` 方法
|
||||
|
||||
**修改前**:
|
||||
```javascript
|
||||
static getRoutes(Main, ParentView, Page404, HomePage) {
|
||||
let mainRoute = {
|
||||
path: '/',
|
||||
name: '主视图',
|
||||
redirect: '/home',
|
||||
component: Main,
|
||||
meta: { title: '首页', notCache: true },
|
||||
children: [] // ❌ 空数组,没有默认首页
|
||||
}
|
||||
|
||||
// ... 后续逻辑尝试使用不存在的 homeRoute
|
||||
const homeRoute = mainRoute.children.find(r => r.name === 'home') // ❌ undefined
|
||||
mainRoute.children = [homeRoute, ...curRoutes] // ❌ 使用 undefined
|
||||
}
|
||||
```
|
||||
|
||||
**修改后**:
|
||||
```javascript
|
||||
static getRoutes(Main, ParentView, Page404, HomePage) {
|
||||
// ✅ 创建默认的首页路由
|
||||
const defaultHomeRoute = {
|
||||
path: '/home',
|
||||
name: 'home',
|
||||
meta: { title: '首页', notCache: true },
|
||||
component: HomePage || {
|
||||
render: h => h('div', { style: { padding: '20px' } }, '欢迎使用管理系统')
|
||||
}
|
||||
}
|
||||
|
||||
let mainRoute = {
|
||||
path: '/',
|
||||
name: '主视图',
|
||||
redirect: '/home',
|
||||
component: Main,
|
||||
meta: { title: '首页', notCache: true },
|
||||
children: [defaultHomeRoute] // ✅ 包含默认首页
|
||||
}
|
||||
|
||||
// 从 localStorage 读取权限菜单
|
||||
if (localStorage.authorityMenus && localStorage.authorityMenus !== 'undefined') {
|
||||
let authorityMenus = JSON.parse(localStorage.authorityMenus) || []
|
||||
|
||||
if (authorityMenus && authorityMenus.length > 0) {
|
||||
let menus = uiTool.transformTree(authorityMenus)
|
||||
let curRoutes = uiTool.menuToRoute(menus, ParentView, Page404)
|
||||
|
||||
// 检查权限路由中是否有 home
|
||||
const hasHome = curRoutes.some(r => r.name === 'home')
|
||||
|
||||
if (hasHome) {
|
||||
// 如果权限路由中有 home,使用权限路由的 home
|
||||
mainRoute.children = curRoutes
|
||||
} else {
|
||||
// 如果权限路由中没有 home,保留默认 home 并添加其他路由
|
||||
mainRoute.children = [defaultHomeRoute, ...curRoutes]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return mainRoute
|
||||
}
|
||||
```
|
||||
|
||||
**改进点**:
|
||||
- ✅ 使用传入的 `HomePage` 参数
|
||||
- ✅ 创建默认的首页路由
|
||||
- ✅ 正确处理权限菜单的合并
|
||||
- ✅ 避免使用 `undefined`
|
||||
|
||||
### 2. 简化 `demo-project/src/main.js`
|
||||
|
||||
**修改前**:
|
||||
```javascript
|
||||
window.rootVue = new Vue({
|
||||
el: '#app',
|
||||
router: AdminFramework.router,
|
||||
store: AdminFramework.store,
|
||||
render: h => h(App),
|
||||
mounted() {
|
||||
AdminFramework.uiTool.setRem()
|
||||
|
||||
// ❌ 重复设置权限菜单
|
||||
this.$store.dispatch('user/setAuthorityMenus', {
|
||||
Main: AdminFramework.Main,
|
||||
ParentView: AdminFramework.ParentView,
|
||||
Page404: AdminFramework.Page404
|
||||
})
|
||||
|
||||
this.$store.dispatch('app/getSysTitle', {
|
||||
defaultTitle: 'Demo 管理系统',
|
||||
defaultLogo: ''
|
||||
})
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
**修改后**:
|
||||
```javascript
|
||||
window.rootVue = new Vue({
|
||||
el: '#app',
|
||||
router: AdminFramework.router,
|
||||
store: AdminFramework.store,
|
||||
render: h => h(App),
|
||||
mounted() {
|
||||
AdminFramework.uiTool.setRem()
|
||||
|
||||
// ✅ 只获取系统标题,不重复设置菜单
|
||||
this.$store.dispatch('app/getSysTitle', {
|
||||
defaultTitle: 'Demo 管理系统',
|
||||
defaultLogo: ''
|
||||
})
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
**改进点**:
|
||||
- ✅ 移除了重复的 `setAuthorityMenus` 调用
|
||||
- ✅ 路由只在框架初始化时创建一次
|
||||
- ✅ 登录后通过刷新页面重新加载路由
|
||||
|
||||
### 3. 修复 Vuex 模块命名空间问题
|
||||
|
||||
**修改的文件**:
|
||||
- `src/components/main/main.vue`
|
||||
- `src/components/main/components/user/user.vue`
|
||||
|
||||
**修改前**:
|
||||
```javascript
|
||||
// main.vue
|
||||
...mapGetters({
|
||||
sysFormModel: 'sysFormModel', // ❌ 缺少模块前缀
|
||||
menuList: 'menuList',
|
||||
userName: 'userName',
|
||||
userAvator: 'avatorImgPath'
|
||||
})
|
||||
|
||||
// user.vue
|
||||
...mapActions(['handleLogOut']) // ❌ 缺少模块前缀
|
||||
```
|
||||
|
||||
**修改后**:
|
||||
```javascript
|
||||
// main.vue
|
||||
...mapGetters({
|
||||
sysFormModel: 'app/sysFormModel', // ✅ 添加模块前缀
|
||||
menuList: 'user/menuList',
|
||||
userName: 'user/userName',
|
||||
userAvator: 'user/avatorImgPath'
|
||||
})
|
||||
|
||||
// user.vue
|
||||
...mapActions('user', ['handleLogOut']) // ✅ 指定模块
|
||||
```
|
||||
|
||||
## 🎯 完整的首页路由流程
|
||||
|
||||
### 首次访问(未登录)
|
||||
|
||||
```
|
||||
1. 用户访问 http://localhost:8080
|
||||
↓
|
||||
2. 框架初始化
|
||||
↓
|
||||
3. getRoutes 创建主路由
|
||||
- localStorage 中没有 authorityMenus
|
||||
- 使用默认首页路由
|
||||
↓
|
||||
4. 路由配置:
|
||||
{
|
||||
path: '/',
|
||||
component: Main,
|
||||
children: [
|
||||
{ path: '/home', name: 'home', component: HomePage }
|
||||
]
|
||||
}
|
||||
↓
|
||||
5. 显示登录页面
|
||||
```
|
||||
|
||||
### 登录流程
|
||||
|
||||
```
|
||||
1. 用户输入用户名密码
|
||||
↓
|
||||
2. 调用登录接口
|
||||
↓
|
||||
3. 保存 token 和用户信息
|
||||
↓
|
||||
4. 调用 setAuthorityMenus
|
||||
- 尝试获取权限菜单(可能失败)
|
||||
- 使用默认菜单配置
|
||||
- 保存到 localStorage
|
||||
↓
|
||||
5. 刷新页面 (window.location.reload())
|
||||
↓
|
||||
6. 重新执行首次访问流程
|
||||
- 此时 localStorage 中有 authorityMenus
|
||||
- 根据权限菜单生成路由
|
||||
```
|
||||
|
||||
### 登录后访问
|
||||
|
||||
```
|
||||
1. 用户访问 http://localhost:8080
|
||||
↓
|
||||
2. 框架初始化
|
||||
↓
|
||||
3. getRoutes 创建主路由
|
||||
- localStorage 中有 authorityMenus
|
||||
- 解析权限菜单
|
||||
- 生成权限路由
|
||||
↓
|
||||
4. 路由配置:
|
||||
{
|
||||
path: '/',
|
||||
component: Main,
|
||||
children: [
|
||||
{ path: '/home', name: 'home', component: HomePage },
|
||||
{ path: '/system/user', name: 'sys_user', component: SysUser },
|
||||
{ path: '/system/role', name: 'sys_role', component: SysRole },
|
||||
// ... 其他权限路由
|
||||
]
|
||||
}
|
||||
↓
|
||||
5. 显示系统首页
|
||||
```
|
||||
|
||||
## 📊 路由结构
|
||||
|
||||
### 完整的路由树
|
||||
|
||||
```
|
||||
routes: [
|
||||
// 登录页面
|
||||
{
|
||||
path: '/login',
|
||||
name: 'login',
|
||||
component: LoginPage
|
||||
},
|
||||
|
||||
// 主路由
|
||||
{
|
||||
path: '/',
|
||||
name: '主视图',
|
||||
redirect: '/home',
|
||||
component: Main,
|
||||
children: [
|
||||
// 默认首页
|
||||
{
|
||||
path: '/home',
|
||||
name: 'home',
|
||||
component: HomePage,
|
||||
meta: { title: '首页', notCache: true }
|
||||
},
|
||||
|
||||
// 权限路由(从 authorityMenus 生成)
|
||||
{
|
||||
path: '/system/user',
|
||||
name: 'sys_user',
|
||||
component: SysUser,
|
||||
meta: { title: '用户管理' }
|
||||
},
|
||||
{
|
||||
path: '/system/role',
|
||||
name: 'sys_role',
|
||||
component: SysRole,
|
||||
meta: { title: '角色管理' }
|
||||
},
|
||||
// ... 更多权限路由
|
||||
]
|
||||
},
|
||||
|
||||
// 错误页面
|
||||
{
|
||||
path: '/401',
|
||||
name: 'error_401',
|
||||
component: Page401
|
||||
},
|
||||
{
|
||||
path: '/500',
|
||||
name: 'error_500',
|
||||
component: Page500
|
||||
},
|
||||
{
|
||||
path: '*',
|
||||
name: 'error_404',
|
||||
component: Page404
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
## ✅ 验证清单
|
||||
|
||||
### 功能验证
|
||||
|
||||
- ✅ 首次访问显示登录页面
|
||||
- ✅ 登录成功后跳转到首页
|
||||
- ✅ 首页正确显示
|
||||
- ✅ 左侧菜单正确显示
|
||||
- ✅ 系统标题正确显示
|
||||
- ✅ 用户名正确显示
|
||||
- ✅ 页面跳转正常
|
||||
- ✅ 刷新页面后状态保持
|
||||
|
||||
### 错误修复
|
||||
|
||||
- ✅ 修复了 `Cannot read properties of undefined (reading 'path')` 错误
|
||||
- ✅ 修复了 `[vuex] unknown action type: getSysTitle` 错误
|
||||
- ✅ 修复了 `[vuex] unknown getter: sysFormModel` 错误
|
||||
- ✅ 修复了首页路由重复设置的问题
|
||||
|
||||
## 💡 最佳实践
|
||||
|
||||
### 1. 路由创建原则
|
||||
|
||||
- **单一入口**:路由只在框架初始化时创建一次
|
||||
- **数据驱动**:根据 localStorage 中的数据动态生成路由
|
||||
- **默认兜底**:始终提供默认的首页路由
|
||||
|
||||
### 2. 权限菜单处理
|
||||
|
||||
- **登录时保存**:登录成功后保存权限菜单到 localStorage
|
||||
- **启动时读取**:应用启动时从 localStorage 读取权限菜单
|
||||
- **刷新更新**:登录后刷新页面,重新生成路由
|
||||
|
||||
### 3. 避免的陷阱
|
||||
|
||||
- ❌ 不要在多个地方创建或修改路由
|
||||
- ❌ 不要在 mounted 钩子中重复设置菜单
|
||||
- ❌ 不要忘记处理 undefined 的情况
|
||||
- ❌ 不要忘记 Vuex 模块的命名空间
|
||||
|
||||
## 🎉 总结
|
||||
|
||||
### 修复的问题
|
||||
|
||||
1. ✅ 首页路由正确创建
|
||||
2. ✅ 权限菜单正确合并
|
||||
3. ✅ 避免了重复设置
|
||||
4. ✅ 修复了 Vuex 命名空间问题
|
||||
5. ✅ 流程清晰可维护
|
||||
|
||||
### 代码改进
|
||||
|
||||
- **减少复杂度**:移除了重复的菜单设置逻辑
|
||||
- **提高可读性**:流程更清晰,易于理解
|
||||
- **增强健壮性**:正确处理各种边界情况
|
||||
- **符合规范**:遵循 Vue 和 Vuex 的最佳实践
|
||||
|
||||
---
|
||||
|
||||
**首页路由梳理完成!** 🎉
|
||||
|
||||
现在路由创建流程清晰、简洁、可靠。
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
module.exports = {
|
||||
presets: [
|
||||
[
|
||||
'@babel/preset-env',
|
||||
{
|
||||
targets: {
|
||||
browsers: ['> 1%', 'last 2 versions', 'not ie <= 8']
|
||||
}
|
||||
}
|
||||
],
|
||||
'@vue/babel-preset-jsx'
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
@echo off
|
||||
echo ====================================
|
||||
echo 安装 Demo 项目依赖
|
||||
echo ====================================
|
||||
echo.
|
||||
|
||||
echo [1/3] 清理旧依赖...
|
||||
if exist "node_modules\" (
|
||||
echo 删除 node_modules 目录...
|
||||
rmdir /s /q node_modules
|
||||
)
|
||||
if exist "package-lock.json" (
|
||||
echo 删除 package-lock.json...
|
||||
del /f /q package-lock.json
|
||||
)
|
||||
echo.
|
||||
|
||||
echo [2/3] 安装所有依赖...
|
||||
echo 这可能需要几分钟时间,请耐心等待...
|
||||
echo.
|
||||
call npm install
|
||||
echo.
|
||||
|
||||
echo [3/3] 验证关键依赖...
|
||||
echo.
|
||||
call npm list @vue/babel-preset-jsx brace vue2-ace-editor --depth=0
|
||||
echo.
|
||||
|
||||
echo ====================================
|
||||
echo 安装完成!
|
||||
echo ====================================
|
||||
echo.
|
||||
echo 现在可以运行以下命令启动项目:
|
||||
echo 1. 双击 start.bat
|
||||
echo 2. 或运行: npm run dev
|
||||
echo.
|
||||
|
||||
pause
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
{
|
||||
"name": "demo-project",
|
||||
"version": "1.0.0",
|
||||
"description": "Admin Framework 使用示例项目",
|
||||
"main": "src/main.js",
|
||||
"scripts": {
|
||||
"dev": "webpack-dev-server --mode development --open",
|
||||
"build": "webpack --mode production"
|
||||
},
|
||||
"keywords": [
|
||||
"admin",
|
||||
"vue",
|
||||
"demo"
|
||||
],
|
||||
"author": "light",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"axios": "^0.21.4",
|
||||
"brace": "^0.11.1",
|
||||
"core-js": "^3.45.1",
|
||||
"dayjs": "^1.10.0",
|
||||
"js-cookie": "^2.2.1",
|
||||
"view-design": "^4.7.0",
|
||||
"vue": "^2.6.14",
|
||||
"vue-router": "^3.5.3",
|
||||
"vue2-ace-editor": "^0.0.15",
|
||||
"vuex": "^3.6.2",
|
||||
"vuex-persistedstate": "^4.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.12.0",
|
||||
"@babel/preset-env": "^7.12.0",
|
||||
"@vue/babel-preset-jsx": "^1.4.0",
|
||||
"babel-loader": "^8.2.0",
|
||||
"css-loader": "^5.0.0",
|
||||
"file-loader": "^6.2.0",
|
||||
"html-webpack-plugin": "^5.5.0",
|
||||
"less": "^4.0.0",
|
||||
"less-loader": "^7.0.0",
|
||||
"style-loader": "^2.0.0",
|
||||
"url-loader": "^4.1.0",
|
||||
"vue-loader": "^15.9.0",
|
||||
"vue-style-loader": "^4.1.0",
|
||||
"vue-template-compiler": "^2.6.14",
|
||||
"webpack": "^5.0.0",
|
||||
"webpack-cli": "^4.0.0",
|
||||
"webpack-dev-server": "^4.0.0"
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<title>Demo 管理系统</title>
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html, body {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
||||
}
|
||||
|
||||
#app {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
<template>
|
||||
<div id="app">
|
||||
<router-view />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'App',
|
||||
mounted() {
|
||||
console.log('App 组件已挂载')
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
/* 全局样式 */
|
||||
#app {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
/* 可以在这里添加你的业务样式 */
|
||||
</style>
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
/**
|
||||
* 项目配置文件
|
||||
*/
|
||||
export default {
|
||||
// 系统标题
|
||||
title: 'Demo 管理系统',
|
||||
|
||||
// API 基础路径
|
||||
apiUrl: 'http://localhost:9098/admin_api/',
|
||||
|
||||
// 文件上传路径
|
||||
uploadUrl: 'http://localhost:9098/admin_api/upload',
|
||||
|
||||
// Token 存储的 key
|
||||
tokenKey: 'demo_token',
|
||||
|
||||
// 其他配置
|
||||
timeout: 30000,
|
||||
|
||||
// 是否显示页面加载进度条
|
||||
showProgressBar: true
|
||||
}
|
||||
|
||||
@@ -1,92 +0,0 @@
|
||||
import Vue from 'vue'
|
||||
import VueRouter from 'vue-router'
|
||||
import Vuex from 'vuex'
|
||||
import ViewUI from 'view-design'
|
||||
import createPersistedState from 'vuex-persistedstate'
|
||||
import 'view-design/dist/styles/iview.css'
|
||||
|
||||
// 引入框架(使用打包好的框架 JS)
|
||||
import AdminFramework from './libs/admin-framework.js'
|
||||
|
||||
// 引入配置
|
||||
import config from './config'
|
||||
|
||||
// 引入根组件
|
||||
import App from './App.vue'
|
||||
|
||||
// 引入业务页面(根据后端权限菜单接口返回的组件路径导入)
|
||||
import ProductList from './views/business/product_list.vue'
|
||||
// 根据你的权限菜单接口,导入所有可能用到的业务组件:
|
||||
// import GamesComponent from './views/ball/games.vue'
|
||||
// import PayOrdersComponent from './views/order/pay_orders.vue'
|
||||
// import WchUsersComponent from './views/ball/wch_users.vue'
|
||||
// ... 导入更多业务组件
|
||||
|
||||
// 🎉 使用框架 - 自动完成所有初始化
|
||||
Vue.use(AdminFramework, {
|
||||
config,
|
||||
ViewUI,
|
||||
VueRouter,
|
||||
Vuex,
|
||||
createPersistedState,
|
||||
// ✅ 在这里一次性注册所有业务组件映射
|
||||
componentMap: {
|
||||
// 业务组件映射(根据后端菜单中的 component 字段配置)
|
||||
'business/product_list': ProductList
|
||||
// 根据权限菜单接口返回的 component 字段添加映射:
|
||||
// 'ball/games': GamesComponent,
|
||||
// 'order/pay_orders': PayOrdersComponent,
|
||||
// 'ball/wch_users': WchUsersComponent,
|
||||
// 'order/wch_wallets': WchWalletsComponent,
|
||||
// 'users/user_follows': UserFollowsComponent,
|
||||
// ... 添加更多业务组件
|
||||
}
|
||||
})
|
||||
|
||||
// 添加自定义业务路由(手动添加的路由)
|
||||
const businessRoutes = [
|
||||
{
|
||||
path: '/business/product',
|
||||
name: 'product_list',
|
||||
meta: { title: '产品列表' },
|
||||
component: ProductList
|
||||
}
|
||||
]
|
||||
|
||||
// 将业务路由添加到主路由中
|
||||
const mainRoute = AdminFramework.router.options.routes.find(r => r.path === '/')
|
||||
if (mainRoute && mainRoute.children) {
|
||||
mainRoute.children.push(...businessRoutes)
|
||||
}
|
||||
|
||||
// 创建 Vue 实例
|
||||
window.rootVue = new Vue({
|
||||
el: '#app',
|
||||
router: AdminFramework.router,
|
||||
store: AdminFramework.store,
|
||||
render: h => h(App),
|
||||
mounted() {
|
||||
// 设置响应式适配
|
||||
AdminFramework.uiTool.setRem()
|
||||
|
||||
// 只在已登录时获取系统标题
|
||||
const token = this.$store.state.user.token
|
||||
if (token) {
|
||||
this.$store.dispatch('app/getSysTitle', {
|
||||
defaultTitle: 'Demo 管理系统',
|
||||
defaultLogo: ''
|
||||
})
|
||||
} else {
|
||||
// 未登录时,直接设置默认标题
|
||||
document.title = 'Demo 管理系统'
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// 响应式适配
|
||||
window.addEventListener('load', AdminFramework.uiTool.setRem)
|
||||
window.addEventListener('resize', AdminFramework.uiTool.setRem)
|
||||
|
||||
console.log('✅ Demo 项目启动成功!')
|
||||
console.log('框架版本:', AdminFramework.version)
|
||||
|
||||
@@ -1,193 +0,0 @@
|
||||
<template>
|
||||
<div class="product-list">
|
||||
<h2>产品列表</h2>
|
||||
|
||||
<div class="toolbar">
|
||||
<Button type="primary" @click="handleAdd">新增产品</Button>
|
||||
<Button @click="handleRefresh">刷新</Button>
|
||||
</div>
|
||||
|
||||
<Table
|
||||
:columns="columns"
|
||||
:data="list"
|
||||
:loading="loading"
|
||||
border
|
||||
stripe
|
||||
/>
|
||||
|
||||
<div class="page-wrapper">
|
||||
<Page
|
||||
:total="total"
|
||||
:current="page"
|
||||
:page-size="page_size"
|
||||
show-total
|
||||
show-elevator
|
||||
@on-change="handlePageChange"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'product_list',
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
list: [],
|
||||
total: 0,
|
||||
page: 1,
|
||||
page_size: 10,
|
||||
columns: [
|
||||
{
|
||||
title: 'ID',
|
||||
key: 'id',
|
||||
width: 80
|
||||
},
|
||||
{
|
||||
title: '产品名称',
|
||||
key: 'name'
|
||||
},
|
||||
{
|
||||
title: '价格',
|
||||
key: 'price',
|
||||
render: (h, params) => {
|
||||
return h('span', '¥' + params.row.price)
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
key: 'status',
|
||||
render: (h, params) => {
|
||||
const statusMap = {
|
||||
1: { text: '上架', color: 'success' },
|
||||
0: { text: '下架', color: 'default' }
|
||||
}
|
||||
const status = statusMap[params.row.status] || statusMap[0]
|
||||
return h('Tag', { props: { color: status.color } }, status.text)
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
width: 200,
|
||||
render: (h, params) => {
|
||||
return h('div', [
|
||||
h('Button', {
|
||||
props: { type: 'primary', size: 'small' },
|
||||
style: { marginRight: '5px' },
|
||||
on: {
|
||||
click: () => {
|
||||
this.handleEdit(params.row)
|
||||
}
|
||||
}
|
||||
}, '编辑'),
|
||||
h('Button', {
|
||||
props: { type: 'error', size: 'small' },
|
||||
on: {
|
||||
click: () => {
|
||||
this.handleDelete(params.row)
|
||||
}
|
||||
}
|
||||
}, '删除')
|
||||
])
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.getData()
|
||||
},
|
||||
methods: {
|
||||
// 获取数据
|
||||
async getData() {
|
||||
this.loading = true
|
||||
|
||||
// 模拟数据(实际项目中从 API 获取)
|
||||
setTimeout(() => {
|
||||
this.list = [
|
||||
{ id: 1, name: '产品 A', price: 99.00, status: 1 },
|
||||
{ id: 2, name: '产品 B', price: 199.00, status: 1 },
|
||||
{ id: 3, name: '产品 C', price: 299.00, status: 0 },
|
||||
{ id: 4, name: '产品 D', price: 399.00, status: 1 }
|
||||
]
|
||||
this.total = 4
|
||||
this.loading = false
|
||||
}, 500)
|
||||
|
||||
// 实际项目中使用:
|
||||
// try {
|
||||
// const res = await this.$http.post('product/list', {
|
||||
// page: this.page,
|
||||
// page_size: this.page_size
|
||||
// })
|
||||
// this.list = res.data.list
|
||||
// this.total = res.data.total
|
||||
// } catch (error) {
|
||||
// this.$Message.error('获取数据失败')
|
||||
// } finally {
|
||||
// this.loading = false
|
||||
// }
|
||||
},
|
||||
|
||||
// 新增
|
||||
handleAdd() {
|
||||
this.$Message.info('打开新增弹窗')
|
||||
},
|
||||
|
||||
// 编辑
|
||||
handleEdit(row) {
|
||||
this.$Message.info('编辑产品: ' + row.name)
|
||||
},
|
||||
|
||||
// 删除
|
||||
handleDelete(row) {
|
||||
this.$Modal.confirm({
|
||||
title: '确认删除',
|
||||
content: `确定要删除产品 "${row.name}" 吗?`,
|
||||
onOk: () => {
|
||||
this.$Message.success('删除成功')
|
||||
this.getData()
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
// 刷新
|
||||
handleRefresh() {
|
||||
this.getData()
|
||||
},
|
||||
|
||||
// 翻页
|
||||
handlePageChange(page) {
|
||||
this.page = page
|
||||
this.getData()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.product-list {
|
||||
padding: 20px;
|
||||
|
||||
h2 {
|
||||
margin-bottom: 20px;
|
||||
color: #2d8cf0;
|
||||
}
|
||||
|
||||
.toolbar {
|
||||
margin-bottom: 15px;
|
||||
|
||||
button {
|
||||
margin-right: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.page-wrapper {
|
||||
margin-top: 20px;
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
@echo off
|
||||
echo ====================================
|
||||
echo Admin Framework Demo 启动脚本
|
||||
echo ====================================
|
||||
echo.
|
||||
|
||||
:: 检查关键依赖是否存在
|
||||
if not exist "node_modules\@vue\babel-preset-jsx\" (
|
||||
echo [1/2] 首次运行或依赖缺失,正在安装依赖...
|
||||
echo.
|
||||
call npm install
|
||||
echo.
|
||||
echo [提示] 如果仍有错误,请运行以下命令:
|
||||
echo npm install @vue/babel-preset-jsx brace vue2-ace-editor --save
|
||||
echo.
|
||||
) else (
|
||||
echo [✓] 依赖已安装
|
||||
echo.
|
||||
)
|
||||
|
||||
echo [2/2] 启动开发服务器...
|
||||
echo.
|
||||
echo 浏览器将自动打开 http://localhost:8080
|
||||
echo.
|
||||
echo 可访问:
|
||||
echo - /login - 登录页面
|
||||
echo - /home - 主页
|
||||
echo - /business/product - 业务示例
|
||||
echo.
|
||||
echo 按 Ctrl+C 停止服务器
|
||||
echo.
|
||||
echo ====================================
|
||||
echo.
|
||||
|
||||
call npm run dev
|
||||
|
||||
pause
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
echo "===================================="
|
||||
echo " Admin Framework Demo 启动脚本"
|
||||
echo "===================================="
|
||||
echo ""
|
||||
|
||||
# 检查 node_modules 是否存在
|
||||
if [ ! -d "node_modules" ]; then
|
||||
echo "[1/2] 首次运行,正在安装依赖..."
|
||||
echo ""
|
||||
npm install
|
||||
echo ""
|
||||
else
|
||||
echo "[✓] 依赖已安装"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
echo "[2/2] 启动开发服务器..."
|
||||
echo ""
|
||||
echo "浏览器将自动打开 http://localhost:8080"
|
||||
echo ""
|
||||
echo "可访问:"
|
||||
echo " - /login - 登录页面"
|
||||
echo " - /home - 主页"
|
||||
echo " - /business/product - 业务示例"
|
||||
echo ""
|
||||
echo "按 Ctrl+C 停止服务器"
|
||||
echo ""
|
||||
echo "===================================="
|
||||
echo ""
|
||||
|
||||
npm run dev
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
// 测试引用框架源码
|
||||
try {
|
||||
const AdminFramework = require('../src/index.js')
|
||||
console.log('✅ 框架引用成功!')
|
||||
console.log('框架版本:', AdminFramework.version || AdminFramework.default?.version)
|
||||
} catch (error) {
|
||||
console.error('❌ 框架引用失败:', error.message)
|
||||
}
|
||||
|
||||
@@ -1,115 +0,0 @@
|
||||
const path = require('path')
|
||||
const HtmlWebpackPlugin = require('html-webpack-plugin')
|
||||
const { VueLoaderPlugin } = require('vue-loader')
|
||||
|
||||
module.exports = (env, argv) => {
|
||||
const isDevelopment = argv.mode === 'development'
|
||||
|
||||
return {
|
||||
entry: './src/main.js',
|
||||
output: {
|
||||
path: path.resolve(__dirname, 'dist'),
|
||||
filename: isDevelopment ? 'bundle.js' : 'bundle.[contenthash:8].js',
|
||||
clean: true
|
||||
},
|
||||
// 开发模式使用高质量的 source map,方便调试
|
||||
devtool: isDevelopment ? 'eval-source-map' : 'source-map',
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.vue$/,
|
||||
loader: 'vue-loader'
|
||||
},
|
||||
{
|
||||
test: /\.js$/,
|
||||
loader: 'babel-loader',
|
||||
exclude: /node_modules/
|
||||
},
|
||||
{
|
||||
test: /\.css$/,
|
||||
use: [
|
||||
'vue-style-loader',
|
||||
{
|
||||
loader: 'css-loader',
|
||||
options: {
|
||||
sourceMap: isDevelopment
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
test: /\.less$/,
|
||||
use: [
|
||||
'vue-style-loader',
|
||||
{
|
||||
loader: 'css-loader',
|
||||
options: {
|
||||
sourceMap: isDevelopment
|
||||
}
|
||||
},
|
||||
{
|
||||
loader: 'less-loader',
|
||||
options: {
|
||||
sourceMap: isDevelopment
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
|
||||
loader: 'url-loader',
|
||||
options: {
|
||||
limit: 10000,
|
||||
name: 'images/[name].[hash:7].[ext]',
|
||||
esModule: false
|
||||
}
|
||||
},
|
||||
{
|
||||
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
|
||||
loader: 'url-loader',
|
||||
options: {
|
||||
limit: 10000,
|
||||
name: 'fonts/[name].[hash:7].[ext]',
|
||||
esModule: false
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
plugins: [
|
||||
new VueLoaderPlugin(),
|
||||
new HtmlWebpackPlugin({
|
||||
template: './public/index.html',
|
||||
filename: 'index.html'
|
||||
})
|
||||
],
|
||||
resolve: {
|
||||
extensions: ['.js', '.vue', '.json'],
|
||||
alias: {
|
||||
// 框架源码的别名(必须在前面,优先级高)
|
||||
'@': path.resolve(__dirname, '../src'),
|
||||
'@component': path.resolve(__dirname, '../src/components'),
|
||||
'@utils': path.resolve(__dirname, '../src/utils'),
|
||||
'@api': path.resolve(__dirname, '../src/api'),
|
||||
'@config': path.resolve(__dirname, '../src/config'),
|
||||
'@assets': path.resolve(__dirname, '../src/assets'),
|
||||
// demo 项目的别名
|
||||
'~': path.resolve(__dirname, 'src'),
|
||||
'vue$': 'vue/dist/vue.esm.js'
|
||||
}
|
||||
},
|
||||
devServer: {
|
||||
port: 8080,
|
||||
hot: true,
|
||||
open: true,
|
||||
historyApiFallback: true,
|
||||
// 启用覆盖层显示编译错误
|
||||
client: {
|
||||
overlay: {
|
||||
errors: true,
|
||||
warnings: false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,206 +0,0 @@
|
||||
# 使用打包框架说明
|
||||
|
||||
## ✅ 已完成的修改
|
||||
|
||||
Demo 项目现在使用打包好的框架 JS 文件,而不是直接引用源码。
|
||||
|
||||
### 修改内容
|
||||
|
||||
**文件**: `demo-project/src/main.js`
|
||||
|
||||
```javascript
|
||||
// 修改前(直接使用源码)
|
||||
import AdminFramework from '../../src/index.js'
|
||||
|
||||
// 修改后(使用打包好的框架)
|
||||
import AdminFramework from './libs/admin-framework.js'
|
||||
```
|
||||
|
||||
### 文件结构
|
||||
|
||||
```
|
||||
demo-project/
|
||||
├── src/
|
||||
│ ├── libs/
|
||||
│ │ └── admin-framework.js ← 打包好的框架文件
|
||||
│ ├── main.js ← 引用框架的入口文件
|
||||
│ └── ...
|
||||
└── ...
|
||||
```
|
||||
|
||||
## 🔄 更新框架的步骤
|
||||
|
||||
当框架源码(`src/` 目录)有更新时,需要重新打包并复制到 demo 项目:
|
||||
|
||||
### 方法一:使用 PowerShell 命令(推荐)
|
||||
|
||||
```powershell
|
||||
# 1. 在项目根目录打包框架
|
||||
npm run build
|
||||
|
||||
# 2. 复制到 demo 项目
|
||||
if (-not (Test-Path "demo-project\src\libs")) {
|
||||
New-Item -ItemType Directory -Path "demo-project\src\libs" -Force | Out-Null
|
||||
}
|
||||
Copy-Item "dist\admin-framework.js" "demo-project\src\libs\admin-framework.js" -Force
|
||||
Write-Host "✅ 复制成功!" -ForegroundColor Green
|
||||
```
|
||||
|
||||
### 方法二:手动操作
|
||||
|
||||
1. **打包框架**
|
||||
```bash
|
||||
# 在项目根目录
|
||||
npm run build
|
||||
```
|
||||
|
||||
2. **复制文件**
|
||||
- 将 `dist/admin-framework.js` 复制到 `demo-project/src/libs/admin-framework.js`
|
||||
|
||||
3. **重启开发服务器**
|
||||
```bash
|
||||
cd demo-project
|
||||
npm run dev
|
||||
```
|
||||
|
||||
## 📊 优势
|
||||
|
||||
### 使用打包框架的优势
|
||||
|
||||
1. **✅ 性能更好**
|
||||
- 打包后的文件经过优化和压缩
|
||||
- 减少了模块解析时间
|
||||
|
||||
2. **✅ 更接近生产环境**
|
||||
- Demo 项目的使用方式和实际项目一致
|
||||
- 更容易发现潜在问题
|
||||
|
||||
3. **✅ 依赖隔离**
|
||||
- Demo 项目不需要安装框架的所有依赖
|
||||
- 减少了依赖冲突的可能性
|
||||
|
||||
4. **✅ 更清晰的边界**
|
||||
- 框架和业务代码分离更明确
|
||||
- 便于理解框架的使用方式
|
||||
|
||||
### 使用源码的优势(调试时)
|
||||
|
||||
1. **✅ 实时更新**
|
||||
- 修改源码后立即生效
|
||||
- 不需要重新打包
|
||||
|
||||
2. **✅ 调试方便**
|
||||
- 可以直接在源码中打断点
|
||||
- 错误堆栈更清晰
|
||||
|
||||
## 🔧 开发建议
|
||||
|
||||
### 开发框架时
|
||||
|
||||
**使用源码引用**(临时修改):
|
||||
```javascript
|
||||
// demo-project/src/main.js
|
||||
import AdminFramework from '../../src/index.js'
|
||||
```
|
||||
|
||||
**优点**:
|
||||
- 修改框架源码后立即生效
|
||||
- 方便调试和测试
|
||||
|
||||
### 测试框架时
|
||||
|
||||
**使用打包文件**(当前配置):
|
||||
```javascript
|
||||
// demo-project/src/main.js
|
||||
import AdminFramework from './libs/admin-framework.js'
|
||||
```
|
||||
|
||||
**优点**:
|
||||
- 测试打包后的实际效果
|
||||
- 发现打包可能引入的问题
|
||||
|
||||
## 📝 注意事项
|
||||
|
||||
1. **打包后需要复制**
|
||||
- 每次修改框架源码并打包后,需要重新复制到 demo 项目
|
||||
- 否则 demo 项目使用的还是旧版本
|
||||
|
||||
2. **Source Map**
|
||||
- 打包时会生成 source map 文件
|
||||
- 方便在浏览器中调试打包后的代码
|
||||
|
||||
3. **版本管理**
|
||||
- `demo-project/src/libs/admin-framework.js` 不应该提交到 Git
|
||||
- 应该在 `.gitignore` 中忽略这个文件
|
||||
|
||||
4. **依赖更新**
|
||||
- 如果框架的依赖有更新,需要重新打包
|
||||
- Demo 项目不需要安装框架的依赖
|
||||
|
||||
## 🎯 快速命令
|
||||
|
||||
### 一键更新框架到 Demo 项目
|
||||
|
||||
```powershell
|
||||
# 打包并复制
|
||||
npm run build; if (-not (Test-Path "demo-project\src\libs")) { New-Item -ItemType Directory -Path "demo-project\src\libs" -Force | Out-Null }; Copy-Item "dist\admin-framework.js" "demo-project\src\libs\admin-framework.js" -Force; Write-Host "✅ 框架已更新到 Demo 项目!" -ForegroundColor Green
|
||||
```
|
||||
|
||||
### 启动 Demo 项目
|
||||
|
||||
```bash
|
||||
cd demo-project
|
||||
npm run dev
|
||||
```
|
||||
|
||||
## 📚 相关文件
|
||||
|
||||
- `build.bat` / `build.sh` - 框架打包脚本
|
||||
- `webpack.config.js` - Webpack 配置文件
|
||||
- `dist/admin-framework.js` - 打包后的框架文件
|
||||
- `demo-project/src/libs/admin-framework.js` - Demo 项目使用的框架文件
|
||||
- `demo-project/src/main.js` - Demo 项目入口文件
|
||||
|
||||
## 🔍 故障排查
|
||||
|
||||
### 问题:修改框架源码后,Demo 项目没有变化
|
||||
|
||||
**原因**:Demo 项目使用的是打包后的文件,不会自动更新
|
||||
|
||||
**解决方案**:
|
||||
1. 重新打包框架:`npm run build`
|
||||
2. 复制到 Demo 项目(使用上面的 PowerShell 命令)
|
||||
3. 重启 Demo 项目的开发服务器
|
||||
|
||||
### 问题:找不到 admin-framework.js 文件
|
||||
|
||||
**原因**:还没有复制打包文件到 Demo 项目
|
||||
|
||||
**解决方案**:
|
||||
```powershell
|
||||
# 确保先打包
|
||||
npm run build
|
||||
|
||||
# 然后复制
|
||||
if (-not (Test-Path "demo-project\src\libs")) {
|
||||
New-Item -ItemType Directory -Path "demo-project\src\libs" -Force | Out-Null
|
||||
}
|
||||
Copy-Item "dist\admin-framework.js" "demo-project\src\libs\admin-framework.js" -Force
|
||||
```
|
||||
|
||||
### 问题:控制台报错找不到模块
|
||||
|
||||
**原因**:可能是打包配置有问题,或者依赖没有正确打包
|
||||
|
||||
**解决方案**:
|
||||
1. 检查 `webpack.config.js` 配置
|
||||
2. 确保所有依赖都已安装
|
||||
3. 重新打包:`npm run build`
|
||||
4. 查看打包日志是否有错误
|
||||
|
||||
---
|
||||
|
||||
**当前状态**:✅ Demo 项目已配置为使用打包后的框架文件
|
||||
|
||||
**下一步**:启动 Demo 项目测试:`cd demo-project && npm run dev`
|
||||
|
||||
@@ -1,119 +0,0 @@
|
||||
# Demo 项目启动说明
|
||||
|
||||
## ✅ 配置完成
|
||||
|
||||
Demo 项目已配置为使用打包好的框架 JS 文件。
|
||||
|
||||
## 🚀 启动步骤
|
||||
|
||||
### 1. 确保框架文件已复制
|
||||
|
||||
检查文件是否存在:
|
||||
```
|
||||
demo-project/src/libs/admin-framework.js
|
||||
```
|
||||
|
||||
如果不存在,在项目根目录执行:
|
||||
```powershell
|
||||
Copy-Item "dist\admin-framework.js" "demo-project\src\libs\admin-framework.js" -Force
|
||||
```
|
||||
|
||||
### 2. 启动开发服务器
|
||||
|
||||
**方法一:使用命令行**
|
||||
```bash
|
||||
cd demo-project
|
||||
npm run dev
|
||||
```
|
||||
|
||||
**方法二:使用 start.bat(Windows)**
|
||||
```bash
|
||||
cd demo-project
|
||||
start.bat
|
||||
```
|
||||
|
||||
**方法三:使用 start.sh(Linux/Mac)**
|
||||
```bash
|
||||
cd demo-project
|
||||
./start.sh
|
||||
```
|
||||
|
||||
### 3. 访问应用
|
||||
|
||||
打开浏览器访问:
|
||||
- **开发环境**:http://localhost:8080
|
||||
- **测试账号**:admin / admin123
|
||||
|
||||
## 📝 注意事项
|
||||
|
||||
1. **端口占用**
|
||||
- 如果 8080 端口被占用,webpack 会自动使用其他端口
|
||||
- 查看终端输出的实际端口号
|
||||
|
||||
2. **首次启动**
|
||||
- 首次启动可能需要较长时间编译
|
||||
- 等待 "Compiled successfully" 消息
|
||||
|
||||
3. **热更新**
|
||||
- 修改代码后会自动刷新浏览器
|
||||
- 如果没有自动刷新,手动刷新页面
|
||||
|
||||
## 🔍 故障排查
|
||||
|
||||
### 问题:找不到 admin-framework.js
|
||||
|
||||
**错误信息**:
|
||||
```
|
||||
Module not found: Error: Can't resolve './libs/admin-framework.js'
|
||||
```
|
||||
|
||||
**解决方案**:
|
||||
```powershell
|
||||
# 在项目根目录执行
|
||||
Copy-Item "dist\admin-framework.js" "demo-project\src\libs\admin-framework.js" -Force
|
||||
```
|
||||
|
||||
### 问题:端口被占用
|
||||
|
||||
**错误信息**:
|
||||
```
|
||||
Error: listen EADDRINUSE: address already in use :::8080
|
||||
```
|
||||
|
||||
**解决方案**:
|
||||
1. 关闭占用 8080 端口的程序
|
||||
2. 或修改 `webpack.config.js` 中的端口配置
|
||||
|
||||
### 问题:编译错误
|
||||
|
||||
**解决方案**:
|
||||
1. 检查 `src/main.js` 的引用路径是否正确
|
||||
2. 确保 `admin-framework.js` 文件完整
|
||||
3. 删除 `node_modules` 重新安装:
|
||||
```bash
|
||||
rm -rf node_modules
|
||||
npm install
|
||||
```
|
||||
|
||||
## 📚 相关文档
|
||||
|
||||
- `使用打包框架说明.md` - 详细使用说明
|
||||
- `框架更新完成.md` - 更新记录
|
||||
- `README.md` - 项目说明
|
||||
|
||||
## 🎯 下一步
|
||||
|
||||
启动成功后:
|
||||
1. 使用测试账号登录
|
||||
2. 测试菜单导航功能
|
||||
3. 测试系统管理功能
|
||||
4. 查看浏览器控制台,确保没有错误
|
||||
|
||||
---
|
||||
|
||||
**当前状态**:✅ 就绪
|
||||
|
||||
**框架版本**:1.0.0
|
||||
|
||||
**启动命令**:`npm run dev`
|
||||
|
||||
@@ -1,157 +0,0 @@
|
||||
# 框架更新完成 ✅
|
||||
|
||||
## 📝 更新内容
|
||||
|
||||
Demo 项目已成功切换为使用打包好的框架 JS 文件。
|
||||
|
||||
### 修改的文件
|
||||
|
||||
1. **`demo-project/src/main.js`**
|
||||
```javascript
|
||||
// 修改前
|
||||
import AdminFramework from '../../src/index.js'
|
||||
|
||||
// 修改后
|
||||
import AdminFramework from './libs/admin-framework.js'
|
||||
```
|
||||
|
||||
2. **`demo-project/.gitignore`**
|
||||
```
|
||||
# 新增
|
||||
src/libs/admin-framework.js
|
||||
```
|
||||
|
||||
3. **新增文件**
|
||||
- `demo-project/src/libs/admin-framework.js` - 打包好的框架文件
|
||||
- `demo-project/使用打包框架说明.md` - 使用说明文档
|
||||
|
||||
## 🎯 当前状态
|
||||
|
||||
- ✅ 框架已打包:`dist/admin-framework.js`
|
||||
- ✅ 已复制到 Demo 项目:`demo-project/src/libs/admin-framework.js`
|
||||
- ✅ Demo 项目已配置为使用打包文件
|
||||
- ✅ `.gitignore` 已更新,忽略框架文件
|
||||
|
||||
## 🚀 启动 Demo 项目
|
||||
|
||||
```bash
|
||||
cd demo-project
|
||||
npm run dev
|
||||
```
|
||||
|
||||
访问:http://localhost:8080
|
||||
|
||||
## 🔄 更新框架的步骤
|
||||
|
||||
当框架源码有更新时:
|
||||
|
||||
### 快速更新(一键命令)
|
||||
|
||||
```powershell
|
||||
# 在项目根目录执行
|
||||
npm run build; Copy-Item "dist\admin-framework.js" "demo-project\src\libs\admin-framework.js" -Force; Write-Host "✅ 框架已更新!" -ForegroundColor Green
|
||||
```
|
||||
|
||||
### 分步操作
|
||||
|
||||
```bash
|
||||
# 1. 打包框架
|
||||
npm run build
|
||||
|
||||
# 2. 复制到 Demo 项目(PowerShell)
|
||||
Copy-Item "dist\admin-framework.js" "demo-project\src\libs\admin-framework.js" -Force
|
||||
|
||||
# 3. 重启 Demo 项目(如果正在运行)
|
||||
cd demo-project
|
||||
npm run dev
|
||||
```
|
||||
|
||||
## 📊 优势
|
||||
|
||||
### 使用打包框架的好处
|
||||
|
||||
1. **性能更好** - 打包后的文件经过优化
|
||||
2. **更接近生产环境** - 和实际使用方式一致
|
||||
3. **依赖隔离** - Demo 项目不需要框架的所有依赖
|
||||
4. **边界清晰** - 框架和业务代码分离明确
|
||||
|
||||
### 开发时的建议
|
||||
|
||||
**调试框架时**:临时改回源码引用
|
||||
```javascript
|
||||
// demo-project/src/main.js
|
||||
import AdminFramework from '../../src/index.js'
|
||||
```
|
||||
|
||||
**测试框架时**:使用打包文件(当前配置)
|
||||
```javascript
|
||||
// demo-project/src/main.js
|
||||
import AdminFramework from './libs/admin-framework.js'
|
||||
```
|
||||
|
||||
## 📚 相关文档
|
||||
|
||||
- `demo-project/使用打包框架说明.md` - 详细使用说明
|
||||
- `完整使用文档.md` - 框架完整文档
|
||||
- `README.md` - 项目说明
|
||||
|
||||
## 🔍 故障排查
|
||||
|
||||
### 问题:修改框架源码后,Demo 项目没有变化
|
||||
|
||||
**解决**:
|
||||
```powershell
|
||||
# 重新打包并复制
|
||||
npm run build
|
||||
Copy-Item "dist\admin-framework.js" "demo-project\src\libs\admin-framework.js" -Force
|
||||
```
|
||||
|
||||
### 问题:找不到 admin-framework.js
|
||||
|
||||
**解决**:
|
||||
```powershell
|
||||
# 确保文件已复制
|
||||
if (-not (Test-Path "demo-project\src\libs\admin-framework.js")) {
|
||||
Write-Host "❌ 文件不存在,正在复制..." -ForegroundColor Yellow
|
||||
Copy-Item "dist\admin-framework.js" "demo-project\src\libs\admin-framework.js" -Force
|
||||
Write-Host "✅ 复制完成!" -ForegroundColor Green
|
||||
}
|
||||
```
|
||||
|
||||
### 问题:控制台报错
|
||||
|
||||
**检查**:
|
||||
1. 确保框架已正确打包:`npm run build`
|
||||
2. 确保文件已复制到正确位置
|
||||
3. 查看浏览器控制台的详细错误信息
|
||||
4. 检查 `demo-project/src/main.js` 的引用路径
|
||||
|
||||
## 🎉 下一步
|
||||
|
||||
1. **启动 Demo 项目**
|
||||
```bash
|
||||
cd demo-project
|
||||
npm run dev
|
||||
```
|
||||
|
||||
2. **访问应用**
|
||||
- 打开浏览器访问:http://localhost:8080
|
||||
- 使用测试账号登录
|
||||
|
||||
3. **测试功能**
|
||||
- 登录功能
|
||||
- 菜单导航
|
||||
- 系统管理功能
|
||||
|
||||
4. **查看控制台**
|
||||
- 确保没有错误
|
||||
- 查看框架版本信息
|
||||
|
||||
---
|
||||
|
||||
**更新完成时间**:2025-10-08
|
||||
|
||||
**框架版本**:1.0.0
|
||||
|
||||
**状态**:✅ 就绪
|
||||
|
||||
@@ -1,175 +0,0 @@
|
||||
# 注释乱码修复说明
|
||||
|
||||
## 🐛 问题描述
|
||||
|
||||
控制台出现多个错误,主要原因是 `src/index.js` 文件中的中文注释出现了乱码,导致:
|
||||
|
||||
1. **Upload 组件错误**:`TypeError: Cannot read properties of undefined (reading 'user')`
|
||||
- 原因:`src/components/upload/Single.vue` 和 `Multiple.vue` 中使用了 `store.state.user.token`,但 store 未正确初始化
|
||||
|
||||
2. **TreeGrid 组件错误**:`ReferenceError: Cannot access '__WEBPACK_DEFAULT_EXPORT__' before initialization`
|
||||
- 原因:`src/components/treeGrid/component/subTreeGrid.vue` 中有循环引用问题
|
||||
|
||||
3. **LoginPage 未定义错误**:`ReferenceError: LoginPage is not defined`
|
||||
- 原因:`src/index.js` 第43行的 import 语句和注释在同一行,导致 import 被注释掉
|
||||
|
||||
4. **Editor 未定义错误**:`ReferenceError: Editor is not defined`
|
||||
- 原因:`registerGlobalComponents` 方法中引用了未导入的组件
|
||||
|
||||
5. **map 未定义错误**:`ReferenceError: map is not defined`
|
||||
- 原因:`setupComponentMap` 方法中的注释格式不对,导致代码被注释掉
|
||||
|
||||
## ✅ 已修复的问题
|
||||
|
||||
### 1. Upload 组件修复
|
||||
|
||||
**修改文件**:
|
||||
- `src/components/upload/Single.vue`
|
||||
- `src/components/upload/Multiple.vue`
|
||||
|
||||
**修改内容**:
|
||||
```javascript
|
||||
// 修改前
|
||||
import store from '@/store'
|
||||
let headers = {
|
||||
'admin-token': store.state.user.token,
|
||||
}
|
||||
|
||||
// 修改后
|
||||
import { getToken } from '@/utils/tools'
|
||||
let headers = {
|
||||
'admin-token': getToken(),
|
||||
}
|
||||
```
|
||||
|
||||
**原因**:
|
||||
- 使用 `getToken()` 函数从 localStorage 获取 token,避免依赖 store 初始化顺序
|
||||
|
||||
### 2. TreeGrid 组件修复
|
||||
|
||||
**修改文件**:
|
||||
- `src/components/treeGrid/component/subTreeGrid.vue`
|
||||
|
||||
**修改内容**:
|
||||
```javascript
|
||||
// 修改前
|
||||
import SubTreeGrid from './subTreeGrid.vue'
|
||||
export default {
|
||||
components: {
|
||||
SubTreeGrid,
|
||||
// ...
|
||||
}
|
||||
}
|
||||
|
||||
// 修改后
|
||||
export default {
|
||||
components: {
|
||||
SubTreeGrid: () => import('./subTreeGrid.vue'), // 使用异步组件
|
||||
// ...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**原因**:
|
||||
- 递归组件需要使用异步组件来避免循环引用问题
|
||||
|
||||
## ⚠️ 待修复的问题
|
||||
|
||||
### 3. src/index.js 文件乱码
|
||||
|
||||
**问题**:
|
||||
- 文件中的中文注释出现乱码(如:`閫氱敤鍚庡彴绠$悊绯荤粺妗嗘灦` 应该是 `通用后台管理系统框架`)
|
||||
- 第43行的 import 语句和注释在同一行
|
||||
- `registerGlobalComponents` 方法中有重复的组件注册
|
||||
- `setupComponentMap` 方法中的注释格式不对
|
||||
|
||||
**需要修复的关键行**:
|
||||
|
||||
#### 第43行(最关键)
|
||||
```javascript
|
||||
// 当前(错误)
|
||||
// 鐧诲綍鍜岄敊璇〉闈?import LoginPage from './views/login/login.vue'
|
||||
|
||||
// 应该改为
|
||||
// 登录和错误页面
|
||||
import LoginPage from './views/login/login.vue'
|
||||
```
|
||||
|
||||
#### registerGlobalComponents 方法
|
||||
```javascript
|
||||
// 删除重复的组件注册(第218-235行)
|
||||
// 只保留第206-216行的组件注册
|
||||
```
|
||||
|
||||
#### setupComponentMap 方法
|
||||
```javascript
|
||||
// 第234行
|
||||
// 当前(错误)
|
||||
// 鍚堝苟澶栭儴浼犲叆鐨勭粍浠舵槧灏? ...customMap
|
||||
|
||||
// 应该改为
|
||||
// 合并外部传入的组件映射
|
||||
...customMap
|
||||
|
||||
// 第237行
|
||||
// 当前(错误)
|
||||
// 鑷姩鐢熸垚甯?.vue 鍜屼笉甯?.vue 鐨勬槧灏? const map = {}
|
||||
|
||||
// 应该改为
|
||||
// 自动生成带.vue 和不带.vue 的映射
|
||||
const map = {}
|
||||
```
|
||||
|
||||
## 🔧 修复方法
|
||||
|
||||
### 方法一:使用 Git 恢复 + 手动修复
|
||||
|
||||
```bash
|
||||
# 1. 恢复文件到最近的正确版本
|
||||
git checkout src/index.js
|
||||
|
||||
# 2. 手动修复关键问题(使用编辑器)
|
||||
# - 确保第43行的 import 语句独立一行
|
||||
# - 删除重复的组件注册
|
||||
# - 修复注释格式
|
||||
```
|
||||
|
||||
### 方法二:重新创建文件
|
||||
|
||||
由于乱码问题严重,建议:
|
||||
1. 备份当前文件
|
||||
2. 从 Git 历史中找到最近的正确版本
|
||||
3. 重新应用必要的修改
|
||||
|
||||
## 📝 预防措施
|
||||
|
||||
1. **使用 UTF-8 编码保存文件**
|
||||
- 确保编辑器设置为 UTF-8 编码
|
||||
- 避免使用 ANSI 或 GBK 编码
|
||||
|
||||
2. **避免注释和代码在同一行**
|
||||
- 注释应该独立一行
|
||||
- 特别是 import 语句前的注释
|
||||
|
||||
3. **定期检查文件编码**
|
||||
- 使用 `file` 命令检查文件编码
|
||||
- 使用 `iconv` 或编辑器转换编码
|
||||
|
||||
## 🎯 下一步
|
||||
|
||||
1. 修复 `src/index.js` 文件的编码问题
|
||||
2. 确保所有中文注释正确显示
|
||||
3. 测试页面是否正常加载
|
||||
4. 检查控制台是否还有错误
|
||||
|
||||
## 📚 相关文件
|
||||
|
||||
- `src/index.js` - 框架入口文件(需要修复)
|
||||
- `src/components/upload/Single.vue` - 已修复
|
||||
- `src/components/upload/Multiple.vue` - 已修复
|
||||
- `src/components/treeGrid/component/subTreeGrid.vue` - 已修复
|
||||
|
||||
---
|
||||
|
||||
**修复状态**:部分完成,等待 `src/index.js` 文件修复
|
||||
|
||||
64
package-lock.json
generated
64
package-lock.json
generated
@@ -24,11 +24,13 @@
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.12.0",
|
||||
"@babel/preset-env": "^7.12.0",
|
||||
"autoprefixer": "^10.4.21",
|
||||
"babel-loader": "^8.2.0",
|
||||
"css-loader": "^5.0.0",
|
||||
"file-loader": "^6.2.0",
|
||||
"less": "^4.0.0",
|
||||
"less-loader": "^7.0.0",
|
||||
"postcss": "^8.5.6",
|
||||
"style-loader": "^2.0.0",
|
||||
"url-loader": "^4.1.0",
|
||||
"vue-loader": "^15.9.0",
|
||||
@@ -2455,6 +2457,44 @@
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/autoprefixer": {
|
||||
"version": "10.4.21",
|
||||
"resolved": "https://registry.npmmirror.com/autoprefixer/-/autoprefixer-10.4.21.tgz",
|
||||
"integrity": "sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/postcss/"
|
||||
},
|
||||
{
|
||||
"type": "tidelift",
|
||||
"url": "https://tidelift.com/funding/github/npm/autoprefixer"
|
||||
},
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ai"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"browserslist": "^4.24.4",
|
||||
"caniuse-lite": "^1.0.30001702",
|
||||
"fraction.js": "^4.3.7",
|
||||
"normalize-range": "^0.1.2",
|
||||
"picocolors": "^1.1.1",
|
||||
"postcss-value-parser": "^4.2.0"
|
||||
},
|
||||
"bin": {
|
||||
"autoprefixer": "bin/autoprefixer"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^10 || ^12 || >=14"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"postcss": "^8.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/axios": {
|
||||
"version": "0.21.4",
|
||||
"resolved": "https://registry.npmmirror.com/axios/-/axios-0.21.4.tgz",
|
||||
@@ -3209,6 +3249,20 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/fraction.js": {
|
||||
"version": "4.3.7",
|
||||
"resolved": "https://registry.npmmirror.com/fraction.js/-/fraction.js-4.3.7.tgz",
|
||||
"integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "*"
|
||||
},
|
||||
"funding": {
|
||||
"type": "patreon",
|
||||
"url": "https://github.com/sponsors/rawify"
|
||||
}
|
||||
},
|
||||
"node_modules/function-bind": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmmirror.com/function-bind/-/function-bind-1.1.2.tgz",
|
||||
@@ -3816,6 +3870,16 @@
|
||||
"integrity": "sha512-cCmFDMSm26S6tQSDpBCg/NR8NENrVPhAJSf+XbxBG4rPFaaonlEoE9wHQmun+cls499TQGSb7ZyPBRlzgKfpeg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/normalize-range": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmmirror.com/normalize-range/-/normalize-range-0.1.2.tgz",
|
||||
"integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/p-limit": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmmirror.com/p-limit/-/p-limit-2.3.0.tgz",
|
||||
|
||||
@@ -38,11 +38,13 @@
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.12.0",
|
||||
"@babel/preset-env": "^7.12.0",
|
||||
"autoprefixer": "^10.4.21",
|
||||
"babel-loader": "^8.2.0",
|
||||
"css-loader": "^5.0.0",
|
||||
"file-loader": "^6.2.0",
|
||||
"less": "^4.0.0",
|
||||
"less-loader": "^7.0.0",
|
||||
"postcss": "^8.5.6",
|
||||
"style-loader": "^2.0.0",
|
||||
"url-loader": "^4.1.0",
|
||||
"vue-loader": "^15.9.0",
|
||||
|
||||
16
postcss.config.js
Normal file
16
postcss.config.js
Normal file
@@ -0,0 +1,16 @@
|
||||
// PostCSS 配置文件
|
||||
// 用于处理 CSS 样式转换和优化
|
||||
module.exports = {
|
||||
plugins: {
|
||||
// 自动添加浏览器前缀
|
||||
autoprefixer: {
|
||||
overrideBrowserslist: [
|
||||
'> 1%',
|
||||
'last 2 versions',
|
||||
'not dead',
|
||||
'not ie <= 11'
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user