element使用tabs+navmenu完成后台首页菜单导航联动

首先上图给大家看看实现效果:

然后直接上核心代码:

      <!-- 左侧菜单栏 -->
      <div class="aside">
        <el-menu
          class="menu"
          :default-active="$route.path"
          @open="handleOpen"
          @close="handleClose"
          router
        >
          <template v-for="(item, index) in menu">
            <!-- 遍历生成菜单的时候根据对象是否有subMenu来判断是否有二级菜单,没有就直接生成一级菜单 -->
            <el-submenu
              v-if="item.subMenu"
              :index="item.subMenu[0].path"
              :key="index + '-Susuk'"
            >
              <template slot="title">
                <span slot="title">{{ item.title }}</span>
              </template>
              <el-menu-item-group>
                <el-menu-item
                  v-for="item2 in item.subMenu"
                  :key="item2.id"
                  :index="item2.path"
                  >{{ item2.title }}</el-menu-item
                >
              </el-menu-item-group>
            </el-submenu>
            <el-menu-item v-else :index="item.path" :key="index + '-Suk'">{{
              item.title
            }}</el-menu-item>
          </template>
        </el-menu>
      </div>
      <!-- 右侧内容区 -->
      <div class="content">
        <el-tabs
          v-model="tabsValue"
          type="card"
          closable
          @tab-remove="removeTab"
          @tab-click="tabClick"
        >
          <el-tab-pane
            v-for="item in tabs"
            :key="item.name"
            :label="item.title"
            :name="item.name"
          >
            <component :is="item.component"></component>
          </el-tab-pane>
        </el-tabs>
      </div>

首先我们要开启el-menu的router模式,这样index填写的就是我们的路由路径,点哪个菜单就会跳转到对应的路由:

然后根据element官网给出的结构改造成自己的,el-menu内包裹的结构大概是这样的:

1.只有一级菜单(也就是没有往下展开的,只有一层),直接用

<!-- index就是路由路径 -->
<el-menu-item v-else :index="item.path" :key="index + '-Suk'">{{item.title}}</el-menu-item>

2.有二级菜单(也就是可以展开,展开后又是一层)

<el-submenu
  v-if="item.subMenu"
  :index="item.subMenu[0].path"
  :key="index + '-Susuk'"
>
<!-- 这是一级菜单上面的标题 -->
  <template slot="title">
    <span slot="title">{{ item.title }}</span>
  </template>
<!-- 这是展开后二级菜单的菜单组,里面可以有多个菜单 -->
  <el-menu-item-group>
    <el-menu-item
      v-for="item2 in item.subMenu"
      :key="item2.id"
      :index="item2.path"
      >{{ item2.title }}</el-menu-item
    >
  </el-menu-item-group>
</el-submenu>

基本结构就是这样,然后菜单的数据结构是这样的:

/**
 * title: 菜单标题
 * component:对应的子组件页面
 * path:子组件对应的路由
 */
export default [
    {
        title: "用户管理",
        component: "User",
        path: "/home/user",
    },
    {
        title: "管理员管理",
        component: "Admin",
        path: "/home/admin",
    },
    {
        title: "词库分类管理",
        component: "RepositoryCategory",
        path: "/home/repositoryCategory",
    },
    {
        title: "词库管理",
        component: "Repository",
        path: "/home/repository",
    },
    {
        title: '词汇分类管理',
        component: 'WordCategory',
        path: '/home/wordCategory'
    },
    {
        title: "词汇管理",
        component: "Word",
        path: "/home/word",
    },
    {
        title: "词汇声调管理",
        component: "WordTone",
        path: "/home/wordTone",
    },
    {
        title: "词汇敬语等级管理",
        component: "WordHonorificLevel",
        path: "/home/wordHonorificLevel",
    },
    {
        title: '系统管理',
        // subMenu就是二级菜单
        subMenu: [
            {
                title: "数据字典管理",
                component: "DataDirectory",
                path: "/home/dataDirectory",
            },
            {
                title: "任务计划管理",
                component: "TaskPlan",
                path: "/home/taskPlan",
            },
        ],
    },
]

将菜单项里面的路由配置到路由规则中:

// 配制路由规则
const routes = [
  {
    path: '/',
    redirect: '/login'
  },
  // 登录页面
  {
    path: '/login',
    component: () => import('@/views/login/Login')
  },
  {
    path: '/home',
    redirect: '/home/user'
  },
  // 后台首页
  {
    path: '/home',
    component: () => import('@/views/layout/Home'),
    /**
     * tab子页面
     * meta:{
     *      component:tab显示的子组件
     *      title:tab的标题
     *      path:tab对应的路由路径
     * }
     */
    children: [
      // 用户管理
      {
        path: '/home/user',
        component: () => import('@/views/layout/user/User'),
        meta: {
          tabInfo: {
            component: 'User',
            title: '用户管理',
            path: '/home/user'
          }
        }
      },
      // 管理员管理
      {
        path: '/home/admin',
        component: () => import('@/views/layout/admin/Admin'),
        meta: {
          tabInfo: {
            component: 'Admin',
            title: '管理员管理',
            path: '/home/admin'
          }
        }
      },
      // 词库分类管理
      {
        path: '/home/repositoryCategory',
        component: () =>
          import('@/views/layout/repositoryCategory/RepositoryCategory'),
        meta: {
          tabInfo: {
            component: 'RepositoryCategory',
            title: '词库分类管理',
            path: '/home/repositoryCategory'
          }
        }
      },
      // 词库管理
      {
        path: '/home/repository',
        component: () => import('@/views/layout/repository/Repository'),
        meta: {
          tabInfo: {
            component: 'Repository',
            title: '词库管理',
            path: '/home/repository'
          }
        }
      },
      // 词汇分类管理
      {
        path: '/home/wordCategory',
        component: () => import('@/views/layout/wordCategory/WordCategory'),
        meta: {
          tabInfo: {
            component: 'WordCategory',
            title: '词汇分类管理',
            path: '/home/wordCategory'
          }
        }
      },
      // 词汇声调管理
      {
        path: '/home/wordTone',
        component: () => import('@/views/layout/wordTone/WordTone'),
        meta: {
          tabInfo: {
            component: 'WordTone',
            title: '词汇声调管理',
            path: '/home/wordTone'
          }
        }
      },
      // 词汇敬语等级管理
      {
        path: '/home/wordHonorificLevel',
        component: () =>
          import('@/views/layout/wordHonorificLevel/WordHonorificLevel'),
        meta: {
          tabInfo: {
            component: 'WordHonorificLevel',
            title: '词汇敬语等级管理',
            path: '/home/wordHonorificLevel'
          }
        }
      },
      // 词汇管理
      {
        path: '/home/word',
        component: () => import('@/views/layout/word/Word'),
        meta: {
          tabInfo: {
            component: 'Word',
            title: '词汇管理',
            path: '/home/word'
          }
        }
      },
      // 数据字典管理
      {
        path: '/home/dataDirectory',
        component: () =>
          import('@/views/layout/system/dataDirectory/DataDirectory'),
        meta: {
          tabInfo: {
            component: 'DataDirectory',
            title: '数据字典管理',
            path: '/home/dataDirectory'
          }
        }
      },
      // 任务计划管理
      {
        path: '/home/taskPlan',
        component: () => import('@/views/layout/system/taskPlan/TaskPlan'),
        meta: {
          tabInfo: {
            component: 'TaskPlan',
            title: '任务计划管理',
            path: '/home/taskPlan'
          }
        }
      }
    ]
  }
]

右侧内容区采用了tabs嵌套动态组件,我们要一次性导入所有子页面组件,然后通过监听路由的变化切换右边对应的子组件页面,路由每次变化都会向tabs数组中插入一个tab,右边遍历出来就多一个tab:

data () {
    return {
      tabsValue: '', // 当前显示的选项卡对应的子组件路由
      tabs: [], // tabs选项卡列表 name就是路由path comp决定该tab显示哪个子组件页面     title:tab标题
      menu: menu // 菜单列表
    }
  },  
watch: {
    // 监听路由变化
    $route: {
      handler (newValue) {
        // tab去重处理
        // 判断子组件名和路由元信息中的子组件名相同
        const index = this.tabs.findIndex(
          item => item.component === newValue.meta.tabInfo.component
        )
        // 如果tab存在,则只切换当前tab,不添加
        if (index !== -1) {
          this.tabsValue = this.tabs[
            this.tabs.findIndex(
              item => item.component === newValue.meta.tabInfo.component
            )
          ].name
          return
        }
        // 路由变化后将当前路由和默认高亮的菜单保持一致,避免页面刷新还是默认的值
        this.defaultActive = newValue.path
        // 添加tab信息到集合列表,添加一个顺序往后排一个
        this.tabs.push({
          title: newValue.meta.tabInfo.title,
          component: newValue.meta.tabInfo.component,
          name: newValue.path
        })
        // 每次添加后取最后一个的name显示新点击的tab
        this.tabsValue = this.tabs[this.tabs.length - 1].name
      },
      // 页面首次加载也监听路由的值
      immediate: true
    }
  },

当tab被点击时,判断点击的tab的name(name就是path)是不是和当前$route.path一致,如果不一致就高亮对应的菜单,一致就啥都不做,直接跳转路由触发监听器将跳转后的新path赋值给默认高亮值defaultValue

    // tab被点击时触发
    tabClick ({ name }) {
      if (name === this.$route.path) return
      this.$router.push(name) // 触发路由监听,改变对应高亮的菜单
    },

监听一级菜单的展开,如果展开了就默认跳转到第一个子菜单,同时高亮:

    // 监听一级菜单的展开
    handleOpen (key) {
      // console.log(key, keyPath);
      this.$router.push(key)
    },

监听tab的删除:

    // 删除tab
    removeTab (targetPath) {
      // 如果只剩一个tab时,就不能再删除了
      if (this.tabs.length < 2) {
        return this.$message({
          type: 'warning',
          message: '至少要有一个标签页!',
          duration: 2000
        })
      }
      const tabs = this.tabs
      let activePath = this.tabsValue
      // 在所有tab中选出删除的目标标签页的上一个或者下一个,相邻的就是下一个当前tab
      tabs.forEach((tab, index) => {
        if (tab.name === targetPath) {
          const nextTab = tabs[index - 1] || tabs[index + 1]
          // 将下一个tab高亮
          activePath = nextTab.name
          // 删除掉要删除的目标tab
          this.tabs.splice(index, 1)
        }
      })
      // 路由跳转到删除后的下一个tab,触发路由监听改变左边对应菜单栏的高亮
      this.$router.push(activePath)
    }

今晚先总结到这吧,加班到11点才回到家写一篇,太TM的困了,睡觉...

  • 5
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值