分散式路由

153 阅读2分钟
  • store 以及 菜单数据源
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
import router from '../router'
import createPersistedState from 'vuex-persistedstate';

/**
 * @desc 查找到视图层的path
 * @param {item}  选中的某一项菜单
 * @return {path} 视图层的path
 */
const findEndPath = (item) => {
  if (item.child && item.child.length > 0) {
    return findEndPath(item.child[0])
  } else {
    return item.path
  }
}

/**
 * @desc 路由按照级别分组
 * @param {item} 选中的某一项菜单
 * @param {obj} 对象结构
 * @return {obj} {1:[1级导航路由源数据],2:[2级导航路由源数据], 3:[3级导航路由源数据]}
 */
const setMenusByLvl = (item, obj) => {
  item.active = true
  if (item.child && item.child.length > 0) {
    item.child.forEach(item => item.active = false)
    obj[item.lvl + 1] = item.child
    setMenusByLvl(item.child[0], obj)
  } else {
    // 清除上一次的脏数据
    [1, 2, 3, 4, 5, 6, 7, 8].forEach(i => {
      obj[Number(item.lvl) + i] = []
    })
  }
  return obj
}

const store = new Vuex.Store({
  plugins: [createPersistedState({
    paths: ["menuTrees", "activeLvlMenus"]
  })],
  state: {
    menuTrees: [
      {
        id: "1", pid: null, name: "/导航1", path: "/daohang1", lvl: 1, child: [
          {
            lvl: 2,
            id: "1-1", pid: "1", name: "/导航1-1", path: "/daohang1-1", child: [
              {
                id: "1-1-3", pid: "1-1", name: "/导航1-1-3", path: "/daohang1-1-3", lvl: 3, child: [
                  { id: "1-1-3-1", pid: "1-1-3", name: "/导航1-1-3-1", path: "/daohang1-1-3-1", lvl: 4, child: [] },
                  { id: "1-1-3-2", pid: "1-1-3", name: "/导航1-1-3-2", path: "/daohang1-1-3-2", lvl: 4, child: [] },
                ]
              },
              { id: "1-1-1", pid: "1-1", name: "/导航1-1-1", path: "/daohang1-1-1", lvl: 3, child: [] },
              { id: "1-1-2", pid: "1-1", name: "/导航1-1-2", path: "/daohang1-1-2", lvl: 3, child: [] },

            ]
          },
          {
            lvl: 2,
            id: "1-2", pid: "1", name: "/导航1-2", path: "/daohang1-2", child: [
              { id: "1-2-1", pid: "1-2", lvl: 3, name: "/导航1-2-1", path: "/daohang1-2-1", child: [] },
              { id: "1-2-2", pid: "1-2", lvl: 3, name: "/导航1-2-2", path: "/daohang1-2-2", child: [] },
            ]
          },
        ]
      },
      {
        lvl: 1,
        id: "2", pid: null, name: "/导航2", path: "/daohang2", child: [
          {
            id: "2-1", pid: "2", lvl: 2, name: "/导航2-1", path: "/daohang2-1", child: [
              { id: "2-1-1", pid: "2-1", lvl: 3, name: "/导航2-1-1", path: "/daohang2-1-1", child: [] },
              { id: "2-1-2", pid: "2-1", lvl: 3, name: "/导航2-1-2", path: "/daohang2-1-2", child: [] },
            ]
          },
          {
            id: "2-2", pid: "2", lvl: 2, name: "/导航2-2", path: "/daohang2-2", child: [
              // { id: "2-2-1", pid: "2-2", lvl: 3, name: "/导航2-2-1", path: "/daohang2-2-1", child: [] },
              // { id: "2-2-2", pid: "2-2", lvl: 3, name: "/导航2-2-2", path: "/daohang2-2-2", child: [] },
            ]
          },
        ]
      }
    ],

    /**
     * @desc 对应层级的菜单 {1:[{active:true, id, lvl, path}],2:[2级导航路由源数据], 3:[3级导航路由源数据]}
     */
    activeLvlMenus: {},
  },
  mutations: {
    /**
     * @desc 设置对应层级的菜单 譬如切换二级导航时,需要设置 三 四 ... 级的导航
     * @param {lvl}  层级
     * @return {} 
     */
    setActiveLvlMenus (state, { lvl, children, path, id }) {
      const selfMenus = state.activeLvlMenus[lvl - 1]
      // 更新自身导航的选中项
      if (selfMenus && selfMenus.length > 0) {
        selfMenus.forEach(item => {
          item.active = false
          if (item.id === id) {
            item.active = true
          }
        })
      }
      state.activeLvlMenus = { ...state.activeLvlMenus, [lvl]: children }
      // 如果没有子集,就认为当前导航是最后一级,并push到对应的路由
      if (!children || children.length === 0) {
        router.push(path)
        return
      }
      children.forEach(item => item.active = false)
      setMenusByLvl(children[0], state.activeLvlMenus)
      // 找到最后的path并push
      router.push(findEndPath(children[0]))
    },

  },
  actions: {

  },
  getters: {
    /**
     * @desc 获取对应层级的导航
     * @param {lvl}  层级
     * @return {menus} 对应层级的导航列表
     */
    getMenusByLvl: (state, getters) => (lvl) => {
      return state.activeLvlMenus[lvl]
    }
  }
})
export default store
  • App.vue
