element-plus 手把手带你用vue建后台 系列四(实战篇)
完整项目地址:xhwy-v3-ts-elementplus
系列文章:
前言
在前面几篇文章已经向大家介绍了我们的的项目,并且把主要的权限功能和使用的工具给大家做了简单介绍,现在我们可以进入实操啦
布局
布局使用的ui框架是element
element-plus是针对vue3项目升级的element组件,在使用的过程中也遇到了一些坑,会写到本文章中。
布局使用的是element当中的container布局容器中的一个常见布局,符合我们后台搭建的基本要求

使用的时候会看到有外边距,可以在根目录index.html中去掉外边框
<style>
html,body {
margin: 0;
padding: 0;
height: 100%;
box-sizing:border-box
}
</style>
layout布局
通过 row
和 col
组件,并通过row
组件的gutter
属性和 col
组件的 span
属性我们就可以自由地组合布局。
需要注意的是col
组件的 span
总和不能超过gutter
设置好的值,防止元素排列到下一行
侧边栏
这里要说一下导航栏的问题,因为业务场景不同,每个人的导航栏菜单肯定是不尽相同的,前面在权限的文章也有提到过,使用动态路由也方便权限管理,本项目里的侧边栏是根据 router.js 配置的路由并且根据权限动态生成的,这样就省去了写一遍路由还要手动再写一次侧边栏这种麻烦事。但是别忘记了根据权限设置二级菜单要跳到一级菜单,具体代码看第二篇文章末尾,这里不再赘述
侧边栏高亮问题
.el-menu {
background-color: $menuBg;
border: none;
// 菜单触碰高亮背景色
.el-menu-item:hover {
background: #112e42 !important;
}
// 多级菜单触碰高亮背景色
.el-sub-menu__title:hover{
background-color: rgb(3, 19, 33) !important;
}
.el-sub-menu {
// 子菜单触碰高亮背景色
.el-menu-item:hover {
background: #112e42 !important;
color: #fff !important;
}
.el-menu-item {
background-color: $subMenuBg;
}
}
}
</style>
点击侧边栏刷新当前路由
1.动态生成路由信息
2.从路由列表的信息遍历到menuList
3.如果有二级菜单,就需要重新制作一个menu,所以我们选择使用组件化递归调用
4.在组件中添加点击跳转和索引就可以正常跳转了

隐藏侧边栏
首先要有个折叠按钮来控制
引入icon
import { Expand,Fold } from '@element-plus/icons-vue'
在layout的index.vue中的header添加icon,加is判断
<el-header>
<el-row >
<!-- 侧边栏折叠按钮 -->
<el-icon style="font-size: 20px;" @click="() =>(collapsed = !collapsed)">
<component :is="collapsed ? Expand : Fold" />
</el-icon>
</el-row>
</el-header>
声明collapsed,并且对子组件传递状态
const collapsed = ref<boolean>(false)
父传子
const collapsed = ref<boolean>(false)
子组件接收collapsed,并且绑定到对应元素
<el-aside :style="'width:' + autoWidth">
<logo-bar :collapsed="collapsed" />
<menu-bar :collapsed="collapsed" />
</el-aside>
defineProps传值
defineProps({
collapsed:{
type:Boolean
}
})
这个时候menu已经可以正常折叠
导航栏
本项目中也封装了一个面包屑导航
有以下几个关键点
- useRoute的应用
- useRoute()的api->matched获取嵌套路由的路由信息
- watche监控path
- Ref配置声明数据的数据类型
- 面包屑监听路由path变化,
- 并且通过route.matched获取当前嵌套路由的路由信息
- 然后通过过滤得到面包屑展开对应数组
<script setup lang="ts">
import { Ref,ref,onMounted,watch } from 'vue'
import {RouteLocationMatched, useRoute} from 'vue-router'
const route = useRoute()
const breadcrumb:Ref<RouteLocationMatched[]> = ref([])
const getBreadCrumb = ()=> {
let matched = route.matched.filter((item)=> item.meta && item.meta.title &&item.children.length !==1)
const frist = matched[0]
if(frist.path !== '/index') {
matched = [{path:'/index', meta:{title:'首页'}} as any ].concat(matched)
}
breadcrumb.value = matched
}
// 初始化加载面包屑
onMounted(() => {
getBreadCrumb()
})
// 监控路由变化,面包屑发生变化
watch(()=>route.path,() => {
getBreadCrumb()
})
</script>
table
使用table这里需要注意:
- 自定义表格使用v-slot插槽
- default是el-tabel默认自带
- 通过scope.row获取当前行的数据
- 内置一些按钮,
- 如果按钮有超出一行,我们通过定义width适当提高宽度
有表格就离不开分页
分页我们使用Pagination组件,任何一个组件离不开属性和事件 属性
- 设置
layout
,表示需要显示的内容 prev
表示上一页,next
为下一页pager
表示页码列表jumper
表示跳页元素total
表示总条目数size
用于设置每页显示的页码数量。current-page
当前页数,支持 v-model 双向绑定total
总条目数page-size
每页显示个数选择器的选项设置
事件
current-change
current-change 改变时触发size-change
pageSize 改变时触发
element排坑
1.element组件升级plus版本之后,插槽统一使用了#
2.所有的原生事件必须添加 .native
修饰符
3.修改element样式问题,使用scoped
封装axios
之前在第一篇文章里有介绍过api文件夹,其中就存放了封装好的axios,用于登录验证,这样封装的好处在于验证权限的时候不用再手动添加token,能够一劳永逸,下面附完整代码:
import axios from 'axios'
// 创建axios实例
const axiosInstance = axios.create({
baseURL: import.meta.env.VITE_URL,
// timeout: 1200000 // 请求超时时间,毫秒(默认2分钟
})
// request拦截器 获取用户数据的时候,要告诉服务器现在是已经的登录的状态,在header附带token值,验证当前用户
axiosInstance.interceptors.request.use(
(requestInfo) => {
if (requestInfo.headers) {
requestInfo.headers['token'] = localStorage.getItem('token') || '0'
requestInfo.headers['Content-Type'] = 'application/json;charset=UTF-8'
return requestInfo
}
},
(error) => {
return Promise.reject(error)
}
)
// response 拦截器
axiosInstance.interceptors.response.use(
(response) => {
console.log('[请求api] [' + response.config.method + '] ', response.config.url, '[接口返回数据]', response.data)
if (response.status < 200 || response.status >= 400) {
alert('未知异常')
return Promise.reject()
}
const res = response.data
if (res.code === 200) return res
else if (res.code === 10001) {
alert('未登录或登录已经过期,请重新登录')
} else if (res.code === 10002) {
alert('权限不足 错误码[' + res.code + ']')
} else alert(res.message + ' [错误码' + res.code + ']')
return Promise.reject()
},
() => {
alert('服务器网络异常')
return Promise.reject()
}
)
export default axiosInstance
提到axios
就想说一下async
和await
在实际的业务场景中遇到了一些状况让我对异步请求有了更深刻的理解,例如用echarts渲染数据的时候,如果使用axios
而没有使用async
和await
,那么图标就会渲染不出来,这是因为echarts先接收数据再开始渲染数据的,这个时候就要我们使用异步请求,先将数据获取到再进行渲染。
alias
当项目逐渐变大之后,文件与文件直接的引用关系会很复杂,这时候就需要使用alias 了。 有的人喜欢alias 指向src目录下,再使用相对路径找文件
alias: {
// 配置别名
'@': path.resolve(__dirname, 'src'),
com: path.resolve(__dirname, 'src/components'),
},