04-HR-路由配置(路由配置介绍,搭建业务模块页面,配置模块路由规则,配置菜单文字和icon图标,拆分路由模块,动态路由和静态路由合并,处理高亮激活)

689 阅读10分钟

路由配置

路由配置介绍

熟悉静态路由和动态路由的概念

  1. 静态路由:提前写死的路由配置(只要打开页面就可以访问的路由)
  2. 动态路由:只有该用户拥有这个路由模块权限的时候才可以访问路由(由用户的权限控制)
  3. 动态路由的权限是由用户相关的数据决定的

image.png

整理路由配置

目标 删除基础模板中附带的多余页面并且删除多余的路由配置

  • 删除多余的路由配置 src/router/index.js
// 静态路由表 =>  静态路由(不需要权限即可访问的)
export const constantRoutes = [
  {
    path: '/login',
    component: () => import('@/views/login/index'),
    hidden: true
  },

  {
    path: '/404',
    component: () => import('@/views/404'),
    hidden: true
  },

  {
    path: '/',
    component: Layout,
    redirect: '/dashboard',
    children: [
      {
        path: 'dashboard',
        name: 'Dashboard',
        component: () => import('@/views/dashboard/index'),
        meta: { title: 'Dashboard', icon: 'dashboard' }
      }
    ]
  },
  // 没有匹配到的页面, 走404
  { path: '*', redirect: '/404', hidden: true }
]

上面代码,我们只对【登录页/404/主页】进行了保留,并且我们发现,删除了其他页面之后,左侧导航菜单的数据也只剩下了首页

注意:

  1. 路由映射会影响左侧菜单的显示
  2. 路由映射中如果包含hidden:true属性,那么左侧就不显示该路由菜单,否则就显示

搭建业务模块页面

目标: 快速搭建 - 人资项目的常规业务模块 - 主页面

├──views
  ├── approvals           # 审批
  ├── attendances         # 考勤
  ├── dashboard           # 首页 -- 已有
  ├── departments         # 组织架构
  ├── employees           # 员工
  ├── login               # 登录 -- 已有
  ├── permission          # 权限管理
  ├── salarys             # 工资
  ├── setting             # 公司设置
  ├── social              # 社保
  ├── 404                 # 404 -- 已有
  • 通过命令快速创建相关目录
mkdir approvals attendances departments employees permission salarys setting social   
  • 每个模块的内容,可以先按照标准的模板建立,如下所示
<template>
  <div class="dashboard-container">
    <div class="app-container">
      <h2>
        首页
      </h2>
    </div>
  </div>
</template>

<script>
export default {

}
</script>

<style lang="scss" scoped>

</style>

总结,如何快速的创建多个文件夹?mkdir 文件夹名称(多个名称空格隔开)

配置模块路由规则

目标:配置路由规则

  • 我们这边会拆分出来, 每个模块一个路由规则, 以组织架构 departments 为例
// 首页模块
{
  path: '/',
  component: Layout,
  redirect: '/dashboard',
  children: [
    {
      path: 'dashboard',
      name: 'dashboard',
      component: () => import('@/views/dashboard/index'),
      meta: { title: '首页', icon: 'dashboard' }
    }
  ]
},
// 组织架构模块
{
  path: '/departments',
  component: Layout,
  children: [
    {
      path: '',
      name: 'departments',
      component: () => import('@/views/departments'),
      meta: { title: '组织架构', icon: 'dashboard' }
    }
  ]
},

总结

  1. 左侧菜单一级路由组件都是Layout
  2. 右侧区域显示的组件实际上是二级路由

注意:为什么要这样设计呢?方便后续拆分路由模块,从而在大型项目中更好维护路由配置。

注意:如果children配置的子路由的path如果是空字符串,那么默认就显示该二级路由

左侧菜单逻辑基本分析

目标:熟悉左侧菜单的渲染逻辑

src/layout/components/Sidebar/SidebarItem.vue

  • 只要配了一个规则, 就多出来一个菜单, 是因为菜单的项, 是遍历路由规则动态渲染出来的
<el-menu
  :default-active="activeMenu"
  :collapse="isCollapse"
  :background-color="variables.menuBg"
  :text-color="variables.menuText"
  :unique-opened="false"
  :active-text-color="variables.menuActiveText"
  :collapse-transition="false"
  mode="vertical"
>
  <sidebar-item v-for="route in routes" :key="route.path" :item="route" :base-path="route.path" />
