1. 无限极树形组件1
(1)组件源码tree-item.vue
<template>
<view>
<view class="tree-ul" v-for="(item,i) in data" :key="item.code+i">
<view class="isTrue" v-if="item.children">
<view class="tree-item">
<label>{{item.name}}</label>
<span v-if="!opends[i]" @tap="onOpends(item,i)">∨</span>
<span v-if="opends[i]" @tap="onOpends(item,i)">∧</span>
</view>
<tree-item class="children-box" :data="item.children" v-show="opends[i]"></tree-item>
</view>
<view class="tree-item isTrue" v-if="!item.children">
<label>{{item.name}}</label>
</view>
</view></view>
</template>
<script>
export default {
name: "tree-item",
props:{
data: {
type: Array,
default: function(e) {
return []
}
}
},
data() {
return {
opends: [],
};
},
onLoad() {
console.log("tree-item onLoad")
},
methods:{
onOpends(item,i){
this.$nextTick(function(){
this.$set(this.opends,i,!this.opends[i])
})
}
}
}
</script>
<style>
.tree-box{
display: inline-block;
width: 220px;
border: 1px solid #eee;
overflow: hidden;
border-radius: 4px;
}
.tree-item{
display: flex;
overflow: hidden;
height: 32px;
border-bottom: 1px solid #eee;
}
.tree-item>label{
flex: 1;
line-height: 32px;
font-size: 14px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.tree-item>span{
width: 32px;
height: 32px;
text-align: center;
line-height: 32px;
}
.isTrue{
padding-left: 15px;
}
</style>
(2)index.vue中调用
<template>
<view>
<view>该组件只能在h5端实现树状列表;无法实现微信小程序和android端的树状列表,只能显示一级而无法展开下级列表</view>
<view position="middle" mode="fixed" style="background-color: #FFFFFF;">
<scroll-view class="uni-bg-white" style="height:500upx;width:300upx" scroll-y="true">
<tree-folder :data="lists"></tree-folder>
</scroll-view>
</view>
</view>
</template>
<script>
import treeFolder from "@/components/tree/tree-item.vue";
export default {
components: {
treeFolder
},
data() {
return {
lists:[
{
name:"椒江新厂",
code:"001",
children:[
{
name:"生产部门A",
code:"021",
children:[
{
name:"A-01",
code:"031",
},
{
name:"A-02",
code:"032",
},
{
name:"A-03",
code:"033",
}
]
},
{
name:"生产部门B",
code:"022",
children:[
{
name:"B-01",
code:"034",
}
]
},
{
name:"生产部门C",
code:"023",
}
]
},
{
name:"杭州工厂",
code:"002",
},
{
name:"西安工厂",
code:"003",
}
]
}
},
methods: {
}
}
</script>
<style>
page {
display: flex;
flex-direction: column;
box-sizing: border-box;
background-color: #fff;
}
</style>
备注
:uni-app使用vue递归组件做无极树形列表时,在H5页面能正常显示(H5兼容递归组件本身),但是在微信小程序和android端只能显示第一级(小程序和安卓app无法递归)。该组件无法实现微信小程序和android端的树状列表,只能显示一级而无法展开下级列表。
2. 无限级树形组件2
(1)组件源码mix-tree.vue
<template>
<view class="content">
<view class="mix-tree-list">
<block v-for="(item, index) in treeList" :key="index">
<view
class="mix-tree-item"
:style="[{
paddingLeft: item.rank*15 + 'px',
zIndex: item.rank*-1 +50
}]"
:class="{
border: treeParams.border === true,
show: item.show,
last: item.lastRank,
showchild: item.showChild
}"
@click.stop="treeItemTap(item, index)"
>
<image class="mix-tree-icon" :src="item.lastRank ? treeParams.lastIcon : item.showChild ? treeParams.currentIcon : treeParams.defaultIcon"></image>
{{item.name}}
</view>
</block>
</view>
</view>
</template>
<script>
export default {
props: {
list: {
type: Array,
default(){
return [];
}
},
params: {
type: Object,
default(){
return {}
}
}
},
data() {
return {
treeList: [],
treeParams: {
defaultIcon: '/static/mix-tree/defaultIcon.png',
currentIcon: '/static/mix-tree/currentIcon.png',
lastIcon: '',
border: false
}
}
},
watch: {
list(list){
this.treeParams = Object.assign(this.treeParams, this.params);
console.log(this.treeParams, this.params);
this.renderTreeList(list);
}
},
onLoad() {
console.log("mix-tree onLoad")
},
methods: {
//扁平化树结构
renderTreeList(list=[], rank=0, parentId=[]){
list.forEach(item=>{
this.treeList.push({
id: item.id,
name: item.name,
parentId, // 父级id数组
rank, // 层级
showChild: false, //子级是否显示
show: rank === 0 // 自身是否显示
})
if(Array.isArray(item.children) && item.children.length > 0){
let parents = [...parentId];
parents.push(item.id);
this.renderTreeList(item.children, rank+1, parents);
}else{
this.treeList[this.treeList.length-1].lastRank = true;
}
})
},
// 点击
treeItemTap(item){
let list = this.treeList;
let id = item.id;
if(item.lastRank === true){
//点击最后一级时触发事件
this.$emit('treeItemClick', item);
return;
}
item.showChild = !item.showChild;
list.forEach(childItem=>{
if(item.showChild === false){
//隐藏所有子级
if(!childItem.parentId.includes(id)){
return;
}
if(childItem.lastRank !== true){
childItem.showChild = false;
}
childItem.show = false;
}else{
if(childItem.parentId[childItem.parentId.length-1] === id){
childItem.show = true;
}
}
})
}
}
}
</script>
<style>
.mix-tree-list{
display: flex;
flex-direction: column;
padding-left: 30upx;
}
.mix-tree-item{
display: flex;
align-items: center;
font-size: 30upx;
color: #333;
height: 0;
opacity: 0;
transition: .2s;
position: relative;
}
.mix-tree-item.border{
border-bottom: 1px solid #eee;
}
.mix-tree-item.show{
height: 80upx;
opacity: 1;
}
.mix-tree-icon{
width: 26upx;
height: 26upx;
margin-right: 8upx;
opacity: .9;
}
.mix-tree-item.showchild:before{
transform: rotate(90deg);
}
.mix-tree-item.last:before{
opacity: 0;
}
</style>
(2)index.vue中调用
<template>
<view>
<view>该组件可实现树状列表</view>
<view position="middle" mode="fixed" style="background-color: #FFFFFF;">
<scroll-view class="uni-bg-white" style="height:500upx;width:300upx" scroll-y="true">
<mix-tree :list="lists" @treeItemClick="treeItemClick"></mix-tree>
</scroll-view>
</view>
</view>
</template>
<script>
import mixTree from '@/components/mix-tree/mix-tree';
let testList = [
{
name:"椒江新厂",
id:"001",
children:[
{
name:"生产部门A",
id:"021",
children:[
{
name:"A-01",
id:"031",
},
{
name:"A-02",
id:"032",
},
{
name:"A-03",
id:"033",
}
]
},
{
name:"生产部门B",
id:"022",
children:[
{
name:"B-01",
id:"034",
}
]
},
{
name:"生产部门C",
id:"023",
}
]
},
{
name:"杭州工厂",
id:"002",
},
{
name:"西安工厂",
id:"003",
}
]
export default {
components: {
mixTree
},
data() {
return {
lists: [],
}
},
onLoad:function(){
//测试树状列表
setTimeout(()=>{
this.lists = testList;
}, 500);
console.log(this.lists);
},
methods: {
//点击最后一级时触发该事件
treeItemClick(item) {
let {
id,
name,
parentId
} = item;
uni.showModal({
content: `点击了${parentId.length+1}级菜单, ${name}, id为${id}, 父id为${parentId.toString()}`
})
console.log(item)
}
}
}
</script>
<style>
</style>
备注
:源码中的id和name字段需根据数据结构定义的字段名进行对应的修改。如网络获取的列表数据为:
"departmentList": [
{
"name": "椒江新厂",
"id": "001",
"children": [
{
"name": "生产部门A",
"id": "021",
"children": [
{
"name": "A-01",
"id": "031"
},
{
"name": "A-02",
"id": "032"
},
{
"name": "A-03",
"id": "033"
}
]
},
{
"name": "生产部门B",
"id": "022",
"children": [
{
"name": "B-01",
"id": "034"
}
]
},
{
"name": "生产部门C",
"id": "023"
}
]
},
{
"name": "杭州工厂",
"id": "002"
},
{
"name": "西安工厂",
"id": "003"
}
]
则源码修改为
<template>
<view class="content">
<view class="mix-tree-list">
<block v-for="(item, index) in treeList" :key="index">
<view
class="mix-tree-item"
:style="[{
paddingLeft: item.rank*15 + 'px',
zIndex: item.rank*-1 +50
}]"
:class="{
border: treeParams.border === true,
show: item.show,
last: item.lastRank,
showchild: item.showChild
}"
@click.stop="treeItemTap(item, index)"
>
<image class="mix-tree-icon" :src="item.lastRank ? treeParams.lastIcon : item.showChild ? treeParams.currentIcon : treeParams.defaultIcon"></image>
{{item.department_name}}
</view>
</block>
</view>
</view>
</template>
<script>
export default {
props: {
list: {
type: Array,
default(){
return [];
}
},
params: {
type: Object,
default(){
return {}
}
}
},
data() {
return {
treeList: [],
treeParams: {
defaultIcon: '/static/mix-tree/defaultIcon.png',
currentIcon: '/static/mix-tree/currentIcon.png',
lastIcon: '',
border: false
}
}
},
watch: {
list(list){
this.treeParams = Object.assign(this.treeParams, this.params);
console.log(this.treeParams, this.params);
this.renderTreeList(list);
}
},
onLoad() {
console.log("mix-tree onLoad")
},
methods: {
//扁平化树结构
renderTreeList(list=[], rank=0, parentId=[]){
list.forEach(item=>{
this.treeList.push({
department_id: item.department_id,
department_name: item.department_name,
parentId, // 父级id数组
rank, // 层级
showChild: false, //子级是否显示
show: rank === 0 // 自身是否显示
})
if(Array.isArray(item.children) && item.children.length > 0){
let parents = [...parentId];
parents.push(item.department_id);
this.renderTreeList(item.children, rank+1, parents);
}else{
this.treeList[this.treeList.length-1].lastRank = true;
}
})
},
// 点击
treeItemTap(item){
let list = this.treeList;
let id = item.department_id;
if(item.lastRank === true){
//点击最后一级时触发事件
this.$emit('treeItemClick', item);
return;
}
item.showChild = !item.showChild;
list.forEach(childItem=>{
if(item.showChild === false){
//隐藏所有子级
if(!childItem.parentId.includes(id)){
return;
}
if(childItem.lastRank !== true){
childItem.showChild = false;
}
childItem.show = false;
}else{
if(childItem.parentId[childItem.parentId.length-1] === id){
childItem.show = true;
}
}
})
}
}
}
</script>
<style>
.mix-tree-list{
display: flex;
flex-direction: column;
padding-left: 30upx;
}
.mix-tree-item{
display: flex;
align-items: center;
font-size: 30upx;
color: #333;
height: 0;
opacity: 0;
transition: .2s;
position: relative;
}
.mix-tree-item.border{
border-bottom: 1px solid #eee;
}
.mix-tree-item.show{
height: 80upx;
opacity: 1;
}
.mix-tree-icon{
width: 26upx;
height: 26upx;
margin-right: 8upx;
opacity: .9;
}
.mix-tree-item.showchild:before{
transform: rotate(90deg);
}
.mix-tree-item.last:before{
opacity: 0;
}
</style>
点击事件修改为:
//点击最后一级时触发该事件
treeItemClick(item) {
let {
department_id,
department_name,
parentId
} = item;
uni.showModal({
content: `点击了${parentId.length+1}级菜单, ${department_name}, id为${department_id}, 父id为${parentId.toString()}`
})
console.log(item)
},
网友评论