vue2自定义菜单循环嵌套

这篇文章展示了如何在Vue.js应用中构建一个树形视图组件,该组件用于展示具有多级子菜单的内容分类。用户点击节点时,通过自定义事件更新分类ID,并利用Vue的路由功能跳转到相应的内容详情页。同时,组件实现了折叠与展开的交互效果,以及子菜单的递归渲染。
摘要由CSDN通过智能技术生成

父:

<template>
  <div class="home">
    <div class="all" @click="getChildId(null)">全部分类</div>
    <div class="side-bar">
      <Tree-view   v-on:childid="getChildId"></Tree-view>
    </div>
    <div class="continer">
      <router-view></router-view>
    </div>
  </div>
</template>

<script>
import TreeView from "@/components/TreeView";

export default {
  name: "Classification",
  components: {
    TreeView
  },
  props: ['classificationId','childid'],
  data() {
    return {
    };
  },
  methods: {

    getChildId(id){
       this.$emit('update:classificationId', id)
    }
  }
};
</script>
<style scoped>
.side-bar {
  background-color: #f2ffff;
  width: 250px;
  height: 90%;
  overflow-y: auto;
  overflow-x: hidden;
  font-size: 14px;
  position: absolute;
  top: 60;
  left: 0;
  line-height: 25px;
}

