介绍
使用iview框架进行开发的话,一般都有展示树形结构数据的需求。那么就用的到其中的Tree组件了。但是其基本架构是满足不了我们的需求的,基本架构如下:
一般来说一条数据肯定不止展示它的title属性,我们的需求是展示多个属性,并且含有多个操作按钮,具体如下:
这其中得用到 Render 函数来自定义节点显示内容和交互。
下面就介绍一下Tree组件如何自定义节点内容。
自定义节点内容
先把完整的代码贴出来,然后在解释一波:
<template>
<Card class="album">
<Tree :data="treeData" :load-data="loadData" :render="renderContent"></Tree>
</Card>
</template>
<script>
import { mapActions } from 'vuex'
export default {
name: 'Album',
data () {
return {
treeData: [],
}
},
methods: {
...mapActions([
'albumCategoryList'
]),
loadData (item, callback) {
let parentId = item.id || 0
let data = []
this.albumCategoryList({
parentId: parentId
}).then((res) => {
if (res.status === 1) {
data = this.getTree(res.fields)
callback(data)
} else {
this.$Notice.error({
title: '错误',
desc: res.msg
})
}
}).catch(error => {
this.$Notice.error({
title: '错误',
desc: '网络连接错误'
})
console.log(error)
})
},
getTree (tree = []) {
let arr = [];
if (tree.length !== 0) {
tree.forEach(item => {
let obj = {};
obj.categoryName = item.categoryName;
obj.id = item.id; // 其他你想要添加的属性
obj.removed = item.removed; // 其他你想要添加的属性
obj.rank = item.rank; // 其他你想要添加的属性
obj.imageUrl = item.imageUrl; // 其他你想要添加的属性
obj.desc = item.desc; // 其他你想要添加的属性
obj.parentId = item.parentId; // 其他你想要添加的属性
if(item.child === 1) {
obj.children = [];
obj.loading = false;
}
arr.push(obj);
});
}
return arr
},
renderContent (h, { root, node, data }) {
return h('span', {
style: {
display: 'inline-block',
width: '100%'
}
}, [
h('span',[
h('Icon', {
style: {
marginRight: '8px'
}
}),
h('span', data.categoryName)
]),
h('span', {
style: {
display: 'inline-block',
float: 'right',
marginRight: '32px'
}
}, [
h('span', {
style: {
display: 'inline-block',
'text-align': 'center',
width: '25px',
marginRight: '40px'
}
}, data.rank),
h('span', {
style: {
display: 'inline-block',
width: '480px',
marginRight: '20px'
}
}, data.imageUrl || ''),
h('Button', {
props: Object.assign({}, {
type: 'primary',
size: 'small',
}),
style: {
marginRight: '8px'
},
on: {
click: () => { this.albumCategoryAdd(data) }
}
}, '添加'),
h('Button', {
props: Object.assign({}, {
type: 'primary',
size: 'small',
}),
style: {
marginRight: '8px'
},
on: {
click: () => { this.albumCategoryEdit(data) }
}
}, '编辑'),
h('Button', {
props: Object.assign({}, {
type: 'error',
size: 'small',
}),
on: {
click: () => { this.changeAlbumCategoryStatus(data.id, data.removed) }
}
}, data.removed === 0 ? '启用' : '停用' )
])
]);
},
// 页面加载后 查询parentID=0 的分类
queryCategoryList (parentId = 0) {
console.log('执行专辑分类queryCategoryList')
this.albumCategoryList({
parentId: parentId
}).then((res) => {
if (res.status === 1) {
// 这里给节点赋值
this.treeData = this.getTree(res.fields)
} else {
this.$Notice.error({
title: '错误',
desc: res.msg
})
}
}).catch(error => {
this.$Notice.error({
title: '错误',
desc: '网络连接错误'
})
console.log(error)
})
},
},
mounted () {
this.queryCategoryList()
}
}
</script>
<style scoped>
</style>
<Tree :data="treeData" :load-data="loadData" :render="renderContent"></Tree>
看过iview中Tree组件介绍的那就知道:data
是绑定展示的数据的,:load-data
是用于异步加载子节点的,:render
是用于自定义节点内容的。
先看一下我们的数据结构是啥啊:
我这是axios设置的response拦截器拦截的数据截了个图,也应该可以看清的吧…
child属性用来表示此节点是不是有子节点,1表示有,0表示无。别的属性就不介绍了。
首先,我们打开页面的时候应该把所有的顶级父节点即parentId = 0的节点展示出来,这是需要查询数据库的,所以在vue生命周期mounted函数中执行了顶级父节点的查找函数,即:
mounted () {
this.queryCategoryList()
}
// 页面加载后 查询parentID=0 的分类
queryCategoryList (parentId = 0) {
console.log('执行专辑分类queryCategoryList')
this.albumCategoryList({
parentId: parentId
}).then((res) => {
if (res.status === 1) {
// 这里给节点赋值
this.treeData = this.getTree(res.fields)
} else {
this.$Notice.error({
title: '错误',
desc: res.msg
})
}
}).catch(error => {
this.$Notice.error({
title: '错误',
desc: '网络连接错误'
})
console.log(error)
})
},
this.albumCategoryList
是使用axios发送请求的函数,这里没有展示出来,但请求得到的数据结构在上面也展示出来了,应该OK的吧!
this.getTree(res.fields)
这个函数主要是转换一下数据结构,其中要关注一下,当数据的child属性为1即其有子节点时,obj.children = []; obj.loading = false;这两个属性是必须附上的,否则不会应用异步加载效果。具体的可看看上面的完整代码。
下面看一下自定义节点内容的函数:
renderContent (h, { root, node, data }) {
return h('span', {
style: {
display: 'inline-block',
width: '100%'
}
}, [
h('span',[
h('Icon', {
style: {
marginRight: '8px'
}
}),
h('span', data.categoryName)
]),
h('span', {
style: {
display: 'inline-block',
float: 'right',
marginRight: '32px'
}
}, [
h('span', {
style: {
display: 'inline-block',
'text-align': 'center',
width: '25px',
marginRight: '40px'
}
}, data.rank),
h('span', {
style: {
display: 'inline-block',
width: '480px',
marginRight: '20px'
}
}, data.imageUrl || ''),
h('Button', {
props: Object.assign({}, {
type: 'primary',
size: 'small',
}),
style: {
marginRight: '8px'
},
on: {
click: () => { this.albumCategoryAdd(data) }
}
}, '添加'),
h('Button', {
props: Object.assign({}, {
type: 'primary',
size: 'small',
}),
style: {
marginRight: '8px'
},
on: {
click: () => { this.albumCategoryEdit(data) }
}
}, '编辑'),
h('Button', {
props: Object.assign({}, {
type: 'error',
size: 'small',
}),
on: {
click: () => { this.changeAlbumCategoryStatus(data.id, data.removed) }
}
}, data.removed === 0 ? '启用' : '停用' )
])
]);
},
这个render函数就不具体解释了吧,就是根据你定义的treeData数组中每一条数据来编织你要的样式。
最后就是异步加载子节点了:
loadData (item, callback) {
let parentId = item.id || 0
let data = []
this.albumCategoryList({
parentId: parentId
}).then((res) => {
if (res.status === 1) {
data = this.getTree(res.fields)
callback(data)
} else {
this.$Notice.error({
title: '错误',
desc: res.msg
})
}
}).catch(error => {
this.$Notice.error({
title: '错误',
desc: '网络连接错误'
})
console.log(error)
})
},
可以看到这里逻辑和queryCategoryList
函数差不多,只是查找条件变成了当前节点的id,表示查找当前节点的子节点。
PS:上面的完整代码是一整个VUE页面拷贝下来的,除了albumCategoryList
这个axios函数需要自己实现以外