Element Plus左侧侧边栏按照屏幕宽度来确定显示和隐藏,如果太小的话,侧边栏消失,菜单会变成一个小按钮,点击按钮以模态框弹出
核心代码
头部的组件(子组件)

用媒体查询 @media



父组件







代码
管理后台管理的头部Header
html
<template>
<!-- 管理后台管理的头部Header -->
<div class="header-container">
<div class="l_content">
<!-- 小屏菜单按钮 -->
<el-button :icon="Operation" @click="click" class="menu-btn" type="primary"/>
<span>市场部BI项目管理系统</span>
</div>
<div class="r_content">
<el-dropdown @command="handleCommand">
<span class="el-dropdown-link">
<h4>{{name}}</h4>
<el-icon class="el-icon--right">
<arrow-down />
</el-icon>
</span>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item command="1">退出登录</el-dropdown-item>
<el-dropdown-item command="2">修改密码</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
</div>
<!-- 修改密码的dialog -->
<div>
<el-dialog v-model="dialogFormVisible" title="修改密码" width="680"
@close="handleADialogClose(ruleModifyPassFormRef)">
<el-form :model="modifyPassRuleForm" ref="ruleModifyPassFormRef" :rules="rules" label-width="auto">
<el-form-item label="员工号:" :label-width="formLabelWidth" prop="emp_Num">
<el-input v-model="modifyPassRuleForm.emp_Num" autocomplete="off" disabled />
</el-form-item>
<el-form-item label="旧密码" :label-width="formLabelWidth" prop="oldPassword">
<el-input v-model="modifyPassRuleForm.oldPassword" type="password" show-password
placeholder="请输入旧密码" />
</el-form-item>
<el-form-item label="新密码" :label-width="formLabelWidth" prop="newPassword">
<el-input v-model="modifyPassRuleForm.newPassword" type="password" show-password
placeholder="请输入新密码" />
</el-form-item>
<el-form-item label="再次输入新密码" :label-width="formLabelWidth" prop="newPasswordAgain">
<el-input v-model="modifyPassRuleForm.newPasswordAgain" type="password" show-password
placeholder="请再次输入新密码" />
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button @click="CancelModifyPass(ruleModifyPassFormRef)">取消</el-button>
<el-button type="primary" @click="ConfirmModifyPass(ruleModifyPassFormRef)">
确认
</el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script lang="ts" setup>
import axios from 'axios'
import { ArrowDown } from '@element-plus/icons-vue'
import { computed, reactive, ref, onMounted } from 'vue'
import router from '@/router' //引入我们配置的路由模块
import {
ElMessage,//消息提示
ElMessageBox
} from 'element-plus'
import { Operation } from '@element-plus/icons-vue'
import type { FormInstance, FormRules } from 'element-plus'
import { useEnvironment } from '@/tools/useEnvironment.js' //引入我们的环境变量
const drawerVisible = ref(false) // 抽屉显示
//环境变量对象
const env = reactive({
VITE_ENV: '',
baseUrl: ''
})
//计算属性,计算现实的登录名称
const name = computed(() => {
let admin = sessionStorage.getItem('admin')
let emp_Num = sessionStorage.getItem('emp_Num')
if (admin == '1') {
return '管理员:' + emp_Num
}
else {
return '普通用户:' + emp_Num
}
})
//修改密码对话框是否可见
const dialogFormVisible = ref(false)
//表单的宽度
const formLabelWidth = '140px'
//创建表单的引用对象
const ruleModifyPassFormRef = ref<FormInstance>()
//修改密码的表单
const modifyPassRuleForm = reactive<RuleForm>({
emp_Num: '',
oldPassword: '',
newPassword: '',
newPasswordAgain: ''
})
//验证旧密码和新密码
const validatePass = (rule : any, value : any, callback : any) => {
if (value === '') {
callback(new Error('新密码必输!请输入新密码'))
} else if (value === modifyPassRuleForm.oldPassword) {
callback(new Error("新密码应该与旧密码不相同!"))
} else {
callback()
}
}
//验证再次输入的密码
const validatePass2 = (rule : any, value : any, callback : any) => {
if (value === '') {
callback(new Error('请再次输入密码'))
} else if (value !== modifyPassRuleForm.newPassword) {
callback(new Error("两次新密码不匹配!"))
} else {
callback()
}
}
//规则
const rules = reactive<FormRules<RuleForm>>({
emp_Num: [
{ required: true, message: '账号必输!请输入账号', trigger: 'blur' },
],
oldPassword: [
{ required: true, message: '旧密码必输!请输入旧密码', trigger: ['blur', 'change'] },
],
newPassword: [
{ required: true, validator: validatePass, trigger: ['blur', 'change'] },
],
newPasswordAgain: [
{ required: true, validator: validatePass2, trigger: ['blur', 'change'] },
],
})
//修改密码的数据结构
const modifyPassData = reactive({
emp_Num: '',
oldPassword: '',
newPassword: '',
})
//下拉框点击命令
const handleCommand = (command : string | number | object) => {
if (command == '1') {
//清除所有的sessionStorage
sessionStorage.clear()
router.push('/')
}
else if (command == '2') {
dialogFormVisible.value = true
}
}
//取消修改
const CancelModifyPass = (formEl : FormInstance | undefined) => {
ElMessage.info('取消修改密码')
//重置表单
formEl.resetFields()
dialogFormVisible.value = false
}
//确认修改
const ConfirmModifyPass = async (formEl : FormInstance | undefined) => {
if (!formEl) return //如果是空的,就直接返回
await formEl.validate((valid, fields) => { //如果不是空的,等待此函数处理完成
if (valid) {
let token = sessionStorage.getItem('token')
modifyPassData.emp_Num = modifyPassRuleForm.emp_Num
modifyPassData.oldPassword = modifyPassRuleForm.oldPassword
modifyPassData.newPassword = modifyPassRuleForm.newPassword
axios.post(`${env.baseUrl}/User/ModifyPass`, JSON.stringify(modifyPassData), {
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`
},
}).then(function (response) {
if (response.status == 200 && response.data.code == 'S') {
ElMessage.success(response.data.msg)
}
else {
ElMessage.error(response.data.msg)
}
}).catch(function (error) {
console.log(error)
//未授权。也就是Token失效,需要重新登录系统
if (error.status == 401) {
ElMessageBox.confirm(
'用户登录的Token失效,请重新登录',
'错误信息',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
}
).then(() => {
// 确认按钮的回调函数
router.push('/') //跳转登录界面
}).catch(() => {
// 取消按钮的回调函数或者关闭模态框
console.log('Canceled');
});
}
if (error.response != undefined && error.response.data.msg != null && error.response.data.msg != undefined) {
ElMessage.error('修改失败,出现异常:' + error.response.data.msg)
}
else {
ElMessage.error('修改失败,出现异常:' + error.message)
}
})
//重置表单
formEl.resetFields()
dialogFormVisible.value = false
}
})
}
//添加用户对话框关闭之后
const handleADialogClose = (formEl : FormInstance | undefined) => {
//重置表单
formEl.resetFields()
}
//挂载的钩子函数
onMounted(() => {
//获取环境变量
const { VITE_ENV, baseUrl } = useEnvironment()
env.VITE_ENV = VITE_ENV.value
env.baseUrl = baseUrl.value
let emp_Num = sessionStorage.getItem('emp_Num')
modifyPassRuleForm.emp_Num = emp_Num
})
// 声明组件会触发的两个事件:'eventA' 和 'eventB'
const emit = defineEmits(['eventA', 'eventB'])
//按钮被点击时
const click = ()=>{
drawerVisible.value = true
// 触发 'eventA' 事件,并传递数据
emit('eventA',drawerVisible.value)
}
</script>
<style>
.header-container {
/* flex布局 */
display: flex;
justify-content: space-between;
align-items: center;
}
.l_content {
margin-top: 10px;
margin-left:10px;
font-size: 20px;
font-weight: bold;
display: flex;
align-items: center; /* 交叉轴(Y轴)居中 */
}
.l-content .el-button{
padding: 0 8px;
}
.l_content span{
margin-left:20px;
}
.r_content {
margin-right: 30px;
}
.user {
width: 40px;
height: 40px;
border-radius: 50%;
}
.el-dropdown-link {
cursor: pointer;
/* color: var(--el-color-primary); */
color: white;
display: flex;
align-items: center;
}
/* 小屏显示菜单按钮 */
.menu-btn {
display: none;
}
@media (max-width: 930px) {
.menu-btn {
display: inline-block;
}
}
</style>
父组件监听子组件
html
<template>
<!-- 后台管理首页 -->
<div class="common-layout">
<el-container>
<el-header>
<!-- 父组件监听子组件的事件 -->
<BackIndexHeader @eventA="handleEventA" />
</el-header>
<el-container class="back-admin-index-con">
<el-aside width="290px" style="background-color: #242f41;" :class="{ 'hide-on-small': isHide }">
<el-scrollbar>
<el-menu active-text-color="#44a1f7" background-color="#242f41" class="el-menu-vertical-demo"
:default-active="activeMenu" text-color="#fff" @open="handleOpen" @close="handleClose"
@select="handleSelect" ref="menuRef">
<el-sub-menu index="1">
<template #title>
<el-icon>
<Document />
</el-icon>
<span>文件导入</span>
</template>
<!-- <el-menu-item index="1-0">Test表文件导入</el-menu-item> -->
<el-menu-item index="1-1">我司交付车型终端销量情况导入</el-menu-item>
<el-menu-item index="1-2">不同级别乘用车批售量导入</el-menu-item>
<el-menu-item index="1-3">新能源乘用车市场销量表现不同价格段零售量导入</el-menu-item>
<el-menu-item index="1-4">乘用车市场不同价格段零售量导入</el-menu-item>
<el-sub-menu index="1-5" v-show="isAdmin">
<template #title>
<span>东南亚新能源汽车市场情况</span>
</template>
<el-menu-item index="1-5-1">泰国(TAIA)市场销量</el-menu-item>
<el-menu-item index="1-5-2">印度尼西亚(GAIKINDO)市场销量</el-menu-item>
<el-menu-item index="1-5-3">越南(VAMA)市场销量</el-menu-item>
<el-menu-item index="1-5-4">马来西亚(MAA)市场销量</el-menu-item>
</el-sub-menu>
<el-menu-item index="1-6">欧洲新能源汽车市场情况导入</el-menu-item>
<el-sub-menu index="1-7" v-show="isAdmin">
<template #title>
<span>国内动力电池单车平均带电量</span>
</template>
<el-menu-item index="1-7-1">新能源汽车月度单台平均装车电量</el-menu-item>
<el-menu-item index="1-7-2">纯电动乘用车续航里程</el-menu-item>
</el-sub-menu>
<el-menu-item index="1-8">国内动力电池企业装车量市场分析导入</el-menu-item>
</el-sub-menu>
<el-menu-item index="2">
<el-icon>
<User />
</el-icon>
<template #title>用户管理</template>
</el-menu-item>
<el-sub-menu index="3" v-show="isAdmin">
<template #title>
<el-icon>
<Service />
</el-icon>
<span>系统管理</span>
</template>
<el-menu-item index="3-1">组管理</el-menu-item>
<el-menu-item index="3-2">用户权限分配</el-menu-item>
</el-sub-menu>
</el-menu>
</el-scrollbar>
</el-aside>
<el-main>
<router-view />
</el-main>
</el-container>
</el-container>
<!-- 小屏抽屉 -->
<el-drawer v-model="drawerVisible" direction="ltr" size="400px" title="菜单">
<el-scrollbar>
<el-menu active-text-color="#44a1f7" class="el-menu-vertical-demo" :default-active="activeMenu"
default-active="1-1" @open="handleOpen" @close="handleClose" @select="handleSelect" ref="menuRef">
<el-sub-menu index="1">
<template #title>
<el-icon>
<Document />
</el-icon>
<span>文件导入</span>
</template>
<el-menu-item index="1-1">我司交付车型终端销量情况导入</el-menu-item>
<el-menu-item index="1-2">不同级别乘用车批售量导入</el-menu-item>
<el-menu-item index="1-3">新能源乘用车市场销量表现不同价格段零售量导入</el-menu-item>
<el-menu-item index="1-4">乘用车市场不同价格段零售量导入</el-menu-item>
<el-sub-menu index="1-5" v-show="isAdmin">
<template #title>
<span>东南亚新能源汽车市场情况</span>
</template>
<el-menu-item index="1-5-1">泰国(TAIA)市场销量</el-menu-item>
<el-menu-item index="1-5-2">印度尼西亚(GAIKINDO)市场销量</el-menu-item>
<el-menu-item index="1-5-3">越南(VAMA)市场销量</el-menu-item>
<el-menu-item index="1-5-4">马来西亚(MAA)市场销量</el-menu-item>
</el-sub-menu>
<el-menu-item index="1-6">欧洲新能源汽车市场情况导入</el-menu-item>
<el-sub-menu index="1-7" v-show="isAdmin">
<template #title>
<span>国内动力电池单车平均带电量</span>
</template>
<el-menu-item index="1-7-1">新能源汽车月度单台平均装车电量</el-menu-item>
<el-menu-item index="1-7-2">纯电动乘用车续航里程</el-menu-item>
</el-sub-menu>
<el-menu-item index="1-8">国内动力电池企业装车量市场分析导入</el-menu-item>
</el-sub-menu>
<el-menu-item index="2">
<el-icon>
<User />
</el-icon>
<template #title>用户管理</template>
</el-menu-item>
<el-sub-menu index="3" v-show="isAdmin">
<template #title>
<el-icon>
<Service />
</el-icon>
<span>系统管理</span>
</template>
<el-menu-item index="3-1">组管理</el-menu-item>
<el-menu-item index="3-2">用户权限分配</el-menu-item>
</el-sub-menu>
</el-menu>
</el-scrollbar>
</el-drawer>
</div>
</template>
<script lang="ts" setup>
//
import {
ref,
reactive,
onMounted,
onBeforeMount,
computed,
onUnmounted
} from 'vue'
import {
Document,
Menu as IconMenu,
Location,
Setting
} from '@element-plus/icons-vue'
import {
ElMessage, //消息提示
ElMessageBox,
MenuItemClicked
} from 'element-plus'
import router from '@/router' //引入我们配置的路由模块
import { useRoute } from 'vue-router'
import BackIndexHeader from './BackIndexHeader.vue';
import axios from 'axios'
import { useEnvironment } from '@/tools/useEnvironment.js' //引入我们的环境变量
const route = useRoute()
const activePath = computed(() => route.path) //当前选中的路径
const drawerVisible = ref(false) // 抽屉显示
const activeMenu = ref('1-1') //激活的菜单
const isHide = ref(false) //隐藏侧边栏
//环境变量对象
const env = reactive({
VITE_ENV: '',
baseUrl: ''
})
const menuRef = ref(null);
// const dialogHeight = ref(0)
const conHeight = ref(0)
//是否是管理员
const isAdmin = ref(false)
//sub-menu 展开的回调
const handleOpen = (key : string, keyPath : string[]) => {
}
//sub-menu 收起的回调
const handleClose = (key : string, keyPath : string[]) => {
router.push('/admin')
}
//菜单激活回调
const handleSelect = (index : string, item : MenuItemClicked) => {
drawerVisible.value = false
switch (index) {
case '1-1':
router.push('/admin/upload-DVT')
break
case '1-2':
router.push('/admin/upload-DGPVW')
break
case '1-3':
router.push('/admin/upload-NPVMR')
break
case '1-4':
router.push('/admin/upload-PVMR')
break
case '1-5-1':
router.push('/admin/upload-Thai')
break
case '1-5-2':
router.push('/admin/upload-Indonesia')
break
case '1-5-3':
router.push('/admin/upload-Vietnam')
break
case '1-5-4':
router.push('/admin/upload-Malaysia')
break
case '1-6':
router.push('/admin/upload-EU')
break
case '1-7-1':
router.push('/admin/upload-NMA')
break
case '1-7-2':
router.push('/admin/upload-PER')
break
case '1-8':
router.push('/admin/upload-DBL')
break
case '2':
router.push('/admin/user-manage')
break
case '3-1':
router.push('/admin/group-manage')
break
}
activeMenu.value = index
}
//注册一个钩子,在组件被挂载之前被调用
onBeforeMount(() => {
//获取环境变量
const { VITE_ENV, baseUrl } = useEnvironment()
env.VITE_ENV = VITE_ENV.value
env.baseUrl = baseUrl.value
let token = sessionStorage.getItem('token')
if (token == null) {
router.push('/')
ElMessage.error('您未登录,请登录再使用后台管理系统')
}
else {
//获取用户
GetUser()
}
})
//获取用户信息
const GetUser = () => {
//获取sessionStorage的键值
let token = sessionStorage.getItem('token')
let emp_Num = sessionStorage.getItem('emp_Num')
axios.get(`${env.baseUrl}/User/GetUser`, {
params: {
emp_Num: emp_Num
},
headers: {
Authorization: `Bearer ${token}`
}
}).then(function (response) {
if (response.status == 200 && response.data.code == 'S') {
let user = JSON.parse(response.data.data) //解析json字符串
//获取组的信息
if (user[0].u_group != null && user[0].u_group != undefined) {
GetGroup(user[0].u_group)
}
}
else {
ElMessage.error(response.data.msg)
}
}).catch(function (error) {
//未授权。也就是Token失效,需要重新登录系统
if (error.status == 401) {
ElMessageBox.confirm(
'用户登录的Token失效,请重新登录',
'错误信息',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
}
).then(() => {
// 确认按钮的回调函数
router.push('/') //跳转登录界面
}).catch(() => {
// 取消按钮的回调函数或者关闭模态框
console.log('Canceled');
});
}
if (error.response != undefined && error.response.data.msg != null && error.response.data.msg != undefined) {
ElMessage.error('获取用户失败,出现异常:' + error.response.data.msg)
}
else {
ElMessage.error('获取用户失败,出现异常:' + error.message)
}
})
}
//获取组信息
const GetGroup = (u_group : string) => {
//获取sessionStorage的键值
let token = sessionStorage.getItem('token')
let admin = sessionStorage.getItem('admin')
axios.get(`${env.baseUrl}/Group/GetGroup`, {
params: {
groupNum: u_group
},
headers: {
Authorization: `Bearer ${token}`
}
}).then(function (response) {
if (response.status == 200 && response.data.code == 'S') {
let data = JSON.parse(response.data.data) //解析json字符串
//管理员且是管理组,显示系统管理
if (admin == 1 && data[0].admin == 1) {
sessionStorage.setItem('adminGroup', 1)
isAdmin.value = true
}
}
else {
ElMessage.error(response.data.msg)
}
}).catch(function (error) {
//未授权。也就是Token失效,需要重新登录系统
if (error.status == 401) {
ElMessageBox.confirm(
'用户登录的Token失效,请重新登录',
'错误信息',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
}
).then(() => {
// 确认按钮的回调函数
router.push('/') //跳转登录界面
}).catch(() => {
// 取消按钮的回调函数或者关闭模态框
console.log('Canceled');
});
}
if (error.response != undefined && error.response.data.msg != null && error.response.data.msg != undefined) {
ElMessage.error('获取组信息失败,出现异常:' + error.response.data.msg)
}
else {
ElMessage.error('获取组信息失败,出现异常:' + error.message)
}
})
}
//刷新当前选中的菜单项
const freshActiveMenuItem = () => {
switch (activePath.value) {
case '/admin/upload-DVT':
activeMenu.value = '1-1'
break
case '/admin/upload-DGPVW':
activeMenu.value = '1-2'
break
case '/admin/upload-NPVMR':
activeMenu.value = '1-3'
break
case '/admin/upload-PVMR':
activeMenu.value = '1-4'
break
case '/admin/upload-Thai':
activeMenu.value = '1-5-1'
break
case '/admin/upload-Indonesia':
activeMenu.value = '1-5-2'
break
case '/admin/upload-Vietnam':
activeMenu.value = '1-5-3'
break
case '/admin/upload-Malaysia':
activeMenu.value = '1-5-4'
break
case '/admin/upload-EU':
activeMenu.value = '1-6'
break
case '/admin/upload-NMA':
activeMenu.value = '1-7-1'
break
case '/admin/upload-PER':
activeMenu.value = '1-7-2'
break
case '/admin/upload-DBL':
activeMenu.value = '1-8'
break
case '/admin/user-manage':
activeMenu.value = '2'
break
case '/admin/group-manage':
activeMenu.value = '3-1'
break
}
}
// 监听窗口尺寸
const handleResize = () => {
const width = window.innerWidth
isHide.value = width < 930
}
//挂载时的钩子函数
onMounted(() => {
handleResize()
freshActiveMenuItem()
//用于添加窗口大小改变事件(resize)监听器的 JavaScript 方法
window.addEventListener('resize', handleResize)
})
onUnmounted(() => {
//用于移除窗口大小改变事件(resize)监听器的 JavaScript 方法
window.removeEventListener('resize', handleResize)
})
//监听子组件触发的事件
const handleEventA = (value) => {
//将抽屉的可见性打开
drawerVisible.value = value
//侧边栏隐藏
isHide.value = true
}
</script>
<style scoped>
.el-header {
background-color: #242f41;
height: 70px;
color: white;
border-bottom: 2px white solid;
}
.el-header h1 {
margin-left: 40px;
}
.back-admin-index-con {
height: 90vh;
}
.el-menu {
border: 0 !important;
}
/* 小屏隐藏侧边栏 */
.hide-on-small {
display: none;
}
.el-menu-item {
/* 允许元素换行 */
white-space: normal !important;
/* 设置行高,否则元素会重叠 */
line-height: 18px;
}
</style>
运行结果




