🔥 一招搞定!Vue 3 通用布局 + 菜单组件:让你的项目颜值与效率齐飞!🚀

922 阅读3分钟

我们将基于之前的 Layout.vue 组件,扩展出一个包含顶部菜单和左侧菜单的布局。顶部菜单分为左、中、右三部分,左侧是纯文字 Logo(“Python私教”),中间是常用菜单(如首页、关于等),右侧是注册、登录等按钮。左侧菜单则是一个多级菜单,支持展开和折叠。

最终效果图: 在这里插入图片描述


实现效果

  1. 顶部菜单

    • 左侧:文字 Logo(“Python私教”)。
    • 中间:常用菜单(首页、关于等)。
    • 右侧:注册、登录等按钮。
  2. 左侧菜单

    • 支持多级菜单。
    • 支持展开和折叠。
  3. 布局结构

    • 顶部菜单固定在页面顶部。
    • 左侧菜单固定在页面左侧。
    • 主体内容在右侧,可以滚动。

实现代码

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: {
    typeString,
    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-height100vh;
}

.header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding0 20px;
  background-color#007bff;
  color: white;
  height60px;
  box-shadow0 2px 4px rgba(0000.1);
}

.header-left .logo {
  font-size20px;
  font-weight: bold;
}

.header-center .top-menu {
  display: flex;
  list-style: none;
  margin0;
  padding0;
}

.top-menu-item {
  margin0 15px;
  cursor: pointer;
}

.top-menu-item:hover {
  text-decoration: underline;
}

.header-right .btn {
  margin-left10px;
  padding5px 10px;
  border: none;
  border-radius4px;
  background-color: white;
  color#007bff;
  cursor: pointer;
}

.header-right .btn:hover {
  background-color#f0f0f0;
}

.main-container {
  display: flex;
  flex1;
}

.sidebar {
  width250px;
  background-color#f8f9fa;
  border-right1px solid #ddd;
  overflow-y: auto;
}

.content {
  flex1;
  padding20px;
  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: {
    typeArray,
    requiredtrue,
  },
});

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;
  padding0;
  margin0;
}

.menu-item {
  margin8px 0;
}

.menu-label {
  display: flex;
  align-items: center;
  padding8px 16px;
  cursor: pointer;
  transition: background-color 0.3s ease;
}

.menu-label:hover {
  background-colorrgba(0000.1);
}

.menu-icon {
  margin-right8px;
}

.menu-text {
  flex1;
}

.menu-arrow {
  margin-left8px;
}

.sub-menu {
  list-style: none;
  padding-left24px;
  margin0;
}

.sub-menu-item {
  padding8px 16px;
  cursor: pointer;
  transition: background-color 0.3s ease;
}

.sub-menu-item:hover {
  background-colorrgba(0000.1);
}
</style>

使用示例

<template>
  <Layout theme="light">
    <div>这里是主体内容</div>
  </Layout>
</template>

<script setup>
import Layout from './Layout.vue';
</script>

加入我们的学习社区,一起成长!

如果你觉得这个组件很棒,欢迎私信我,加入我们的学习社区!在这里,你可以:

  • 学习更多前端开发技巧。
  • 参与开源项目,提升实战能力。
  • 和志同道合的开发者一起交流。

私信我,获取更多实用教程和资源!
👉 发送私信【通用菜单组件】,我会分享完整代码和更多学习资料!
👉 也可以私信我【加入社区】,拉你进我们的开发者交流群,一起探讨技术!


结语

通过结合 Layout.vueMenu.vue,我们实现了一个功能强大的布局组件,支持顶部菜单和左侧菜单。希望这篇文章能帮到你,也期待你在评论区分享你的想法和改进建议!

记住,代码的世界里,分享即是成长! 😄