• 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);
    复制代码

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

  • 相关阅读:
    Quartz使用总结
    ubuntu 16.04 下载源
    samba搭建
    搭建FTP服务器
    ubuntu 快捷图标
    mysql Fatal error encountered during command execution
    vs2013调试的时候卡顿
    javascript父窗口与子窗口通信
    mysql设置字体
    前台声明变量
  • 原文地址:https://www.cnblogs.com/applelife/p/11016616.html
Copyright © 2020-2023  润新知