vue搭建一个树形菜单项目

首先搭建项目需要先通过步骤搭建一个vue的项目,然后创建一个component文件,里面新建一个index.vue页面来。

这是引入的element-ui组件库里的组件,来实现我的路由,渲染的是我存储的动态路由,所以需要先安装并且引用。

复制代码
npm install element-plus @element-plus/icons-vue

这是菜单管理的

复制代码
<template>
  <div class="sidebar-container">
    <!-- 折叠控制 -->
    <div class="collapse-control" style="background-color:#293246" @click="toggleCollapse">
      <el-icon :size="20" :color="isCollapse ? '#fff' : '#ffd04b'">
        <component :is="isCollapse ? Expand : Fold" />
      </el-icon>
    </div>
    <!-- 导航菜单 -->
    <div>
      <el-menu router :default-active="$route.path" background-color="#293246" text-color="#fff" style="height: 100vh"
        active-text-color="#ffd04b" :collapse="isCollapse">
        <template v-for="menu in menuArray" :key="menu.path">
          <!-- 有子级的多层菜单项 -->
          <el-sub-menu v-if="menu.children?.length" index="/layout">
            <template #title>
              <span>{{ menu.meta.title }}</span>
            </template>
            <el-menu-item v-for="sub in menu.children" :key="'/layout' + sub.path" :index="'/layout' + sub.path">
              <span>{{ sub.title }}</span>
            </el-menu-item>
          </el-sub-menu>
          <!-- 没子级的单层菜单项 -->
          <el-menu-item v-else :index="menu.path == '/home' ? menu.path : menu.path">
            <span>{{ menu.meta.title }}</span>
          </el-menu-item>
        </template>
      </el-menu>
    </div>
  </div>
</template>
<script setup>
import { ref } from 'vue'
import { Expand, Fold } from '@element-plus/icons-vue'
const isCollapse = ref(false)
const toggleCollapse = () => {
  isCollapse.value = !isCollapse.value
}
const menuArray = ref([]);
render();//初始化渲染
function render() {
  try {
    const menuList = JSON.parse(sessionStorage.getItem('menuPath') || '[]');
    const menuName = JSON.parse(sessionStorage.getItem('menuName') || '[]');
    console.log(menuList, menuName);
    if (!Array.isArray(menuList) || !Array.isArray(menuName)) {
      throw new Error('存储数据格式不正确');
    }
    const nameMap = new Map(menuName.map(item => [item.name, item]));
    menuArray.value = menuList
      .filter(item => item?.name && nameMap.has(item.name))
      .map(item => ({
        ...item,
        ...nameMap.get(item.name)
      }));
    console.log('安全筛选结果:', menuArray.value);
  } catch (error) {
    console.error('数据处理失败:', error);
    // 可以在这里设置默认值或进行错误上报
    return [];
  }
}
</script>
<style scoped>
.sidebar-container {
  transition: width 0.3s;
}

.collapse-control {
  padding: 15px;
  cursor: pointer;
  border-bottom: 1px solid #1f2d3d;
}

.el-menu--collapse {
  width: 64px;
}

.el-menu-vertical-demo:not(.el-menu--collapse) {
  width: 200px;
}
</style>

组件面包屑

复制代码
<template>
  <!-- <div style="margin-bottom: 20px;width: 100%;">
    <el-button size="small" @click="addTab(editableTabsValue)">
      添加标签页
    </el-button>
  </div> -->
  <el-tabs v-model="editableTabsValue" @tab-click="tabBread" type="card" @tab-remove="removeTab">
    <el-tab-pane v-for="item in editableTabs" :key="item.name" :label="item.title"
      :name="item.name" :closable="item.name !== '/home'">
      <!-- 主页面 -->
      <el-main>
        <router-view />
      </el-main>
    </el-tab-pane>
  </el-tabs>
</template>

<script setup>
import { ref, reactive, watch } from 'vue';
import { useRoute, useRouter } from 'vue-router';

const route = useRoute();
const router = useRouter();

const editableTabsValue = ref(route.path);
const editableTabs = reactive([
  { title: '首页', name: '/home' }
]);

// 监听路由变化,同步标签页
watch(
  () => route.path,
  (newPath) => {
    editableTabsValue.value = newPath;
    if (!editableTabs.some(tab => tab.name === newPath)) {
      const title = getTitleFromPath(newPath);
      editableTabs.push({ title, name: newPath });
    }
  },
  { immediate: true }
);

function getTitleFromPath(path) {
  const titleMap = {
    '/home': '首页',
    '/layout/user/list': '用户列表',
    '/layout/user/role': '角色列表',
    // 扩展其他路由...
  };
  return titleMap[path] || '新标签';
}
// 点击面包屑标签跳转路由
const tabBread = () => {
  console.log(editableTabsValue.value);
  router.push(editableTabsValue.value);
}
const removeTab = (targetName) => {
  const tabs = editableTabs;
  let activeName = editableTabsValue.value;
  if (activeName === targetName) {
    const currentIndex = tabs.findIndex(tab => tab.name === targetName);
    const nextTab = tabs[currentIndex + 1] || tabs[currentIndex - 1];
    activeName = nextTab?.name || '/home';
    router.push(activeName);
  }
  editableTabsValue.value = activeName;
  editableTabs.splice(0, editableTabs.length, ...tabs.filter(tab => tab.name !== targetName));
};
</script>

<style scoped>
/* 可以添加自定义样式 */
.el-tabs {
  margin: 20px;
}
</style>

这部分代码是用来布局菜单框架结构的,然后我们在路由部分引入这个文件的路由即可。

复制代码
<!-- src/layouts/MainLayout.vue -->
<template>
    <div class="app">
        <el-container style="height: 100vh;">
            <!-- 左侧导航栏 -->
            <el-aside width="200px">
                <aside-nav />
            </el-aside>
            <div class="menu">
                <!-- 顶部菜单 -->
                <MyHeader />
            </div>
        </el-container>
    </div>
</template>

<script>
import MyHeader from '@/Layout/topHeader.vue'
import AsideNav from '@/components/MenuItem.vue'

export default {
    components: {
        MyHeader,
        AsideNav
    }
}
</script>

<style>
html,
body {
    padding: 0;
    margin: 0;
}

.menu {
    width: 100%;
}
</style>
相关推荐
阿镇吃橙子1 分钟前
一些手写及业务场景处理问题汇总
前端·算法·面试
庸俗今天不摸鱼2 分钟前
【万字总结】前端全方位性能优化指南(九)——FSP(First Screen Paint)像素级分析、RUM+合成监控、Lighthouse CI
前端·性能优化
逆袭的小黄鸭2 分钟前
JavaScript:作用域与作用域链的底层逻辑
前端·javascript·面试
用户26124583401612 分钟前
vue2实现滚动条自动滚动
前端
FanetheDivine6 分钟前
实现"选中表格项将元素加入集合"的动画效果
javascript·vue.js
前端Hardy7 分钟前
HTML&CSS:超好看的轮播图,你绝对用得上(建议收藏)
javascript·css·html
傻球10 分钟前
Jotai 使用详解:React 轻量级状态管理库
前端·react.js
Linhieng11 分钟前
JS 解析 png 图片的分辨率(宽高)
javascript
Json_12 分钟前
Vue 构造器 Vue.extend
前端·vue.js·深度学习
伶俜monster13 分钟前
UV 法向量实验室:Threejs 纹理与光照炼金术
前端·webgl·three.js