vue3写法
多级数据
1. home.vue 先把页面基底搭建好
<template>
<heads />
<Sidebar />
<div class="content-box">
<div class="content">
<router-view v-slot="{ Component, route }">
<transition name="fade" mode="out-in">
<div :key="route.path">
<component :is="Component"></component>
</div>
</transition>
</router-view>
</div>
</div>
</template>
<script setup>
import heads from '../components/Header';
import Sidebar from '../components/Sidebar';
</script>
<style lang="scss" scoped>
.content-box {
position: absolute;
left: 200px;
right: 0;
top: 88px;
bottom: 0;
background: #f5f5f5;
}
.content {
width: auto;
height: 100%;
overflow-y: auto;
box-sizing: border-box;
}
</style>
2. Sidebar.vue 左侧菜单栏(多级数据)
<template>
<div class="sidebar">
<el-menu class="sidebar-el-menu" :default-active="onRoutes" router>
<template v-for="item in dataReactive.menus">
<template v-if="item.subs">
<el-sub-menu :index="item.route" :key="item.route">
<template #title>
<el-icon>
<component :is="item.icon" />
</el-icon>
<span>{{ item.name }}</span>
</template>
<template v-for="subItem in item.subs">
<el-sub-menu v-if="subItem.subs" :index="subItem.route" :key="subItem.route">
<template #title>
<span>{{ subItem.name }}</span>
</template>
</el-sub-menu>
<el-menu-item v-else :index="subItem.route" :key="subItem.index + 'a'">
<template #title>
<span>{{ subItem.name }}</span>
</template>
</el-menu-item>
</template>
</el-sub-menu>
</template>
<template v-else>
<el-menu-item :index="item.route" :key="item.route">
<template #title>
<el-icon :size="18">
<component :is="item.icon" />
</el-icon>
<span>{{ item.name }}</span>
</template>
</el-menu-item>
</template>
</template>
</el-menu>
</div>
</template>
<script setup>
import { reactive, computed, nextTick, getCurrentInstance, watch, onMounted } from 'vue';
import { useRoute } from 'vue-router';
import $store from "../store/index.js";
const route = useRoute();
const { proxy } = getCurrentInstance();
const dataReactive = reactive({
// 可以自己写死数据,这边我用的动态路由
// menus: [
// {
// route: '/basic',
// name: '首页',
// icon: 'House',
// },
// {
// route: '/quickScheduling',
// name: '快速排期',
// icon: 'Notification',
// },
// {
// route: '/schedulingOperation',
// name: '新增排期',
// icon: 'FolderAdd',
// },
// {
// route: '3',
// name: '排期列表',
// icon: 'Memo',
// subs: [
// {
// route: "/notStartSche",
// name: "未开始",
// },
// {
// route: '/scheduling',
// name: '进行中',
// },
// {
// route: '/closeSche',
// name: '已结束',
// }
// ],
// },
// {
// route: '/fileQuery',
// name: '文件查询',
// icon: 'Folder',
// },
// {
// route: '4',
// name: '系统设置',
// icon: 'Setting',
// subs: [
// {
// route: '/mechanism',
// name: '机构设置',
// },
// {
// route: "/personnel",
// name: "人员设置",
// },
// {
// route: '/role',
// name: '角色设置',
// },
// {
// route: '/menus',
// name: '菜单设置',
// },
// {
// route: '/jurisdiction',
// name: '权限设置',
// },
// {
// route: '/arbitral',
// name: '法庭设置',
// },
// {
// route: '/template',
// name: '模板设置',
// },
// {
// route: '/fingerprint',
// name: '指纹设置',
// },
// ],
// },
// ],
menus: []
});
onMounted(() => {
setTimeout(() => {
let menus = JSON.parse(sessionStorage.getItem("menus")); //权限
menus.forEach((e) => {
// 后端返回的路由子级children如果没值,会给我们返回空数组,这样我们需要符合menu数据规则,把[]改成null
let subs = e.children.length == 0 ? null : e.children
dataReactive.menus.push({
route: e.route,
name: e.name,
icon: e.icon,
subs: subs
});
});
}, 1000);
})
const onRoutes = computed(() => {
return route.path;
});
</script>
<style lang="scss" scoped>
.sidebar {
display: block;
position: absolute;
left: 0;
top: 88px;
bottom: 0;
}
.sidebar-el-menu:not(.el-menu--collapse) {
width: 200px;
// background-image: url("../assets/img/Group.png") !important;
// background-size: 100% 100%;
}
.sidebar::-webkit-scrollbar {
width: 0;
}
.sidebar>ul {
height: 100%;
}
.sidebar ::v-deep .el-menu {
border: none;
background-color: #292E4A;
span {
font-weight: 400;
font-size: 18px;
color: #FFFFFF;
}
}
.sidebar ::v-deep .el-sub-menu__title:hover {
background: #40466C;
}
.sidebar ::v-deep .el-menu-item.is-active {
color: #FFFFFF;
background: #40466C;
}
.sidebar ::v-deep .el-menu-item:hover {
background: #40466C;
}
.sidebar ::v-deep .el-menu-item.is-active i {
color: #FFFFFF;
}
.sidebar ::v-deep .el-icon {
color: #FFFFFF;
}
</style>
3. Header.vue 顶部导航栏
<template>
<div class="header flex flex-between">
<div class="left flex flex-AItems">
<img src="../assets/img/logo.png" />
<p>{{ storeForm.nameInfo == '' ? '融合云法庭系统' : storeForm.nameInfo }}</p>
</div>
<div class="right flex flex-AItems">
<el-popover trigger="hover" placement="bottom" :width="160">
<p>需要修改密码?</p>
<div style="text-align: right; margin: 0">
<el-button size="small" type="danger" @click="visible = true">确定</el-button>
</div>
<template #reference>
<p>欢迎您,{{ info.name }}</p>
</template>
</el-popover>
<div class="image" @click="outLogin">
<img src="../assets/img/return.png" alt="">
</div>
</div>
</div>
<el-dialog v-model="visible" title="修改密码" width="30%" center :close-on-click-modal="false">
<div class="dialog">
<el-form ref="ruleFormRef" :model="storeForm" :rules="rules" label-width="120px" status-icon>
<el-form-item label="旧密码:" prop="oldPassword">
<el-input size="large" type="password" v-model="storeForm.oldPassword" placeholder="请输入旧密码"
clearable show-password />
</el-form-item>
<el-form-item label="新密码:" prop="newPassword">
<el-input size="large" type="password" v-model="storeForm.newPassword" placeholder="请输入新密码"
clearable show-password />
</el-form-item>
</el-form>
</div>
<template #footer>
<span class="dialog-footer">
<el-button size="large" @click="visible = false">取消</el-button>
<el-button size="large" type="danger" @click="confrim(ruleFormRef)">确定</el-button>
</span>
</template>
</el-dialog>
</template>
<script setup>
import { reactive, ref, getCurrentInstance } from 'vue';
import { useRouter } from 'vue-router';
const router = useRouter();
const { proxy } = getCurrentInstance();
const visible = ref(false)
const ruleFormRef = ref('');
const info = reactive({
name: sessionStorage.getItem('userName')
})
const storeForm = reactive({
id: sessionStorage.getItem('userID'),
nameInfo: sessionStorage.getItem('nameInfo'),
newPassword: '',
oldPassword: ''
})
const telValidator11 = (rule, value, callback) => {
if (!value) {
callback(new Error("必须输入新密码"));
} else if (!/^(?=.*[A-Z])(?=.*[a-z])(?=.*[0-9])[a-zA-Z0-9]{8,64}$/.test(value)) {
callback(new Error("密码只能为大写字母+小写字母+数字的8至64位字符组合"));
} else {
callback();
}
};
const rules = ref({
oldPassword: [
{ required: true, message: '必须输入旧密码', trigger: 'blur' },
],
newPassword: [
{ required: true, validator: telValidator11, trigger: 'blur' }
]
});
const confrim = (formEl) => {
if (!formEl) return;
formEl.validate(async (valid) => {
if (valid) {
let { code, message } = await proxy.$post(proxy.api.updatePass, storeForm)
if (code == 200) {
ElMessage.success(message);
visible.value = false
sessionStorage.clear();
router.push({
path: '/',
});
}
}
});
};
// 退出登录
const outLogin = () => {
sessionStorage.clear();
router.push({
path: '/',
});
};
</script>
<style lang="scss" scoped>
.header {
height: 88px;
line-height: 88px;
background: url(../assets/img/hearder.png) no-repeat;
background-size: 100% 100%;
.left {
padding-left: 40px;
img {
width: 52px;
height: 60px;
}
p {
font-weight: 700;
font-size: 36px;
letter-spacing: 0.04em;
color: #FFFFFF;
margin-left: 16px;
}
}
.right {
p {
font-weight: 400;
font-size: 18px;
color: #FFFFFF;
margin-right: 38px;
cursor: pointer;
}
.image {
width: 105px;
height: 105px;
text-align: center;
line-height: 105px;
border-left: 1px solid rgba(255, 255, 255, 0.3);
}
}
}
</style>
一级数据
home页面是一样的文章来源地址https://www.toymoban.com/news/detail-715588.html
Sidebar.vue
<template>
<div class="sidebar">
<el-menu class="sidebar-el-menu" :default-active="onRoutes" router>
<el-menu-item :index="item.route" v-for="item in dataReactive.menus" :key="item.route">
<el-icon :size="18">
<component :is="item.icon" />
</el-icon>
<template #title>
<span>{{ item.name }}</span>
<span class="tip" v-if="item.tip == 'dingdan' && dataReactive.numOrder > 0">{{ dataReactive.numOrder
}}</span>
</template>
</el-menu-item>
</el-menu>
</div>
<div>
<audio src="https://diancan-1252107261.cos.accelerate.myqcloud.com/mp3/dingdantixing.mp3" ref="audio"></audio>
</div>
<!-- <el-button @click="plays()" v-show="dataReactive.a == '1'"></el-button> -->
</template>
<script setup>
import { reactive, computed, nextTick, getCurrentInstance, watch, onMounted } from 'vue';
import { useRoute } from 'vue-router';
import $store from "../store/index.js";
const route = useRoute();
const { proxy } = getCurrentInstance();
const dataReactive = reactive({
a: '0',
menus: [
{
route: '/dataAnalysis',
name: '数据分析',
icon: 'Histogram',
tip: 'shuju',
},
{
route: '/orderManage',
name: '订单管理',
icon: 'Bell',
tip: 'dingdan',
},
{
route: '/foodManage',
name: '菜品管理',
icon: 'Bowl',
tip: 'caiping',
},
{
route: '/foodCategory',
name: '菜品类目',
icon: 'Dish',
tip: 'leimu',
},
{
route: '/tableNumManage',
name: '桌号管理',
icon: 'User',
tip: 'zhuohao',
},
{
route: '/storeSetting',
name: '店铺设置',
icon: 'Setting',
tip: 'shezhi',
},
],
numOrder: 0,
});
watch(() => $store.state.remind, (newVal, oldVal) => {
if (newVal > 0) {
plays()
}
dataReactive.numOrder = newVal > 99 ? '99+' : newVal
}
, {
deep: true
});
onMounted(() => {
dataReactive.numOrder = sessionStorage.getItem('order_num') > 99 ? '99+' : sessionStorage.getItem('order_num');
});
// 播放音频
const plays = () => {
nextTick(() => {
console.log(123);
proxy.$refs.audio.currentTime = 0
let Audio = proxy.$refs.audio
Audio.play()
})
}
const onRoutes = computed(() => {
return route.path;
});
</script>
<style lang="scss" scoped>
.tip {
width: 28px;
height: 25px;
background-color: rgba(238, 0, 0, 0.89);
border-radius: 50%;
line-height: 25px;
text-align: center;
color: #fff;
font-size: 12px !important;
margin-left: 50px;
}
.sidebar {
display: block;
position: absolute;
left: 0;
top: 60px;
bottom: 0;
border-right: 1px solid #ebedf0;
}
.sidebar-el-menu:not(.el-menu--collapse) {
width: 250px;
}
.sidebar::-webkit-scrollbar {
width: 0;
}
.sidebar>ul {
height: 100%;
}
.sidebar /deep/ .el-menu {
border: none;
padding-left: 15px;
span {
font-size: 18px;
}
}
</style>
Header.vue(我这里还做了一个切换昼夜间模式的主题)
<template>
<div class="header flex flex-between">
<p>{{ dataReactive.name }}</p>
<div class="flex flex-AItems">
<el-button
:icon="isDark ? Moon : Sunny"
circle
@click="toggleDark()"
/>
<el-button
type="primary"
:icon="SwitchButton"
circle
@click="outLogin()"
/>
</div>
</div>
</template>
<script setup>
import { reactive } from 'vue';
import { useDark, useToggle } from '@vueuse/core';
import { Moon, Sunny, SwitchButton } from '@element-plus/icons-vue';
import { useRouter } from 'vue-router';
const router = useRouter();
const isDark = useDark();
const toggleDark = useToggle(isDark);
const dataReactive = reactive({
name: sessionStorage.getItem('name'),
});
// 退出登录
const outLogin = () => {
sessionStorage.clear();
router.push({
path: '/',
});
};
</script>
<style lang="scss" scoped>
.header {
height: 60px;
line-height: 60px;
border-bottom: 1px solid #ebedf0;
padding: 0 80px;
p {
font-size: 20px;
letter-spacing: 0.05em;
font-weight: 600;
}
/deep/ .el-button {
margin-right: 20px;
}
}
</style>
vue2
多级数据
<template>
<div class="sidebar">
<el-menu class="sidebar-el-menu" :default-active="onRoutes" :default-openeds="opends" router>
<template v-for="item in menus">
<div class="big-title">{{ item.name }}</div>
<template v-if="item.subs">
<el-submenu :index="item.index" :key="item.index">
<template slot="title">
<i :class="item.icon"></i>
<span>{{ item.title }}</span>
</template>
<template v-for="subItem in item.subs">
<el-submenu v-if="subItem.subs" :index="subItem.index" :key="subItem.index">
<template slot="title" :class="onRoutes == subItem.index ? 'active' : ''">
<span class="content">{{ subItem.title }}</span>
</template>
</el-submenu>
<el-menu-item v-else :index="subItem.index" :key="subItem.index + 'a'">
<span class="content">{{ subItem.title }}</span>
</el-menu-item>
</template>
</el-submenu>
</template>
<template v-else>
<el-menu-item :index="item.index" :key="item.index">
<template slot="title">
<i :class="item.icon"></i>
<span>{{ item.title }}</span>
</template>
</el-menu-item>
</template>
</template>
</el-menu>
</div>
</template>
<script>
export default {
data () {
return {
isAdmin: sessionStorage.getItem('isAdmin'),
menus: [
{
name: '基础功能',
index: "/activity",
title: "活动管理",
icon: 'el-icon-s-flag'
},
{
name: '管理功能',
index: "3",
title: "会员管理",
icon: 'el-icon-user',
subs: [
{
index: "/memberList",
title: "会员列表",
},
{
index: '/points',
title: '积分排行榜',
},
{
index: '/account',
title: '子账号管理',
},
{
index: '/billList',
title: '缴费记录',
},
{
index: '/introduction',
title: '引荐记录',
},
{
index: '/industry',
title: '行业管理',
},
],
},
],
opends: ['3']
}
},
computed: {
onRoutes () {
return this.$route.path;
}
}
}
</script>
<style lang="scss" scoped>
.sidebar {
display: block;
position: absolute;
left: 0;
top: 65px;
bottom: 0;
border-right: 1px solid #ebedf0;
::v-deep .el-menu {
border: none;
}
.big-title {
font-size: 20px;
border-left: 4px solid #e8473f !important;
margin-top: 20px;
padding: 0 10px;
}
i {
font-size: 22px;
}
.content {
font-size: 16px;
padding-left: 10px;
}
}
.sidebar::-webkit-scrollbar {
width: 0;
}
.sidebar>ul {
height: 100%;
}
.sidebar-el-menu {
span {
font-family: 'Microsoft YaHei UI';
font-weight: 400;
font-size: 18px;
color: #333333;
&:hover {
color: #e8473f;
}
}
::v-deep .el-menu-item:hover,
.el-menu-item:focus {
border-left: 2px solid #e8473f;
}
}
.sidebar-el-menu ::v-deep .el-menu-item.is-active {
// border-left: 2px solid #e8473f !important;
background: rgba(182, 1, 22, 0.1) !important;
span {
color: #e8473f;
}
}
.sidebar-el-menu:not(.el-menu--collapse) {
width: 250px;
}
</style>
一级数据(使用自己的图标
<template>
<div class="sidebar">
<!-- 左边菜单栏 -->
<el-menu class="sidebar-el-menu" :default-active="onRoutes" unique-opened router>
<el-menu-item :index="item.route" v-for="item in items" :key="item.route">
<img :src="routesIcon(item.name)" />
<template slot="title">
<span>{{ item.name }}</span>
</template>
</el-menu-item>
</el-menu>
</div>
</template>
<script>
export default {
data() {
return {
menus: [],
items: [
{
route: '/systemintro',
name: '首页'
},
// {
// route: "/systemhome",
// name: "系统首页",
// },
// {
// index: "/usermanage",
// title: "用户管理",
// },
// {
// index: "/rolemanage",
// title: "角色管理",
// },
// {
// index: "/templatemanage",
// title: "模板管理",
// },
// {
// index: "/fingerprintmanage",
// title: "指纹管理",
// },
// {
// index: "/organmanage",
// title: "机构管理",
// },
// {
// index: "/scenemanage",
// title: "场景管理",
// },
// {
// index: "/departmanage",
// title: "部门管理",
// },
// {
// index: "/arraignmentcase",
// title: "提讯案件",
// },
// {
// index: "/historycase",
// title: "历史案件",
// },
],
};
},
methods: {},
mounted() {
setTimeout(() => {
this.menus = JSON.parse(sessionStorage.getItem("menus")); //权限
this.menus.forEach((e) => {
this.items.push({
route: e.route,
name: e.name,
});
});
}, 1000);
},
computed: {
onRoutes() {
let path = this.$route.path;
switch (path) {
case "/templatemanage/addtemplate":
return "/templatemanage";
case "/fingerprintmanage/addfinger":
return "/fingerprintmanage";
case "/arraignmentcase/addeditcase":
case "/arraignmentcase/startcase":
return "/arraignmentcase";
default:
return path;
}
},
// 导航图标
routesIcon() {
return function (name) {
switch (name) {
case '首页':
return require("@assets/img/icon/nav_icon_home_pes.png");
case "案件受理":
return require("@assets/img/icon/nav_icon_set_pes-6.png");
case "用户管理":
return require("@assets/img/icon/nav_icon_set_pes-1.png");
case "角色管理":
return require("@assets/img/icon/nav_icon_set_pes.png");
case "模板管理":
return require("@assets/img/icon/nav_icon_set_pes-2.png");
case "指纹管理":
return require("@assets/img/icon/nav_icon_fing_pes.png");
case "机构管理":
return require("@assets/img/icon/nav_icon_jour_pes.png");
case "场景管理":
return require("@assets/img/icon/nav_icon_pes.png");
case "部门管理":
return require("@assets/img/icon/nav_icon_set_pes-3.png");
case "提讯案件管理":
return require("@assets/img/icon/nav_icon_set_pes-4.png");
case "归档案件管理":
return require("@assets/img/icon/nav_icon_set_pes-5.png");
case '执法统计':
return require('@assets/img/icon/nav_icon_set_nor-6.png');
case '授权管理':
return require('@assets/img/icon/nav_icon_set_nor-7.png');
}
};
},
},
};
</script>
<style lang="scss" scoped>
.sidebar-el-menu /deep/ .el-menu-item img {
width: 24px;
height: 24px;
margin-right: 22px;
}
.sidebar-el-menu /deep/ .el-menu-item.is-active {
color: white;
background: linear-gradient(180deg, #53c1ff 0%, #0e62dd 91.64%);
box-shadow: 0px 4px 8px rgba(12, 53, 157, 0.9);
opacity: 1;
}
.sidebar-el-menu /deep/ .el-menu-item:hover {
background: linear-gradient(180deg, #53c1ff 0%, #0e62dd 91.64%);
box-shadow: 0px 4px 8px rgba(12, 53, 157, 0.9);
opacity: 1;
}
.sidebar-el-menu /deep/ .el-menu-item {
height: 48px;
line-height: 48px;
padding-left: 45px !important;
color: white;
font-size: 14px;
opacity: 0.6;
transition: all 0.3s;
}
.sidebar-el-menu /deep/ .el-submenu__title i {
color: white;
}
.sidebar-el-menu /deep/ .el-menu--inline .el-menu-item {
font-size: 14px;
}
.sidebar-el-menu i {
color: white;
}
.sidebar {
display: block;
position: absolute;
left: 0;
top: 70px;
bottom: 0;
}
.sidebar::-webkit-scrollbar {
width: 0;
}
.sidebar-el-menu:not(.el-menu--collapse) {
width: 200px;
}
.sidebar > ul {
height: 100%;
}
.sidebar /deep/ .el-menu {
padding-top: 21px;
background-color: transparent;
border: none;
}
</style>
文章来源:https://www.toymoban.com/news/detail-715588.html
到了这里,关于el-menu实现左侧菜单栏(vue2,vue3)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!