一、表单的验证
完成表单验证的步骤
-  
在
el-form表单元素上添加一个rules属性,rules中配置相关的验证规则 -  
在
el-form表单元素上添加:model将data中验证的响应式数据关联起来 -  
在
el-form-item中添加prop属性,该属性一定和rules中的key值对应来 
 <el-form :rules="userFormRules" :model="userForm">
      <el-form-item prop="username">
           <el-input  prefix-icon="el-icon-user-solid" v-model="userForm.username"></el-input>
      </el-form-item>
  </el-form> 
export default {
    data(){
        return{
            userForm:{
                username:'',
                password:''
            },
            userFormRules:{
                username:[
                    {required:true,message:'用户名不能为空',trigger:'blur'}
                ]
            }
        }
    }
} 
登录功能实现
-  
给登录按钮绑定单击事件
 
 <el-button type="primary" class="loginbtn" @click="login()">登录</el-button> 
-  
在methods选项定义login方法,完成登录业务
 
methods:{
        login(){
            this.$refs.loginForm.validate(async args=>{
               if(args){
                 const {code,message,token}=await this.$api.users.login(this.userForm)
                 if(code){
                    //this.$message.success(message)
                    this.$message({
                        message,
                        type:'success'
                    })
                    //保存token
                    setStore(TOKEN_KEY,token)
                    this.$router.replace('/')
                 }else{
                    this.$message.warning(message)
                 }
               }
            })
        }
    } 
-  
这里将保存token的方法写成了一个工具
 
在utils下创建h5store.js文件,编写代码如下
export const setStore=(key,value)=>localStorage.setItem(key,value)
export const getStore=key=>localStorage.getItem(key)
export const removeStore=key=>localStorage.removeItem(key) 
在utils下创建constants.js,专门存放常量,便于后续的管理
export const TOKEN_KEY="token" 
二、后台首页布局
1、静态页面布局
用于布局的容器组件,方便快速搭建页面的基本结构:
<el-container>:外层容器。当子元素中包含 <el-header> 或 <el-footer> 时,全部子元素会垂直上下排列,否则会水平左右排列。
<el-header>:顶栏容器。
<el-aside>:侧边栏容器。
<el-main>:主要区域容器。
<el-footer>:底栏容器。
<template>
   <el-container>
      <!-- 侧边栏 -->
      <el-aside width="220px">asider</el-aside>
      <el-container>
          <el-header>header</el-header>
          <el-main>main</el-main>
      </el-container>
   </el-container>
</template> 
2、左侧菜单栏
<template>
    <el-container class="container">
        <el-aside>
           <div class="logo">蜗牛BOSS系统</div>
           <el-menu
            background-color="#112243"
            text-color="#fff"
            active-text-color="#ff0"
            :unique-opened="true">
                <el-submenu index="1">
                    <template slot="title">
                        <i class="el-icon-s-management"></i>
                        <span>控制面板</span>
                    </template>
                    <el-menu-item index="11">
                        <i class="el-icon-tickets"></i>
                        <span>工作台</span>
                    </el-menu-item>
                     <el-menu-item index="12">
                        <i class="el-icon-tickets"></i>
                        <span>报表分析</span>
                    </el-menu-item>
                </el-submenu>
                <el-submenu index="2">
                    <template slot="title">
                        <i class="el-icon-s-management"></i>
                        <span>业务管理</span>
                    </template>
                    <el-menu-item index="21">
                        <i class="el-icon-tickets"></i>
                        <span>学生管理</span>
                    </el-menu-item>
                     <el-menu-item index="22">
                        <i class="el-icon-tickets"></i>
                        <span>教师管理</span>
                    </el-menu-item>
                </el-submenu>
           </el-menu>
        </el-aside>
        <el-container class="main-container">
            <el-header></el-header>
            <el-main></el-main>
        </el-container>
    </el-container>
</template> 
3、动态菜单
-  
在请求拦截器中设置Authorization
 
在request.js文件中进行设置
instance.interceptors.request.use(config=>{
    //可以设置携带token信息
    config.headers.Authorization=getStore(TOKEN_KEY)
    return config
},err=>{
    return Promise.reject(err)
}) 
-  
在api中添加获取权限的api方法
 
//导入封装后的axios
import {instance} from '@/util/request'
export default{
   getAuthMenus:()=>instance.get('/menus/getAuthMenus')
} 
-  
在Home组件中在created方法中调用获取权限的方法
 
export default {
    data(){
        return{
            menus:[]
        }
    },
    methods:{
        async getAuthMenus(){
            const result=await this.$api.users.getAuthMenus()
            console.log('result',result);
            this.menus=result.data
        }
    },
    created(){
        this.getAuthMenus()
    }
} 
-  
在菜单栏中完成动态渲染
 
