后台管理系统项目

11.17项目day1–login页面

如果忘记配置Linst

在vue.config.js中添加如下配置

image-20201117194648416

①安装vue项目

image-20201117144456994

在合适的位置打开cmd或集成终端,输入 vue create 项目名称,选择第二项

image-20201117144606535

预安装选择:Babel,Router,Vuex,css预编译,Linter

image-20201117144730137

是否启用默认设置,选择否

image-20201117144823465

选择css预编译插件,建议Less

image-20201117144944389

选择语法检查ESLint的模式,选最后一个报错少

image-20201117145046325

Lint的报错方式,只有严重错误才报错

image-20201117145118475

配置存放?选第一个

image-20201117145141883

是否设置为默认的配置 --n

②创建git 仓库

git图像化操作,创建新仓库image-20201117150143391

然后删除项目的git文件夹,在文件家中右击选择Git Bash Here

image-20201117145458425

先git init创建git路径然后输入Git仓库推荐配置中的如下几项

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FHYyvjVu-1610026014599)(C:\Users\轩\AppData\Roaming\Typora\typora-user-images\image-20201117150506473.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SfEacjJC-1610026014602)(C:\Users\轩\AppData\Roaming\Typora\typora-user-images\image-20201117150853472.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FwyZX2Tv-1610026014603)(C:\Users\轩\AppData\Roaming\Typora\typora-user-images\image-20201117151002430.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-X3nTo71U-1610026014605)(C:\Users\轩\AppData\Roaming\Typora\typora-user-images\image-20201117151244234.png)]

//项目git初始化
git init 
//设置用户名和e-mail信息
git config --global user.name "朱泽轩"
git config --global user.email "925640536@qq.com"
//项目收集
git add .
//项目打包
git commit -m"打包描述"
//设置仓库路径
git remote add origin https://gitee.com/blackwitchcraft/background-management-system.git
//传输到主分支
git push -u origin master

③安装,并引用elelment-ui

在项目文件夹路径下cmd 安装element-ui

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AIKUIMOI-1610026014606)(C:\Users\轩\AppData\Roaming\Typora\typora-user-images\image-20201117151701151.png)]

yarn add element-ui

在src路径下的main.js根据官方文档复制一下几句

image-20201117152537285

④重置预设vue spa文件

删除views文件夹,新建pages文件夹

在路由中替换所有的views成pages

根据新建的vue文件重新设置路由,login页面不需要按需加载,其他页面按需加载,在登陆后通过路由守卫后才需要加载

//⑤设置element-ui按需应用

提高页面性能,优化用户体验

⑥从element-ui中寻找合适的form登录表单,之后根据文档修改

<template>
  <div class="login">
    <el-form
      :model="loginForm"
      status-icon
      :rules="rules"
      ref="loginForm"
      label-width="100px"
      class="demo-loginForm"
    >
      <el-form-item label="用户名" prop="userName">
        <el-input
          type="userName"
          v-model="loginForm.userName"
          autocomplete="off"
        ></el-input>
      </el-form-item>
      <el-form-item label="密码" prop="passWord">
        <el-input
          type="password"
          v-model="loginForm.passWord"
          autocomplete="off"
        ></el-input>
      </el-form-item>
      <el-form-item>
        <el-button type="warning" @click="submitForm('loginForm')"
          >提交</el-button
        >
      </el-form-item>
    </el-form>
  </div>
</template>

<script>
export default {
  data() {
      /**
       * @param {object} rule 就是选定的表单组件
       * @param {string} value 当前表单的value值
       * @param {Function} callback 校验通过传参,不通过不传参
       */
    var validateUserName = (rule, value, callback) => {
        // console.log(rule);
        // console.log(value);
      if (!value) {
        callback(new Error("请输入用户名"));
      } else {
        callback();
      }
    };
    var validatePassWord = (rule, value, callback) => {
      if (value === "") {
        callback(new Error("请输入密码"));
      } else {
        callback();
      }
    };
    return {
      loginForm: {
        userName: "",
        passWord: ""
      },
      rules: {
        userName: [{ validator: validateUserName, trigger: "blur" }],
        passWord: [{ validator: validatePassWord, trigger: "blur" }],
      },
    };
  },
  methods: {
    submitForm(formName) {
      this.$refs[formName].validate((valid) => {
        if (valid) {
          alert("submit!");
        } else {
          console.log("error submit!!");
          return false;
        }
      });
    },
    resetForm(formName) {
      this.$refs[formName].resetFields();
    },
  },
};
</script>

11.18登录交互实现

①登录过程实现逻辑

