Vue3+Element-Plus 动态加载主页Aside(左侧)菜单数据 二五
1. 通过接口动态获取左侧菜单数据1.1 获取接口前的注意事项1. 按照接口文档要求,除了登录接口外,需要授权的API必须要在请求头中使用Authorization 字段提供 token 令牌意思是说,访问接口必须得到服务器授权才能正常调用。2. 如何获取服务器的授权认证。前面章节已经讲过,登录成功后。服务器端会返回一个登录成功后的( token)令牌,这个token 令牌就是权限认证的字段 (A
1. 通过接口动态获取左侧菜单数据
1.1 获取接口前的注意事项
1. 按照接口文档要求,除了登录接口外,需要授权的API 必须要在请求头中使用 Authorization 字段提供 token 令牌
- 意思是说,访问接口必须得到服务器授权才能正常调用。
2. 如何获取服务器的授权认证。
- 前面章节已经讲过,登录成功后。服务器端会返回一个登录成功后的( token)令牌,这个token 令牌就是权限认证的字段 (Authorization)
- 所以在请求一些授权API时,必须在请求头中使用 (Authorization)字段,让Authorization 字段 的值等于token 令牌的字符串
3. 如何在请求头中添加 Authorization (权限认证)字段
- 通过 Axios 拦截器添加 Authorization 字段
- 原理:在Axios 请求拦截器中获取 token 令牌的字符串,再赋值给 Authorization 字段,从而保证请求时拥有获取数据的权限
3.1 如何为Axios 添加请求拦截器,并挂载一个自定义属性
// 请求拦截器
axios.interceptors.request.use(config => {
// 为请求头对象,添加 Token 验证的 Authorization 字段
config.headers.Authorization = window.sessionStorage.getItem('token')
// 在最后必须 return config
return config
})
3.2 请求拦截器原理解析
- 导入了axios 包后,通过axios调用interceptors 属性,该属性有一个成员属性叫request,request就是一个请求拦截器。那么可以通过use 属性为request 请求拦截器挂载一个回调函数。这个回调函数作用是,对发送的请求进行预处理。
- 也就是说,只要通过axios 向服务器发起数据请求,那么在请求期间必然会优先调用 use 函数进行数据的预处理。
总结:
- 请求拦截器相当于预处理的过程,预处理的对象是:本次的请求。
3.3 接下来对设置的请求对象 config 进行详细讲解,它包含了多个属性
- config 对象中,包含了headers 属性,它就是一个请求头 ,但是它并没有Authorization 字段
2. 接下来,需要给请求头headers ,通过自定义拦载器为它挂载一个Authorization 字段
3. 刷新页面,再来查看字段已添加成功。但是Authorization 的值为什么是空呢,因为在登录期间,服务器还没有给用户颁发 token 令牌 ,因此在这里它默认就是空。
4. 如果登录之后,再去调用其他接口 ,例如左侧菜单接口。再去监听请求的话,Authorization 值肯定不为空了。而是一个真正的 token 令牌了。
总结
- 服务器接受请求时,判断Authorization 是否符合要求。如果符合要求,才会进行响应。如若不符合要求,或者是说 Authorization 值为空,服务器就驳回本次请求。
- 通过axios 配置了请求拦截器,为每一次的api 请求挂载了自定义的一个Authorization 请求头,确保了能正常访问调用有权限的API
4. 发起请求,获取左侧数据菜单
4.1 根据左侧菜单文档
- get 请求
- 路径 menus
4.2 如上图,服务器响应的大对象中,包含在 data 和meta 两个属性。其中meta 状态码等于200 证明请求成功。请求成功后,data 中包含了服务器所返回的菜单列表,每一个菜单都是由id,authName,path,children 属性组成。children属性 代表有二级菜单。每一个二菜单也都是由id,authName,path,children 属性组成。
- 请求左侧思路:当界面加载前,就应该立即获取左侧菜单数据。
- 通过vue 生命周期函数created 来实现
3. 如上,通过使用花括号进行解构赋值,把data 属性解构出来并重命名为 res 进行接收
4.3 数据获取成功后,如何渲染在页面中呢?
1. 渲染思路:获取到数据后,需要挂载在 data 中。所以需要在data 中定义一个数据接收对象。该数据接收对象需要和返回的data 数据属性一至。例如。返回的data 是一个数组,那么定义的数据接收对像 menulist 也应当是一个数组。
2. 渲染左侧菜单
2.1 通过上面解构赋值后,已成功把获取到的左侧菜单数据保存在 menulist 中
1. 接下来,根据 menulist 中保存的数据进行绘制左侧菜单UI 结构
1.1 如何绘制
首先,根据数据结构进行分析,所有的一级菜单数据保存在data 中。每一个一级菜单中,通过children 属性又嵌套了二级菜单
1.2 绘制思路,通过双层 for 循环进行解析
- 外层 for 循环主要负责渲染一级菜单
- 内层 for 循环主要负责渲染二级菜单
1.3 Home.vue 页面代码实现
- 首先,渲染一级菜单。为一级菜单添加 v- for 指令进行循环。其中每一个v -for 指令尽量提供一个key 唯一值
1.1 刷新查看页面效果,有5条数据循环出来了,但是文本写死了。所以我们应该把每一项对应的 authName 属性,当做显示的文本名称。
1.2 接下来,为一级菜单中的span 标签动态绑定一个名称属性。
1.3 再次刷新页面,效果都已经正常渲染出来了
1.4 为了防止页面点击一个时,全部展开的现象。需要给一级菜单 el-submenu 每一项添加一个index 唯一值。
小技巧:
- 动态的数据绑定,在属性前面加冒号
- el-submenu 的index 唯一值,必须是字符串,不接收数值。所以转换成字符串的简单方式是在后面拼接一个空字符串
2. 接下来,渲染二级菜单。
2.1 二级菜单的渲染,应该是循环所有一级菜单的children 属性
2.2 二级菜单渲染代码实现
2. 最终效果
3. Home.vue 页面代码
<template>
<el-container class="home_container">
<!-- 头部区域 -->
<el-header>
<div>
<img src="../assets/heima.png" alt="" />
<span>电商后台管理系统</span>
</div>
<el-button type="info" @click="logout">退出</el-button>
</el-header>
<!-- 页面主体区域 -->
<el-container>
<!-- 侧边栏 -->
<el-aside width="200px">
<!-- 侧边栏菜单区域 -->
<el-menu
active-text-color="#ffd04b"
background-color="#545c64"
text-color="#fff">
<!-- 一级菜单 -->
<el-submenu :index="item.id+''" v-for="item in menulist" :key="item.id">
<!-- 一级菜单模板区域 -->
<template #title>
<el-icon><location /></el-icon>
<span>{{item.authName}}</span>
</template>
<!-- 二级菜单 -->
<el-menu-item :index="subItem.id+''" v-for="subItem in item.children" :key="subItem.id">
<template #title>
<el-icon><iconMenu /></el-icon>
<span>{{subItem.authName}}</span>
</template>
</el-menu-item>
</el-submenu>
</el-menu>
</el-aside>
<!-- 右侧内容主体区域 -->
<el-main>Main</el-main>
</el-container>
</el-container>
</template>
<script>
export default {
data () {
return {
// 左侧菜单数据对象
menulist: []
}
},
created () {
this.getMenuList()
},
methods: {
logout () {
window.sessionStorage.clear()
this.$router.push('/login')
},
// 获取所有的菜单数据
async getMenuList () {
const { data: res } = await this.$http.get('menus')
if (res.meta.status !== 200) return this.$message.error(res.meta.msg)
// 成功了,进行赋值
this.menulist = res.data
console.log(res)
}
}
}
</script>
<style lang="less" scoped>
.home_container {
height: 100%;
}
.el-header {
background-color: #363d40;
// 给头部设置一下弹性布局
display: flex;
// 让它贴标左右对齐
justify-content: space-between;
// 清空图片左侧padding
padding-left: 0;
// 按钮居中
align-items: center;
// 文本颜色
color: #fff;
// 设置文本字体大小
font-size: 20px;
// 嵌套
> div {
// 弹性布局
display: flex;
// 纵向上居中对齐
align-items: center;
// 给文本和图片添加间距,使用类选择器
span {
margin-left: 15px;
}
}
}
.el-aside {
background-color: #313743;
}
.el-main {
background-color: #e9edf1;
}
</style>
4.main.js 页面代码
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import installElementPlus from './plugins/element'
// 导入字体图标
import './assets/font/iconfont.css'
// 导入全局样式表
import './assets/css/global.css'
import axios from 'axios'
// 配置请求根路径
axios.defaults.baseURL = 'http://127.0.0.1:8888/api/private/v1/'
// 请求拦截器
axios.interceptors.request.use(config => {
console.log(config)
// 为请求头对象,添加 Token 验证的 Authorization 字段
config.headers.Authorization = window.sessionStorage.getItem('token')
// 在最后必须 return config
return config
})
const app = createApp(App)
installElementPlus(app)
app.config.globalProperties.$http = axios
app.use(router).mount('#app')
更多推荐
所有评论(0)