.continer {
  padding-left: 320px;
}
.all{
  height:30px;
  line-height: 25px;
  background-color: #5f8f8f;
  color: beige;
  letter-spacing: 1px;
  font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
</style>

子:

<template>
  <div class="tree-view-menu">
    <Tree-view-item :menus='menus'   v-bind="$attrs" v-on="$listeners"></Tree-view-item>
  </div>
</template>
<script>
import TreeViewItem from "@/components/TreeViewItem";
import {
  blogClassificationTreeSelect
} from "@/api/blog/reception/classification";
const menusData = [];
export default {
  components: {
    TreeViewItem
  },
  name: "TreeViewMenu",
  data() {
    return {
      // menus: this.$store.state.menusModule.menus
      menus : [
        {id: 1, level: 1, name: '快速上手', type: "link", url: "/detail/quickstart"},
        {
          id: 2,
          level: 1,
          name: '教程',
          type: "button",
          isExpanded: false,
          isSelected: false,
          subMenu: [
            {id: 21, level: 2, name: '简介', type: "link", url: "/detail/tutorial"},
            {id: 22, level: 2, name: '英雄编辑器', type: "link", url: "/detail/toh-pt1"},
            {id: 23, level: 2, name: '主从结构', type: "link", url: "/detail/toh-pt2"},
            {id: 24, level: 2, name: '多个组件', type: "link", url: "/detail/toh-pt3"},
            {id: 25, level: 2, name: '服务', type: "link", url: "/detail/toh-pt4"},
            {id: 26, level: 2, name: '路由', type: "link", url: "/detail/toh-pt5"},
            {id: 27, level: 2, name: 'HTTP', type: "link", url: "/detail/toh-pt6"},
          ]
        },
        {
          id: 3,
          level: 1,
          name: '核心知识',
          type: "button",
          isExpanded: false,
          isSelected: false,
          subMenu: [
            {id: 31, level: 2, name: '架构', type: "link", url: "/detail/architecture"},
            {
              id: 32,
              level: 2,
              name: '模板与数据绑定',
              type: "button",
              isExpanded: false,
              isSelected: false,
              subMenu: [
                {id: 321, level: 3, name: '显示数据', type: "link", url: "/detail/displaying-data"},
                {id: 322, level: 3, name: '模板语法', type: "link", url: "/detail/template-syntax"},
                {id: 323, level: 3, name: '生命周期钩子', type: "link", url: "/detail/lifecycle-hooks"},
                {id: 324, level: 3, name: '组件交互', type: "link", url: "/detail/component-interaction"},
                {id: 325, level: 3, name: '组件样式', type: "link", url: "/detail/component-styles"},
                {id: 326, level: 3, name: '动态组件', type: "link", url: "/detail/dynamic-component-loader"},
                {id: 327, level: 3, name: '属性型指令', type: "link", url: "/detail/attribute-directives"},
                {id: 328, level: 3, name: '结构型指令', type: "link", url: "/detail/structural-directives"},
                {id: 329, level: 3, name: '管道', type: "link", url: "/detail/pipes"},
                {id: 3210, level: 3, name: '动画', type: "link", url: "/detail/animations"},
              ]
            },
            {
              id: 33,
              level: 2,
              name: '表单',
              type: "button",
              isExpanded: false,
              isSelected: false,
              subMenu: [
                {name: '用户输入', type: "link", url: "/detail/user-input"},
                {name: '模板驱动表单', type: "link", url: "/detail/forms"},
                {name: '表单验证', type: "link", url: "/detail/form-validation"},
                {name: '响应式表单', type: "link", url: "/detail/reactive-forms"},
                {name: '动态表单', type: "link", url: "/detail/dynamic-form"}
              ]
            },
            {id: 34, level: 2, name: '引用启动', type: "link", url: "/detail/bootstrapping"},
            {
              id: 35,
              level: 2,
              name: 'NgModules',
              type: "button",
              isExpanded: false,
              isSelected: false,
              subMenu: [
                {id: 341, level: 3, name: 'NgModule', type: "link", url: "/detail/ngmodule"},
                {id: 342, level: 3, name: 'NgModule 常见问题', type: "link", url: "/detail/ngmodule-faq"}
              ]
            },
            {
              id: 36,
              level: 2,
              name: '依赖注入',
              type: "button",
              isExpanded: false,
              isSelected: false,
              subMenu: [
                {id: 361, level: 3, name: '依赖注入', type: "link", url: "/detail/dependency-injection"},
                {id: 362, level: 3, name: '多级注入器', type: "link", url: "/detail/hierarchical-dependency-injection"},
                {id: 363, level: 3, name: 'DI 实例技巧', type: "link", url: "/detail/dependency-injection-in-action"}
              ]
            },
            {id: 37, level: 2, name: 'HttpClient', type: "link", url: "/detail/http"},
            {id: 38, level: 2, name: '路由与导航', type: "link", url: "/detail/router"},
            {id: 39, level: 2, name: '测试', type: "link", url: "/detail/testing"},
            {id: 310, level: 2, name: '速查表', type: "link", url: "/detail/cheatsheet"},
          ]
        },
        {
          id: 4,
          level: 1,
          name: '其它技术',
          type: "button",
          isExpanded: false,
          isSelected: false,
          subMenu: [
            {id: 41, level: 2, name: '国际化(i18n)', type: "link", url: "/detail/i18n"},
            {id: 42, level: 2, name: '语言服务', type: "link", url: "/detail/language-service"},
            {id: 43, level: 2, name: '安全', type: "link", url: "/detail/security"},
            {
              id: 44,
              level: 2,
              name: '环境设置与部署',
              type: "button",
              isExpanded: false,
              isSelected: false,
              subMenu: [
                {id: 441, level: 3, name: '搭建本地开发环境', type: "link", url: "/detail/setup"},
                {id: 442, level: 3, name: '搭建方式剖析', type: "link", url: "/detail/setup-systemjs-anatomy"},
                {id: 443, level: 3, name: '浏览器支持', type: "link", url: "/detail/browser-support"},
                {id: 444, level: 3, name: 'npm 包', type: "link", url: "/detail/npm-packages"},
                {id: 445, level: 3, name: 'TypeScript 配置', type: "link", url: "/detail/typescript-configuration"},
                {id: 446, level: 3, name: '预 (AoT) 编译器', type: "link", url: "/detail/aot-compiler"},
                {id: 447, level: 3, name: '预 (AoT) 编译器', type: "link", url: "/detail/metadata"},
                {id: 448, level: 3, name: '部署', type: "link", url: "/detail/deployment"}
              ]
            },
            {
              id: 45,
              level: 2,
              name: '升级',
              type: "button",
              isExpanded: false,
              isSelected: false,
              subMenu: [
                {id: 451, level: 3, name: '从 AngularJS 升级', type: "link", url: "/detail/upgrade"},
                {id: 452, level: 3, name: '升级速查表', type: "link", url: "/detail/ajs-quick-reference"}
              ]
            },
            {id: 46, level: 2, name: 'Visual Studio 2015 快速上手', type: "link", url: "/detail/visual-studio-2015"},
            {id: 47, level: 2, name: '风格指南', type: "link", url: "/detail/styleguide"},
            {id: 48, level: 2, name: '词汇表', type: "link", url: "/detail/glossary"}
          ]
        },
        {id: 5, level: 1, name: 'API 参考手册', type: "link", url: "/detail/api"}
      ]
    };
  },
  created() {
   
  },
  methods: {
    
  }

};
</script>
<style scoped>
.tree-view-menu {
  width: 250px;
  height: 100%;
  overflow-y: auto;
  overflow-x: hidden;
}

.tree-view-menu::-webkit-scrollbar {
  height: 6px;
  width: 6px;
}

.tree-view-menu::-webkit-scrollbar-trac {
  -webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3);
  box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3);
}

