封装ant design vue 的a-tree组件,不是下拉框树的组件哈!!!别看错了。
工作中某些人要求偷懒省的麻烦,想通过直接给定url就能完整渲染的。说实话,我感觉官方给的已经很好用得了,也不是啰嗦的代码,而且我这项目一个项目用了两个地方都没有。反正挺无语的吧,于是自己尝试着搞了搞,功能肯定没有官网那么全面,但是基本使用肯定是没得问题的。需要的宝贝们可以收藏一波啦。可花费了我不少心思呢!!!!
<template>
<div>
<a-tree
:checkable="checkable"
:tree-data="treeData"
v-model="checkedKeys_"
:expandedKeys.sync="expandedKeys_"
:replace-fields="replaceFields"
@select="onSelect"
@check="onCheck"
@expand="expand"
:checkStrictly="checkStrictly"
/>
</div>
<!--
组件使用实例,其中很多参数看情况使用,不是必须使用,有存在的默认值
<PublicTree
url="/sys/role/queryTreeList"
:params="{}"
:checkable="checkable"
:checkedKeys.sync='checkedKeys'
:expandedKeys.sync="expandedKeys"
:checkStrictly='checkStrictly'
@onSelect="onSelect"
:replaceFields="{children: 'children',title: 'slotTitle',}"
/> -->
</template>
<script>
import { getPublicSelectList } from "@/api/api.js";
export default {
name: 'PublicTree',
props: {
/*树的接口地址url */
url: {
type: String,
default: ''
},
/* 如果当前树状图接口需要参数,传params,非必填,看实际接口情况 */
params: {
type: Object,
default: () => { }
},
/* 是否可以勾选,非必填 */
checkable: {
type: Boolean,
default: true
},
/* replaceFields重定向字段,非必填*/
replaceFields: {
type: Object,
default: () => ({
children: 'children',
title: 'title',
key: 'key'
})
},
/* 默认展开的key,非必填,需要通过expandedKeys.sync 来父组件获取实时值*/
expandedKeys: {
type: Array,
default: () => []
},
/* 勾选的key数组,非必填,但是一般情况下不要在子组件修改东西,通过checkedKeys.sync可以父组件获取 */
checkedKeys: {
type: Array,
default: () => []
},
/* true时,父子节点选中状态不再关联,非必填 */
checkStrictly: {
type: Boolean,
default: false
},
/* 非必填,数据是否需要处理,默认后端给处理好了,这里就不要操作。没有处理的数据,修改下方对应的id和父节点id实际字段。其中需要根据哪个字段排序就使用order,默认只能数字排序。其中boot是对应着根节点id,处理数据的时候得确保存在才能正常返回数据。deel代表是否需要自己处理数据,不需要就默认使用后端的返回,此时其余配置都失效,sort表示是否需要排序,为true的时候记得填写排序对应字段order。 */
deelTreeData: {
type: Object,
default: () => ({
deel: false,
sort: false,
config: {
id: 'id',
parentId: 'parentId',
children: 'children',
order: 'order',
},
boot: 0//根部id值
})
},
/* 接口如果不一样,返回的结构形式就会不一样,如何拿到对应的数据。这里就不通过对象遍历依次查找了,直接通过数组直接对应着一层一层的键。一切都通过返回的res基础上执行的,此项目默认res.result.treeList拿取对应的tree树,那么就用下面默认的形式。同理,后端返回keys数组一样同样的方法。 */
urlPointer: {
type: Array,
default: () => ['result', 'treeList']
},
/* 如上说明。标识集合id的位置 */
keysPointer: {
type: Array,
default: () => ['result', 'ids']
}
},
data() {
return {
treeData: [],
allTreeKeys: [],
checkedKeys_: [],
expandedKeys_: [],
}
},
watch: {
expandedKeys: {
handler(newvalue, oldvalue) {
setTimeout(() => {
this.$nextTick(() => {
this.expandedKeys_ = newvalue
})
}, 500);
}
},
expandedKeys_: {
handler(newvalue, oldvalue) {
this.$emit('update:expandedKeys', newvalue)
}
},
checkedKeys: {
handler(newvalue, oldvalue) {
this.$nextTick(() => {
this.checkedKeys_ = newvalue
})
}
},
checkedKeys_: {
handler(newvalue, oldvalue) {
this.$emit('update:checkedKeys', newvalue)
}
},
},
created() {
this.init()
},
beforeDestroy() {
this.reset()
},
methods: {
//数据树普通处理
treeDataNoSort(source, id, parentId, children) {
let cloneData = JSON.parse(JSON.stringify(source))
return cloneData.filter(father => {
let branchArr = cloneData.filter(child => father[id] == child[parentId]);
branchArr.length > 0 ? father[children] = branchArr : ''
return father[parentId] == this.deelTreeData.boot // 如果第一层不是parentId=0,请自行修改
})
/*
无处理数据样式
this.source = [
{id:1,parentId:0,name:"一级菜单A",rank:1,order:'3'},
{id:2,parentId:0,name:"一级菜单B",rank:1,order:'2'},
{id:3,parentId:0,name:"一级菜单C",rank:1,order:'1'},
{id:4,parentId:1,name:"二级菜单A-A",rank:2,order:'2'},
{id:5,parentId:1,name:"二级菜单A-B",rank:2,order:'1'},
{id:6,parentId:2,name:"二级菜单B-A",rank:2},
{id:7,parentId:4,name:"三级菜单A-A-A",rank:3},
{id:8,parentId:7,name:"四级菜单A-A-A-A",rank:4},
{id:9,parentId:8,name:"五级菜单A-A-A-A-A",rank:5},
{id:10,parentId:9,name:"六级菜单A-A-A-A-A-A",rank:6},
{id:11,parentId:10,name:"七级菜单A-A-A-A-A-A-A",rank:7},
{id:12,parentId:11,name:"八级菜单A-A-A-A-A-A-A-A",rank:8},
{id:13,parentId:12,name:"九级菜单A-A-A-A-A-A-A-A-A",rank:9},
{id:14,parentId:13,name:"十级菜单A-A-A-A-A-A-A-A-A-A",rank:10},
] */
},
//数据树排序处理
treeDataBySort(source, id, parentId, children, order) {
var compare = function (prop) {
return function (obj1, obj2) {
var val1 = obj1[prop];
var val2 = obj2[prop];
return val1 - val2 //升序
// if (val1 < val2) {
// console.log('val1,val2 :>> ', val1,val2);
// return -1;//表示第一个参数应该排在第二个参数的前面
// } else if (val1 > val2) {
// return 1;//表示第一个参数应该排在第二个参数的后面
// } else {
// return 0;
// }
}
}
let cloneData = JSON.parse(JSON.stringify(source))
return cloneData.filter(father => {
let branchArr = cloneData.filter(child => father[id] == child[parentId]).sort(compare(order));
branchArr.length > 0 ? father[children] = branchArr : ''
return father[parentId] == this.deelTreeData.boot // 如果第一层不是parentId=0,请自行修改
}).sort(compare(order));
},
init() {
getPublicSelectList(this.url, this.params).then(res => {
let data = res
let TreeData = res
for (const item of this.urlPointer) {
data = data[item]
}
for (const item of this.keysPointer) {
TreeData = TreeData[item]
}
this.treeData = data
this.allTreeKeys = TreeData || data.map(i => i[this.replaceFields['key']])
if (this.deelTreeData.deel) {
if (this.deelTreeData.sort) {
this.treeData = this.treeDataBySort(this.treeData, this.deelTreeData.config['id'], this.deelTreeData.config['parentId'], this.deelTreeData.config['children'], this.deelTreeData.config['order'])
} else {
this.treeData = this.treeDataNoSort(this.treeData, this.deelTreeData.config['id'], this.deelTreeData.config['parentId'], this.deelTreeData.config['children'])
}
}
this.expandedKeys_ = this.expandedKeys
this.checkedKeys_ = this.checkedKeys
})
},
onSelect(selectedKeys, info) {
this.$emit('onSelect', selectedKeys, info)
},
onCheck(checkedKeys, info) {
if (this.checkStrictly) {
this.checkedKeys_ = checkedKeys.checked;
this.$emit('update:checkedKeys', checkedKeys.checked)
} else {
this.checkedKeys_ = checkedKeys
this.$emit('update:checkedKeys', checkedKeys)
}
},
expand(expandedKeys) {
this.expandedKeys_ = expandedKeys
this.$emit('update:expandedKeys', expandedKeys)
},
/* 全选,父调用子 */
checkALL() {
this.checkedKeys_ = this.allTreeKeys
this.$emit('update:checkedKeys', this.allTreeKeys)
},
/* 取消全选,父调用子 */
cancelCheckALL() {
this.checkedKeys_ = []
this.$emit('update:checkedKeys', [])
},
/* 展开全部 */
expandAll() {
this.expandedKeys_ = this.allTreeKeys
this.$emit('update:expandedKeys', this.allTreeKeys)
},
/* 合并全部 */
closeAll() {
this.expandedKeys_ = []
this.$emit('update:expandedKeys', [])
},
reset() {
this.checkedKeys_ = []
this.expandedKeys_ = []
}
},
}
</script>
<style lang='less' scoped>
</style>
注意点:
1.此组件只配置了checkedkeys? ,expendedkeys,其余的就没搞双向绑定了。
2.例子在代码里面,可以直接用
3.有些props配置如果你们拿去得稍微的对组件改改。比如urlPointer,keysPointer。这两个实际作用看代码注释,再配合代码应该看懂不是问题,其实不改组件也问题不大,只是父节点就得添加个一两行绑定了。
4.此组件默认情况下后端已经把树结构给你了,很多配置你就不需要动他了。如果你后端懒得搞就想搞你,那你就需要配置deelTreeData参数。
5.仔细看最后几个方法,包括全选,取消全选,展开全部,取消展开全部四个方法。父组件如果需要就可以直接使用。这个应该没有问题,我好像还没有验证。
6.不会有小伙伴好奇getPublicSelectList方法是什么吧?这个根据每个人项目的接口封装不同而不同的。比如我这个
const getPublicSelectList = (url,params)=>getAction(url,params);
反正兄弟们,这里根据你们代码封装的接口自己处理一个这种结构的应该问题不大?。
7.关于树结构的部分怎么自己处理可以看看这里树数据处理
8.待优化的地方肯定是有的,反正能简单则简单,想到啥就弄啥?,发现代码中还是有点多余的部分,不过问题不大。能用不报错