ant-design-vue 导航菜单实现3级及以上(函数式组件)

emmm…发现前面一篇我自己在没做过的情况下也看不懂((/ω\))。3级及以上导航菜单的实现说到底也不会很难,就是交互(菜单展开与否)比较烦人,在这里就稍微提一下需要注意的东西。推荐大家还是直接clone一下项目,查看log信息吧。 在线demo

按照官网信息,根据项目来进行一定的处理,最终的code如下:

import { generateTitle } from './breadcrumb/index.js'
import { Menu } from 'ant-design-vue'
import path from 'path'
// 函数式组件
const SubMenu = {
  template: `
      <a-sub-menu :key="menuInfo.path" v-bind="$props" v-on="$listeners">
        <span slot="title" class="menu-title">
          <a-icon :type="menuInfo.meta && menuInfo.meta.icon" />
          <span v-if="menuInfo.meta">{{ generateTitle(menuInfo.meta.title) }}</span>
        </span>
        <template v-for="item in menuInfo.children">
          <a-menu-item v-if="!item.children" :key="resovePath(item.path)">
            <router-link style="display: inline-block;" :to="resovePath(item.path)">
              {{ generateTitle(item.meta.title) }}
            </router-link>
          </a-menu-item>
          <sub-menu v-else :key="item.path" :menu-info="item"  :base-route="resovePath(item.path)" />
        </template>
      </a-sub-menu>
    `,
  name: 'SubMenu',
  // must add isSubMenu: true
  isSubMenu: true,
  props: {
    ...Menu.SubMenu.props,
    // Cannot overlap with properties within Menu.SubMenu.props
    menuInfo: {
      type: Object,
      default: () => ({})
    },
    baseRoute: {
      type: String,
      default: ''
    }
  },
  methods: {
    generateTitle,
    resovePath(item) {
      return path.resolve(this.baseRoute, item)
    }
  }
}

export default SubMenu

在这里需要用到node的path模块来对路由hash进行拼接(鬼知道我掉了多少头发才想到这个方法的…)。每次传入的baseRouter即父节点的path,将path一个个拼接起来就是完整的path。

上一篇说到判断优化、多级路由啥的。推荐大家将项目clone下来根据log一步步查看吧,都过去好几天了,懒得再reset回去看…我把最终的code贴出来吧,有兴趣的可以研究下

<template>
  <div class="sidebar-container">
    <a-layout-sider v-model="isCollapsed" :trigger="null" collapsible>
      <div class="logo" />
      <a-menu theme="dark" mode="inline" :selected-keys="currentSelectMenu" :open-keys="openKeys" @click="menuClick" @openChange="onOpenChange">
        <template v-for="item in routers">
          <template v-if="!item.hidden">
            <!-- menu -->
            <a-menu-item v-if="hasOneChildren(item.children)" :key="item.redirect">
              <a-icon :type="item.children[0] && item.children[0].meta && item.children[0].meta.icon" />
              <span class="menu-title">
                <router-link style="display: inline-block;" :to="{path: item.path === '/' ? `${item.path}${item.children[0].path}` : `${item.path}/${item.children[0].path}`}">
                  {{ generateTitle(item.children[0].meta.title) }}
                </router-link>
              </span>
            </a-menu-item>
            <!-- submenu -->
            <sub-menu v-else :key="item.path" :menu-info="item" :base-route="item.path" />
          </template>
        </template>
      </a-menu>
    </a-layout-sider>
  </div>
</template>

<script>
import { generateTitle } from './breadcrumb/index.js'
import SubMenu from './subMenu'
export default {
  components: {
    SubMenu
  },
  props: {
    collapsed: [Boolean],
    routers: {
      type: Array,
      default: () => []
    },
    parentNodeList: {
      type: Array,
      default: () => []
    }
  },
  data() {
    return {
      isCollapsed: false,
      onlyOneChild: null,
      openKeys: []
    }
  },
  computed: {
    // 当前选中的菜单项
    currentSelectMenu() {
      return [this.$route.path]
    },
    router() {
      return this.$route.path
    }
  },
  watch: {
    collapsed: {
      handler: function(val) {
        this.isCollapsed = val
      }
    }
  },
  created() {
    const openKeysArr = (this.$route.path).split('/')
    openKeysArr.shift()
    openKeysArr[0] = '/' + openKeysArr[0]
    openKeysArr.forEach((item, index) => {
      this.openKeys.unshift(item)
    })
  },
  methods: {
    generateTitle,
    // 点击菜单触发事件
    menuClick({ item, key, keyPath }) {
      // length为1则说明没有子菜单
      keyPath.length === 1 ? this.openKeys = [] : ''
    },
    // 子菜单展开/关闭的回调
    onOpenChange(openKeys) {
      const latestOpenKey = openKeys.find(key => this.openKeys.indexOf(key) === -1)
      this.openKeys = this.parentNodeList.indexOf(latestOpenKey) === -1 ? openKeys : latestOpenKey ? [latestOpenKey] : []
    },
    // 单个子节点
    hasOneChildren(item) {
      return !item.some(menu => menu.children) && item.length === 1
    }
  }
}
</script>

<style lang="less" scoped>
.logo {
  height: 32px;
  background: rgba(255, 255, 255, 0.2);
  margin: 16px;
}
.menu-title {
  display: inline-block;
  width: 85%;
  a {
    display: inline-block;
    width: 100%;
    color: #ccc;
    &:hover {
      color: #fff;
    }
  }
}
</style>
  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值