</el-menu>
computed: {
  routes() {
    // this.$router.options.routes 代表的是配置的路由映射
    // 这个API是由vue-router提供
    return this.$router.options.routes
  },
}
  • 路由规则上, 可以配置一个 hidden 属性, 配置 true, 就是将来不渲染菜单(除了hidden之外也与children属性有关)
{
  path: '/login',
  component: () => import('@/views/login/index'),
  hidden: true
},
  • 这里菜单的渲染, 处理了一级导航菜单二级导航菜单, 二级导航我们这边不需要, 可以注释掉
<template>
  <div v-if="!item.hidden">

总结:通过vue-router的api获取所有的路由映射数组,动态遍历形成左侧菜单,通过hidden属性控制路由映射是否显示在左侧菜单列表中。

总结

  • ※脚手架配置代理:前端解决跨域问题,这种解决方案仅仅适用于开发阶段,上线时依然需要后端配置。

  • 获取用户信息并且显示到左上角:调用接口;获取数据保存在状态里;组件进行展示

    • axios
    • vuex:state/mutation/action/getters
    • 通过NavBar组件触发action获取数据更新到state,组件中通过getters获取state中的username
  • ※获取用户头像信息:和获取用户信息进行了合并(action合并了);两个对象的合并操作(延展运算符)

  • ※Vue插件:用于扩展Vue核心特性:扩展原型属性;扩展组件;扩展指令。。。。,通过插件的方式扩展会更加通用,比如ElementUI如何使用?先导入再配置 Vue.use(ElementUI)

    • 我们如何自己做一个Vue插件?Vue插件本质就是一个对象,对象中需要有一个方法install
    • 定义好的插件如何使用?先在入口文件导入插件,配置插件 Vue.use(MyPlugins)
    • 插件支持选项配置
  • ※基于插件扩展一个自定义指令:头像的图片如果加载失败,就显示默认的图片

    • 怎么做?封装自定义指令(Vue自定义指令的基本规则)
    • 指令功能:控制图片动态给src属性赋值;当图片地址加载失败时,触发img的onerror事件,再赋值默认图片
    • 支持默认图片的设置
  • 退出

    • ※下拉组件的事件绑定:native修饰符作用,用于给组件绑定原生事件
    • 实现退出功能:添加$confirm提示;触发action退出
    • 基于Promise的方式实现
    • 基于Async函数的方式实现
    • ※catch后面可以继续then
  • 处理token失效的问题

    • 后端返回的状态码是401,在响应拦截器进行处理即可
    • 删除用户信息;跳转登录页
    • ※解决Chrome的cookie不可以修改问题:修改Chrome设置
      • chrome://flags 打开页面,搜索cookie,所有选项修改为enabled
  • ※路由模块

    • 静态路由和动态路由基本概念
    • 初始化业务组件:mkdir命令的基本使用
    • 添加部门管理的路由映射
      • 所有的一级路由组件都是Layout,为何要如此设计?方便后续拆分路由配置
      • 左侧菜单基本结构的分析:左侧菜单项由路由映射决定
      • 如何获取路由映射的信息?this.$router.options.routes
      • 路由映射的选项中如果有hidden:true,左侧就不显示该路由信息

关于template标签的使用

  • 很多时候需要统一控制一组标签进行条件渲染,但是不可以引入额外的DOM结构(可能会影响样式)
<div v-if='isShow'>tom</div>
<div v-if='isShow'>jerry</div>
<div v-if='isShow'>spike</div>

<!-- 如下的写法虽然可以实现需求,但是引入了额外的DOM,可能会影响样式 -->
<div v-if='isShow'>
  <div >tom</div>
  <div >jerry</div>
  <div >spike</div>
</div>
  • 为了简化上述操作,可以基于template标签进行优化
<template v-if='isShow'>
  <div>tom</div>
  <div>jerry</div>
  <div >spike</div>
</template>

总结:

  1. 集中控制一组元素的条件渲染,但是template标签本身不会被渲染出来
  2. template标签上是否可以使用v-show?不可以(因为v-show本质上控制的是标签的display属性,但是template标签不会被渲染出来)

动态组件

动态组件:<component is='组件名称或者DOM标签名称'></component/>

<component is='router-link'>跳转</component/>
<!--渲染结果-->
<router-link>跳转</router-link>

