官方文档
功能
- 登录 / 注销
- 权限验证
- 页面权限
- 指令权限
- 权限配置
- 二步登录
- 多环境发布
- dev sit stage prod
- 全局功能
- 国际化多语言
- 多种动态换肤
- 动态侧边栏(支持多级路由嵌套)
- 动态面包屑
- 快捷导航(标签页)
- Svg Sprite 图标
- 本地/后端 mock 数据
- Screenfull全屏
- 自适应收缩侧边栏
- 编辑器
- 富文本
- Markdown
- JSON 等多格式
- Excel
- 导出excel
- 导入excel
- 前端可视化excel
- 导出zip
- 表格
- 动态表格
- 拖拽表格
- 内联编辑
- 错误页面
- 401
- 404
- 組件
- 头像上传
- 返回顶部
- 拖拽Dialog
- 拖拽Select
- 拖拽看板
- 列表拖拽
- SplitPane
- Dropzone
- Sticky
- CountTo
- 综合实例
- 错误日志
- Dashboard
- 引导页
- ECharts 图表
- Clipboard(剪贴复制)
- Markdown2html
前序准备
项目技术栈基于 ES2015+、vue、vuex、vue-router 、vue-cli 、axios 和 element-ui,所有的请求数据都使用Mock.js进行模拟。
项目代码
vue-element-admin
目录结构
├── build # 构建相关
├── mock # 项目mock 模拟数据
├── plop-templates # 基本模板
├── public # 静态资源
│ │── favicon.ico # favicon图标
│ └── index.html # html模板
├── src # 源代码
│ ├── api # 所有请求
│ ├── assets # 主题 字体等静态资源
│ ├── components # 全局公用组件
│ ├── directive # 全局指令
│ ├── filters # 全局 filter
│ ├── icons # 项目所有 svg icons
│ ├── lang # 国际化 language
│ ├── layout # 全局 layout
│ ├── router # 路由
│ ├── store # 全局 store管理
│ ├── styles # 全局样式
│ ├── utils # 全局公用方法
│ ├── vendor # 公用vendor
│ ├── views # views 所有页面
│ ├── App.vue # 入口页面
│ ├── main.js # 入口文件 加载组件 初始化等
│ └── permission.js # 权限管理
├── tests # 测试
├── .env.xxx # 环境变量配置
├── .eslintrc.js # eslint 配置项
├── .babelrc # babel-loader 配置
├── .travis.yml # 自动化CI配置
├── vue.config.js # vue-cli 配置
├── postcss.config.js # postcss 配置
└── package.json # package.json
开发:
工具安装:npm包管理工具,git 代码管理
# 克隆项目
`git clone https://github.com/PanJiaChen/vue-element-admin.git`
# 进入项目目录
`cd vue-element-admin`
# 安装依赖
`npm install`
# 建议不要直接使用 cnpm 安装依赖,会有各种诡异的 bug。
# 可以通过如下淘宝镜像解决 npm 下载速度慢的问题
`npm install --registry=https://registry.npm.taobao.org`
# 项目运行
`npm run dev`
浏览器访问 http://localhost:9527
若出现node-sass安装错误,项目根目录新建文件.npmrc,并添加如下代码后重新安装。
sass_binary_site=https://npm.taobao.org/mirrors/node-sass/ registry=https://registry.npm.taobao.org
路由和侧边栏
本项目侧边栏和路由是绑定在一起的,所以你只有在 @/router/index.js 下面配置对应的路由,侧边栏就能动态的生成了。
配置项
//当设置 true 的时候该路由不会在侧边栏出现 如401,login等页面,或者如一些编辑页面/edit/1
hidden: true // (默认 false)
//当设置 noRedirect 的时候该路由在面包屑导航中不可被点击
redirect: 'noRedirect'
//当你一个路由下面的 children 声明的路由大于1个时,自动会变成嵌套的模式--如组件页面
//只有一个时,会将那个子路由当做根路由显示在侧边栏--如引导页面
//若你想不管路由下面的 children 声明的个数都显示你的根路由
//你可以设置 alwaysShow: true,这样它就会忽略之前定义的规则,一直显示根路由
alwaysShow: true
name: 'router-name' //设定路由的名字,一定要填写不然使用时会出现各种问题
meta: {
roles: ['admin', 'editor'] //设置该路由进入的权限,支持多个权限叠加
title: 'title' //设置该路由在侧边栏和面包屑中展示的名字
icon: 'svg-name' //设置该路由的图标
noCache: true //如果设置为true,则不会被 缓存(默认 false)
breadcrumb: false // 如果设置为false,则不会在breadcrumb面包屑中显示
}
示例:
{
path: '/permission',
component: Layout,
redirect: '/permission/index', //重定向地址,在面包屑中点击会重定向去的地址
hidden: true, // 不在侧边栏线上
alwaysShow: true, //一直显示根路由
meta: { roles: ['admin','editor'] }, //你可以在根路由设置权限,这样它下面所以的子路由都继承了这个权限
children: [{
path: 'index',
component: ()=>import('permission/index'),
name: 'permission',
meta: {
title: 'permission',
icon: 'lock', //图标
role: ['admin','editor'], //或者你可以给每一个子路由设置自己的权限
noCache: true // 不会被 缓存
}
}]
}
路由
这里的路由分为两种,constantRoutes
和 asyncRoutes
。
constantRoutes: 代表那些不需要动态判断权限的路由,如登录页、404、等通用页面。
asyncRoutes: 代表那些需求动态判断权限并通过 addRoutes 动态添加的页面。
侧边栏
代码地址:@/views/layout/components/Sidebar
- 侧边栏有两种形式:submenu 和 直接 el-menu-item。 一个是嵌套子菜单,另一个则是直接一个链接。
- 当你一个路由下面的 children 声明的路由大于>1 个时,自动会变成嵌套的模式。
- 如果子路由正好等于一个就会默认将子路由作为根路由显示在侧边栏中。若不想这样,可以通过设置在根路由中设置
alwaysShow: true
来取消这一特性。- 所有的 css 都可以在 @/styles/sidebar.scss 中找到,可以根据自己的需求进行修改。
- unique-opened
你可以在Sidebar/index.vue中设置unique-opened
来控制侧边栏,是否只保持一个子菜单的展开。
多级目录(嵌套路由)
如果路由是多级目录,如本项目 @/views/nested 那样, 有三级路由嵌套的情况下,不要忘记还要手动在二级目录的根文件下添加一个
。
如:@/views/nested/menu1/index.vue,原则上有多少级路由嵌套就需要多少个
。
点击侧边栏 刷新当前路由
- 方法1:监听侧边栏每个 link 的 click 事件
clickLink(path) {
this.$router.push({
path,
query: {
t: +new Date() //保证每次点击路由的query项都是不一样的,确保会重新刷新view
}
})
}
ps:不要忘了在 router-view 加上一个特定唯一的 key,如
- 方法2:判断当前点击的菜单路由和当前的路由是否一致,一致的时候,会先跳转到一个专门 Redirect 的页面,它会将路由重定向到我想去的页面,这样就起到了刷新的效果了。
点击的时候重定向页面至 /redirect
const { fullPath } = this.$route
this.$router.replace({
path: '/redirect' + fullPath
})
redirect 页面在重定向回原始页面
// redirect.vue
// https://github.com/PanJiaChen/vue-element-admin/blob/master/src/views/redirect/index.vue
export default {
beforeCreate() {
const { params, query } = this.$route
const { path } = params
this.$router.replace({ path: '/' + path, query })
},
render: function(h) {
return h() // avoid warning message
}
}
内容模块:
@
是 webpack 的 alias 配置,对应本项目vue.config.js > configureWebpack > alias配置。
布局: @/layout
对应上图页面整体结构,可根据需求自行修改样式
内容区:@/layout/components/AppMain
标签是定义了页面之间切换动画,可在transition.scss文件中添加修改动画; 标签主要是为了缓存 路由,配合页面的 tabs-view
标签导航使用,如不需要可删除。子页面渲染处,根据路由children下配置
新增页面步骤:
- 新增路由
{
path: '/excel',
component: Layout,
redirect: '/excel/export-excel',
name: 'excel',
meta: {
title: 'excel',
icon: 'excel'
},
children: [
{
path: 'export-excel',
component: ()=>import('excel/exportExcel'),
name: 'exportExcel',
meta: { title: 'exportExcel' }
}
]
}
- 新增View
在 @/views 文件下 创建对应的文件夹,一般一个路由对应一个文件,该模块下的功能组件或者方法就建议在本文件夹下创建一个 utils 或 components 文件夹,各个功能模块维护自己的 utils 或 components 组件。
- 新增api
@/api
- 新增组件
@/components:公共组件
- 新增样式
@/style:全局样式,加 scoped 可防止样式全局污染
- 自定义 element-ui 样式
.article-page {
/* 你的命名空间 */
.el-tag {
/* element-ui 元素*/
margin-right: 0px;
}
}
- 父组件改变子组件样式 深度选择器
总结:不管组件还是样式,公共放外部,私有放内部,方便维护
Postcss
新版的 vue-cli webpack 模板 init 之后根目录下默认有一个postcss.config.js
。vue-loader 的 postcss 会默认读取这个文件的里的配置项,所以在这里直接改配置文件就可以了。配置和 postcss是一样的。
// postcss.config.js
module.exports = {
plugins: {
autoprefixer: {}
}
}
// package.json
"browserslist": [
"> 1%",
"last 2 versions",
"not ie <= 8"
]
如上面代码所述的,autoprefixer 会去读取 package.json 下 browserslist 的配置参数
-
> 1%
兼容全球使用率大于 1%的浏览器 -
last 2 versions
兼容每个浏览器的最近两个版本 -
not ie <= 8
不兼容 ie8 及以下
具体可见 browserslist
Mixin
本项目没有设置自动注入 sass 的 mixin 到全局,所以需要在使用的地方手动引入 mixin
如需要自动将 mixin 注入到全局 ,可以使用sass-resources-loader
和服务端进行交互
前端请求流程
在 vue-element-admin 中,一个完整的前端 UI 交互到服务端处理流程是这样的:
- UI 组件交互操作;
- 调用统一管理的 api service 请求函数;
- 使用封装的 request.js 发送请求;
- 获取服务端返回;
- 更新 data;
为了方便管理维护,统一的请求处理都放在 @/src/api 文件夹中,并且一般按照 model 纬度进行拆分文件
api/
login.js
article.js
remoteSearch.js
...
request.js
@/src/utils/request.js
是基于 axios 的封装,便于统一处理 POST,GET 等请求参数,请求头,以及错误提示信息等。具体可以参看 request.js。 它封装了全局 request拦截器
、response拦截器
、统一的错误处理
、统一做了超时处理
、baseURL设置等
。
一个请求文章列表页的例子:
// api/article.js
import request from '../utils/request';
export function fetchList(query) {
return request({
url: '/article/list',
method: 'get',
params: query
})
}
// views/example/list
import { fetchList } from '@/api/article'
export default {
data() {
list: null,
listLoading: true
},
methods: {
fetchData() {
this.listLoading = true
fetchList().then(response => {
this.list = response.data.items
this.listLoading = false
})
}
}
}