eyb:项目介绍到获取用户信息功能实现(一)


(1)项目介绍

(2)创建项目

(3)项目结构介绍

(4)登录页面的编写

(5)处理登陆事件

(6)配置相应拦截器

(7)配置请求转发解决跨域

(8)登录成功跳转页面

(9)导航栏功能实现

(10)安装Vuex

(11)封装菜单请求工具类

(13)路由导航守卫介绍

(14)菜单功能完整实现

(15)获取用户信息功能实现


(1)项目介绍

为什么要学习云E办呢?

  因为受今年疫情的影响,很多企业从之前的线下办公,转到线上办公,线上办公页一度成为了微博的热搜词,可见线上办公热度也是非常高的。线上办公的人数增多,我们也发现了线上办公的优点,比如说可以实现办公流程的自动化,节省企业办公费用,实现绿色办公,同时呢也可以去提高我们的工作效率…由于这些优点,线上办公已经成为了当今的一个主流趋势。

我们的项目主要目的是为了实现中小型企业的在线办公系统,是用来管理日常办公事务的一个系统,能够管理的内容非常的多,比如说:日常的各种流程的审批呀,公告啊,财务,人事,资产,行政,项目移动办公等等…最主要的一个作用是通过以软件的形式,让我们办公系统更加方便管理,更加简单,高效规范,能够提高管理运营水平,就想我们的项目名字一样,让我们的办公更容易。

这个项目在技术方面采用了最主流前后端分离开发的模式,后端采用SpringBoot MVC MyBatis-plus 安全框架用了Spring Security

基本Vue全家桶

项目搭建:vue-cli

状态管理:Vuex

路由:VueRouter

UI界面:elementUI

通讯框架:Axios

前端语法:ES6

打包:Webpack

在线聊天:WebSocket

字体:font-awesome

文件上传下载:js-file-download

在线聊天开源项目:vue-chat

 (2)创建项目

首先下载Node.js:

之后: 

安装vue-cli:下面是新版本的安装办法:

 查看是否安装成功:

 创建项目:使用Windows PowerShell搭建项目

 进入一个目录创建yeb项目

如果出现问题:运行命令 

 不使用骨架,我们手动去选,选择第二项

 

选择一些东西:先选择这两个

 下面直接选择:no

创建完成:

 启动项目:

启动成功 

 浏览器可以正常访问:

(3)项目结构介绍

 我们这里使用WebStorm进行打开编辑,也可以使用IDEA,但是需要安装一个插件vue.js就可以了

第一个目录是整个项目用的依赖:可以通过npm install 去安装依赖了

public目录:有一个图标,一个index.html:它相当于是项目的入口,可以理解为首页,或模板页,在实际开发中用不到,因为vue的话开发出来的叫做单页面应用,就一个html的页面,剩下的叫做组件

这个项目是单页面应用,所以有一个index.html,只是作为模板页,在开发中用不到,真正的入口在main.js

src:是项目的原码目录 

assets:放一些资源的 component:组件目录 router:路由的目录(在创建项目的时候选了路由,所以这里有这个目录)views:页面目录

main.js:整个程序的入口了  import是ES6的语法,当被babel打包的时候转成ES5的语法,ES5是reequire

Vue.config.productionTip = false :关闭浏览器对应的提示 当为true的时候,会开启提示:

render: h => h(App) :是渲染我们对应的组件

 .$mount('#app'):是手动挂和之前通过el:‘#app‘ 是一样的

 

 

.gitgnore:git的忽略的配置文件

bable.config.js:是babel的配置文件,主要是将ES6的语法转换成ES5 

README.md:项目说明

package.json:项目的配置文件

scrips下面是封装的常用的命令 向server启动服务啊 build:构建 他们都是可以直接去运行的  小路三角

dependercies:正式环境用到的依赖

devDependencies:开发环境用到的依赖

 配置一下启动项目:

 (4)登录页面的编写

vue是关注度分离的,所以没办法提供UI的操作,需要安装ElementUI

安转完之后,package.json:多个elementUI

 引入到项目中去使用:main.js

 修改一下App.vue:

<template>
  <div id="app">
    <!--把路由配置跳转的组件放在这里 当你的路由path与访问的地址相符时会将指定的组件,替换router-view-->
    <router-view/>
  </div>
</template>

<style>

</style>

删除这两个组价:

在views目录下创建Login.vue组件: 

 

配置路由:

import Vue from 'vue'
import VueRouter from 'vue-router'
import Login from "@/views/Login";

Vue.use(VueRouter)

const routes = [
  {
    //加载斜杠,会找到Login
    path: '/',
    name: 'Login',
    component: Login
  },

]

const router = new VueRouter({
  routes
})

export default router

Login.vue:

<template>
  <div>
     <el-form :rules="rules" ref="loginForm" :model="loginForm" class="loginContainer">
       <h3 class="loginTitle">系统登录</h3>
       <el-form-item prop="username">
         <el-input type="text" auto-complete="false" v-model="loginForm.username" placeholder="请输入用户名"></el-input>
       </el-form-item>

       <el-form-item prop="password">
         <el-input type="text" auto-complete="false" v-model="loginForm.password" placeholder="请输入密码"></el-input>
       </el-form-item>

       <el-form-item prop="code">
         <el-input type="text" auto-complete="false" v-model="loginForm.code" placeholder="点击图片更换验证码" style="width:250px;margin-right: 5px"></el-input>
         <img :src="captchaUrl">
       </el-form-item>

       <el-checkbox v-model="checked" calss="loginRemember">记住我</el-checkbox>
       <el-button type="primary" style="width: 100%">登录</el-button>

     </el-form>


  </div>
</template>

<script>
export default {
  name: "Login",
  data(){
    return{
      loginForm:{
        username:'admin',
        password:'123',
        code:''
      },
      checked:true
    }
  }
}
</script>

<style>
.loginContainer {
  border-radius: 15px;
  background-clip: padding-box;
  margin: 180px auto;
  width: 350px;
  padding: 15px 35px 15px 35px;
  background: #fff;
  border: 1px solid #eaeaea;
  box-shadow: 0 0 25px #cac6c6;
}

.loginTitle {
  margin: 0 auto 40px auto;
  text-align: center;
  color: #505458;
}

/*.loginRemember {
  text-align: left;
  margin: 0px 0px 15px 0px;
}*/

.el-form-item__content {
  display: flex;
  align-items: center;
}
</style>

(5)处理登陆事件

首先去校验一下用户明密码不能为空,添加校验规则:

添加@click

 添加 :rules=“rules”

 

 

 (6)配置相应拦截器

下面需要调用后端的接口,去实现相关的 功能,那怎么去调呢?Vue注重关注度分离原则的,Vue框架并没有通讯的能力,提倡使用Axios框架进行通信

安装Axios

 在package.json:里面加入了axios:

 下面就可以写我们的请求了,比如说在登录这一块直接通过axios去调用后端的接口,然后进行相应的返回,仅进行处理,那么每一个事件需要调用后端接口,都需要写axios框架,有一个问题,你这个请求有可能成功有可能失败,成功有成功的处理方法,失败有失败的处理方法,也就是说每一个调用后端接口的方法,都要去判断你的请求是否成功或者失败,每一个地方都要去做,过于麻烦了,这个时候我们可以把它封装起来,用统一的响应拦截器,判断它是否成功或者失败,失败的话把它提示出来,省略了响应的处理了:

创建目录utils 创建配置 api.js:

import axios from 'axios'
//导入elemenui的提示信息
import {Message} from "element-ui";
import router from '../router'
import Vue from 'vue'

axios.defaults.baseURL = '/'
Vue.prototype.axios = axios

// 请求拦截器
axios.interceptors.request.use(config=>{
    if (window.sessionStorage.getItem('tokenStr')) {
//请求携带自定义token
        config.headers['Authorization'] =
            window.sessionStorage.getItem('tokenStr');
    }
    return config
},error => {
    console.log(error);
})

//响应拦截器 请求的统一的处理
axios.interceptors.response.use(success => {
    //业务逻辑错误 成功调到接口返回响应码200
    if (success.status && success.status == 200) {
        //后端返回的 code
        if (success.data.code == 500 || success.data.code == 401 ||
            success.data.code == 403) {
            //输出返回响应信息
            Message.error({message: success.data.message});
            return;
        }
        //后端返回响应信息
        if (success.data.message) {
            Message.success({message: success.data.message});
        }
    }
    return success.data;
    //error表示没有调到后端接口
}, error => {
    if (error.response.code == 504 || error.response.code == 404) {
        Message.error({message: '服务器被吃了o(╯□╰)o'});
    } else if (error.response.code == 403) {
        Message.error({message: '权限不足,请联系管理员!'});
    } else if (error.response.code == 401) {
        Message.error({message: '尚未登录,请登录'});
        router.replace('/');
    } else {
        if (error.response.data.message) {
            Message.error({message: error.response.data.message});
        } else {
            Message.error({message: '未知错误!'});
        }
    }
    return;
});

