Vue.js 递归组件实现树形菜单

最近看了 Vue.js 的递归组件,实现了一个最基本的树形菜单。

main.js 作为入口:

import Vue from 'vue'
import main from './components/main.vue'

new Vue({
  el: '#app',
  render: h => h(main)
})

 

它引入了一个组件 main.vue:

<template>
  <div>
    <my-tree :data="theData" :name="menuName" :loading="loading" @getSubMenu="getSubMenu"></my-tree>
  </div>
</template>

<script>
const myData = [
  {
    id: '1',
    menuName: '基础管理',
    menuCode: '10'
  },
  {
    id: '2',
    menuName: '商品管理',
    menuCode: ''
  },
  {
    id: '3',
    menuName: '订单管理',
    menuCode: '30',
    children: [
      {
        menuName: '订单列表',
        menuCode: '31'
      },
      {
        menuName: '退货列表',
        menuCode: '32',
        children: []
      }
    ]
  },
  {
    id: '4',
    menuName: '商家管理',
    menuCode: '',
    children: []
  }
];

const subMenuData1 = {
  parentId: '1',
  children: [
    {
      menuName: '用户管理',
      menuCode: '11'
    },
    {
      id: '12',
      menuName: '角色管理',
      menuCode: '12',
      children: [
        {
          menuName: '管理员',
          menuCode: '121'
        },
        {
          menuName: 'CEO',
          menuCode: '122'
        },
        {
          menuName: 'CFO',
          menuCode: '123'
        },
        {
          menuName: 'COO',
          menuCode: '124'
        },
        {
          menuName: '普通人',
          menuCode: '124'
        }
      ]
    },
    {
      menuName: '权限管理',
      menuCode: '13'
    }
  ]
};

const subMenuData2 = {
  parentId: '2',
  children: [
    {
      menuName: '商品一',
      menuCode: '21'
    },
    {
      id: '22',
      menuName: '商品二',
      menuCode: '22',
      children: [
        {
          menuName: '子类商品1',
          menuCode: '221'
        },
        {
          menuName: '子类商品2',
          menuCode: '222'
        }
      ]
    }
  ]
};

import myTree from './common/treeMenu.vue'
export default {
  components: {
    myTree
  },
  data () {
    return {
      theData: myData,
      menuName: 'menuName', // 显示菜单名称的属性
      loading: false
    }
  },
  methods: {
    getSubMenu (menuItem, callback) {
      this.loading = true;

      if (menuItem.id === subMenuData1.parentId) {
        this.loading = false;
        menuItem.children = subMenuData1.children;
        callback(menuItem.children);
      }

      setTimeout(() => {
        if (menuItem.id === subMenuData2.parentId) {
          this.loading = false;
          menuItem.children = subMenuData2.children;
          callback(menuItem.children);
        }
      }, 2000);
    }
  }
}
</script>

 subMenuData1, subMenuData2 存放子菜单数据,可以从服务器获取,以实现动态加载。

该文件引入了树形组件 treeMenu.vue:

<template>
  <ul class="tree-menu">
    <li v-for="(item, index) in data">
      <span @click="toggle(item, index)">
        <i :class="['icon', item.children && item.children.length ? folderIconList[index] : 'file-text', loading ? loadingIconList[index] : '']"></i>
        {{ item[name] || item.menuName }}
      </span>
      <tree-menu v-if="scope[index]" :data="item.children"></tree-menu>
    </li>
  </ul>
</template>

<script>
export default {
  name: 'treeMenu',
  props: {
    data: Array,
    name: String,
    loading: Boolean
  },
  data () {
    return {
      folderIconList: [],
      loadingIconList: [],
      scope: {}
    }
  },
  created () {
    this.data.forEach((item, index) => {
      if (item.children && item.children.length) {
        this.folderIconList[index] = 'folder';
      }
    });
  },
  methods: {
    doTask (index) {
      this.$set(this.scope, index, !this.scope[index]);
      this.folderIconList[index] = this.scope[index] ? 'folder-open' : 'folder';
    },
    toggle (item, index) {
      this.loadingIconList = [];

      if (item.children && item.children.length) {
        this.doTask(index);
      } else {
        this.loadingIconList[index] = 'loading';

        this.$emit('getSubMenu', item, (subMenuList) => {
          if (subMenuList && subMenuList.length) {
            this.doTask(index);
          }
        });
      }
    }
  }
}
</script>

<style scoped>
.tree-menu {
  list-style: none;
}
.tree-menu li {
  line-height: 2;
}
.tree-menu li span {
  cursor: default;
}
.icon {
  display: inline-block;
  width: 15px;
  height: 15px;
  background-repeat: no-repeat;
  vertical-align: -2px;
}
.icon.folder {
  background-image: url(/src/assets/folder.png);
}
.icon.folder-open {
  background-image: url(/src/assets/folder-open.png);
}
.icon.file-text {
  background-image: url(/src/assets/file-text.png);
}
.icon.loading {
  background-image: url(/src/assets/loading.gif);
  background-size: 15px;
}
</style>

 

效果图:

示例代码放在这里

posted on 2016-12-21 16:15  caihg  阅读(36950)  评论(5编辑  收藏  举报