.tree-view-menu::-webkit-scrollbar-thumb {
  background-color: #6e6e6e;
  outline: 1px solid #333;
}

.tree-view-menu::-webkit-scrollbar {
  height: 4px;
  width: 4px;
}

.tree-view-menu::-webkit-scrollbar-track {
  -webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3);
  box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3);
}

.tree-view-menu::-webkit-scrollbar-thumb {
  background-color: #6e6e6e;
  outline: 1px solid #708090;
}
</style>

子子:

<template>
  <div class="tree-view-item">
    <div class="level" :class="'level-'+ menu.level" v-for="menu in menus" :key="menu.id">
      <div v-if="menu.type === 'link'">
        <span class="link"  @click="toggle(menu)">{{ menu.name }}</span>
        <!-- <router-link class="link" v-bind:to="menu.url" @click.native="toggle(menu)">{{ menu.name }}</router-link> -->
      </div>
      <div v-if="menu.type === 'button'">
        <div class="button heading" :class="{selected: menu.isSelected,expand:menu.isExpanded}" @click="toggle(menu)">
          {{ menu.name }}
          <div class="icon">
            <svg xmlns="http://www.w3.org/2000/svg" focusable="false" viewBox="0 0 24 24">
              <path d="M8.59 16.34l4.58-4.59-4.58-4.59L10 5.75l6 6-6 6z "></path>
            </svg>
          </div>
        </div>
        <transition name="fade">
          <div class="heading-children" v-show="menu.isExpanded" v-if="menu.children">
            <Tree-view-item :menus='menu.children'  v-bind="$attrs" v-on="$listeners"></Tree-view-item>
          </div>
        </transition>
      </div>
    </div>
  </div>
</template>
<script>
export default {
  name: "TreeViewItem",
  props: ["menus"],
  created() {
    this.$store.commit("firstInit", {url: this.$route.path});
  },
  methods: {
    toggle(menu) {
      this.$store.commit("findParents", {menu});
      this.$emit('childid', menu.id)
    }
  }
};
</script>
<style scoped>
a {
  text-decoration: none;
  color: #333;
}

.link,
.button {
  display: block;
  padding: 10px 15px;
  transition: background-color 0.2s ease-in-out 0s, color 0.3s ease-in-out 0.1s;
  -moz-user-select: none;
  -webkit-user-select: none;
  -ms-user-select: none;
  -khtml-user-select: none;
  user-select: none;
}

.button {
  position: relative;
}

.link:hover,
.button:hover {
  color: #1976d2;
  background-color: #eee;
  cursor: pointer;
}

.icon {
  position: absolute;
  right: 0;
  display: inline-block;
  height: 24px;
  width: 24px;
  fill: currentColor;
  transition: -webkit-transform 0.15s;
  transition: transform 0.15s;
  transition: transform 0.15s, -webkit-transform 0.15s;
  transition-timing-function: ease-in-out;
}

.heading-children {
  padding-left: 14px;
  overflow: hidden;
}

.expand {
  display: block;
}

.collapsed {
  display: none;
}

.expand .icon {
  -webkit-transform: rotate(90deg);
  transform: rotate(90deg);
}

.selected {
  color: #1976d2;
}

.fade-enter-active {
  transition: all 0.5s ease 0s;
}

.fade-enter {
  opacity: 0;
}

.fade-enter-to {
  opacity: 1;
}

.fade-leave-to {
  height: 0;
}
</style>

js:menusModule.js