let base = '';//访问地址后来可能加的东西,如果需要加的话直接修改base

//传送json格式的post请求
export const postRequest = (url, params) => {
    return axios({
        method: 'post',
        url: `${base}${url}`,
        data: params
    })
}


//封装请求
//传递json的put请求
export const putRequest = (url, params) => {
    return axios({
        method: 'put',
        url: `${base}${url}`,
        data: params
    })
}
//传递json的get请求
export const getRequest = (url, params) => {
    return axios({
        method: 'get',
        url: `${base}${url}`,
        data: params
    })
}
//传递json的delete请求
export const deleteRequest = (url, params) => {
    return axios({
        method: 'delete',
        url: `${base}${url}`,
        data: params
    })
}

 (7)配置请求转发解决跨域

先获取后端的验证码,请求后端的接口:

在Login.vue里面

 

 访问:图片没有正常显示,

它默认是访问前端的接口 ,前端没有接口,后端的接口是8081,这时候就涉及到跨域:我们8080接口去调8081接口,需要进行跨域:

用了简单的办法:Node.js的代理类去转发,也就是说我们请求还是请求8080,但是请求到Nodejs哪里,Nodejs会给我们转发到8081

 

 新建一个配置:vue.config.js:进行跨域配置:


//新建代理
let proxyObj = {}

proxyObj['/ws'] = {
  ws: true,
  target: 'ws://localhost:8081'
};

proxyObj['/'] = {
  //websocket
  ws: false,
  //目标地址
  target: 'http://localhost:8081',
  //发送请求头中host会设置成target
  changeOrigin: true,
  //不重写请求地址
  pathRewrite: {
    '^/': '/'
  }
}

//默认访问的路径端口
module.exports = {
  devServer: {
    host: 'localhost',
    port: 8080,
    proxy: proxyObj //代理转发
  }
}

重启项目,就获取到后端的验证码 

(8)登录成功跳转页面

 先准备home.vue的组件:用来测试登录成功的

 在路由配置中配置home路由:

import Vue from 'vue'
import VueRouter from 'vue-router'
import Login from "@/views/Login";
import Home from '../views/Home'

Vue.use(VueRouter)

const routes = [
  {
    //加载斜杠,会找到Login
    path: '/',
    name: 'Login',
    component: Login
  },
  {
    path:'/home',
    name:'Home',
    component:Home
  }

]

const router = new VueRouter({
  routes
})

export default router

先封装请求拦截器:把token放到请求头里面,上面已经写过这个代码

 

 使用ElementUI的组件:登录的时候动画加载显示:

 在Login.vue:el-form中添加:

 

输入了postRequest之后会自动需要导入:可以把它加入main.js,当在调用的时候用this.postRequest 

main.js:

import Vue from 'vue'
import App from './App.vue'
import router from './router'
//引入elementUI
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';

import {postRequest} from "./utils/api";
import {putRequest} from "./utils/api";
import {getRequest} from "./utils/api";
import {deleteRequest} from "./utils/api";

Vue.use(ElementUI);

//插件形式使用请求
Vue.prototype.postRequest=postRequest;
Vue.prototype.putRequest=putRequest;
Vue.prototype.getRequest=getRequest;
Vue.prototype.deleteRequest=deleteRequest;

Vue.config.productionTip = false

new Vue({
  router,
  render: h => h(App)
}).$mount('#app')

Login.vue组件 

<template>
  <div>
     <el-form :rules="rules"
              v-laoding="loading"
              element-loading-text="正在登录..."
              element-loading-spinner="el-icon-loading"
              element-loading-background="rgba(0,0,0,0.8)"
              ref="loginForm" :model="loginForm" class="loginContainer">
       <h3 class="loginTitle">系统登录</h3>
       <el-form-item prop="username">
         <el-input type="text" auto-complete="false" v-model="loginForm.username" placeholder="请输入用户名"></el-input>
       </el-form-item>

       <el-form-item prop="password">
         <el-input type="text" auto-complete="false" v-model="loginForm.password" placeholder="请输入密码"></el-input>
       </el-form-item>

       <el-form-item prop="code">
         <el-input type="text" auto-complete="false" v-model="loginForm.code" placeholder="点击图片更换验证码" style="width:250px;margin-right: 5px"></el-input>
         <img :src="captchaUrl" @click="updateCaptcha">
       </el-form-item>

       <el-checkbox v-model="checked" calss="loginRemember">记住我</el-checkbox>
       <el-button type="primary" style="width: 100%" @click="submitLogin">登录</el-button>

     </el-form>


  </div>
