el-menu实现左侧菜单栏(vue2,vue3)

这篇具有很好参考价值的文章主要介绍了el-menu实现左侧菜单栏(vue2,vue3)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

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>

到了这里,关于el-menu实现左侧菜单栏(vue2,vue3)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包