登录流程:

1.收集用户输入的userName和passWord传递给后端

2.将后端返回的token存到本地localstorage

3.每次请求的时候都携带token到请求头

4.展示token校验正确的数据

5.校验不通过则返回登录页

image-20201118094715097

②配置api

1.安装 axios :cmd下使用yarn add axios

2.在src目录下创建文件夹api,再在api中创建config.js和index.js

3.配置api/config.js

import axios from "axios";

// 定义跨域代理
// 设置axios的默认基础URL如果是开发模式,则采用/api,如果不为开发模式则http://www.chst.vip
axios.defaults.baseURL = process.env.NODE_ENV === "development" ? "/api" : "http://www.chst.vip"

axios.defaults.withCredentials = true;//允许请求携带认证

//设置响应最长时间
axios.create({
    timeout:4000
})

export default axios

4.配置api/index.js

引用config中配置好的axios,根据接口文档配置

image-20201118114649208
//封装登录接口请求
export const login = (username,password) => axios({
    url:"/users/login",
    method:"post",
    data:{
        username,
        password
    }
})

5.配置代理

在src目录下的vue.config.js中增加如下配置

  devServer:{
    // port:1234,//设置项目端口号
    // host:"0.0.0.0", //允许所有的主机访问当前项目
      proxy:{
          '/api':{ //正则匹配到以这个开头的时候 就用代理
              target:"http://www.chst.vip", //指向的是目标服务器地址
              pathRewrite:{
                  '^/api':""
              }
          }
      }
  }

③登录逻辑实现

在login/index.vue中解构引入api/index.js中的login方法

//解构引入封装的api
import {login} from "@/api"

在methods中找到按钮触发后的函数,本地校验通过后执行login方法并传参,查看后端成功数据的返回值

login(this.loginForm.userName,this.loginForm.passWord)
          .then(res=>{
            console.log(res);
          })
          .catch(err=>{
            console.log(err);
          })

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sOFlvfEx-1610026014608)(C:\Users\轩\AppData\Roaming\Typora\typora-user-images\image-20201118141017440.png)]

在res.data.state是本次请求的状态,根据此数据判断是否通过后台登陆成功,如果成功将res.data.token存入localStorage中,同时跳转到主页去

          login(this.loginForm.userName,this.loginForm.passWord)
          .then(res=>{
            console.log(res);
            if(res.data.state){//验证用户名和密码是否正确
            //将获得的token存入localStorage中
              localStorage.setItem('bms-token',res.data.token)
              //跳转到主页
              this.$router.push("/")
            }else{
              alert("登录有误")
            }
          })
          .catch(err=>{
            console.log(err);
          })

在login中从api中导入getLoginLog方法,同时在mounted中发送getLoginLog请求

//从api中导入getLoginLog方法
import { getLoginLog } from "@/api";
export default {
  data() {
      return {
      };
    },
  mounted() {
  getLoginLog()
        .then(res => {
          console.log(res);
        })
  }
};

④美化登录效果(需要element-ui支持)

根据element-ui中Message 消息提示,Loading 加载代码美化

//本地校验成功后登录加载动画执行
          const loading = this.$loading({
            lock: true,
            text: "正在玩命加载",
            spinner: "el-icon-loading",
            background: "rgba(0, 0, 0, 0.6)",
          });
 //在跳转页面之前登录动画停止
                loading.close();
//美化登陆成功提示
                this.$message.success('登录成功');
//美化登陆失败提示
                this.$message.error('用户名或密码错误');

⑤设置请求拦截器

在api/config.js文件中添加请求拦截器

先使用axios.interceptors.request.use(config=>{})拦截请求,如果需要放行,return config

判断是否为登录,注册,验证码等不需要token的请求,如果是就放行,不是则添加token在请求头

添加token在请求头:1.从本地存储localStorage获取token,2.给config.headers添加authorization属性,将token赋值3.放行

axios.interceptors.request.use(config => {//config是每个请求对象
    //登录和注册是不要token的,一次判断是否为登录,如果是登陆就放行
    if (config.url === "/users/login") {
        return config
    } else {
        //从localStorage中取出本地token
        let token = localStorage.getItem("bms-token")
        // config.url="/users/login?name=zzx"
        //给请求头添加token证书
        config.headers['authorization'] = token;
        return config
    }
    // console.log(config);
})

⑥设置路由守卫

防止用户访问没有权限的页面

Vue 的路由钩子

在main.js中设置路由前置钩子(路由守卫)

1.设置路由前置钩子

2.判断用户是否登录(用户登录之后,localstorage中有token,由此判断用户是否登录)

3.如果未登录,判断访问的是否是登录或注册页面,如果是也放行否则next({path:"/login"})转跳到登录页面

router.beforeEach((to, from, next) => {
  // 用户登录之后,localstorage中有token,由此判断用户是否登录
  let token = localStorage.getItem('bms-token')
  if(token){
    next();
  }else{
    //如果是登录,注册页面,也放行
    if(to.path ==="/login"){
      next();
    }else{
      //访问的不是登录页,就跳转到登录页
      next({path:"/login"})
    }
  }
})

⑦响应拦截

在api/config.js中添加响应拦截

1.打印config,结构config中的data,根据当前后端返回的config.data.code判断登录状态

2.如果登录失效则提示信息,放回登录页

3.如果登录信息未失效,则放行

//响应拦截
axios.interceptors.response.use(config => {
    // console.log(config);
    let {data} = config;
    //该后台管理系统的后台api中1004代表token失效,10022表示seeion登录失效,提示错误转跳到登录页
    if (data.code == "1004"||data.code=="10022") {
        //提示登录失效
        // alert("登录失效")
        //美化提示
        //console.log(ElementUI);
        ElementUI.Message.error("登录信息失效,请重新登陆")
        // console.log(router);
        //返回login页面
        router.push("/login")
    }
    return config
})

⑧使用vuex存储用户信息

1.在本地存储中存放userInfo

image-20201118214656416

根据network中login的preview中的返回值将所需要的用户信息存在localstroage中

留心:localStorage中只能存字符串,需要将获得的userInfo转化为字符串

//将用户信息存入localStorage中,留心:localStorage中只能存字符串
                localStorage.setItem("bms-userInfo", JSON.stringify(res.data.userInfo));

2.在vuex中设置

在state中增加userInfo空对象

在mutations中增加SET_USERINFO方法

import Vue from "vue";
import Vuex from "vuex";

Vue.use(Vuex);

export default new Vuex.Store({
  state: {
    userInfo:{}
  },
  mutations: {
    //更改userInfo
    SET_USERINFO(state,payload){
      state.userInfo = payload
    }
  },
  actions: {},
  modules: {}
});

3.在login中设置

映射vuex

//映射vuex
import {mapMutations} from "vuex"
//同时在methods中解构
methods: {
    ...mapMutations(['SET_USERINFO']),
}

在登录成功后设置vuex中的userInfo

//设置vuex的state['userInfo]的值
                this.SET_USERINFO(res.data.userInfo);

浏览器插件中验证userInfo是否添加成功

image-20201118223203955

4.在home页设置

//从vuex中导入mapState
import {mapState} from "vuex"
//接口中computed导入vuexstate数据
computed:{
    ...mapState(['userInfo'])
  }
//页面中调用userInfo的nickname
{{userInfo.nickname}}

5.解决刷新页面vuex中state数据丢失问题

解决方法:存一份到本地存储中,再在vuex进行设置

留心:需要短路运算,{}需要加引号,否则不能被JSON.parse识别,会报错

//从本地存储中获得userInfo
let userInfo = localStorage.getItem('bms-userInfo')|| "{}"
userInfo = JSON.parse(userInfo)
export default new Vuex.Store({
  state: {
    userInfo
  },
  mutations: {
    //更改userInfo
    SET_USERINFO(state,playload){
      state.userInfo = playload
    }
  },
  actions: {},
  modules: {}
});

⑨退出登录

给页面上的对出按钮绑定@click=“quiet”,然后在methods中写退出函数

quiet(){//退出登录
        //清除token,userInfo
        localStorage.removeItem('bms-token')
        localStorage.removeItem('bms-userInfo')
        //退回登录页
        this.$router.push("/login")
      }

11.19配置子路由以及侧边栏递归插件

①昨日样式处理

给头像标签增加vertical-align: middle;属性

element-ui组件,el-aside中默认width="200px"将其改为

②home页子路由

1.定义路由三部曲:

1.定义组件,创建文件夹及文件
image-20201119112540832
2.配置4路由
{
    path: "/",
    component:Home,
    children:[
      {
        path:"student",
        component: () =>import(/* webpackChunkName: "student" */ "../pages/Home/student")
      }
    ]
    
  }
3.视图组件放在合适的地方

放入合适的标签中

2.实际配置(定义全部的路由和组件)

第一步:配置全部的路由文件
image-20201119140820130
第二步:创建allRouters.js在router文件夹中

其中allRouters,是符合路由配置规则的数组,

1.引入所有的子路由文件

2.按照路由配置规则定义数组(展示单个路由和有子路由的配置方法)

3.暴露接口allRoutes

import Attendance from "../pages/Home/Attendance"
import StudentManager from "../pages/Home/StudentManager"
import studentDormitory from "../pages/Home/StudentManager/studentDormitory.vue"
import studentProduct from "../pages/Home/StudentManager/studentProduct.vue"
import studentProfile from "../pages/Home/StudentManager/studentProfile.vue"

const allRoutes = [
    {
        path: 'Attendance',
        name: "Attendance",
        component: Attendance,
        meta: {
            name: "考勤管理",
            icon: "iconfont icon-kaoqin"
        }
    },
    {
        path: "StudentManager",
        name: "StudentManager",
        redirect: "/StudentManager/studentProduct",//设置路由重定向
        component: StudentManager,
        meta: {
            name: "学员管理",
            icon: "iconfont icon-xueyuan"
        },
        children: [
            {
                path: 'studentProduct',
                name: 'studentProduct',
                component: studentProduct,
                meta: {
                    name: '学员项目管理',
                    icon: 'iconfont icon-wode1',
                },
            },
            {
                path: 'studentProfile',
                name: 'studentProfile',
                component: studentProfile,
                meta: {
                    name: '学员资料',
                    icon: 'iconfont icon-kaoqin2'
                }
            },
            {
                path: 'studentDormitory',
                name: 'studentDormitory',
                component: studentDormitory,
                meta: {
                    name: '学员宿舍',
                    icon: 'iconfont icon-shuju2'
                }
            }
        ]
    }
]
//暴露接口
export default allRoutes
第三步:在router/index.js导入allRoutes

再把Home页的子路由设置为allRoutes

//导入allRoutes
import allRoutes from "./allRoutes"
{
    path: "/",
    component:Home,
    children:allRoutes//设置allRoutes为home的子路由
  }
第四步:测试路由是否可用

登录后手动输入这值得路由页面地址测试

image-20201119143458562
在router/index.js,引入allRoutes

由于allRouter就是一个路由配置项,所以直接加到home的路由配置的children上

测试:方培已经配置好的路由看是否生效

③权限菜单设计思路

1.权限菜单思路

一个菜单名字对应一个路由(页面)

目的:根据后台响应不同的时速局,渲染不同的路由

2.测试用户权限菜单接口

根据用户权限菜单接口文档配合postman测试之后得到结果.留心:请求头需要token

image-20201119115717757image-20201119115813478

由此不同等级的用户权限菜单也不相同

④路由递归思路及实现

服务器根据用户返回的menulist数据和allRoutes数据进行匹配,得到符合当前用户的路由配置

发送请求,并携带token从后端接口可以获得用户权限菜单,再与本地路由进行递归对比,匹配name字段.匹配符合权限菜单的路由

image-20201119144122748

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-v3BJTSnN-1610026014609)(C:\Users\轩\AppData\Roaming\Typora\typora-user-images\image-20201119153748961.png)]

1.传入本地全部路由配置和用户合法路由配置进行对比

2.设置数组存储对比后符合的路由参数(所以push的都是allRoutes中的内容)

3.遍历全部路由配置allRoutes

3.1.嵌套遍历服务器返回的用户合法路由menuList

3.1.1如果全部路由配置allRoutes中meta的name是否和用户合法路由menuList的name相等

3.1.2.判断用户合法路由menuList中有没有children属性,同时children的值必须是一个数组

3.1.2.1设置合法子路由menuList.children再次递归调用函数去匹配子路由(甚至3层乃至更多层路由)

3.2将合法的路由配置(包括其中更多层路由)push添加到合法menuList数组中

4.返回最终组合的合法路由配置(其类型是一个数组)

/**
 * 
 * @param {Array} allRoutes 用户的有效的完整路由配置
 * @param {Array} menuList 服务器返回的用户合法的菜单名字
 */
const recursionRoutes = (allRoutes,menuList) => {
    let usersRoutes = [];//用户合法的路由配置
    allRoutes.forEach(item=>{//遍历本地所有路由配置
        menuList.forEach(v=>{//遍历用户合法菜单
            //如果路由配置中meta的name和路由合法菜单中的name相同,则加入用户合法的路由配置
            if(item.meta.name === v.name){//如果本地路由和用户合法菜单中匹配执行
                if(v.children && v.children.length>0){//如果用户合法路由菜单中有chlidren属性并且是一个有值数组
                    item.children = recursionRoutes(item.children,v.children)//递归调用匹配合法的子路由(甚至多层路由)
                }
                usersRoutes.push(item)//符合条件的路由添加到用户路由中
            }
        })
    })
    return usersRoutes//返回用户的合法路由一定要返回结果
}
export default recursionRoutes//暴露端口

