在uniapp中由于没有封装好的树形组件,只能自己纯手写实现。树形结构是无限层级的,所以想到了递归,使用组件循环技术实现。
子组件: tree-item.vue
<view>
<view style="display: flex;line-height: 30px;" @click="clickNode(data)">
<view v-if="data.children && data.children.length">
<uni-icons v-if="data.isOpen" type="bottom" size="16" color="#2A5EFF"></uni-icons>
<uni-icons v-else type="forward" size="16"></uni-icons>
</view>
<view style="padding-left: 10px;">{{ data.label }}</view>
</view>
<view v-if="data.children && data.children.length && data.isOpen" style="margin-left: 16px;">
<tree-item v-for="(child,index) in data.children" :key="child.id" :node="child"
@clickNode="clickNode"></tree-item>
</view>
</view>
<script>
export default {
props: ['node'],
name: "TreeItem",
data() {
return {
data: null
};
},
computed: {},
methods: {
clickNode(item) {
console.log(item)
if (item.type) {
this.$emit('clickNode', item)
} else {
item.isOpen = !item.isOpen
}
}
},
created() {
this.data = this.node
}
}
</script>
父组件popupTree.vue
//uni-popup,uni-icons,uni-easyinput记得引入 在uniapp中的扩展组件中。
也可使用uview中封装好的popup
<uni-popup ref="popup" background-color="#fff" :mask-click="false" :is-mask-click="false">
<view style="width: 75vw;">
<view class="header">
<text style="font-size: 16px;">人员选择</text>
<uni-icons type='closeempty' @click="close()" size="18"></uni-icons>
</view>
<view style="padding: 10px 10px 0 10px;">
<uni-easyinput suffixIcon="search" v-model="value" placeholder="请输入人名搜索" @iconClick="iconClick"
@blur="iconClick" trim></uni-easyinput>
</view>
<view style="padding: 10px;" v-if="loading">
<scroll-view style="height: 400px;" scroll-y="true">
<tree-item v-for="(item,index) in treeData" :key="item.id" :node="item" @clickNode="clickNode"></tree-item>
</scroll-view>
</view>
</view>
</uni-popup>
<script>
export default {
props: ['show'],
name: "popupTree",
data() {
return {
loading: true,
value: '',
data: [],
treeData: []
};
},
watch: {
show(val) {
if (val) {
this.$refs.popup.open()
} else {
this.$refs.popup.close()
}
}
},
methods: {
close() {
this.$emit('closePop')
},
clickNode(item) {
this.$emit('clickNode', item)
this.$emit('closePop')
},
//扁平数据转换成树
convertToTreeData(data, pid) {
const result = []
let temp = []
for (let i = 0; i < data.length; i++) {
if (data[i].pid === pid) {
const obj = {
label: data[i].name,
id: data[i].id,
isOpen: data[i].type !== 'user'&&data[i].pid===null,
type: data[i].type === 'user'
}
temp = this.convertToTreeData(data, data[i].id)
if (temp.length > 0) {
obj.children = temp
}
result.push(obj)
}
}
return result
},
//从后端获取人员列表(拿到的是扁平的数据结构)
getUserTree() {
uni.$u.http.post('/dept/listAll').then(res => {
this.data = this.convertToTreeData(res, null)
const data = JSON.parse(JSON.stringify(this.data))
console.log(data)
this.treeData = this.filterData(this.data, this.value || '')
console.log(this.treeData)
})
},
//筛选出符合条件的数据
filterData(data, keyword) {
return data.filter(node => {
// 判断当前节点是否匹配筛选条件
if (node.label.includes(keyword)) {
if(!node.type){
node.isOpen=true
}
return true;
}
// 递归过滤子节点
if (node.children && node.children.length) {
node.children = this.filterData(node.children, keyword);
node.isOpen=true
return node.children.length > 0;
}
return false;
});
},
//点击搜索
iconClick() {
this.loading = false
const data = JSON.parse(JSON.stringify(this.data))
this.treeData = this.filterData(data, this.value || '')
setTimeout(() => {
this.loading = true
}, 10)
}
},
mounted() {
this.getUserTree()
}
}
</script>
<style lang="scss" scoped>
.header {
display: flex;
justify-content: space-between;
border-bottom: 1px solid rgb(214, 215, 217);
padding: 10px;
}
</style>
实现效果: