The background,

Due to the small program project to display the tree diagram of local industrial chain, the width of the mobile phone is limited, and the problem of visual area cannot be changed by sliding left and right, so A single-column tree view function is designed. As there is no small program plug-in and related cases that meet the conditions in a short time, so it avoids wasting time to write one directly.

First attach the cover image of the VUE version of the source, out of the box: KTree

Because I didn’t know where to put the wechat small program code, I modified a version of VUE3 and submitted it to Github. The reason is the same.

Then attach wechat small program to achieve the effect:

Second, the effect of

This implementation idea is relatively clear. The recursive method is used to traverse all the children under the selected first-level node, and then continue the first node to traverse all the children, and repeat this action until no children can be found.

I’m encapsulating a component, passing in a subset of the current node, rendering a list of subsets inside the component, and then determining if there’s a subset under the first node in the list, tuning the component itself, passing in a subset of the first node, and so on and so forth.

HTML structure

<template>
<div class="c-viewTree-container">
    <div class="tree-node">
        <div :class="{'node-item': true, 'node-hide': ! item.label}" v-for="(item, index) in treeData" :key="item.id">
            <div class="item-vertical-top">
                <div class="vertical-line"></div>
                <i class="el-icon-caret-bottom"></i>
                <div :class="{'vertical-transverse': true, 'none-transverse-last': item.lastLineHide, 'none-transverse-first': item.firstLineHide}" v-if=! "" item.only && treeData.length > 1"></div>
            </div>
            <div class="item-content">
                <div :class="{'content-box': true, 'constnt-pointer': item.children && item.children.length > 0}" @click="treeChange(item, index)">
                    <div class="item-text">{{item.label}}</div>
                    <div :class="{'item-icon': true, 'item-icon-active': targetTree.id === item.id}">
                        <i class="iconfont">Square root</i>
                    </div>
                </div>
            </div>
            <div class="item-vertical-bottom" v-if="targetTree.id === item.id && targetTree.children && targetTree.children.length > 0">
                <div class="vertical-line"></div>
            </div>
        </div>
    </div>
    <CKTree v-if="targetTree.children && targetTree.children.length > 0" :treeData="targetTree.children" @KTreeChange="viewClick">
</div>
</template>
Copy the code

Points, lines and triangles are all made up of node styles (I thought pseudo-class styles were more appropriate, but I didn’t bother to change them). The layout is solid, so it doesn’t have to worry about falling apart! (I was worried, too.)

Point, triangle judgment of the lower nodes on the rendering. The line is judged by whether there are brothers to control the length 100% or 50%, and left or right.

When switching nodes, take a subset of the currently selected nodes to recursively render.

Nodes are as follows:

Single processing

Each time a component is rendered, it is positioned to fetch a current node to continue rendering its children.

if (props.treeData && props.treeData.length > 0) {
    let attr = props.treeData.findIndex((item:any) = > { return item.id })
    state.targetIndex = 0
        if (attr > -1) {
            state.targetIndex = attr
            treeRegular(props.treeData[attr])
        } else {
            state.targetIndex = 0}}else {
    state.targetTree = {}
}
Copy the code

The treeRegular method is used to deal with assembling the number of child nodes to ensure alignment, as well as point and line drawing logic. TargetIndex is the subscript of the current node set that needs to go down to the subset.

First, if the length of the subset of the currently selected node is larger than the length of the current node and its sibling nodes, the line length of the first tree node and the last tree node is judged to be 50% and to the right and left respectively.

const treeRegular:Function = (res:any) = > {
    if (res && res.children && res.children.length > 0) {
        let attr = JSON.parse(JSON.stringify(res))
        if (props.treeData.length > attr.children.length) {
            ...
        }
        attr.children.forEach((item: any, index: any) = > {
            if(! attr.children[index].id && attr.children[index -1]) {
                attr.children[index - 1].lastLineHide = true
            }
            if(! attr.children[index].id && attr.children[index +1]) {
                attr.children[index + 1].firstLineHide = true
            }
        })
        state.targetTree = attr
    } else {
        state.targetTree = {}
    }
}
Copy the code

Then if the length of the subset of the currently selected node is smaller than the length of the current node and its siblings, to adjust the alignment position, an empty set of the current node and its siblings is first set.

const treeRegular:Function = (res:any) = >{... attr.children = []for(let i=0; i < chainLen; i++) {
        attr.children.push({})
    }
    ...
}
Copy the code

If the length of the subset of the currently selected node is 1, set a horizontal hidden identifier only, and then insert the unique node at the same location of the subset with the subscript targetIndex.

const treeRegular:Function = (res:any) = >{...let attr = JSON.parse(JSON.stringify(res))
    res.children[0].only = true
    attr.children.splice(state.targetIndex, 1, res.children[0])... }Copy the code

If the length of the subset of the currently selected node is greater than 1, the traversal will judge each subset of the subset. If the subset length of the current subset targetIndex is less than the length of the current set, the subnodes will be inserted to the right of the targetIndex of the empty set atr. Children. Otherwise, the children are inserted to the left of the empty set targetIndex at the attr. Children subscript.

const treeRegular:Function = (res:any) = >{...let dataLen = res.children.length
    for(let i=0; i < dataLen; i++) {
        if ((dataLen + state.targetIndex) < chainLen) {
            attr.children.splice(state.targetIndex + i, 1, res.children[i])
        } else {
            attr.children.splice(chainLen - (i + 1), 1, res.children[i])
        }
    } 
    ...
}
Copy the code

Finally, the newly created subset that needs to be displayed is passed into the next layer of components. The complete code is as follows

const treeRegular:Function = (res:any) = > {
    if (res && res.children && res.children.length > 0) {
        let attr = JSON.parse(JSON.stringify(res))
        if (props.treeData.length > attr.children.length) {
            let dataLen = res.children.length
            let chainLen = props.treeData.length
            attr.children = []
            for(let i=0; i < chainLen; i++) {
                attr.children.push({})
            }
            if (dataLen === 1) {
                res.children[0].only = true
                attr.children.splice(state.targetIndex, 1, res.children[0])}else {
                for(let i=0; i < dataLen; i++) {
                    if ((dataLen + state.targetIndex) < chainLen) {
                        attr.children.splice(state.targetIndex + i, 1, res.children[i])
                    } else {
                        attr.children.splice(chainLen - (i + 1), 1, res.children[i])
                    }
                } 
            }
        }
        attr.children.forEach((item: any, index: any) = > {
            if(! attr.children[index].id && attr.children[index -1]) {
                attr.children[index - 1].lastLineHide = true
            }
            if(! attr.children[index].id && attr.children[index +1]) {
                attr.children[index + 1].firstLineHide = true
            }
        })
        state.targetTree = attr
    } else {
        state.targetTree = {}
    }
}
Copy the code

conclusion

When the current level is smaller than the previous level, an empty set is created by the previous level’s length for alignment, and a new subset is generated by calculating the position for the next level rendering. This needs to be improved later. Don’t ask me why I don’t use canvas.

Like Diandian Star.

Even if the wind comes, life does not give up. The wind, when courageously intention this life.