当前位置:首页 » 《我的小黑屋》 » 正文

Vue3 Element-plus el-menu无限级菜单组件封装

27 人参与  2024年04月09日 08:01  分类 : 《我的小黑屋》  评论

点击全文阅读


对于element中提供给我们的el-menu组件最多可以实现三层嵌套,如果多一层数据只能自己通过变量去加一层,如果加了两层、三层这种往往是行不通的,所以只能进行封装

效果图

 

 一、定义数据

MenuData.ts

export default [    {        id: "1",        name: "第一级菜单",        level: '1',        child: [            {                id: "11",                name: "第二级菜单",                level: '1-1',                child: [                    {                        id: "111",                        name: "第三级菜单",                        level: '1-1-1',                        child: [                            {                                id: "1111",                                name: "第四级菜单",                                level: '1-1-1-1',                                child: [                                    {                                        id: "11111",                                        name: "第五级菜单",                                        level: '1-1-1-1-1',                                        child: []                                    }                                ]                            }                        ]                    }]            }        ]    },    {        id: "2",        name: "第一级同级菜单",        level: '2',        child: []    }]

二、封装组件 

封装思想:

 1.对本身组件进行循环使用,如果有子集使用本身组件 把child数据传给自己

 2.如果没有子集 使用 el-menu-item

以下代码对setup( )函数和setup语法糖分别做了实现 

setup语法糖

<template>  <el-menu    :default-active="defaultActive"    :unique-opened="true"    class="el-menu-vertical-demo"  >    <template v-for="item in menu">      <!-- 如果有子集 -->      <template v-if="item.child && item.child.length > 0">        <el-sub-menu          :key="item.id"          :index="item.level"          :disabled="item.meta?.disabled"          :popper-append-to-body="false"        >          <template #title>            <i :class="[item.meta?.icon]"></i>            <!-- 添加空格 表示下级-->            <span> {{ generateSpaces(item.level) }} </span>            <span slot="title"> {{ item.name }}</span>          </template>          <MenuTree            :menu="item.child"            :defaultActive="defaultActive"            @clickItem="clickItemHandle"          />        </el-sub-menu>      </template>      <!-- 如果没有子集 -->      <template v-else>        <el-menu-item          :key="item.id"          :index="item.level"          :disabled="item.meta?.disabled"          :popper-append-to-body="false"          @click="clickItemHandle(item)"        >          <i :class="[item.meta?.icon]"></i>          <!-- 添加空格 表示下级-->          <span> {{ generateSpaces(item.level) }} </span>          <span slot="title">{{ item.name }}</span>        </el-menu-item>      </template>    </template>  </el-menu></template><script lang="ts" name="MenuTree" setup>// 把下面代码变成setup语法糖的形式 import type { PropType } from "vue";import type { MenuItem } from "@/types/lesson";// type 为了方便写成这样 可以根据自己项目设定type defineProps({  menu: {    type: Array as unknown as PropType<any[]>,    required: true,    default: () => [],  },  defaultActive: {    type: String as unknown as PropType<string>,    required: true,    default: [],  },});const emit = defineEmits(["update-active-path", "clickItem"]);// 返回的空格字符串 用于显示菜单层级 const generateSpaces = (level: string) => {  let str = "";  level.split("")  .filter((it) => it != "-") .forEach(() => {      str += " ";  });  return str;};// 点击当前菜单项const clickItemHandle = (item: MenuItem) => {  emit("clickItem", item);};</script><style scoped lang="less">.el-menu {  width: 288px;}</style>

setup函数

<template>  <el-menu :default-active="defaultActive"  :unique-opened="true"   class="el-menu-vertical-demo"  >    <template v-for="item in menu">        <template v-if="item.child && item.child.length > 0">          <el-sub-menu             :key="item.id"            :index="item.level"            :disabled="item.meta?.disabled"            :popper-append-to-body="false"          >            <template #title>              <i :class="[item.meta?.icon]"></i>              <!-- 添加空格 表示下级-->              <span> {{ generateSpaces(item.level) }} </span>              <span slot="title"> {{ item.name }}</span>            </template>            <MenuTree :menu="item.child" :defaultActive="defaultActive"  @clickItem="clickItemHandle"  />          </el-sub-menu>        </template>        <template v-else>          <el-menu-item             :key="item.id"            :index="item.level"            :disabled="item.meta?.disabled"            :popper-append-to-body="false"            @click="clickItemHandle(item)"          >            <i :class="[item.meta?.icon]"></i>            <!-- 添加空格 表示下级-->            <span> {{ generateSpaces(item.level) }} </span>            <span slot="title">{{ item.name }}</span>          </el-menu-item>        </template>      </template>  </el-menu></template><script lang="ts">import { defineComponent, toRefs } from 'vue';import type { PropType } from 'vue'import type {MenuItem} from '@/types/lesson'export default defineComponent({  name: 'MenuTree',  props: {    menu: {      type: Array as unknown as PropType<any[]>,      required: true,      default: () => [],    },    defaultActive: {      type: String as unknown as PropType<string>,      required: true,      default: '',    },  },  emits: ['update-active-path','clickItem'],  setup(props, context) {    const { menu, defaultActive } = toRefs(props);    const generateSpaces = (level:string) => {      let str = ''      level.split('').filter(it=>it!='-').forEach(() => {        str += ' '      })      return str    }    const clickItemHandle = (item:MenuItem) => {      context.emit('clickItem', item)    }       return {      clickItemHandle,      menu,      defaultActive,      generateSpaces,    }  },});</script><style scoped lang="less">  .el-menu {    width: 288px;  }</style>

 type就不补充了 可根据自己项目定义,可临时改成any

三、使用组件

<template> <MenuTree        :menu="menuList"        :defaultActive="defaultActive"        @clickItem="handleMenuClick"        :update-click="handleMenuClick"      /></template><script setup lang="ts">import MenuTree from "./components/MenuTree.vue";import type {MenuItem} from '@/types/lesson'import menuData from './MenuData'const defaultActive = ref<string>(''); // "1-1-1-1" 默认选中的数据const menuList = ref(menuData)const handleMenuClick = (item:MenuItem) => {  console.log('父组件',item);};</script>

补充default-active变量,如果一开始想默认点开第一层的数据 就需要找规律啦

拿到所有的level,通过接口方式返给你 自己平铺拿到所有的level也好 

例如数据格式:

let arr = [  "1-1",  "1-1-1",  "1-1-1-1",  "1-1-1-2",  "1-1-1-3",  "1-1-1-4",  "1-1-1-5",  "1-1-1-6",  "1-1-2",  "1-1-2-1"]

 想要的结果就是 最长且相同数字最多的元素 1-1-1-1

arr.sort((a,b)=> b.split('-').length - a.split('-').length)[0]

使用split防止有些字符串是10、11 两位数字的


点击全文阅读


本文链接:http://zhangshiyu.com/post/92524.html

<< 上一篇 下一篇 >>

  • 评论(0)
  • 赞助本站

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

关于我们 | 我要投稿 | 免责申明

Copyright © 2020-2022 ZhangShiYu.com Rights Reserved.豫ICP备2022013469号-1