基础版

目录结构

  1. ├── build # 构建相关
  2. ├── mock # 项目mock 模拟数据
  3. ├── plop-templates # 基本模板
  4. ├── public # 静态资源
  5. │── favicon.ico # favicon图标
  6. └── index.html # html模板
  7. ├── src # 源代码
  8. ├── api # 所有请求
  9. ├── assets # 主题 字体等静态资源
  10. ├── components # 全局公用组件
  11. ├── directive # 全局指令
  12. ├── filters # 全局 filter
  13. ├── icons # 项目所有 svg icons
  14. ├── lang # 国际化 language
  15. ├── layout # 全局 layout
  16. ├── router # 路由
  17. ├── store # 全局 store管理
  18. ├── styles # 全局样式
  19. ├── utils # 全局公用方法
  20. ├── vendor # 公用vendor
  21. ├── views # views 所有页面
  22. ├── App.vue # 入口页面
  23. ├── main.js # 入口文件 加载组件 初始化等
  24. └── permission.js # 权限管理
  25. ├── tests # 测试
  26. ├── .env.xxx # 环境变量配置
  27. ├── .eslintrc.js # eslint 配置项
  28. ├── .babelrc # babel-loader 配置
  29. ├── .travis.yml # 自动化CI配置
  30. ├── vue.config.js # vue-cli 配置
  31. ├── postcss.config.js # postcss 配置
  32. └── package.json # package.json

注意: layout(全局layout) 和 views(views所有页面)

布局

页面整体布局是一个产品最外层的框架结构,往往会包含导航、侧边栏、面包屑以及内容等。想要了解一个后台项目,先要了解它的基础布局

layout

基础篇 - 图1

对应代码@/layout
解决问题:整个页面在app中使用了公共的头和底,这就会导致想要新增一个其他类型的页面,就会很不方便,在路由中不好配置。所以需要加上一层管理,也就是layout进行区分。

因为在项目中大部分的页面都是基于 layout 的。除了个别页面,比如 login 404 等页面没使用该layout。如果你想在一个项目中有多种不同的layout也是很方便的,只要在一级路由那里选择不同的layout组件就行。

  1. // No layout
  2. {
  3. path: '/401',
  4. component: () => import('errorPage/401')
  5. }
  6. // Has layout
  7. {
  8. path: '/documentation',
  9. // 你可以选择不同的layout组件
  10. component: Layout,
  11. // 这里开始对应的路由都会显示在app-main中 如上图所示
  12. children: [{
  13. path: 'index',
  14. component: () => import('documentation/index'),
  15. name: 'documentation'
  16. }]
  17. }

app-main

app-mainlayout 文件夹中index.vue文件中使用。
使用了 keep-alive<router-view> 进行缓存。

router-view

在真实场景中
基础篇 - 图2

我创建和编辑的页面都是使用同一个 component ,默认情况下这两个页面切换时并不会触发vue的created 和 mounted 钩子。官方说你可以通过 watch $route 的变化来进行处理,但说真的还是蛮麻烦的。后来发现其实可以简单的在 router-view 上加上一个唯一的 key,来保证路由切换时都会重新渲染触发钩子了。这样简单的多了。

  1. <router-view :key="key"></router-view>
  2. computed: {
  3. key() {
  4. // 只要保证 key 唯一性就可以了,保证不同页面的 key 不相同
  5. return this.$route.fullPath
  6. }
  7. }

路由和侧边栏

路由和侧边栏是组织起一个后台应用的关键骨架。 本项目侧边栏和路由是绑定在一起的,所以你只有在 @/router/index.js 下面配置对应的路由,侧边栏就能动态的生成了。