<component is='a'>跳转</component/>
<!--渲染结果-->
<a>跳转</a>

<component is='MyComTest'>跳转</component/>
<!--渲染结果-->
<MyComTest>跳转</MyComTest>

总结:基于component标签可以动态渲染组件(包括原生DOM和自定义组件)

注意:在component标签上绑定的属性,最终会绑定到渲染的标签上面

v-bind独立用法

<div v-bind:msg='obj.msg' v-bind:info='obj.info'></div>
<div v-bind='obj'></div>

data () {
  return {
    obj: {
      msg: 'nihao',
      info: 'hello',
    }
  }
}

总结:上述两种写法等效;如果v-bind直接赋值一个对象,那么就是把对象的每一个属性分别绑定到标签上

关于左侧菜单逻辑总结

  • 菜单显示的条件:hidden和children共同决定
    • 左侧菜单项目的链接路径是基于一级路由和二级路由路径拼接的
  • 组件的嵌套关系
    • router-link标签是由动态组件component渲染出来
    • 动态组件单独再分析
<el-menu>
  <router-link :to='to'>
    <el-menu-item>
      <!-- item组件用于显示菜单的图标和标题文本 -->
      <item></item>
    </el-menu-item>
  </router-link>
</el-menu>

总结:

  1. 单个菜单实际上是 Item.vue 组件,组件显示的数据来源是二级路由映射的对象
  2. 路由的跳转由app-link组件控制,目前可以暂且理解router-link的作用
  3. 接下来分析AppLink组件内部的实现:动态组件

组件的模板由一种渲染方式

  • 单文件组件的template标签
  • el属性,设置组件渲染的模板(只在用 new 创建实例时生效)
  • template也是一个选项(非单文件组件),用于给组件提供模板(脚手架环境中不使用)
  • render函数可以提供组件的模板(createElement回调的参数)
    • 参数一表示标签名称(包括组件名称和DOM标签名称)
    • 参数二表示属性键值对
    • 参数三表示子元素
new Vue({
  el: '#app'
})

import MyCom from './MyCom.vue'
Vue.component('my-com', MyCom)

// 全局组件
Vue.component('my-com', {
  template: '<div>hello</div>'
})

Vue.component('alert-box', {
  template: `
    <div class="demo-alert-box">
      <strong>Error!</strong>
      <slot></slot>
    </div>
  `
})

// <ul>
//   <li>apple</li>
//   <li>orange</li>
// </ul>

Vue.component('anchored-heading', {
  render: function (createElement) {
    // <h1 class='active'>hello</h1>
    const li1 = createElement('li', null, 'apple')
    const li2 = createElement('li', null, 'orange')
    return createElement('ul', { class: 'active'}, [li1, li2])
  },
  props: {
    level: {
      type: Number,
      required: true
    }
  }
})

Vue.component('anchored-heading', {
  render: function (createElement) {
    // <h1 class='active'>hello</h1>
    return createElement('h1', { class: 'active'}, 'hello')
  },
  props: {
    level: {
      type: Number,
      required: true
    }
  }
})
  • 基于render函数自己实现菜单的模板效果
render: function (createElement) {
  // <h1 class='active'>hello</h1>
  const svg = createElement('svg-icon', { props: { iconClass: icon } }, null)
  const span = createElement('span', null, '首页')
  return [svg, span]
},
  • 基于jsx实现
render (h, context) {
  const { icon, title } = context.props
  const vnodes = []

  if (icon) {
    if (icon.includes('el-icon')) {
      // 如果包含el-icon类名,证明是字体图标
      vnodes.push(<i class={[icon, 'sub-el-icon']} />)
    } else {
      // 否则为svg图标
      vnodes.push(<svg-icon icon-class={icon} />)
    }
  }

  if (title) {
    // 菜单名称
    vnodes.push(<span slot='title'>{(title)}</span>)
  }
  return vnodes
}

总结:JSX是js的扩展,这种语法引进与React框架,但是Vue已经兼容这种语法,本质上就是在JS代码里面嵌入HTML标签;插值表达式使用单个花括号,JSX元素可以直接放到数组里面。

注意:标签的属性可以直接通过插值表达式绑定动态的值

配置菜单文字和icon图标

目标: 配置菜单文字 和 菜单图标

  1. 拷贝svg图标到项目中: 该资源已经在资料svg目录中提供,请将该目录下所有svg放到**src/icons/svg**目录下

  2. 修改icon名字: 具体的icon名称可参考线上地址