<template>
  <div>
    <p>app</p>
    <SideBar />
    <NavBar />
    <router-view></router-view>
  </div>
</template>
  • NavBar 放置 二级菜单
<template>
  <div>
    <!-- 侧边导航 -->
    <h1>navbar</h1>
    <el-menu default-active="2" class="el-menu-vertical-demo" @select="handleSelect">
      <el-menu-item :index="item.path" v-for="item in menus" :key="item.id" :class="{'active':item.active}">
        <span slot="title">{{item.name}}</span>
      </el-menu-item>
    </el-menu>
  </div>
</template>

<script>
import { mapState, mapGetters, mapMutations, mapActions } from 'vuex'

export default {
  components: {

  },
  props: {

  },
  data () {
    return {
      // activePath: "/daohang1-1"
    }
  },
  computed: {
    menus () {
      return this.$store.getters.getMenusByLvl('2')
    }
  },
  created () {

  },
  mounted () {

  },
  watch: {

  },
  methods: {
    ...mapMutations(['setActiveLvlMenus',]),
    handleSelect (index) {
      const currentItem = this.menus.find(item => item.path === index)
      const children = currentItem?.child
      const id = currentItem?.id
      this.setActiveLvlMenus({ lvl: "3", children, path: index, id })
    }
  },
};
</script>

<style scoped>
.el-menu-vertical-demo {
  width: 250px;
}
.el-menu-item.active {
  color: yellowgreen;
}
</style>

  • SideBar 放置 一级菜单 三级菜单 四级菜单
<template>
  <div>
    <!-- 头部导航 -->
    <h1>sidebar</h1>
    <!-- 一级菜单 -->
    <div class="yiji">
      <el-dropdown @command="handleCommand">
        <span class="el-dropdown-link">
          下拉菜单<i class="el-icon-arrow-down el-icon--right"></i>
        </span>
        <el-dropdown-menu slot="dropdown">
          <el-dropdown-item :command="item" v-for="item in menuTrees" :key="item.id">{{item.name}}</el-dropdown-item>
        </el-dropdown-menu>
      </el-dropdown>
    </div>
    <!-- 三级菜单 -->
    <ul v-if="menus && menus.length > 0">
      <li v-for="item in menus" :key="item.id" :class="{'active':item.active}" @click="handleClick(item)">{{item.name}} </li>
    </ul>
  </div>
</template>

<script>
import { mapState, mapGetters, mapMutations, mapActions } from 'vuex'

export default {
  components: {

  },
  props: {

  },
  data () {
    return {

    }
  },
  computed: {
    ...mapState(['menuTrees']),
    menus () {
      return this.$store.getters.getMenusByLvl('3')
    }
  },
  created () {

  },
  mounted () {

  },
  watch: {

  },
  methods: {
    ...mapMutations(['setActiveLvlMenus',]),
    handleCommand (item) {
      this.setActiveLvlMenus({ lvl: "2", children: item.child, path: item.path, id: item.id })
    },
    handleClick (item) {
      this.setActiveLvlMenus({ lvl: "4", children: item.child, path: item.path, id: item.id })

    }
  },
};
</script>

<style scoped>
li.active {
  color: yellowgreen;
}
</style>

image.png