⑤定义动态路由

在router路径下新建dynamicRoutes.js,配置动态路由,

const dynamicRoutes = [
    { path: '/',
    component:Home,
    children:[]//子页面需要另外配置
    },
    { path: '*', 
    component: ()=> import(/*webpackChunkName:'page404' */'../pages/page404') }//配置404页面
]

在pages路径下新建page404页面

再在router/index.js中做如下配置

const routes = [
  {
    path: "/login",
    name: "login",
    component: Login
  },
    { path: '*', 
    component: ()=> import(/*webpackChunkName:'page404' */'../pages/page404') }
];

⑥通过vuex获取用户菜单,并对比出用户菜单

1.定义接口

在api/index.js目录下定义获取用户菜单接口

//获取用户菜单
export const getMenuList = ()=> axios.get("permission/getMenuList")

2.在vuex中开辟存储空间

在store/index.js中1.导入引入allRoutes,引入递归函数,引入动态路由

​ 2.在state开辟用户侧边栏菜单数据的存储空间menuList

​ 3.导入api/index.js中getMenuList在actions中发送请求并接收返回数据:1.发送请求获取用户菜单数据2.用递归函数对比allRoutes和请求回来的用户菜单数据对比3.将结果提交给mutation

import Vue from "vue";
import Vuex from "vuex";

//引入用户菜单请求
import { getMenuList } from "@/api"
// 引入allRoutes
import allRoutes from "../router/allRoutes"
// 引入递归函数
import recursionRoutes from "../untils/recursionRoutes"
// 引入动态路由
import dynamicRoutes from "../router/dynamicRoutes"
Vue.use(Vuex);
//从本地存储中获得userInfo
let userInfo = localStorage.getItem("bms-userInfo") || "{}";
userInfo = JSON.parse(userInfo);
export default new Vuex.Store({
  state: {
    userInfo,
    menuList: [] //用户的侧边栏菜单
  },
  mutations: {
    //更改userInfo
    SET_USERINFO(state, payload) {
      state.userInfo = payload;
    },
    SET_MENULIST(state, payload) {
      //设置state中的menuList
      state.sideMenu = payload;
      //动态的将路由添加到routes中 核心方法:router.addRoutes(符合用户路由规则的数组)
    }
  },
  actions: {
    async FETCH_MENULIST({ commit }) {
      //1.发送请求获取用户菜单数据
      let userMenu =  await getMenuList()
      console.log(userMenu);
      // getMenuList()
      // .then(res=>{
      //   console.log(res);
      // })
      // 2.用递归函数对比allRoutes和请求回来的用户菜单数据对比
      let sideMenu =  recursionRoutes(allRoutes,userMenu.data.menuList)
      console.log(sideMenu);
      // 3.将结果提交给mutation
      commit("SET_MENULIST",sideMenu)
    }
  },
  modules: {}
});
import Vue from "vue";
import Vuex from "vuex";

//引入用户菜单请求
import { getMenuList } from "@/api"
// 引入allRoutes
import allRoutes from "../router/allRoutes"
// 引入递归函数
import recursionRoutes from "../untils/recursionRoutes"
// 引入动态路由
import dynamicRoutes from "../router/dynamicRoutes"
Vue.use(Vuex);
//从本地存储中获得userInfo
let userInfo = localStorage.getItem("bms-userInfo") || "{}";
userInfo = JSON.parse(userInfo);
export default new Vuex.Store({
  state: {
    userInfo,
    menuList: [] //用户的侧边栏菜单
  },
  mutations: {
    //更改userInfo
    SET_USERINFO(state, payload) {
      state.userInfo = payload;
    },
    SET_MENULIST(state, payload) {
      //设置state中的menuList
      state.sideMenu = payload;
      //动态的将路由添加到routes中 核心方法:router.addRoutes(符合用户路由规则的数组)
    }
  },
  actions: {
    async FETCH_MENULIST({ commit }) {
      //1.发送请求获取用户菜单数据
      let userMenu =  await getMenuList()
      console.log(userMenu);
      // getMenuList()
      // .then(res=>{
      //   console.log(res);
      // })
      // 2.用递归函数对比allRoutes和请求回来的用户菜单数据对比
      let sideMenu =  recursionRoutes(allRoutes,userMenu.data.menuList)
      console.log(sideMenu);
      // 3.将结果提交给mutation
      commit("SET_MENULIST",sideMenu)
    }
  },
  modules: {}
});