{
  path: '/departments',
  component: Layout,
  children: [
    {
      path: '',
      name: 'departments',
      component: () => import('@/views/departments'),
      meta: { title: '组织架构', icon: 'tree' }
    }
  ]
},
  • 模块对应icon参照表
├── dashboard           # dashboard
├── departments         # tree
├── employees           # people
├── setting             # setting
├── salarys             # money
├── social              # table
├── attendances         # skill
├── approvals           # tree-table
├── permission          # lock

总结:

  1. 把svg图标文件拷贝到 icons/svg目录里面
  2. 修改路由映射的meta中的icon属性的值为svg文件名称

拆分路由模块

目标: 新建拆分路由模块

按刚才的方式, 成功新增了一个路由模块, 但是如果所有的路由全都写在一个文件中肯定不合适, 所以要拆模块

  • 业务路由模块目录结构
├── router               # 路由目录
 ├── index.js            # 路由主文件
 ├── modules             # 模块目录
  ├── approvals.js       # 审批  			图标: tree-table
  ├── attendances.js     # 考勤  			图标: skill
  ├── departments.js     # 组织架构 	   图标: tree
  ├── employees.js       # 员工 			图标: people
  ├── permission.js      # 权限管理		   图标: lock
  ├── salarys.js         # 工资			图标: money
  ├── setting.js         # 角色管理		   图标: setting
  ├── social.js          # 社保			图标: table 
  • 快速创建文件命令
touch approvals.js attendances.js departments.js employees.js permission.js setting.js salarys.js social.js 
  • 重构文件内容
// 组织架构模块
import Layout from '@/layout'

export default {
  path: '/departments',
  component: Layout,
  children: [
    {
      path: '',
      name: 'departments',
      component: () => import('@/views/departments'),
      meta: { title: '组织架构', icon: 'tree' }
    }
  ]
}

总结:把每一个业务模块的路由单独进行拆分然后再导入并进行配置

  1. 基于touch命令批量创建文件
  2. 拆分各个路由模块
  3. 创建不同模块的组件

动态路由和静态路由合并

目标: 将静态路由和动态路由的路由表进行临时合并

  • 什么叫临时合并?

在第一个小节中,我们讲过了,动态路由是需要权限进行访问的,但是权限的动态路由访问是比较复杂的,

我们后面再进行讲解,所以为了更好地看到效果,我们可以先将 静态路由和动态路由进行合并, 也就是默认开放所有路由权限

路由主文件 src/router/index.js

// 引入多个模块的规则
import approvalsRouter from '@/router/modules/approvals'
import departmentsRouter from '@/router/modules/departments'
import employeesRouter from '@/router/modules/employees'
import permissionRouter from '@/router/modules/permission'
import attendancesRouter from '@/router/modules/attendances'
import salarysRouter from '@/router/modules/salarys'
import settingRouter from '@/router/modules/setting'
import socialRouter from '@/router/modules/social'

// 动态路由表 => 动态路由(需要权限才可以访问的) 我们这里准备一个数组存放
export const asyncRoutes = [
  approvalsRouter,
  departmentsRouter,
  employeesRouter,
  permissionRouter,
  attendancesRouter,
  salarysRouter,
  settingRouter,
  socialRouter
]

const createRouter = () => new Router({
  // mode: 'history', // require service support
  scrollBehavior: () => ({ y: 0 }), // 管理滚动行为, 让页面切换时回到顶部
  routes: [...constantRoutes, ...asyncRoutes] // 临时合并动态路由和静态路由
})

总结:静态路由配置和动态路由配置分开处理

  1. 数组的合并写法 cosnt arr = [...arr1, ...arr2]
  2. 动态路由后续需要根据用户的权限进行控制

处理高亮激活

目标:控制点击路由链接高亮

左侧菜单被选中时, 会加上一个 is-active 类, 可以在 sidebar.scss 覆盖下样式

li.is-active {
  background-color: #fff !important;
  .svg-icon {
    color: #43a7fe;
  }
  span {
    color: #43a7fe;
  }
}

总结:点中左侧菜单后,自动添加一个is-active类名(el-menu的行为),我们需要提供对应的样式即可。

注意:点中菜单高亮的行为是 Element-UI的菜单组件的行为 (is-active类名是Element-UI自动添加的)

注意:基于路由也可以实现 router-link-exact-active router-link-active