Vue3.0实现无限级菜单

业务需求

菜单项是业务系统的重要组成部分,一般业务系统都要支持显示多级业务菜单,但是根据每个业务人员的权责不同,看到的的菜单项也是不同的。

这就要求页面可以支持无限极菜单显示,根据每个用户的权限不同,后台服务返回对应的菜单项。

本文基于Vue 3.0实现了一个可配置的无限等级菜单,关键代码如下:

后端返回的菜单项数据结构

后端服务一般不会直接返回一个树型结构菜单集合给前端,这样做也不合理。前端应该根据自己的具体需求,构建自己的菜型单树。后端返回的数据结构一般包含以下一个字段:

  • Id 菜单ID, 数字类型
  • pId当前菜单的父级菜单ID, 数字类型
  • title 菜单的标题
  • link 菜单对应的链接
  • order 同级菜单的排列顺序,数字类型

其他业务字段需要具体问题具体分析,在这里不再赘述。本文不再讨论后端如何进行菜单项的权限控制,所使用的菜单内容,包括在一个JSON文件中,具体见附录。

菜单内容是一个足球数据管理系统,包括多级菜单:

  • 第一级菜单只有一项,是所有节点的祖先节点。
  • 第二级菜单包括联赛管理,俱乐部管理和球员管理
  • 第三级菜单包括二级菜单内容的CRUD。

关键代码

为了支持无限级菜单,本文所有关键算法全部基于递归实现。主要包括:

1.后端数据转换为树形结构
2.后端数据排序
3.基于菜单树形结构生成Vue的路由数据
4.菜单组件的递归调用

后端数据转为树形结构

dataToTree函数调用的实参是附录的JSON数据,该代码参考Vue 3.0的AST树转换的代码,具体思想是:

1.将集合的数据分为父节点和子节集合,最外层的父节点为pId为0的节点。
2.在子节点中找到当前父节点的直接子节点,将其从当前子节点集合剔除。
3.递归回到1,寻找子节点的子节点。
4.如果当前子节点不是任何节点的父节点,将该子节点放入父节点的children集合中。

在生成当前树型结构菜单数据后,可以将该数据保存在vuex中,作为公共数据便于其他模块使用。

function dataToTree(data) {
  const parents = data.filter((item) => item.pId === 0);
  const children = data.filter((item) => item.pId !== 0);
  toTree(parents, children);
  return parents;
  function toTree(parents, children) {
    for (var i = 0; i < parents.length; ++i) {
      for (var j = 0; j < children.length; ++j) {
        if (children[j].pId === parents[i].Id) {
          let _children = deepClone(children, []);
          toTree([children[j]], _children);
          if (parents[i].children) {
            parents[i].children.push(children[j]);
          } else {
            parents[i].children = [children[j]];
          }
        }
      }
    }
  }
}

function deepClone(source, target) {
  var _tar = target || {};
  let keys = Reflect.ownKeys(source);
  keys.map((key) => {
    if (typeof source[key] === "object") {
      _tar[key] =
        Object.prototype.toString.call(source[key]) === "[object Array]"
          ? []
          : {};
      deepClone(source[key], _tar[key]);
    } else {
      _tar[key] = source[key];
    }
  });
  return _tar;
}

菜单项排序

根据同级节点的order值进行排序,本文没有将该排序和上节的树型结构转换放在一起,主要是考虑有些系统可能不需要排序。如果需要,每次添加元素都要进行一次排序,效率低下,所以在获取树型结构后,再进行一次排序,具体排序函数如下:

function SortTree(tree) {
  tree = tree.sort((a, b) => a.order - b.order);
  tree.map((t) => {
    if (t.children) {
      t.children = SortTree(t.children);
    }
  });

  return tree;

采用最简单的递归方式,遍历当前树型集合,按照order字段的升序方式进行排序,如果当前节点有children项,递归排序。

基于菜单树形结构生成Vue的路由数据

在获取树型菜单后后,我们可以基于当前数据,生成该用户在App中要使用到的路由项,具体代码如下:

function TreeToRoutes(treeData, routes) {
  routes = routes || [];
  for (var i = 0; i < treeData.length; ++i) {
    routes[i] = {
      path: treeData[i].link,
      name: treeData[i].name,
      component: () => import(`@/views/${treeData[i].name}`),
    };
    if (treeData[i].children) {
      routes[i].children = TreeToRoutes(
        treeData[i].children,
        routes[i].children
      );
    }
  }
  return routes;
}

1.遍历树型菜单,将当前菜单项的link和tname复制到Vue路由数据的path和name上,component采用动态加载方式。
2.如果当前菜单项包含子节点children,递归调用,复制其子节点内容。

在main.js方法中,将菜单数据通过vuex进行读取,然后调用上述算法生成路由数据。将该数据直接加载到Vue的路由中,保证了如果当前用户没有某一个菜单的权限,即使通过URL进行访问,也是访问不到的,因为App只会为有权限的菜单项生成路由数据。如果用户没有某一个菜单的权限,也就不会从后端获取到该菜单的数据,也就不会为该菜单项生成路由。

菜单组件的递归调用

菜单组件代码如下:

<template>
  <div>
      <ul v-if="data.children && data.children.length > 0">
          <li><router-link :to="data.link">{{data.title}}</router-link></li> 
          <menu-item :data="item" :key="index"  v-for="(item,index) in data.children">
      </ul>
      <ul v-else>
          <li><router-link :to="data.link">{{data.title}}</router-link></li> 
      </ul>
  </div>
</template>

<script>
export default {
    name: "MenuItem",
    props:{
        data: Object
    }
}
</script>

如果当前菜单项包含子节点,则递归调用MenuItem组件自己

菜单组件调用的代码如下:

<template>
  <div>
     <menu-item :data="item" :key="index" v-for="(item,index) in data" />
  </div>
</template>

<script>
import MenuItem from './MenuItem'
export default {
    name: "Page",
    components:{
        MenuItem
    }
}
</script>

由于生成的菜单数据结构最外层是数据,所以MenuItem组件需要进行循环调用。

附录-菜单项数据

export default [
  {
    Id: 15,
    pId: 0,
    name: "all",
    title: "all",
    link: "/all",
    order: 2,
  },
  {
    Id: 1,
    pId: 15,
    name: "clubs",
    title: "Club Management",
    link: "/clubs",
    order: 2,
  },
  {
    Id: 2,
    pId: 15,
    name: "leagues",
    title: "League Management",
    link: "/leagues",
    order: 1,
  },
  {
    Id: 3,
    pId: 15,
    name: "players",
    title: "Player Management",
    link: "/players",
    order: 3,
  },
  {
    Id: 5,
    pId: 2,
    name: "LeagueDelete",
    title: "Delete League",
    link: "/leagues/delete",
    order: 3,
  },
  {
    Id: 6,
    pId: 2,
    name: "LeagueUpdate",
    title: "Update League",
    link: "/leagues/update",
    order: 2,
  },
  {
    Id: 7,
    pId: 2,
    name: "LeagueAdd",
    title: "Add League",
    link: "/leagues/add",
    order: 1,
  },
  {
    Id: 8,
    pId: 3,
    name: "PlayerAdd",
    title: "Add Player",
    link: "/players",
    order: 1,
  },
  {
    Id: 9,
    pId: 3,
    name: "PlayerUpdate",
    title: "Update Player",
    link: "/players",
    order: 3,
  },
  {
    Id: 10,
    pId: 3,
    name: "PlayerDelete",
    title: "Delete Player",
    link: "/players",
    order: 2,
  },
  {
    Id: 11,
    pId: 1,
    name: "ClubAdd",
    title: "Add Club",
    link: "/clubs/add",
    order: 3,
  },
  {
    Id: 12,
    pId: 1,
    name: "ClubUpdate",
    title: "Update Club",
    link: "/clubs/update",
    order: 1,
  },
  {
    Id: 13,
    pId: 1,
    name: "ClubDelete",
    title: "Delete Club",
    link: "/clubs/delete",
    order: 2,
  },
];

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • vue实现右键弹出菜单

    在最近工作中,有一个需求,需要做一个表格,并且对该表格右键弹出菜单支持增删改,这里做下总结,功能截图如下: 首先定义菜单结构 <!-- 菜单 --> <div class="menu-list" :style="{position:'fixed',top:top+'px',left:left+'px'}" v-if="visible">     <div class="menu" v-for=&q

  • Vue实现导航栏菜单

    本文实例为大家分享了Vue实现导航栏菜单的具体代码,供大家参考,具体内容如下 这里是刚学习vue的时候,没有用vue的任何UI组件库写的导航栏菜单. menu.html <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>导航栏左</title> <link rel="stylesheet" href="css/

  • vue Element左侧无限级菜单实现

    最近项目中,要用到element-ui的无限级分类菜单,根据角色生成不同的递归数据,查阅了网上很多资料,发现很多都不太完整并且没有很多的延伸性. 梳理递归数据 我们一般拿到后台的数据是:1.扁平化数据格式2.递归式数据格式 let arr = [ { name:小七, id:1 }, { name:小八, id:2 }, { name: 小九, id:3 } ] 我们从后台拿到的是这种扁平化处理数据,那我们实现递归菜单的话需要什么数据格式呢,需要格式如下: let menuItems = [ {

  • Vue.js组件tree实现无限级树形菜单

    分享一段用 <ul>和<li>标签实现tree的代码,可能写的不是很好,如果大家有更好的希望分享下. 代码看这里喽: html代码: <div class="tree"> <nav class='navbar'> <ul class='nav nav-stacked'> <template v-for='item in menus'> <li role='presentation' v-if='!item.c

  • Vue2组件tree实现无限级树形菜单

    一直打算偷懒使用个现成的树组件,但是在github上找了一大圈没有找到真正满足应用开发的树组件,所以没办法只能自己写了一个,开源出来希望可以帮助到需要的人,同时如果大家觉得好用,我可以顺带骗骗★(希望喜欢的朋友对我体力的肯定可以点下★ ),由于我也算刚接触vue,所以难免有所考虑不周的地方,希望大家在issue里面指正.组件重点是父子组件数据的共享和状态保持,我是利用了下vuex的思路,采用一个控制仓库完成. github 地址 vue-tree How to run demo npm inst

  • Vue.js 递归组件实现树形菜单(实例分享)

    最近看了 Vue.js 的递归组件,实现了一个最基本的树形菜单. 项目结构: main.js 作为入口,很简单: import Vue from 'vue' Vue.config.debug = true import main from './components/main.vue' new Vue({ el: '#app', render: h => h(main) }) 它引入了一个组件 main.vue: <template> <div class="tree-m

  • Vue+Element ui实现树形控件右键菜单

    本文实例为大家分享了Vue+Element ui实现树形控件右键菜单的具体代码,供大家参考,具体内容如下 需求 实现树形控件右键菜单功能,有添加文件.删除文件.重命名功能 一.按需引入ELEMENTUI组件 按需引入ELEMENTUI组件 二.实现菜单功能 1.TEMPLATE 代码如下(示例): <!-- 树形组件 -->   <el-tree               :data="data"               @node-contextmenu=&q

  • 基于vue.js实现侧边菜单栏

    侧边菜单栏应该是很多项目里必不可少的 自己手写了一个 下面是效果图 下面就说一下实现的过程 还是比较简单的 首先导入一下需要的文件 <link rel="stylesheet" type="text/css" href="bootstrap/css/bootstrap.min.css" rel="external nofollow" > <link rel="stylesheet" typ

  • vue-router+vuex addRoutes实现路由动态加载及菜单动态加载

    此案例主要实现了一个功能是,在vue实例首次运行时,在加载了login和404两个路由规则,登录成功后,根据登录用户角色权限获取该角色相应菜单权限,生成新的路由规则添加进去. 做过后台管理系统都一定做过这个功能,在对菜单权限进行粗粒度权限控制的时候,通过角色获取菜单后,异步生成菜单,所以一开始拿到需求的时候,我也以为这和平常的没什么不同,不过做起来就发现了很多问题, 1.vue-router的实例,在new vue实例的时候,就加载了,且必须加载,这个时候,登录路由一定要加载,可是这个时候没有登

  • Vue3.0实现无限级菜单

    业务需求 菜单项是业务系统的重要组成部分,一般业务系统都要支持显示多级业务菜单,但是根据每个业务人员的权责不同,看到的的菜单项也是不同的. 这就要求页面可以支持无限极菜单显示,根据每个用户的权限不同,后台服务返回对应的菜单项. 本文基于Vue 3.0实现了一个可配置的无限等级菜单,关键代码如下: 后端返回的菜单项数据结构 后端服务一般不会直接返回一个树型结构菜单集合给前端,这样做也不合理.前端应该根据自己的具体需求,构建自己的菜型单树.后端返回的数据结构一般包含以下一个字段: Id 菜单ID,

  • vue3.0实现下拉菜单的封装

    vue3.0出来已经有段时间的了,也与必要开始研究它了! 先看下我们要实现的效果 很常见的展开显示菜单项的内容,在vue3.0里面怎么开发,这里样式我们用的是bootstrap的默认样式 思路一: <DropDown :title="'退出'" :list="menuLists" /> 思路二: <drop-down :title="'退出'"> <drop-dowm-item>新建文章</drop-do

  • Vue3 封装 Element Plus Menu 无限级菜单组件功能的详细代码

    目录 1 数据结构定义 1.1 菜单项数据结构 1.2 菜单配置数据结构 2 使用 tsx 实现封装 2.1 tsx 基本结构 2.2 定义 prop 2.3 递归实现组件 3 使用 SFC 实现菜单封装 3.1 封装菜单项的渲染 3.2 封装菜单组件 4 测试组件 4.1 菜单测试数据 4.2 测试页面 4.3 运行效果 总结: 本文分别使用 SFC(模板方式)和 tsx 方式对 Element Plus el-menu 组件进行二次封装,实现配置化的菜单,有了配置化的菜单,后续便可以根据路由

  • jQuery 无限级菜单的简单实例

    我将CSS完全分离出来用jQuery附加式样,就是为了多级染色,并且生成目录树和控制式样也很容易,生成时也不需要考虑式样.数据表建议用事先Order排序的方式,不要读取数据的时候才分级排序,这样性能会较佳. 我把它做成了个.Net的控件,作为轻量级的无限目录树,还是相当好用的.只是还不完善,我先慢慢修改,等差不多了再发布出来. 复制代码 代码如下: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" &qu

  • 使用jquery菜单插件HoverTree仿京东无限级菜单

    效果图: 看了上面效果图,你或许已经明白为什么是仿京东菜单.如果还不明白,请访问http://list.jd.com/list.html?cat=9987,653,655 看看左上的菜单.当然这个HoverTree菜单的功能更加强大,可以实现无限级菜单. HTML代码: 复制代码 代码如下: <!DOCTYPE html> <html> <head> <title>HoverTree - jquery菜单插件</title> <base t

  • 基于Vue3.0开发轻量级手机端弹框组件V3Popup的场景分析

    之前有分享一个vue2.x移动端弹框组件,今天给大家带来的是Vue3实现自定义弹框组件. V3Popup 基于vue3.x实现的移动端弹出框组件,集合msg.alert.dialog.modal.actionSheet.toast等多种效果.支持20+种自定义参数配置,旨在通过极简的布局.精简的调用方式解决多样化的弹框场景. v3popup 在开发之初参考借鉴了Vant3.ElementPlus等组件化思想.并且功能效果和之前vue2.0保持一致. ◆ 快速引入 在main.js中全局引入v3p

  • vue3.0 项目搭建和使用流程

    最近在重构一个老项目,领导要求使用新的技术栈.好吧,是时候秀一波我新学的vue3.0了. 不多bb,开始我的表演...(以下只是我自己个人的理解和使用习惯,仅供参考哦) 一:项目搭建 1. 可以自己配置vite,但是为了节省时间,我就使用脚手架直接搭建.(有兴趣可以研究一下vite,还是很香的) 2. 项目生成:iTerm下: vue create myproject 之后根据自己的要求选择不同的配置 选择我们需要的3.x 之后按照要求配置一下router,已经pack.json ... 然后n

  • Vue3+TypeScript实现递归菜单组件的完整实例

    目录 前言 需求 实现 首次渲染 点击菜单项 样式区分 默认高亮 数据源变动引发的 bug 完整代码 App.vue 总结 前言 小伙伴们好久不见,最近刚入职新公司,需求排的很满,平常是实在没时间写文章了,更新频率会变得比较慢. 周末在家闲着无聊,突然小弟过来紧急求助,说是面试腾讯的时候,对方给了个 Vue 的递归菜单要求实现,回来找我复盘. 正好这周是小周,没想着出去玩,就在家写写代码吧,我看了一下需求,确实是比较复杂,需要利用好递归组件,正好趁着这 个机会总结一篇 Vue3 + TS 实现递

  • 如何在vue3.0+中使用tinymce及实现多图上传文件上传公式编辑功能

    相关文档 本文部分内容借鉴: https://www.cnblogs.com/zhongchao666/p/11142537.html tinymce中文文档: http://tinymce.ax-z.cn/ 安装tinymce 1.安装相关依赖 yarn add tinymce || npm install tinymce -S yarn add @tinymce/tinymce-vue || npm install @tinymce/tinymce-vue -S 2.汉化编辑器前往此地址下载

随机推荐