无限树状列表的实现

作者: 瑟闻风倾 | 来源:发表于2019-06-04 09:08 被阅读2次
    树状列表示例效果.png

    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)">&or;</span>  
                    <span v-if="opends[i]"  @tap="onOpends(item,i)">&and;</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)
                },
    

    相关文章

      网友评论

        本文标题:无限树状列表的实现

        本文链接:https://www.haomeiwen.com/subject/nswcxctx.html