谷歌插件中查看vuex的state

image-20201119215905896

⑦设置动态路由(产生bug于11.20修复)

将匹配到的路由动态添加到router/index的routes中

在store的actions中函数FETCH_MENULIST中

​ 1.异步请求用户菜单数据

​ 2.用递归函数对比allRoutes和后端返回的用户菜单数据,得出"当前用户的合法菜单"

​ 3.将结果提交给mutations中的SET_MENULIST

在store的mutations的SET_MENULIST中接收"当前用户的合法菜单"将menuList赋值给dynamicRoutes的Home的children(需要寻找dynamicRoutes中path为"/"的项),

最后理由router.addRoutes()方法动态添加到路由参数中

import Vue from "vue";
import Vuex from "vuex";

//引入用户菜单请求
import { getMenuList } from "@/api";
// 引入allRoutes
import allRoutes from "../router/allRoutes";
// 引入递归函数
import recursionRoutes from "../untils/recursionRoutes";
// 引入动态路由
import dynamicRoutes from "../router/dynamicRoutes";
//引入router
import router from "../router";

Vue.use(Vuex);
//从本地存储中获得userInfo
let userInfo = localStorage.getItem("bms-userInfo") || "{}";
userInfo = JSON.parse(userInfo);
export default new Vuex.Store({
  state: {
    userInfo,
    menuList: [] //用户的侧边栏菜单
  },
  mutations: {
    //更改userInfo
    SET_USERINFO(state, payload) {
      state.userInfo = payload;
    },
    SET_MENULIST(state, payload) {
      //设置state中的menuList
      state.menuList = payload;
      // console.log(state.menuList);
      //动态的将路由添加到routes中 核心方法:router.addRoutes(符合用户路由规则的数组)
      //将menuList赋值给dynamicRoutes的Home的children,将dynamicRoutes数组中寻找path为"/"的项赋值给target,
      let target = dynamicRoutes.find(item => item.path === "/");
      // console.log(target);
      // 通过展开拷贝赋值给item.children
      target.children = [...state.menuList];
      // console.log(target.children);
      // console.log(dynamicRoutes);
      //动态添加路由配置
      router.addRoutes(dynamicRoutes);
    }
  },
  actions: {
    async FETCH_MENULIST({ commit }) {
      //1.发送请求获取用户菜单数据
      let userMenu = await getMenuList();
      // console.log(userMenu);
      // getMenuList()
      // .then(res=>{
      //   console.log(res);
      // })
      // 2.用递归函数对比allRoutes和请求回来的用户菜单数据对比
      let sideMenu = recursionRoutes(allRoutes, userMenu.data.menuList);
      // console.log(sideMenu);
      // 3.将结果提交给mutation
      commit("SET_MENULIST", sideMenu);
    }
  },
  modules: {}
});

11.20修复BUG,用户列表渲染,面包屑,进度条

①昨日bug修复

在router/dynamicRoutes.js中Home路由没有给component属性无法正确显示home页路由,后续添加的动态路由也木有用

②处理前置钩子

目前页面可以跳转到home但是常常出现用户合法的动态路由配置添加不上去,为了确保用户在跳转到home页时可以正确设置动态路由,需要在main中的路由前置钩子加一个判断

在判断有token就放行的语句next改为一下语句可以确保addRoutes已经生效

if(store.state.menuList.length == 0){// 判断vuex(store)中是否有用户合法的动态路由
      //如果没有就调用store的actions中的FETCH_MENULIST
      store.dispatch('FETCH_MENULIST')
      .then(()=>{
        next({path:to.path})//留心:next里面要传参数即要进入的页面信息,因为next执行后,当前的to路由将会废弃,path传入to的path,虽然跳转的还是相同路由,这样还可以确保addRoutes已经生效
      })
    }else{
      next();//如果有就放行
    }

③细节处理解决登录过期死循环的问题

登录信息过期后会进行跳转,但没有清除token,在前置钩子判断有token的情况下,一直会触发FETCH_MENULIST获取用户路由的申请,单反毁一直是错误码就陷入了死循环,因此在登录信息过期后还需要清理token和跳转,

因此在api/config.js响应拦截最后跳转前添加如下语句:

//清理token,防止死循环
    localStorage.removeItem("bms-token")

④渲染用户列表

1.安装封装好的侧边栏插件(cmd,当前项目路径下),该插件基于element-ui二次封装,只能在引用element-ui前提下使用,还需要iconfont的图标支持

