zoukankan      html  css  js  c++  java
  • Php无限层级并显示层级数

    今天在处理递归无限层级菜单时,遇到一个稍微烧脑的问题,如何显示当前节点所在的层级数。
    废话不多说,我们先看个直观的无限层级:

    复制代码
    <?php
    // 这里的arr是直接从数据库取出的,仅作为测试数据
    $arr = array(
        array('id' => 1, 'name' => '一级菜单a', 'pid' => 0),// pid 父级id
        array('id' => 2, 'name' => '一级菜单b', 'pid' => 0),
        array('id' => 3, 'name' => '二级菜单a', 'pid' => 1),
        array('id' => 4, 'name' => '二级菜单b', 'pid' => 1),
        array('id' => 5, 'name' => '二级菜单c', 'pid' => 2),
        array('id' => 6, 'name' => '二级菜单d', 'pid' => 2),
        array('id' => 7, 'name' => '三级菜单a', 'pid' => 3),
        array('id' => 8, 'name' => '三级菜单b', 'pid' => 3),
        array('id' => 9, 'name' => '四级菜单a', 'pid' => 8),
    );
    
    /** 获取所有子节点
     * @param $data 所有节点数组
     * @param $id   $pid 父级节点id
     * @param $level  层级
     * @return array
     */
    function getTree($data, $pid, $level = 0)
    {
        $list = array();
        foreach ($data as $k => $v) {
            if ($v['pid'] == $pid) {
                $v['level'] = $level;
                $v['name'] = $v['name'].'('.($level+1).'级)'; // 这里可以加个层级次数
                $v['children'] = getTree($data, $v['id'], $level + 1);
                if ($v['children'] == null){
                    unset($v['children']);
                }
                $list[] = $v;
            }
        }
        return $list;
    }
    
    
    $menu = getTree($arr, 0, 0);
    
    $json = json_encode($menu);
    ?>
    <!DOCTYPE html>
    <html lang="en">
    
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>Document</title>
    
        <script src="https://cdn.jsdelivr.net/npm/vue"></script>
    
        <style>
            *{
                box-sizing: border-box;
                margin: 0;padding: 0;
            }
            *:before,*:after{
                box-sizing: border-box;
            }
            ul,
            li {
                list-style: none;
            }
    
            .l_tree_container {
                width: 100%;
                height: 100%;
                box-shadow: 0 0 3px #ccc;
                margin: 13px;
                position: relative;
            }
    
            .l_tree {
                width: calc(100% - 44px);
                height: 100%;
                padding-left: 42px;
            }
            .l_tree_branch {
                width: 100%;
                height: 100%;
                display: block;
                padding: 13px;
                position: relative;
            }
    
            .l_tree_branch .l_tree_children_btn {
                width: 19px;
                height: 19px;
                background-color: #23b1f0;
                font-size: 14px;
                text-align: center;
                color: #ffffff;
                outline: none;
                border: 0;
                cursor: pointer;
            }
    
            ul.l_tree:before {
                content: '';
                border-left: 1px dashed #999999;
                height: calc(100%);
                position: absolute;
                left: 10px;
                top: 0px;
            }
    
            .l_tree .l_tree_branch:last-child::before {
                content: '';
                width: 3px;
                height: calc(100% - 24px);
                display: block;
                background-color: #ffffff;
                position: absolute;
                bottom: 0;
                left: -34px;
            }
    
            .l_tree,
            .l_tree_branch {
                position: relative;
            }
    
            .l_tree_branch::after {
                content: '';
                width: 40px;
                height: 0;
                border-bottom: 1px dashed #000;
                position: absolute;
                right: calc(100% - 9px);
                top: 22px;
            }
    
            .l_tree_container>.l_tree::before,
            .l_tree_container>.l_tree>.l_tree_branch::after {
                display: none;
            }
        </style>
    </head>
    
    <body>
    
    <div id="demo">
        <div class="l_tree_container">
            <ew-tree :model="testdata"></ew-tree>
        </div>
    </div>
    
    <script>
        // 树组件
        Vue.component('ew-tree', {
            template: `
            <ul class="l_tree">
                <li class="l_tree_branch" v-for="item in model" :key="item.id">
                    <div class="l_tree_click">
                        <button type="button" class="l_tree_children_btn" v-if="item.children"  @click="toggle(item)">{{ !item.show ? '-' : '+' }}</button>
                        <span class="l_folder">{{ item.name }}</span>
                    </div>
                    <ew-tree v-show="!item.show" v-if="item.children" :model="item.children"></ew-tree>
                </li>
            </ul>`,
            props: {
                model: {}
            },
            methods: {
                toggle: function (item) {
                    var idx = this.model.indexOf(item)
                    Vue.set(this.model[idx], 'show', !item.show)
                }
            }
        });
        new Vue({
            el: "#demo",
            data() {
                return {
                    testdata: <?php echo $json?>
                }
            }
        })
    </script>
    
    </body>
    </html>
    复制代码

    我们看到所有节点层级数没问题,那么我如何查看节点中pid=3的所有节点层级关系呢

    $menu = getTree($arr, 3, 0);

    显然不对,况且菜单展示并不友好,pid=3的父节点至少要显示在顶层吧。层级数暂且不管,我们先解决如何显示顶层pid=3的树形结构:
    pid=3对应的节点是:二级菜单a。这个可以直接根据数据id查询出来,此处仅做演示哈。调整下代码:

    $menu = getTree($arr, 3, 0);
    $menu = array(['name' => '二级菜单a','children'=> $menu]);
    $json = json_encode($menu);

    OK,完美,同样要显示所有pid=1的节点层级关系,一样,pid=1对应的节点是 一级菜单a

    $menu = getTree($arr, 1, 0);
    $menu = array(['name' => '一级菜单a','children'=> $menu]);
    $json = json_encode($menu);

    好,回到 刚才的话题,如何正确的显示每个节点所在的层级数呢? 这里我也思考了很久,也没找到快捷的方法。
    最终我还是递归的查询本节点所有的父节点id集合:完整代码如下:

    复制代码
    <?php
    // 这里的arr是直接从数据库取出的,仅作为测试数据
    $arr = array(
        array('id' => 1, 'name' => '一级菜单a', 'pid' => 0),// pid 父级id
        array('id' => 2, 'name' => '一级菜单b', 'pid' => 0),
        array('id' => 3, 'name' => '二级菜单a', 'pid' => 1),
        array('id' => 4, 'name' => '二级菜单b', 'pid' => 1),
        array('id' => 5, 'name' => '二级菜单c', 'pid' => 2),
        array('id' => 6, 'name' => '二级菜单d', 'pid' => 2),
        array('id' => 7, 'name' => '三级菜单a', 'pid' => 3),
        array('id' => 8, 'name' => '三级菜单b', 'pid' => 3),
        array('id' => 9, 'name' => '四级菜单a', 'pid' => 8),
    );
    
    /** 获取所有子节点
     * @param $data 所有节点数组
     * @param $id $pid 父级节点id
     * @param $level  层级
     * @return array
     */
    function getTree($data, $pid, $level = 0)
    {
        $list = array();
        foreach ($data as $k => $v) {
            if ($v['pid'] == $pid) {
                $v['level'] = $level;
                $v['name'] = $v['name'] . '(' . ($level + 1) . '级)'; // 这里可以加个层级次数
                $v['children'] = getTree($data, $v['id'], $level + 1);
                if ($v['children'] == null) {
                    unset($v['children']);
                }
                $list[] = $v;
            }
        }
        return $list;
    }
    
    /** 根据子节点获取父节点id
     * @param $data 所有节点数组
     * @param $id   id  主键id
     * @return array
     */
    function getParentid($data, $id)
    {
        $arr = array();
        foreach ($data as $v) {
            if ($v['id'] == $id) {
                $arr[] = $v;
                //$arr[$v['id']]=$v['name'];
                $arr = array_merge(getParentid($data, $v['pid']), $arr);
            }
        }
        return $arr;
    
    }
    
    $id = 8 ; // 对应的节点是: 三级菜单b 对应的pid 是 3
    $pid = 3; // 对应的节点是  二级菜单a
    
    $toparr = getParentid($arr, $id); // 节点为8的所有父节点 id: 1 3  8 这里包含了自身,注意剔除
    
    $level = count($toparr); // 节点所在的层级数
    $menu = getTree($arr, $pid, $level-1);
    if($pid)
        $menu = array(['name' => '二级菜单a' . '(' . ($level-1) . '级)', 'children' => $menu]);
    
    $json = json_encode($menu);
    ?>
    <!DOCTYPE html>
    <html lang="en">
    
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>Document</title>
    
        <script src="https://cdn.jsdelivr.net/npm/vue"></script>
    
        <style>
            * {
                box-sizing: border-box;
                margin: 0;
                padding: 0;
            }
    
            *:before, *:after {
                box-sizing: border-box;
            }
    
            ul,
            li {
                list-style: none;
            }
    
            .l_tree_container {
                width: 100%;
                height: 100%;
                box-shadow: 0 0 3px #ccc;
                margin: 13px;
                position: relative;
            }
    
            .l_tree {
                width: calc(100% - 44px);
                height: 100%;
                padding-left: 42px;
            }
    
            .l_tree_branch {
                width: 100%;
                height: 100%;
                display: block;
                padding: 13px;
                position: relative;
            }
    
            .l_tree_branch .l_tree_children_btn {
                width: 19px;
                height: 19px;
                background-color: #23b1f0;
                font-size: 14px;
                text-align: center;
                color: #ffffff;
                outline: none;
                border: 0;
                cursor: pointer;
            }
    
            ul.l_tree:before {
                content: '';
                border-left: 1px dashed #999999;
                height: calc(100%);
                position: absolute;
                left: 10px;
                top: 0px;
            }
    
            .l_tree .l_tree_branch:last-child::before {
                content: '';
                width: 3px;
                height: calc(100% - 24px);
                display: block;
                background-color: #ffffff;
                position: absolute;
                bottom: 0;
                left: -34px;
            }
    
            .l_tree,
            .l_tree_branch {
                position: relative;
            }
    
            .l_tree_branch::after {
                content: '';
                width: 40px;
                height: 0;
                border-bottom: 1px dashed #000;
                position: absolute;
                right: calc(100% - 9px);
                top: 22px;
            }
    
            .l_tree_container > .l_tree::before,
            .l_tree_container > .l_tree > .l_tree_branch::after {
                display: none;
            }
        </style>
    </head>
    
    <body>
    
    <div id="demo">
        <div class="l_tree_container">
            <ew-tree :model="testdata"></ew-tree>
        </div>
    </div>
    
    <script>
        // 树组件
        Vue.component('ew-tree', {
            template: `
            <ul class="l_tree">
                <li class="l_tree_branch" v-for="item in model" :key="item.id">
                    <div class="l_tree_click">
                        <button type="button" class="l_tree_children_btn" v-if="item.children"  @click="toggle(item)">{{ !item.show ? '-' : '+' }}</button>
                        <span class="l_folder">{{ item.name }}</span>
                    </div>
                    <ew-tree v-show="!item.show" v-if="item.children" :model="item.children"></ew-tree>
                </li>
            </ul>`,
            props: {
                model: {}
            },
            methods: {
                toggle: function (item) {
                    var idx = this.model.indexOf(item)
                    Vue.set(this.model[idx], 'show', !item.show)
                }
            }
        });
        new Vue({
            el: "#demo",
            data() {
                return {
                    testdata: <?php echo $json?>
                }
            }
        })
    </script>
    
    </body>
    </html>
    复制代码

    同样要显示所有节点:直接把pid赋值0,比如:

    复制代码
    $id = 2 ; // 对应的节点是: 一级菜单b 对应的pid 是 0
    $pid = 0; // 对应的节点是 0 为 一级节点  显示所有节点,上面的$id用不到
    
    $toparr = getParentid($arr, $id); //
    
    $level = count($toparr); // 节点所在的层级数
    $menu = getTree($arr, $pid, $level-1);
    if($pid)
        $menu = array(['name' => '二级菜单a' . '(' . ($level-1) . '级)', 'children' => $menu]);
    
    $json = json_encode($menu);
    复制代码

     看起来没毛病,博友们如果有好的办法获取任意一个节点所处的层级数,欢迎拍砖留言哈。

  • 相关阅读:
    Chapter 03Using SingleRow Functions to Customize Output(03)
    Chapter 03Using SingleRow Functions to Customize Output(01)
    Chapter 04Using Conversion Functions and Conditional ExpressionsNesting Functions
    Chapter 04Using Conversion Functions and Conditional ExpressionsGeneral Functions
    Chapter 11Creating Other Schema Objects Index
    传奇程序员John Carmack 访谈实录 (zz.is2120)
    保持简单纪念丹尼斯里奇(Dennis Ritchie) (zz.is2120.BG57IV3)
    王江民:传奇一生 (zz.is2120)
    2011台湾游日月潭
    2011台湾游星云大师的佛光寺
  • 原文地址:https://www.cnblogs.com/applelife/p/11016616.html
Copyright © 2011-2022 走看看