<el-menu
    background-color="#112243"
    text-color="#fff"
    active-text-color="#ff0"
    :unique-opened="true">
    <el-submenu :index="item._id" v-for="item in menus" :key="item._id">
           <template slot="title">
                <i :class="item.icon"></i>
                <span>{{item.title}}</span>
           </template>
           <el-menu-item :index="subitem._id" v-for="subitem in item.children" :key="subitem._id">
                <i class="el-icon-tickets"></i>
                <span>{{subitem.title}}</span>
           </el-menu-item>      
     </el-submenu>    
</el-menu> 
4、静态路由的配置
实现步骤
-  
在views的目录下按照模块创建相应的页面
按照老师指定的要求去定义路由页面

 -  
在config.js文件中进行二级的配置,这里配置二级路由路径的时候,请参照上图数据库表中的path字段来进行定义
 
//通过ES6的导入语法将组件导入进来
import Login from '@/views/Login'
import Home from '@/views/Home'
import NotFound from '@/views/NotFound'
export default[
    {
        path:'/login',
        component:Login
    },
    {
        path:'/',
        redirect:'/home'
    },
    {
        path:'/home',
        component:Home,
        children:[
            {
                path:'workplace',
                component:()=>import('@/views/dashboard/workplace.vue')
            },
            {
                path:'analysis',
                component:()=>import('@/views/dashboard/analysis.vue')
            }
        ]
    },
    {
        path:'*',
        component:NotFound
    },
] 
-  
如果要让elementui的menu菜单启用vu-router路由模式,需要在
el-menu标签中设置:router="true" 
<el-menu
	:router="true">         
-  
设置完如上的属性后,就需要在
el-menu-item标签中将:index的属性值,由之前的subItem._id改成subItem.path -  
在Home的
el-main区域内容配置二级路由出口 
<el-main>
     <!-- 配置二级路由出口 -->
     <router-view></router-view>
</el-main> 
5、菜单折叠
-  
在Home.vue文件中定义数据isCollpase
 
export default {
  data(){
    return{
      isCollapse:false,
      logotitle:'蜗牛BOSS系统'
    }
  }
} 
-  
在
<el-header>区域添加折叠和展开按钮,并根据isCollapse的值控制这两个按钮的显示或隐藏 
<el-header>
     <i v-if="!isCollapse" class="el-icon-s-fold"></i>
     <i v-else class="el-icon-s-unfold"></i>
    <!--...其余代码省略-->   
</el-header> 
-  
为展开和折叠按钮添加绑定单击事件
 
<el-aside>
     <i v-if="!isCollapse" class="el-icon-s-fold" @click="toggleCollpase" style="margin-left:190px"></i>
     <i v-else class="el-icon-s-unfold" @click="toggleCollpase" style="margin-left:40px"></i>
    <!--...其余代码省略-->   
</el-aside>
methods: {
    toggleCollpase(){
      this.isCollapse=!this.isCollapse;
	  if(this.isCollapse){
               this.logotitle="蜗牛"
        }else{
               this.logotitle="蜗牛BOSS系统"
        }
    }
} 
-  
在
<el-menu>标签中添加collpase属性来控制菜单的折叠或展开 
<el-menu :collapse="isCollapse"> 
-  
在
<el-aside>中动态设置菜单栏的宽度,同时删除掉之前style="width: 200px" 
<el-aside :width="isCollapse?'68px':'220px'"> 
-  
取消切换动画
 
<el-menu :collapse-transition="false"> 
-  
去掉菜单栏边框
 
 .el-menu{
       border: none;
  } 
三、路由守卫
在单页面应用SPA,组件之间的切换(页面的跳转)实际上是通过路由跳转来完成,路由守卫的主要作用就是在进入目标路由的时候(可以在之前,也可以在后)先进入到这些函数里边,这些函数可以用来完成逻辑判断,适合要求的进入目标路由,不符合要求的会被拦截到其他地方去
我们把这种函数称为路由守卫函数
路由守卫函数分类
-  
全局前置守卫:所有的路由都要进入的守卫函数,而且时机在前
 -  
全局后置守卫:所有的路由都要进入的守卫函数,时机在后
 -  
路由独享守卫:指定只进入指定路由的时候触发的函数
 -  
组件内守卫:进入组件触发,离开组件触发
 
1、全局前置守卫
-  
在router/index.js文件中添加路由守卫
 
//设置路由全局前置守卫
router.beforeEach(async(to,from,next)=>{
  console.log('*********路由全局前置守卫*************');
  console.log('to',to);
  if(to.path=="/login"){
    next()//进入目标路由
  }else{
    console.log('*********您等等*************');
    //获取token信息
    if(getStore(TOKEN_KEY)){
      try{
        await Vue.prototype.$api.users.getUserInfo()
      }catch(err){
         next('/login')
      }
      console.log('result',result);
      next()//进入到目标地
    }else{
      Vue.prototype.$message.warning("您还没有登录请先登录")
      next("/login")
    }
  }
}) 
-  
修改request.js中响应拦截器
 