npm i qf-sub-menu -S
yarn add qf-sub-menu

在main页面侧边栏的el-menu组件中添加

<!-- 使用qf-sub-menu插件,注入sideMenu数据从store中映射的menuList -->
          <qf-sub-menu :sideMenu="menuList"></qf-sub-menu>

可是当权限不同的用户退出登录后登录其他账号依然会渲染前一个用户的侧边栏,只需要在退出登录的地方加入刷新页面的指令即可

location.reload();

并且在全局样式中键入以下样式:

body, ul, p, li, h1, h2, h3, h4, h5, h6 {
    margin: 0;
    padding: 0;
}

.el-menu--collapse .el-submenu__title span {
    height: 0;
    width: 0;
    overflow: hidden;
    visibility: hidden;
    display: inline-block;
}

.el-menu--collapse .el-menu-item .el-submenu__icon-arrow, .el-menu--collapse .el-submenu .el-submenu__title .el-submenu__icon-arrow {
    display: none;
}
.el-menu-item-group .el-menu-item{
   padding:0 0 0 30px;

}

.el-menu-item-group .el-menu-item-group__title{
    padding:0
}

⑤解决跳转同意路径报错的问题

路由导航到统一路径重复报错处理方法:不让答应错误只是返回错误,将如下的代码添加到router/index.js中

//解决路由导航到统一路径重复报错的问题
const originalPush = VueRouter.prototype.push
VueRouter.prototype.push = function push(location) {
  return originalPush.call(this, location).catch(err => err)
}

⑥解决侧边栏插件不高亮当前显示路由

将el-menu的default-active变为动态的值为路由路径----:default-active="$router.path"

⑦处理面包屑

所有的home子路由页面都需要面包屑所以就写到home/index.vue的el-main中,插在路由视图前

处理vuex(store)中的数据存放

//state中新增:处理面包屑state中新增:处理面包屑存放数据
crumbs:[]
//mutation新增函数操作面包屑数据
     SET_CRUMBS(state,payload){
      state.crumbs = payload;
     }

处理面包屑需要路由后置钩子,

打印参数to

image-20201120163141121

to.matched第一项为空获取后需要用数组slice(1),去掉第一项,剩下的就是构建面包屑所需的路由参数

to.matched.slice(1)处理结果如下,在mate.name是面包屑文字展示path是路径

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fcP5ArM3-1610026014610)(C:\Users\轩\AppData\Roaming\Typora\typora-user-images\image-20201120163452094.png)]

调用store(vuex)中的处理面包屑方法SET_CRUMBS,将to.matched.slice(1)传入其中

//路由后置钩子处理面包屑
router.afterEach((to,from)=>{
  // console.log(to);
  let crumblist = to.matched.slice(1)
  // console.log(crumblist);
  store.commit('SET_CRUMBS', crumblist)
})

调试store中有以下数据

image-20201120164057543

main从stoer(vuex)中获取crumbs,循环遍历面包屑,最终main中面包屑结果:

<el-breadcrumb separator-class="el-icon-arrow-right">
            <el-breadcrumb-item :to="{ path: '/Welcome' }">首页</el-breadcrumb-item>
            <el-breadcrumb-item :to="{path : crumb.path}" v-for="crumb in crumbs" :key="crumb.name">{{crumb.meta.name}}
            </el-breadcrumb-item>
          </el-breadcrumb>

⑧进度条生成nprogress

1.安装

yarn add nprogress

2.用法

NProgress.start(); //进度条出现
NProgress.done();  //进度条消失
2.1 路由中使用使用

路由文件中导入,页面跳转出现进度条(router.js)

1.导入 可以在main.js导入 (导入文件可以是 路由文件 也可以是 异步请求文件)

//导入
import NProgress from 'nprogress'
import 'nprogress/nprogress.css'

2.使用

router.beforeEach((to, from, next) => { //出现进度条
  NProgress.start()
  next()
})

router.afterEach(() => {  //进度条消失
  NProgress.done()
}
2.2 异步请求中使用

异步请求文件中导入,发送请求出现进度条(这里使用 axios 发送请求)

1.导入

// 引入nprogress插件
import NProgress from 'nprogress' import 'nprogress/nprogress.css'

2.使用

// 添加请求拦截器
axios.interceptors.request.use(function (config) {  // 出现进度条
    NProgress.start()
    return config
}, function (error) {
    // Do something with request error
    return Promise.reject(error)
})

// 在 response 拦截器中,隐藏进度条 NProgress.done()
axios.interceptors.response.use(config => {
    NProgress.done()
    return config
})

3.NProgress配置

1.showSpinner:进度环显示隐藏
NProgress.configure({showSpinner: false});
2.ease:调整动画设置

ease可传递CSS3缓冲动画字符串(如ease、linear 等)。speed为动画速度(单位ms)

NProgress.configure({ease:'ease',speed:500});
3.minimum:

最低百分比

NProgress.configure({minimum:0.3});
4.百分比:

通过设置progress的百分比,调用 .set(n)来控制进度,其中n的取值范围为0-1。

NProgress.set(0.4);

####4.VUE中修改进度条颜色

App.vue中的style中增加:

 #nprogress .bar { /*进度条样式*/
        background: red !important;
    }

