我们将基于之前的 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,我们实现了一个功能强大的布局组件,支持顶部菜单和左侧菜单。希望这篇文章能帮到你,也期待你在评论区分享你的想法和改进建议!
记住,代码的世界里,分享即是成长! 😄