目录结构
├── 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
注意: layout(全局layout) 和 views(views所有页面)
布局
页面整体布局是一个产品最外层的框架结构,往往会包含导航、侧边栏、面包屑以及内容等。想要了解一个后台项目,先要了解它的基础布局
layout
对应代码@/layout
解决问题:整个页面在app中使用了公共的头和底,这就会导致想要新增一个其他类型的页面,就会很不方便,在路由中不好配置。所以需要加上一层管理,也就是layout进行区分。
因为在项目中大部分的页面都是基于 layout 的。除了个别页面,比如 login 404 等页面没使用该layout。如果你想在一个项目中有多种不同的layout也是很方便的,只要在一级路由那里选择不同的layout组件就行。
// No layout
{
path: '/401',
component: () => import('errorPage/401')
}
// Has layout
{
path: '/documentation',
// 你可以选择不同的layout组件
component: Layout,
// 这里开始对应的路由都会显示在app-main中 如上图所示
children: [{
path: 'index',
component: () => import('documentation/index'),
name: 'documentation'
}]
}
app-main
app-main
在 layout
文件夹中index.vue
文件中使用。
使用了 keep-alive
对 <router-view>
进行缓存。
router-view
在真实场景中
我创建和编辑的页面都是使用同一个 component ,默认情况下这两个页面切换时并不会触发vue的created 和 mounted 钩子。官方说你可以通过 watch $route 的变化来进行处理,但说真的还是蛮麻烦的。后来发现其实可以简单的在 router-view 上加上一个唯一的 key,来保证路由切换时都会重新渲染触发钩子了。这样简单的多了。
<router-view :key="key"></router-view>
computed: {
key() {
// 只要保证 key 唯一性就可以了,保证不同页面的 key 不相同
return this.$route.fullPath
}
}
路由和侧边栏
路由和侧边栏是组织起一个后台应用的关键骨架。 本项目侧边栏和路由是绑定在一起的,所以你只有在
@/router/index.js
下面配置对应的路由,侧边栏就能动态的生成了。
配置项
// 当设置 true 的时候该路由不会在侧边栏出现 如401,login等页面,或者如一些编辑页面/edit/1
hidden: true // (默认 false)
//当设置 noRedirect 的时候该路由在面包屑导航中不可被点击
redirect: 'noRedirect'
// 当你一个路由下面的 children 声明的路由大于1个时,自动会变成嵌套的模式--如组件页面
// 只有一个时,会将那个子路由当做根路由显示在侧边栏--如引导页面
// 若你想不管路由下面的 children 声明的个数都显示你的根路由
// 你可以设置 alwaysShow: true,这样它就会忽略之前定义的规则,一直显示根路由
alwaysShow: true
name: 'router-name' // 设定路由的名字,一定要填写不然使用<keep-alive>时会出现各种问题
meta: {
roles: ['admin', 'editor'] // 设置该路由进入的权限,支持多个权限叠加
title: 'title' // 设置该路由在侧边栏和面包屑中展示的名字
icon: 'svg-name' // 设置该路由的图标,支持 svg-class,也支持 el-icon-x element-ui 的 icon
noCache: true // 如果设置为true,则不会被 <keep-alive> 缓存(默认 false)
breadcrumb: false // 如果设置为false,则不会在breadcrumb面包屑中显示(默认 true)
affix: true // 如果设置为true,它则会固定在tags-view中(默认 false)
// 当路由设置了该属性,则会高亮相对应的侧边栏。
// 这在某些场景非常有用,比如:一个文章的列表页路由为:/article/list
// 点击文章进入文章详情页,这时候路由为/article/1,但你想在侧边栏高亮文章列表的路由,就可以进行如下设置
activeMenu: '/article/list'
}
示例
{
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', //图标
roles: ['admin','editor'], //或者你可以给每一个子路由设置自己的权限
noCache: true // 不会被 <keep-alive> 缓存
}
}]
}
路由
这里的路由分为两种,constantRoutes
和 asyncRoutes
。
constantRoutes: 代表那些不需要动态判断权限的路由,如登录页、404、等通用页面。
asyncRoutes: 代表那些需求动态判断权限并通过 addRoutes
动态添加的页面。
注意事项
如果这里有一个需要非常注意的地方就是 404
页面一定要最后加载,如果放在 constantRoutes 一同声明了 404
,后面的所有页面都会被拦截到404
,详细的问题见 addRoutes when you’ve got a wildcard route for 404s does not work
侧边栏
本项目侧边栏主要基于 element-ui
的 el-menu
改造。
这里需要注意一下,一般侧边栏有两种形式即:submenu
和 直接 el-menu-item
。 一个是嵌套子菜单,另一个则是直接一个链接。如下图:
并且在 Sidebar 中已经做了判断,当你判断一个路由下面的 children 声明的路由大于1个时,自动会变成嵌套的模式
多级目录(嵌套路由)
在有三级路由嵌套的情况下,注意要在二级目录的根文件下添加 <router-view>
原则上有多少级路由嵌套就需要多少个<router-view>
。
点击侧边栏 刷新当前路由
在用spa(单页面应用)这种开发模式之前,用户每次点击侧边栏都会重新请求这个页面,用户渐渐养成了点击侧边栏当前路由刷新view的习惯。但现在spa就不一样了,用户点击当前高亮的路由并不会刷新 view,因为 vue-router 会拦截你的路由,它判断你的 url 并没有任何变化,所以它不会触发任何钩子或者是 view 的变化。issue 地址,社区也对该问题展开了激烈讨论。
解决方法:通过不断改变url的query来触发view的变化,我们监听侧边栏每个link的click事件,每次点击都给 router
push 一个不一样的query来确保会重新刷新view
clickLink(path) {
this.$router.push({
path,
query: {
t: +new Date() //保证每次点击路由的query项都是不一样的,确保会重新刷新view
}
})
}
侧边栏滚动问题
之前版本的滚动都是用css来做处理
overflow-y: scroll;
::-webkit-scrollbar {
display: none;
}
首先这样写会有兼容性问题,在火狐或者其它低版本浏览器中都会比较不美观。其次在侧边栏收起的情况下,受限于 element-ui
的 menu
组件的实现方式,不能使用该方式来处理。
所以现版本中使用了 el-scrollbar
来处理侧边栏滚动问题。
备注:
首先进入后套管理系统的通用页面的模板全部是放在 layout 中的,其中 sidebar 就是左侧菜单功能
左侧菜单是根据 路由中的配置进行自动生成,例如在 views中 新建了几个目录结构,并且在路由中进行配置了,那么就会生成对应的菜单结构,在模板构建的时候很清晰