配置项

  1. // 当设置 true 的时候该路由不会在侧边栏出现 如401,login等页面,或者如一些编辑页面/edit/1
  2. hidden: true // (默认 false)
  3. //当设置 noRedirect 的时候该路由在面包屑导航中不可被点击
  4. redirect: 'noRedirect'
  5. // 当你一个路由下面的 children 声明的路由大于1个时,自动会变成嵌套的模式--如组件页面
  6. // 只有一个时,会将那个子路由当做根路由显示在侧边栏--如引导页面
  7. // 若你想不管路由下面的 children 声明的个数都显示你的根路由
  8. // 你可以设置 alwaysShow: true,这样它就会忽略之前定义的规则,一直显示根路由
  9. alwaysShow: true
  10. name: 'router-name' // 设定路由的名字,一定要填写不然使用<keep-alive>时会出现各种问题
  11. meta: {
  12. roles: ['admin', 'editor'] // 设置该路由进入的权限,支持多个权限叠加
  13. title: 'title' // 设置该路由在侧边栏和面包屑中展示的名字
  14. icon: 'svg-name' // 设置该路由的图标,支持 svg-class,也支持 el-icon-x element-ui 的 icon
  15. noCache: true // 如果设置为true,则不会被 <keep-alive> 缓存(默认 false)
  16. breadcrumb: false // 如果设置为false,则不会在breadcrumb面包屑中显示(默认 true)
  17. affix: true // 如果设置为true,它则会固定在tags-view中(默认 false)
  18. // 当路由设置了该属性,则会高亮相对应的侧边栏。
  19. // 这在某些场景非常有用,比如:一个文章的列表页路由为:/article/list
  20. // 点击文章进入文章详情页,这时候路由为/article/1,但你想在侧边栏高亮文章列表的路由,就可以进行如下设置
  21. activeMenu: '/article/list'
  22. }

示例

  1. {
  2. path: '/permission',
  3. component: Layout,
  4. redirect: '/permission/index', //重定向地址,在面包屑中点击会重定向去的地址
  5. hidden: true, // 不在侧边栏显示
  6. alwaysShow: true, //一直显示根路由
  7. meta: { roles: ['admin','editor'] }, //你可以在根路由设置权限,这样它下面所有的子路由都继承了这个权限
  8. children: [{
  9. path: 'index',
  10. component: ()=>import('permission/index'),
  11. name: 'permission',
  12. meta: {
  13. title: 'permission',
  14. icon: 'lock', //图标
  15. roles: ['admin','editor'], //或者你可以给每一个子路由设置自己的权限
  16. noCache: true // 不会被 <keep-alive> 缓存
  17. }
  18. }]
  19. }

路由

这里的路由分为两种,constantRoutesasyncRoutes
constantRoutes: 代表那些不需要动态判断权限的路由,如登录页、404、等通用页面。
asyncRoutes: 代表那些需求动态判断权限并通过 addRoutes 动态添加的页面。

注意事项
如果这里有一个需要非常注意的地方就是 404 页面一定要最后加载,如果放在 constantRoutes 一同声明了 404,后面的所有页面都会被拦截到404 ,详细的问题见 addRoutes when you’ve got a wildcard route for 404s does not work

侧边栏

本项目侧边栏主要基于 element-uiel-menu 改造。

代码地址 @/layout/components/Sidebar

这里需要注意一下,一般侧边栏有两种形式即:submenu 和 直接 el-menu-item。 一个是嵌套子菜单,另一个则是直接一个链接。如下图:
基础篇 - 图3
并且在 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

  1. clickLink(path) {
  2. this.$router.push({
  3. path,
  4. query: {
  5. t: +new Date() //保证每次点击路由的query项都是不一样的,确保会重新刷新view
  6. }
  7. })
  8. }

侧边栏滚动问题

之前版本的滚动都是用css来做处理

  1. overflow-y: scroll;
  2. ::-webkit-scrollbar {
  3. display: none;
  4. }

首先这样写会有兼容性问题,在火狐或者其它低版本浏览器中都会比较不美观。其次在侧边栏收起的情况下,受限于 element-uimenu 组件的实现方式,不能使用该方式来处理。
所以现版本中使用了 el-scrollbar 来处理侧边栏滚动问题。

备注:
首先进入后套管理系统的通用页面的模板全部是放在 layout 中的,其中 sidebar 就是左侧菜单功能
左侧菜单是根据 路由中的配置进行自动生成,例如在 views中 新建了几个目录结构,并且在路由中进行配置了,那么就会生成对应的菜单结构,在模板构建的时候很清晰