syTree.vue
<style lang="scss">
label{font-size: 14px;}
.notetitle{cursor: pointer;}
.notetitle:hover{background-color: #eaf4fe;}//移上样式
.notetitle-selected{background-color: #d5e8fc;}//点击后样式
ul.tree-ul li{line-height:28px;}
.tree-square{font-size:17px;margin-right: 8px;margin-top:6px;color:$sy-default-color;margin-left: 5px;width:0.5em;height: 1em;}
.tree-folder{font-size:14px;margin-right: 8px;margin-top:8px;color:$sy-ioc-color;margin-left:0px}
<style>
<template>
<ul class="tree-ul">
<li v-for="nodeItem,index in dataset">
<div class="notetitle" :class="{'notetitle-selected': cursor_node == nodeItem[value]}">
<div style="float: left;">
<div v-if="(nodeItem.children && nodeItem.children.length > 0)">
<i v-if="!nodeItem.expand" @click="changeState(nodeItem)" class="tree-square fa fa-angle-right"></i>
<i v-else @click="changeState(nodeItem)" class="tree-square fa fa-angle-down"></i>
</div>
<div v-else style="width:14px; height: 14px;"></div>
</div>
<div style="float: left;">
<vxe-checkbox v-model="nodeItem.checked" v-if="multiple" @change="checkNode(nodeItem)" :true-value="true" :false-value="false"></vxe-checkbox>
<i v-else @click="changeState(nodeItem)" class="tree-folder fa fa-file-text-o"></i>
</div>
<div>
<div style="cursor: pointer;" @click="childClick(nodeItem)">
{{nodeItem[title]}}
</div>
<div class="clear"></div>
</div>
<div style="clear: both;"></div>
</div>
<div v-if="nodeItem.children && nodeItem.children.length > 0 && nodeItem.expand" class="childTree" style="margin-left:22px;">
<syTree ref="syTree"
:dataset="nodeItem.children"
:title="title"
:father="nodeItem"
:multiple="multiple"
:auto-cascade="autoCascade"
:cursor-node.sync="cursor_node"
@on-select-change="onSelectChange"
@on-check-change="onCheckChange"
></syTree>
</div>
</li>
</ul>
</template>
<script>
export default {
name: "syTree",
data(){
return {
// 当前点击的节点
cursor_node:'',
}
},
// dataset: 树节点数据数组
props:{
// 树结构数据
/*[{
...
expand: false, // 是否展开
checked: false,// 是否勾选
children: [] // 子节点
]*/
dataset: Array,
// 树节点上显示的标签对象属性键名,对应dataset里的键名
title:{
type: String,
default:'title'
},
// 树节点表示的值的键名,对应dataset里的键名
value:{
type: String,
default:'value'
},
// 是否显示复选框,显示复选框表示可以勾选
multiple:{
type: Boolean,
default: false
},
// 是否级联选择,即父级选择自动勾选子级节点
autoCascade:{
type: Boolean,
default: true
},
// 当前树节点中被点击激活的节点的value值,组件类递归时使用,外部调用不需要配置该值
cursorNode:{
default:''
},
// 父级节点,组件类递归时使用,外部调用不需要配置该值
father:{
type: Object,
default:()=>{
return {}
}
}
},
methods: {
// 子组件节点点击回调事件
onSelectChange({nodeItem}){
this.cursor_node = nodeItem[this.value];
// 回调父组件
this.$emit('update:cursor-node', this.cursor_node);
this.$emit('on-select-change', {nodeItem});
},
// 子组件节点复选框点击回调事件
onCheckChange({nodeItem}){
// 设置父级节点复选框状态
if(this.autoCascade){
let nodes = this.getCheckedChildren();
if (nodes.length == this.dataset.length){
this.father.checked = true;
}else{
this.father.checked = false
}
}
if(this.father !== {} || nodeItem !== null){
// 回调父组件
this.$emit('on-check-change', {nodeItem});
}
},
// 当前组件节点点击事件
childClick(nodeItem){
this.changeState(nodeItem);
this.onSelectChange({nodeItem})
},
// 展开和收起的切换
changeState: function(nodeItem) {
nodeItem.expand = !nodeItem.expand;
//this.$forceUpdate();
},
// 点击复选框
checkNode: function (nodeItem) {
let allselect = nodeItem.checked;
let that = this;
let setCheck = function (childs, allselect) {
for(let n=0; n< childs.length; n++){
//childs[n].checked = allselect;
// 此处改为用Vue.set来给数组赋值,这样Vue才能监听到数组的change事件,才会重新渲染Dom
that.$set(childs[n], 'checked', allselect);
setCheck(childs[n].children || [], allselect);
}
}
// 设置为级联,才会自动设置子节点和父节点的勾选状态
if(this.autoCascade){
// 设置子节点选中状态
setCheck(nodeItem.children || [], allselect);
// 设置父节点选中状态
this.onCheckChange({nodeItem})
}else{
// 回调父组件
this.$emit('on-check-change', {nodeItem});
}
},
// 获取所有选中的节点
getCheckedNodes: function () {
let ChoiceData = [];
let getNodes = function (Dataset) {
for(let n = 0; n < Dataset.length; n++){
if (Dataset[n].checked){
ChoiceData.push(Dataset[n]);
}
if (Dataset[n].children != null){
getNodes(Dataset[n].children);
}
}
}
getNodes(this.dataset);
return ChoiceData;
},
/****************************** 组件提供给外部调用的method定义 ***********************************/
// 获取当前组件的dataset中checked = true 的nodes ,不包含子级
getCheckedChildren(){
let ChoiceData = [];
for(let n = 0; n < this.dataset.length; n++){
if (this.dataset[n].checked){
ChoiceData.push(this.dataset[n]);
}
}
return ChoiceData;
},
// 设置树节点全部展开或全部收起,option = true 表示全部展开
setExpand(option){
let a = function(arr){
for(let n = 0; n < arr.length; n++){
arr[n].expand = option;
if(arr[n].children.length>0){
a(arr[n].children);
}
}
}
for(let n = 0; n < this.dataset.length; n++){
this.dataset[n].expand = option;
a(this.dataset[n].children);
}
},
},
watch: {
'cursorNode':{
handler(newName, oldName) {
this.cursor_node = newName;
},
immediate: false
}
}
}
</script>
demo 使用
<template>
<div>
<div style="border: 1px solid #eee; padding: 15px; margin-bottom: 10px;">
<div>
级次:<input type="number" v-model="grade" style="width: 80px;"></input>
</div>
<div>
每级条数:<input type="number" v-model="num" style="width: 80px;"></input>
</div>
<div>
<Button @click="createList">生成</Button>
</div>
</div>
<div style="width: 400px; height: 700px; padding: 10px; border: 1px solid #eee;">
<syTree ref="listTree" :multiple="true" :dataset="datalist"></syTree>
</div>
</div>
</template>
<script>
import syTree from '@/components/sy-tree/syTree.vue'
export default {
components: {
syTree
},
data() {
return {
num:5,
grade:3,
datalist:[],
}
},
created() {
this.createList();
},
methods:{
createChildren(grade, f_title){
let children = [];
for (let i=0; i<this.num; i++){
this.$set(children, i, {
'title': f_title + `-${i}`,
'expand':false,
'checked':false,
'value': f_title + `-${i}`
})
if (grade > 1){
children[i].children = this.createChildren( grade-1, children[i]['title'])
}
}
return children;
},
createList(){
let numbers = 1;
for (let i = 0; i<this.grade; i++){
numbers = numbers * this.num;
}
if (numbers > 100000){
this.$Message.info('记录条数,不能超过10万条!');
return;
}
this.datalist = [];
for (let i=0; i<this.num; i++){
this.$set(this.datalist, i, {});
this.$set(this.datalist[i], 'title', `code-${i}`);
this.$set(this.datalist[i], 'value', `code-${i}`);
this.$set(this.datalist[i], 'expand', false);
this.$set(this.datalist[i], 'checked', false);
this.$set(this.datalist[i], 'children', this.createChildren(this.grade-1 , this.datalist[i].title));
}
}
}
}
</script>