1.子组件
<template> <li :id="'li_'+model.id" > <span :id="'li_switch_'+model.id" class="button switch " :class="getSwitchClazz" @click='toggle'></span> <span v-if="getChoiceType('checkbox')" :id="'li_checkbox_'+model.id" class="button chk " :class="backfill(model.id)?'checkbox_true_full':'checkbox_false_full'" @click='nodeChecked(model.id,$event)'></span> <a :id="'li_a_'+model.id" @click="nodeClick(model,$event)" class="node " :class="id+'_node'"> <span v-if="getChoiceType('folder')" :id="'li_a_icon_'+model.id" class="button ico " :class="getIconClazz"></span> <span :id="'li_a_name_'+model.id" class="node_name" >{{model.name}}</span> </a> <ul v-show="isOpen" v-if='isFolder' :class="isLine" :id="'li_ul_'+model.id"> <template v-for='(cel,index) in model.children'> <items :model='cel' :id="id" :recordList="recordList" @node-click="nodeClick" @node-checkbox="nodeCheckbox" :choiceType="choiceType" :sort="index" :open="open" :listSize="model.children.length"></items> </template> </ul> </li> </template> <script> export default { name: 'items', props: ['model', 'id', 'clazz', 'sort', 'open', 'listSize', 'choiceType', 'recordList'], data() { return { isOpen: true } }, computed: { isFolder() { return this.model.children && this.model.children.length }, isLine() { if (this.listSize === this.sort + 1) { return '' } else { if (this.listSize > 1) { return 'line' } else { return '' } } }, getIconClazz() { if (this.model.children.length > 0) { if (this.isOpen) { return 'ico_open' } else { return 'ico_close' } } else { return 'ico_docu' } }, getSwitchClazz() { if (this.clazz === 'tree_' && this.sort === 0) { if (this.sort > 1) { if (this.isOpen) { return 'roots_open' } else { return 'roots_close' } } else { if (this.isOpen) { return 'root_open' } else { return 'root_close' } } } else if (this.listSize === this.sort + 1) { if (!this.isFolder) { return 'bottom_docu' } else { return this.isOpen ? 'bottom_open' : 'bottom_close' } } else if (!this.isFolder) { return 'center_docu' } else { return this.isOpen ? 'center_open' : 'center_close' } } }, created() { this.isOpen = this.open this.backfill() }, methods: { backfill(id) { if (this.recordList && this.recordList.length) { for (var i = 0; i < this.recordList.length; i++) { if (id === this.recordList[i]) { return true } } } return false }, nodeCheckbox(val, isAdd) { this.$emit('node-checkbox', val, isAdd) }, nodeChecked(id, e) { var classList = e.currentTarget.classList if (this.hasClass(classList, 'checkbox_true_full')) { classList.add('checkbox_false_full') classList.remove('checkbox_true_full') this.$emit('node-checkbox', id, false) } else { classList.add('checkbox_true_full') classList.remove('checkbox_false_full') this.$emit('node-checkbox', id, true) } }, getChoiceType(type) { return this.choiceType.indexOf(type) !== -1 }, nodeClick(node, e) { var a = document.getElementsByClassName(this.id + '_node') for (var i = 0; i < a.length; i++) { a[i].classList.remove('selected') } e.currentTarget.classList.add('selected') this.$emit('node-click', node, e) }, hasClass(classList, clazz) { for (var i = 0; i < classList.length; i++) { if (classList[i] === clazz) { return true } } return false }, toggle() { if (this.isFolder) { this.isOpen = !this.isOpen } } } } </script> <style scoped> li ul.line { background: url(../img/line_conn.png) 0 0 repeat-y; } li ul { margin: 0; padding: 0 0 0 12px; } li { padding: 0; margin: 0; list-style: none; line-height: 17px; text-align: left; white-space: nowrap; outline: 0; } * { padding: 0; margin: 0; font-size: 14px; font-family: Verdana, Arial, Helvetica, AppleGothic, sans-serif; } li a { padding-right: 3px; margin: 0; cursor: pointer; height: 21px; color: #333; background-color: transparent; text-decoration: none; vertical-align: top; display: inline-block; } li span.button { line-height: 0; margin: 0; padding: 0; width: 21px; height: 21px; display: inline-block; vertical-align: middle; border: 0 none; cursor: pointer; outline: none; background-color: transparent; background-repeat: no-repeat; background-attachment: scroll; background-image: url(../img/bootstrap.png); } li a.node{ margin-left: -7px; } li a.node.selected{ background-color: #dcdfe6; } li span.button.ico { margin-right: -7px; } li span.button.chk.checkbox_false_full { background-position: -5px 0px; } li span.button.chk.checkbox_true_full { background-position: -26px 0px; } li span.button.ico_docu { background-position: -147px -43px; vertical-align: top; } li span.button.ico_close { background-position: -147px 0; vertical-align: top; } li span.button.ico_open { background-position: -147px -21px; vertical-align: top; } li span.button.roots_open { background-position: -105px 0; } li span.button.roots_close { background-position: -126px 0; } li span.button.center_open { background-position: -105px -20px; } li span.button.center_close { background-position: -126px -20px; } li span.button.bottom_open { background-position: -105px -42px; } li span.button.bottom_close { background-position: -126px -42px; } li ul.line { background: url(../img/line_conn.png) 0 0 repeat-y; } li span.button.center_docu { background-position: -84px -21px; } li span.button.bottom_docu { background-position: -84px -42px; } li span.button.root_open { background-position: -105px -62px; } li span.button.root_close { background-position: -126px -62px; } li span.button.center_docu { background-position: -84px -21px; } </style>2.父组件
<template> <ul id="tree" class="tree"> <template v-for="(model,index) in list"> <leaf :model="model" :recordList="recordList" @node-click="nodeClick" @node-checkbox="nodeCheckbox" :choiceType="choiceType" :id="id" clazz="tree_" :sort="index" :open="open" :listSize="list.length"></leaf> </template> </ul> </template> <script> import Leaf from '@/components/Tree/leaf' export default { name: 'tree', components: { Leaf }, props: ['list', 'open', 'id', 'choiceType', 'recordList'], data() { return { node: null } }, methods: { nodeCheckbox(val, isAdd) { this.$emit('node-checkbox', val, isAdd) }, nodeClick(node) { this.$emit('node-click', node) } } } </script> <style scoped> .tree * { padding: 0; margin: 0; font-size: 14px; font-family: Verdana, Arial, Helvetica, AppleGothic, sans-serif; } </style>
3.调用
<template> <!-- -- 参数 list:数据 recordList:有复选框时回选节点集合 id:如果同页面使用多个需要id区分 open:是否展开 folder choiceType:类型 folder显示文件夹图标 checkbox显示复选框 folder_checkbox 都显示 -- 事件 @node-click:点击节点触发,返回节点对象 @node-checkbox:点击复选框时触发 类型为checkbox时用来获取选中集合 传出两个参数 (val, isAdd) val 节点id ,isAdd true选中,false取消 --> <tree :list="list" :recordList="recordList" id="tree" :open="true" choiceType="checkbox" @node-click="nodeClick" @node-checkbox="nodeCheckbox"></tree> </template> <script> import Tree from '@/components/Tree' export default { components: { Tree }, name: 'dashboard', data() { return { recordList: [1, 2, 3], list: [ { 'id': 1, 'name': '项目管理', 'children': [ { 'id': 2, 'name': '项目', 'children': [ { 'id': 3, 'name': '项目1', 'children': [ { 'id': 4, 'name': '项目1-1', 'children': [ { 'id': 5, 'name': '项目1-1-1', 'children': [] } ] } ] } ] }, { 'id': 6, 'name': '我的任务', 'children': [] }, { 'id': 7, 'name': '人员周报', 'children': [] } ] }, { 'id': 8, 'name': '数据统计', 'children': [] }, { 'id': 9, 'name': '人事管理', 'children': [] }, { 'id': 10, 'name': '基础管理', 'children': [ { 'id': 12, 'name': '基础管理1', 'children': [] } ] } ] } }, methods: { nodeClick: function(node) { // console.log(node) }, nodeCheckbox(val, isAdd) { if (isAdd) { this.checkbox.push(val) } else { this.delNodeId(val) } console.log(this.checkbox) }, delNodeId(id) { for (var i = 0; i < this.checkbox.length; i++) { if (this.checkbox[i] === id) this.checkbox.splice(i, 1) } } } } </script>