let menus = [
  {id: 1, level: 1, name: '快速上手', type: "link", url: "/detail/quickstart"},
  {
    id: 2,
    level: 1,
    name: '教程',
    type: "button",
    isExpanded: false,
    isSelected: false,
    subMenu: [
      {id: 21, level: 2, name: '简介', type: "link", url: "/detail/tutorial"},
      {id: 22, level: 2, name: '英雄编辑器', type: "link", url: "/detail/toh-pt1"},
      {id: 23, level: 2, name: '主从结构', type: "link", url: "/detail/toh-pt2"},
      {id: 24, level: 2, name: '多个组件', type: "link", url: "/detail/toh-pt3"},
      {id: 25, level: 2, name: '服务', type: "link", url: "/detail/toh-pt4"},
      {id: 26, level: 2, name: '路由', type: "link", url: "/detail/toh-pt5"},
      {id: 27, level: 2, name: 'HTTP', type: "link", url: "/detail/toh-pt6"},
    ]
  },
  {
    id: 3,
    level: 1,
    name: '核心知识',
    type: "button",
    isExpanded: false,
    isSelected: false,
    subMenu: [
      {id: 31, level: 2, name: '架构', type: "link", url: "/detail/architecture"},
      {
        id: 32,
        level: 2,
        name: '模板与数据绑定',
        type: "button",
        isExpanded: false,
        isSelected: false,
        subMenu: [
          {id: 321, level: 3, name: '显示数据', type: "link", url: "/detail/displaying-data"},
          {id: 322, level: 3, name: '模板语法', type: "link", url: "/detail/template-syntax"},
          {id: 323, level: 3, name: '生命周期钩子', type: "link", url: "/detail/lifecycle-hooks"},
          {id: 324, level: 3, name: '组件交互', type: "link", url: "/detail/component-interaction"},
          {id: 325, level: 3, name: '组件样式', type: "link", url: "/detail/component-styles"},
          {id: 326, level: 3, name: '动态组件', type: "link", url: "/detail/dynamic-component-loader"},
          {id: 327, level: 3, name: '属性型指令', type: "link", url: "/detail/attribute-directives"},
          {id: 328, level: 3, name: '结构型指令', type: "link", url: "/detail/structural-directives"},
          {id: 329, level: 3, name: '管道', type: "link", url: "/detail/pipes"},
          {id: 3210, level: 3, name: '动画', type: "link", url: "/detail/animations"},
        ]
      },
      {
        id: 33,
        level: 2,
        name: '表单',
        type: "button",
        isExpanded: false,
        isSelected: false,
        subMenu: [
          {name: '用户输入', type: "link", url: "/detail/user-input"},
          {name: '模板驱动表单', type: "link", url: "/detail/forms"},
          {name: '表单验证', type: "link", url: "/detail/form-validation"},
          {name: '响应式表单', type: "link", url: "/detail/reactive-forms"},
          {name: '动态表单', type: "link", url: "/detail/dynamic-form"}
        ]
      },
      {id: 34, level: 2, name: '引用启动', type: "link", url: "/detail/bootstrapping"},
      {
        id: 35,
        level: 2,
        name: 'NgModules',
        type: "button",
        isExpanded: false,
        isSelected: false,
        subMenu: [
          {id: 341, level: 3, name: 'NgModule', type: "link", url: "/detail/ngmodule"},
          {id: 342, level: 3, name: 'NgModule 常见问题', type: "link", url: "/detail/ngmodule-faq"}
        ]
      },
      {
        id: 36,
        level: 2,
        name: '依赖注入',
        type: "button",
        isExpanded: false,
        isSelected: false,
        subMenu: [
          {id: 361, level: 3, name: '依赖注入', type: "link", url: "/detail/dependency-injection"},
          {id: 362, level: 3, name: '多级注入器', type: "link", url: "/detail/hierarchical-dependency-injection"},
          {id: 363, level: 3, name: 'DI 实例技巧', type: "link", url: "/detail/dependency-injection-in-action"}
        ]
      },
      {id: 37, level: 2, name: 'HttpClient', type: "link", url: "/detail/http"},
      {id: 38, level: 2, name: '路由与导航', type: "link", url: "/detail/router"},
      {id: 39, level: 2, name: '测试', type: "link", url: "/detail/testing"},
      {id: 310, level: 2, name: '速查表', type: "link", url: "/detail/cheatsheet"},
    ]
  },
  {
    id: 4,
    level: 1,
    name: '其它技术',
    type: "button",
    isExpanded: false,
    isSelected: false,
    subMenu: [
      {id: 41, level: 2, name: '国际化(i18n)', type: "link", url: "/detail/i18n"},
      {id: 42, level: 2, name: '语言服务', type: "link", url: "/detail/language-service"},
      {id: 43, level: 2, name: '安全', type: "link", url: "/detail/security"},
      {
        id: 44,
        level: 2,
        name: '环境设置与部署',
        type: "button",
        isExpanded: false,
        isSelected: false,
        subMenu: [
          {id: 441, level: 3, name: '搭建本地开发环境', type: "link", url: "/detail/setup"},
          {id: 442, level: 3, name: '搭建方式剖析', type: "link", url: "/detail/setup-systemjs-anatomy"},
          {id: 443, level: 3, name: '浏览器支持', type: "link", url: "/detail/browser-support"},
          {id: 444, level: 3, name: 'npm 包', type: "link", url: "/detail/npm-packages"},
          {id: 445, level: 3, name: 'TypeScript 配置', type: "link", url: "/detail/typescript-configuration"},
          {id: 446, level: 3, name: '预 (AoT) 编译器', type: "link", url: "/detail/aot-compiler"},
          {id: 447, level: 3, name: '预 (AoT) 编译器', type: "link", url: "/detail/metadata"},
          {id: 448, level: 3, name: '部署', type: "link", url: "/detail/deployment"}
        ]
      },
      {
        id: 45,
        level: 2,
        name: '升级',
        type: "button",
        isExpanded: false,
        isSelected: false,
        subMenu: [
          {id: 451, level: 3, name: '从 AngularJS 升级', type: "link", url: "/detail/upgrade"},
          {id: 452, level: 3, name: '升级速查表', type: "link", url: "/detail/ajs-quick-reference"}
        ]
      },
      {id: 46, level: 2, name: 'Visual Studio 2015 快速上手', type: "link", url: "/detail/visual-studio-2015"},
      {id: 47, level: 2, name: '风格指南', type: "link", url: "/detail/styleguide"},
      {id: 48, level: 2, name: '词汇表', type: "link", url: "/detail/glossary"}
    ]
  },
  {id: 5, level: 1, name: 'API 参考手册', type: "link", url: "/detail/api"}
];
let levelNum = 1;
let startExpand = []; // 保存刷新后当前要展开的菜单项
function setExpand(source, url) {
  let sourceItem = '';
  for (let i = 0; i < source.length; i++) {
    sourceItem = JSON.stringify(source[i]); // 把菜单项转为字符串
    if (sourceItem.indexOf(url) > -1) { // 查找当前 URL 所对应的子菜单属于哪一个祖先菜单
      if (source[i].type === 'button') { // 导航菜单为按钮
        source[i].isSelected = true; // 设置选中高亮
        source[i].isExpanded = true; // 设置为展开
        startExpand.push(source[i]);
// 递归下一级菜单,以此类推
        setExpand(source[i].subMenu, url);
      }
      break;
    }
  }
}

const state = {
  menus,
  levelNum
};
const mutations = {
  findParents(state, payload) {
    if (payload.menu.type === "button") {
      payload.menu.isExpanded = !payload.menu.isExpanded;
    } else if (payload.menu.type === "link") {
      if (startExpand.length > 0) {
        for (let i = 0; i < startExpand.length; i++) {
          startExpand[i].isSelected = false;
        }
      }
      startExpand = []; // 清空展开菜单记录项
      setExpand(state.menus, payload.menu.url);
    }
    ;
  },
  firstInit(state, payload) {
    setExpand(state.menus, payload.url);
  }
}
export default {
  state,
  mutations
};

store.js:

import Vue from 'vue'
import Vuex from 'vuex'
import app from './modules/app'
import dict from './modules/dict'
import user from './modules/user'
import userBlog from './modules/userBlog'
import tagsView from './modules/tagsView'
import permission from './modules/permission'
import settings from './modules/settings'
import getters from './getters'
import menusModule from './modules/menusModule'

Vue.use(Vuex)

const store = new Vuex.Store({
  modules: {
    app,
    dict,
    user,
    userBlog,
    tagsView,
    permission,
    settings,
    menusModule
  },
  getters
})

export default store

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

飞流银河

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值