</template>

<script>
export default {
  name: "Login",
  //输入框默认显示
  data(){
    return{
      //访问后端接口capcha
      captchaUrl: '/captcha?time=' + new Date(),
      loginForm:{
        username:'admin',
        password:'123',
        code:''
      },
      loading:false,//定义loading
      checked:true,
      //校验规则
      rules:{
        username: [{required:true,message:'请输入名字',trigger:'blur'}],
        password: [{required:true,message:'请输入密码',trigger:'blur'}],
        code: [{required:true,message:'请输入验证码',trigger:'blur'}]
      }
    }
  },
  methods:{
    //点击更新图片
    updateCaptcha(){
      //访问后端接口captcha
      this.captchaUrl='/captcha?time='+new Date();
    },
    submitLogin(){
      //登录校验
      this.$refs.loginForm.validate((valid) => {
        //通过执行
        if (valid) {

          this.loading=true;//登录的时候是true
          //api.js封装的post请求,调用后端login接口
          this.postRequest('/login', this.loginForm).then(resp => {
            if (resp) {
              this.loading=false;//登录成功之后loading是false
              //alert(JSON.stringify(resp));
              //存储用户token
              const tokenStr = resp.obj.tokenHead + resp.obj.token;//获取tokenstr
              window.sessionStorage.setItem('tokenStr', tokenStr);//先存放到sessionStorage里面
             /* //清空菜单
              this.$store.commit('initRoutes', []);
              //页面跳转
              let path = this.$route.query.redirect;
              this.$router.replace((path == '/' || path == undefined) ? '/home' : path)*/
              //跳转页面 使用replace 浏览器地址栏不会有往后退的效果
              this.$router.replace('/home')
            }
          })
        } else {
          this.$message.error('请输入所有字段');
          return false;
        }
      })
    }
  }
}
</script>

<style>
.loginContainer {
  border-radius: 15px;
  background-clip: padding-box;
  margin: 180px auto;
  width: 350px;
  padding: 15px 35px 15px 35px;
  background: #fff;
  border: 1px solid #eaeaea;
  box-shadow: 0 0 25px #cac6c6;
}

.loginTitle {
  margin: 0 auto 40px auto;
  text-align: center;
  color: #505458;
}

/*.loginRemember {
  text-align: left;
  margin: 0px 0px 15px 0px;
}*/

.el-form-item__content {
  display: flex;
  align-items: center;
}
</style>

 

点击登录:

 (9)导航栏功能实现

创建两个子组件:Test1.vue、Test2.vue

<template>
  <div>
    test1
  </div>
</template>

<script>
export default {
  name: "Test1"
}
</script>

<style scoped>

</style>
<template>
  <div>
    test2
  </div>
</template>

<script>
export default {
  name: "Test2"
}
</script>

<style scoped>

</style>

更改Home.vue:

这里是通过事件进行跳转:以后会有很多很多的菜单,发现以后会写很多的路由,需要频繁的在路由配置里面去添加,那我们频繁的添加菜单的时候,操作步骤是重复的,所以可以把菜单和路由数据统一起来,将路由数据动态渲染到菜单上去

 路由配置:index.js

import Vue from 'vue'
import VueRouter from 'vue-router'
import Login from "@/views/Login"
import Home from '../views/Home'
import Test1 from "../views/Test1";
import Test2 from "../views/Test2";


Vue.use(VueRouter)

const routes = [
  {
    //加载斜杠,会找到Login
    path: '/',
    name: 'Login',
    component: Login,
    hidden:true
  },
  {
    path:'/home',
    name:'导航一',
    component:Home,
    children:[
      {
        path:'/test1',
        name:'选项1',
        component:Test1
      },
      {
        path:'/test2',
        name:'选项2',
        component:Test2
      }
    ]
  }


]

const router = new VueRouter({
  routes
})

export default router

 更改Home.vue:

<template>
  <div>
   <el-container>
     <el-header>Header</el-header>
     <el-container>
       <el-aside width="200px">
         <!--router使用router模式,路由进行跳转:把路由数据进行渲染-->
         <el-menu router >
           <!--循环路由,v-if当循环的item是hidden时候不显示-->
           <el-submenu index="1" v-for="(item,index) in this.$router.options.routes" :key="index" v-if="!item.hidden">
             <template slot="title">
               <i class="el-icon-location"></i>
               <span>{{item.name}}</span>
             </template>
             <!--子路由循环 -->
             <el-menu-item :index="children.path" v-for="(children,index) in item.children">{{children.name}}</el-menu-item>
           </el-submenu>
         </el-menu>
       </el-aside>
       <el-main>
         <router-view class="homeRouterView"/>
       </el-main>
     </el-container>
   </el-container>
  </div>
</template>

<script>
export  default{
  name:"Home"
}

</script>

<style>
  .homeHeader {
    background-color: #409eff;
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 0px 15px;
    box-sizing: border-box;
  }

  .homeHeader .title {
    font-size: 30px;
    font-family: 华文行楷;
    color: #ffffff;
  }

  .homeHeader .userInfo {
    cursor: pointer;
  }

  .homeWelcome {
    text-align: center;
    font-size: 30px;
    font-family: 华文行楷;
    color: #409eff;
    padding-top: 50px;
  }

  .homeRouterView {
    margin-top: 10px;
  }

  .el-dropdown-link img {
    width: 48px;
    height: 48px;
    border-radius: 24px;
    margin-left: 8px;
  }

  .el-dropdown-link {
    display: flex;
    align-items: center;
  }
</style>

(10)安装Vuex

导航栏已经写好了,可以实现菜单的功能,怎么实现呢?两种办法:

第一种,可以在路由配置中去写,根据有多少菜单,在路由配置中写死就行了,这是比较简单的

第二种:是从后端获取对应的一个菜单数据,然后在我们前端路由这边进行一个相应的转化,为什么从后端获取数据呢?因为这些菜单不能说一直是写死的,就是这些,更能会有变更,变更之后,还要在路由配置中修改,这是比较麻烦的

所以我们选择去后端获取,数据库中存好了,变更之后,数据库也会变,变好之后呢,我们数据是从菜单接口数据获取的,菜单就没必要手动的修改了

通过后端接口获取菜单:

有了这个菜单之后,把菜单存到哪里?

Local Storage Session Storage可以存放我们的菜单,除了这两种,还可以使用Vuex,vuex本质上就是为vue开发的状态管理模式,它采用集中式管理应用的所有组件的状态,并且以相应的规则去保证状态呢以一种可预测的方式发生变化,可以理解为整体的数据的状态管理,只不过它不是存放到浏览器的Session Storage上面,它直接存放在vuex对应的内存里面,Session Storage存储的对用户是可见的

 

 安装vuex:

安装的时候出现问题: 

下载完之后:package.json:多了vuex

创建目录:store,创建配置index.js

 这里只是简单的写了下配置,以后还会继续写

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({
    //state可以理解为全局的对象,用来保存组件的公共数据
    state:{
        routes:[]
    },

    //mutations:表示可以改变state里面的对应值的一个对应方法(同步执行的)异步执行的是actions方法
    mutations:{
        //初始化路由
        initRoutes(state,data){
            state.routes=data;
        }
    }
})

在main.js引入vuex的配置

加一个store:

(11)封装菜单请求工具类

 要获取到后端的菜单数据,后端的是一个字符串,需要把它转换为前端所需要的一个对象,并且把这个数据放到路由的配置里面去,我们需要封装一个菜单请求工具类去实现我们的要求

创建menus.js: 

import {getRequest} from "./api";

export const initMenu = (router, store) => {
    //先判断store中的state是否为空
    if (store.state.routes.length > 0) {
        return;
    }
    //get请求调用接口/system/cfg/menu:,初始化  data:拿到的数据
    getRequest("/system/cfg/menu").then(data => {
        //判断data是否存在
        if (data) {
            //格式化router
            let fmtRoutes = formatRoutes(data);
            //添加到router
            router.addRoutes(fmtRoutes);
            //将数据存入vuex
            store.commit('initRoutes', fmtRoutes);
            
        }
    })
}