instance.interceptors.response.use(response=>{
    return response.data
},err=>{
    if(err.response){
        switch(err.response.status){
            case 401:
                Vue.prototype.$message.warning('Token已经过期,请重新登录')
                break;
        }
    }
    return Promise.reject(err)
}) 
2、动态设置路由规则
前提:由于项目中路由配置都是静态,除了这种方式之外还另外一种方式,是通过代码方法进行动态路由设置
router.beforeEach(async(to,from,next)=>{
  if(to.path=="/login"){
    next()
  }else{
    if(getStore(TOKEN_KEY)){
        try{
          const result=await Vue.prototype.$api.users.getUserInfo()
          //定义一个变量用来接收后端的所有路由规则
          let permissionList=result.data.permissionList
          //将查出来的数据赋值到menus
          menus=permissionList
          //使用for循环进行遍历
          for(let i=0;i<permissionList.length;i++){
            let routeObj=permissionList[i]
            //addRoute(参数1,参数2):用来动态添加一条新的路由规则,参数1:父路由的name,参数2:路由对象
            router.addRoute("homeName",{
              path:routeObj.path,
              component:()=>import(`@/views${routeObj.permission}.vue`)
            })
          }
          //查看所有路由
          console.log('routes',router.getRoutes());
         
        }catch(err){
           next('/login')
        }
      next()//进入到目标地
    }else{
      Vue.prototype.$message.warning("您还没有登录请先登录")
      next("/login")
    }
  }
}) 
 

如上错误的解决办法有三种
-  
降低版本
 -  
在路由跳转中使用catch来处理
 
this.$router.replace('/').catch(()=>{}) 
-  
统一重写replace或者push方法
 
const originalPush = VueRouter.prototype.push;
const originalReplace = VueRouter.prototype.replace;
//push
VueRouter.prototype.push = function push(location, onResolve, onReject) {
  if (onResolve || onReject)
    return originalPush.call(this, location, onResolve, onReject);
  return originalPush.call(this, location).catch(err => err);
};
//replace
VueRouter.prototype.replace = function push(location, onResolve, onReject) {
    if (onResolve || onReject)
        return originalReplace.call(this, location, onResolve, onReject);
    return originalReplace.call(this, location).catch(err => err);
}; 
如上问题解决完毕后,下面来打印一下添加好的路由规则

如上效果,就表示添加成功,路由添加成功后,我们需要让跳转的目标路由上,这里我们通过next({path\:to.path})方式让其进入,会发现网络发送多请求,造成严重影响,如下图所示

解决的办法设置menus变量,然后判断只有menus为空的时候,采取发送网路请求
//定义一个变量
let menus=null
//设置路由全局前置守卫:当进入目标路由之前会调用该函数
router.beforeEach(async(to,from,next)=>{
  if(to.path=="/login"){
    next()
  }else{
    if(getStore(TOKEN_KEY)){
      if(!menus){
        try{
          const result=await Vue.prototype.$api.users.getUserInfo()
          //定义一个变量用来接收后端的所有路由规则
          let permissionList=result.data.permissionList
          //将查出来的数据赋值到menus
          menus=permissionList
          //使用for循环进行遍历
          for(let i=0;i<permissionList.length;i++){
            let routeObj=permissionList[i]
            //addRoute(参数1,参数2):用来动态添加一条新的路由规则,参数1:父路由的name,参数2:路由对象
            router.addRoute("homeName",{
              path:routeObj.path,
              component:()=>import(`@/views${routeObj.permission}.vue`)
            })
          }
          //查看所有路由
          console.log('routes',router.getRoutes());
          next({path:to.path})
        }catch(err){
           next('/login')
        }
      }
      next()//进入到目标地
    }else{
      Vue.prototype.$message.warning("您还没有登录请先登录")
      next("/login")
    }
  }
}) 
3、路由进度条
使用nprogress插件来完成路由进度操作
-  
下载依赖包
 
npm i nprogress  
-  
配置
 
//导入nprogress依赖包
import NProgress from 'nprogress'
//将nprogress的css导入
import 'nprogress/nprogress.css'
NProgress.configure({
   easing: "ease-in-out",
   speed: 300,
   trickeSpeed: 300,
}) 
-  
在路由全局前置守卫上启动动画
 
router.beforeEach(async(to,from,next)=>{
      NProgress.start()
}) 
-  
在路由全局后置守卫上结束动画
 
router.afterEach((to,from)=>{
  NProgress.done()
})
                


















