(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>