//格式化数据方法
export const formatRoutes = (routes) => {
    let fmRoutes = [];
    routes.forEach(router => {
        let {
            path,
            component,
            name,
            meta,
            iconCls,
            children,
        } = router;
        if (children && children instanceof Array) {
            //递归
            children = formatRoutes(children);
        }
        //格式化
        let fmRouter = {
            path: path,
            name: name,
            meta: meta,
            iconCls: iconCls,
            children: children,
            component(resolve) {
                require(['../views/' + component + '.vue'], resolve);
            
            }
        }
        fmRoutes.push(fmRouter);
    })
    return fmRoutes;
}

(12)完善菜单请求工具类

创建组件:一 一的创建出来,以EmpAdv为例:其他的不写了

<template>
  <div>
    高级资料
  </div>
</template>

<script>
export default {
  name: "EmpAdv"
}
</script>

<style scoped>

</style>

 修改菜单配置menu.js:

import {getRequest} from "./api";
//初始化方法
export const initMenu = (router, store) => {
    //先判断store中的state是否为空
    if (store.state.routes.length > 0) {
        return;
    }
    //get请求调用接口/system/cfg/menu:,初始化  data:拿到的数据
    getRequest("/system/cfg/menu").then(data => {
        //判断data是否存在
        if (data) {
            //格式化router
            let fmtRoutes = formatRoutes(data);
            //添加到router
            router.addRoutes(fmtRoutes);
            //将数据存入vuex
            store.commit('initRoutes', fmtRoutes);
            //连接websocket
            //store.dispatch('connect');
        }
    })
}

//格式化数据方法
export const formatRoutes = (routes) => {
    let fmRoutes = [];
    routes.forEach(router => {
        let {
            path,
            component,
            name,
            meta,
            iconCls,
            children,
        } = router;
        if (children && children instanceof Array) {
            //递归
            children = formatRoutes(children);
        }
        //格式化
        let fmRouter = {
            path: path,
            name: name,
            meta: meta,
            iconCls: iconCls,
            children: children,
            component(resolve) {
                if (component.startsWith("Home")) {
                    require(['../views/' + component + '.vue'], resolve);
                } else if (component.startsWith("Emp")) {
                    require(['../views/emp/' + component + '.vue'], resolve);
                } else if (component.startsWith("Per")) {
                    require(['../views/per/' + component + '.vue'], resolve);
                } else if (component.startsWith("Sal")) {
                    require(['../views/sal/' + component + '.vue'], resolve);
                } else if (component.startsWith("Sta")) {
                    require(['../views/sta/' + component + '.vue'], resolve);
                } else if (component.startsWith("Sys")) {
                    require(['../views/sys/' + component + '.vue'], resolve);
                }
            }
        }
        fmRoutes.push(fmRouter);
    })
    return fmRoutes;
}

 (13)路由导航守卫介绍

现在把菜单请求工具类写完了,就是在什么时候调用initMenu初始化方法,去初始化菜单,毫无疑问,就是在登录的时候,但是需要考虑,现在是把菜单放在vuex里面的,也就类似内存里面,而且现在是浏览器的运用,如果用户按下了刷新按钮,就会可能被初始化数据(空数据)刷新,菜单就可能丢失,怎么解决丢失呢?

比如说在Home.vue按了F5刷新,那我页面Home添加上初始化,菜单就行了呀!这样处理也可以,但是需要考虑用户不只是在Home页,按F5刷新,比如说在高级资料EmpAdv去刷新,那么之前放在Home页的初始化菜单,它也没有办法去调,也就不会生效,还会出现菜单丢失的情况,那怎么解决,只能在每个页面添加初始化菜单的方法,这样是非常麻烦的,那我们还有非常简单的东西叫:“路由导航守卫”

 

 

to:是跳转到那个路由

from:是从那个路由跳转的

next():如果没有next() 是不能实现跳转的

(14)菜单功能完整实现

main.js:

import Vue from 'vue'
import App from './App.vue'
import router from './router'
//引入vuex
import store from "./store";
//引入elementUI
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';

import {postRequest} from "./utils/api";
import {putRequest} from "./utils/api";
import {getRequest} from "./utils/api";
import {deleteRequest} from "./utils/api";
import {initMenu} from "./utils/menus";

Vue.use(ElementUI);

//插件形式使用请求
Vue.prototype.postRequest=postRequest;
Vue.prototype.putRequest=putRequest;
Vue.prototype.getRequest=getRequest;
Vue.prototype.deleteRequest=deleteRequest;

