我们将基于之前的 Layout.vue 组件,扩展出一个包含顶部菜单和左侧菜单的布局。顶部菜单分为左、中、右三部分,左侧是纯文字 Logo(“Python私教”),中间是常用菜单(如首页、关于等),右侧是注册、登录等按钮。左侧菜单则是一个多级菜单,支持展开和折叠。
最终效果图:实现效果顶部菜单:
左侧:文字 Logo(“Python私教”)。中间:常用菜单(首页、关于等)。右侧:注册、登录等按钮。左侧菜单:
支持多级菜单。支持展开和折叠。布局结构:
顶部菜单固定在页面顶部。左侧菜单固定在页面左侧。主体内容在右侧,可以滚动。实现代码1. 修改 Layout.vue 组件<template> <div class="layout" :class="`theme-${theme}`"> <!-- 顶部菜单 --> <header class="header"> <div class="header-left"> <span class="logo">Python私教</span> </div> <div class="header-center"> <ul class="top-menu"> <li v-for="item in topMenuData" :key="item.label" class="top-menu-item"> {{ item.label }} </li> </ul> </div> <div class="header-right"> <button class="btn">注册</button> <button class="btn">登录</button> </div> </header> <div class="main-container"> <!-- 左侧菜单 --> <aside class="sidebar"> <Menu :menuData="leftMenuData" /> </aside> <!-- 主体内容 --> <main class="content"> <slot></slot> </main> </div> </div></template><script setup>import { ref } from 'vue';import Menu from './Menu.vue'; // 导入菜单组件const props = defineProps({ theme: { type: String, default: 'light', validator: (value) => ['light', 'dark'].includes(value), },});// 顶部菜单数据const topMenuData = ref([ { label: '首页' }, { label: '关于我们' }, { label: '服务' }, { label: '联系我们' },]);// 左侧菜单数据const leftMenuData = ref([ { label: '首页', icon: 'fas fa-home', }, { label: '关于我们', icon: 'fas fa-info-circle', }, { label: '服务', icon: 'fas fa-cogs', children: [ { label: '服务1', icon: 'fas fa-wrench' }, { label: '服务2', icon: 'fas fa-wrench' }, ], },]);</script><style scoped>.layout { display: flex; flex-direction: column; min-height: 100vh;}.header { display: flex; justify-content: space-between; align-items: center; padding: 0 20px; background-color: #007bff; color: white; height: 60px; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);}.header-left .logo { font-size: 20px; font-weight: bold;}.header-center .top-menu { display: flex; list-style: none; margin: 0; padding: 0;}.top-menu-item { margin: 0 15px; cursor: pointer;}.top-menu-item:hover { text-decoration: underline;}.header-right .btn { margin-left: 10px; padding: 5px 10px; border: none; border-radius: 4px; background-color: white; color: #007bff; cursor: pointer;}.header-right .btn:hover { background-color: #f0f0f0;}.main-container { display: flex; flex: 1;}.sidebar { width: 250px; background-color: #f8f9fa; border-right: 1px solid #ddd; overflow-y: auto;}.content { flex: 1; padding: 20px; overflow-y: auto;}/* 浅色主题 */.theme-light { background-color: #ffffff; color: #333333;}.theme-light .sidebar { background-color: #f8f9fa;}/* 深色主题 */.theme-dark { background-color: #1a1a1a; color: #ffffff;}.theme-dark .sidebar { background-color: #2d2d2d;}</style>
2. 创建 Menu.vue 组件<template> <ul class="menu"> <li v-for="item in menuData" :key="item.label" class="menu-item"> <!-- 菜单项 --> <div class="menu-label" @click="toggleSubMenu(item)"> <span v-if="item.icon" class="menu-icon"> <i :class="item.icon"></i> </span> <span class="menu-text">{{ item.label }}</span> <span v-if="item.children" class="menu-arrow"> {{ isSubMenuOpen(item) ? '▼' : '▶' }} </span> </div> <!-- 子菜单 --> <ul v-if="item.children && isSubMenuOpen(item)" class="sub-menu"> <li v-for="child in item.children" :key="child.label" class="sub-menu-item" > <span v-if="child.icon" class="menu-icon"> <i :class="child.icon"></i> </span> <span class="menu-text">{{ child.label }}</span> </li> </ul> </li> </ul></template><script setup>import { ref } from 'vue';const props = defineProps({ menuData: { type: Array, required: true, },});const openMenus = ref([]);// 切换子菜单展开/收起const toggleSubMenu = (item) => { if (item.children) { const index = openMenus.value.indexOf(item.label); if (index === -1) { openMenus.value.push(item.label); } else { openMenus.value.splice(index, 1); } }};// 判断子菜单是否展开const isSubMenuOpen = (item) => { return openMenus.value.includes(item.label);};</script><style scoped>.menu { list-style: none; padding: 0; margin: 0;}.menu-item { margin: 8px 0;}.menu-label { display: flex; align-items: center; padding: 8px 16px; cursor: pointer; transition: background-color 0.3s ease;}.menu-label:hover { background-color: rgba(0, 0, 0, 0.1);}.menu-icon { margin-right: 8px;}.menu-text { flex: 1;}.menu-arrow { margin-left: 8px;}.sub-menu { list-style: none; padding-left: 24px; margin: 0;}.sub-menu-item { padding: 8px 16px; cursor: pointer; transition: background-color 0.3s ease;}.sub-menu-item:hover { background-color: rgba(0, 0, 0, 0.1);}</style>
使用示例<template> <Layout theme="light"> <div>这里是主体内容</div> </Layout></template><script setup>import Layout from './Layout.vue';</script>
加入我们的学习社区,一起成长!如果你觉得这个组件很棒,欢迎私信我,加入我们的学习社区!在这里,你可以:
学习更多前端开发技巧。参与开源项目,提升实战能力。和志同道合的开发者一起交流。私信我,获取更多实用教程和资源!👉 发送私信【通用菜单组件】,我会分享完整代码和更多学习资料!👉 也可以私信我【加入社区】,拉你进我们的开发者交流群,一起探讨技术!
结语通过结合 Layout.vue 和 Menu.vue,我们实现了一个功能强大的布局组件,支持顶部菜单和左侧菜单。希望这篇文章能帮到你,也期待你在评论区分享你的想法和改进建议!
记住,代码的世界里,分享即是成长! 😄