.spinner-icon {/*圆圈样式*/
    border-top-color: #f56c6c!important;
    border-left-color: #003df1!important;
}

11.21

①中断请求

在welcome页面创建按钮并绑定事件,在methods中写事件处理方法

11.22

①学生信息表渲染

使用element-ui 设置页面表格

api/index.js接口请求后端学生信息数据

export const getStuList = () => axios.get("/students/getstulist")

studentProduct文件导出,data创建存储空间stuData,methods发送请求函数判断成功时将需要的res.data.data赋值给stuData,mounted调用,在页面加载时自动发送请求,并成功时替换数据

import { getStuList } from "../../../api/index";
export default {
  data() {
    return {
      stuData: [],
      loading: true,
      options: [],
      value: "",
    };
  },
  methods: {
    updateStuTbale() {
      getStuList()
        .then((res) => {
          // console.log(res);
          if (res.data && res.data.state) {
            this.stuData = res.data.data;
            this.loading = false;
          } else {
            this.$message.error("数据获取失败");
            this.loading = false;
          }
        })
        .catch((err) => {
          console.log(err);
          this.$message.error("获取数据错误或网络错误");
          this.loading = false;
        });
    },
  },
  mounted() {
    //页面加载时,获取学生信息表数据
    this.updateStuTbale();
  },
};
</script>

②学生信息表添加数据

项目总结心得

以后工作出现404问题,先去测试接口

配置完路由后要重启服务器

路由守卫(路由前置钩子),后置钩子—在main.js中

前置钩子beforeEach三个参数(to, from, next),后置钩子连个参数(to,from)

git指令

git中提示vue项目有import引用但未使用,commit -m"描述" --no-verify

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rDEhfEwQ-1610026014611)(C:\Users\轩\AppData\Roaming\Typora\typora-user-images\image-20201120101355137.png)]

创建分支

语法:git branch 分支名

img

但是此刻HEAD指针依然指向主分支master

切换分支

语法:git checkout 分支名

img

查看是否切换成功 git log --decorate – oneline

img

查看分支

git log --decorate --oneline //一行显示

git log --decorate --oneline --graph -all //图形化

创建并且切换到分支里

git checkout -b 分支名

img

   this.stuData = res.data.data;
        this.loading = false;
      } else {
        this.$message.error("数据获取失败");
        this.loading = false;
      }
    })
    .catch((err) => {
      console.log(err);
      this.$message.error("获取数据错误或网络错误");
      this.loading = false;
    });
},

},
mounted() {
//页面加载时,获取学生信息表数据
this.updateStuTbale();
},
};


## ②学生信息表添加数据

# 项目总结心得

以后工作出现404问题,先去测试接口

配置完路由后要重启服务器 

路由守卫(路由前置钩子),后置钩子---在main.js中

前置钩子beforeEach三个参数(to, from, next),后置钩子连个参数(to,from)



## git指令

git中提示vue项目有import引用但未使用,commit -m"描述" --no-verify

[外链图片转存中...(img-rDEhfEwQ-1610026014611)]

### 创建分支

语法:git branch 分支名

![img](https://i-blog.csdnimg.cn/blog_migrate/29a02d780dc3940b7ff886394b7b1f02.png)

但是此刻HEAD指针依然指向主分支master

### 切换分支

语法:git checkout 分支名

![img](https://i-blog.csdnimg.cn/blog_migrate/d20fee4a2918392282de9f71262aae1a.png)

查看是否切换成功 git log --decorate -- oneline

![img](https://i-blog.csdnimg.cn/blog_migrate/f54188f5b0a43734117eb9ea2158527c.png)

查看分支

git log --decorate --oneline                 //一行显示

git log --decorate --oneline --graph -all       //图形化

### 创建并且切换到分支里

git checkout -b 分支名

![img](https://i-blog.csdnimg.cn/blog_migrate/4c798e1d760c89431c85fd32df677289.png)

 



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值