Vue.config.productionTip = false

//路由导航守卫
router.beforeEach((to,from,next)=>{
  ///登录的时候把token放在了sessionStorage里面,可以去判断是否有这个token
  if (window.sessionStorage.getItem('tokenStr')) {
    //初始化菜单
    initMenu(router, store);
    next();
  }else{
    next();
  }

})


new Vue({
  router,
  store,
  render: h => h(App)
}).$mount('#app')

更改Home.vue:

<template>
  <div>
   <el-container>
     <el-header>Header</el-header>
     <el-container>
       <el-aside width="200px">
         <!--router使用router模式,路由进行跳转:把路由数据进行渲染-->
         <el-menu router unique-opened>
           <!--循环路由,v-if当循环的item是hidden时候不显示-->
           <el-submenu :index="index+''" v-for="(item,index) in routes" :key="index" v-if="!item.hidden">
             <template slot="title">
               <i class="el-icon-location"></i>
               <span>{{item.name}}</span>
             </template>
             <!--子路由循环 -->
             <el-menu-item :index="children.path" v-for="(children,index) in item.children">{{children.name}}</el-menu-item>
           </el-submenu>
         </el-menu>
       </el-aside>
       <el-main>
         <router-view class="homeRouterView"/>
       </el-main>
     </el-container>
   </el-container>
  </div>
</template>

<script>
export  default{
  name:"Home",
  //计算属性
  computed: {
    routes() {
      return this.$store.state.routes;
    }
  }
}

</script>

<style>

</style>

 

更换一下图标:图标是font-awesome的图标需要安装:

 

在main.js:导入css

 

然后更改Home.vue:

 (15)获取用户信息功能实现

先设置一下标题:

Home.vue:

设置样式:

 添加头像:

首先在main.js中添加:

在Home.vue中首先获取到用户信息,就可以进行相关的操作了:

添加:表头信息:

 

<template>
  <div>
   <el-container>
     <el-header class="homeHeader">
       <div class="title">云E办</div>

       <el-dropdown class="userInfo">
         <span class="el-dropdown-link">
           {{user.name}}
           <i><img :src="user.userFace"></i>
         </span>
         <el-dropdown-menu slot="dropdown">
           <el-dropdown-item >个人中心</el-dropdown-item>
           <el-dropdown-item >设置中心</el-dropdown-item>
           <el-dropdown-item>注销</el-dropdown-item>
         </el-dropdown-menu>
       </el-dropdown>
     </el-header>
     <el-container>
       <el-aside width="200px">
         <!--router使用router模式,路由进行跳转:把路由数据进行渲染-->
         <el-menu router unique-opened>
           <!--循环路由,v-if当循环的item是hidden时候不显示-->
           <el-submenu :index="index+''" v-for="(item,index) in routes" :key="index" v-if="!item.hidden">
             <template slot="title">
               <i :class="item.iconCls" style="color: #1accff;margin-right: 5px"></i>
               <span>{{item.name}}</span>
             </template>
             <!--子路由循环 -->
             <el-menu-item :index="children.path" v-for="(children,index) in item.children" :key="index">
               {{children.name}}
             </el-menu-item>
           </el-submenu>
         </el-menu>
       </el-aside>
       <el-main>
         <router-view class="homeRouterView"/>
       </el-main>
     </el-container>
   </el-container>
  </div>
</template>

<script>
export  default{
  name:"Home",
  data() {
    return {
      //拿到用户对象
      user: JSON.parse(window.sessionStorage.getItem("user"))
    }
  },
  //计算属性
  computed: {
    routes() {
      return this.$store.state.routes;
    }
  }
}

</script>

<style>
  .homeHeader {
    background-color: #409eff;
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 0px 15px;
    box-sizing: border-box;
  }

  .homeHeader .title {
    font-size: 30px;
    font-family: 华文行楷;
    color: #ffffff;
  }

  .homeHeader .userInfo {
    cursor: pointer;
  }

  .homeWelcome {
    text-align: center;
    font-size: 30px;
    font-family: 华文行楷;
    color: #409eff;
    padding-top: 50px;
  }

  .homeRouterView {
    margin-top: 10px;
  }

  .el-dropdown-link img {
    width: 48px;
    height: 48px;
    border-radius: 24px;
    margin-left: 8px;
  }

  .el-dropdown-link {
    display: flex;
    align-items: center;
  }
</style>

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

喵俺第一专栏

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值