動態樹形菜單的幾種遞歸寫法小結


VUE中遞歸算法實現樹形菜單的寫法:

<template>
    <div>
        <!-- 父組件將數據傳入子組件 -->
        <tree :msg='msg' />
    </div>
</template>

<script>

export default {  
    data (){  //模擬數據
        return{
            n:0,
            msg: [{name:'北京',
                    sub:[{name:'東城區',
                    sub:[
                        {name:'朝陽區'}
                    ]                 
                    },{name:'西城區',
                        sub:[
                            {name:'關虎屯'}
                        ]
                    
                    },{name:'南城區'},{name:'北城區'}]
                    }
                    ,{
                    name:'廣東',
                    sub:[{name:'廣州',
                        sub:[{name:'越秀區'},{name:'白雲區'},{name:'海珠區'}]
                        },
                        {name:'深圳',
                        sub:[{name:'蛇口區'},{name:'保安區'},{name:'鹽田區'}]
                        },
                    ]
                    },{
                    name:'湖北',
                    sub:[{name:'武漢',
                        sub:[{name:'江夏區'},{name:"洪山區"},{name:'江漢區'}]
                        },
                        {name:'天門市',
                        sub:[{name:'精靈'},{name:"小班"},{name:'打扮'}]
                        }]
                    }],
        }
    },
    // 注冊父組件
    components: {
        tree:{
            name:'gs',//遞歸的構造函數名

            //父組件模板,相當於構造函數return的值
            template:`
            <ul>
                <li v-for="(v,i) in msg" :key=i @click.stop.self='n=i'>  //第一層的數據,點擊之后,子集菜單會展開,其他子集菜單會關閉
                     {{v.name}} 
                     
                     <gs :msg=v.sub  v-if="i==n"/>//將下一層數據傳入構造函數,進行調用,形成遞歸,相當於自己調用自己,這一步是最關鍵的一步,
                </li>
            </ul> 
            `,
            props: ['msg'],//接受父組件傳的值
            data(){
                return{
                    n:0  //默認展開的菜單下標
                }
            }
        },
    }
}
</script>

 

JS遞歸方法實現:

<!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>
</head>

<body>
    <button onclick="fun()">遞歸測試</button>
    <div id="div">

    </div>

    <script>
        //模擬數據
        var data = [{
            name: 1,
            sub: [{
                name: '1-1',
                sub: []
            }]
        }, {
            name: 2,
            sub: [{
                    name: "2-1",
                    sub: [{
                            name: '2-1-1',
                            sub: []
                        }, {
                            name: '2-1-2',
                            sub: []
                        }

                    ]
                }, {
                    name: "2-2",
                    sub: [{
                        name: '2-2-1',
                        sub: []
                    }, {
                        name: '2-2-2',
                        sub: [{
                            name: '2-2-2-1',
                            sub: []
                        }]
                    }]
                }

            ]
        }, {
            name: 3,
            sub: []
        }]


        var div = document.getElementById('div')
        var str = '';

      //遞歸函數
function list(data) { if (data) { if (data.length > 0) { str += "<ul>"; for (let v = 0; v < data.length; v++) { const item = data[v]; str += '<li>' + item.name; list(item.sub) str += '</li>'; } str += "</ul>"; } } } list(data) console.log(str) div.innerHTML = str;


//以下是遞歸測試函數,與樹形菜單無關
var arr = []; function fun() { var num = parseFloat(prompt("輸入數字:")); if (typeof(num) == 'number') { while (num > 0) { arr.push(num) num--; } //console.log(arr) } } var arrlist = []; // var data=[1,[2,3],[4,[5,6,[8]]],9,[10]]; var fun3 = arr => [...arr].map((item) => Array.isArray(item) ? fun3(item) : arrlist.push(item)); fun3(data) // console.log( fun3(data)) </script> </body> </html>

 

使用VUE + element-ui   實現樹形菜單:

<template>
  <div class="custom-tree-container">
    <div class="block">
      <p>使用 render-content</p>
      <el-tree
        :data="data"
        show-checkbox
        node-key="id"
        default-expand-all
        :expand-on-click-node="false"
        :render-content="renderContent"
      ></el-tree>
    </div>
    <div class="block">
      <p>使用 scoped slot</p>
      <el-tree
        :data="data"
        show-checkbox
        node-key="id"
        default-expand-all
        :expand-on-click-node="false"
      >
        <span class="custom-tree-node" slot-scope="{ node, data }">
          <span>{{ node.label }}</span>
          <span>
            <el-button type="text" size="mini" @click="() => append(data)">Append</el-button>
            <el-button type="text" size="mini" @click="() => remove(node, data)">Delete</el-button>
          </span>
        </span>
      </el-tree>
    </div>

    <button @click="getAdd">+</button>
    <button @click="getjian">-</button>
  </div>
</template>
<script>
let id = 1000;

export default {
  data() {
    const data = [
      {
        name: "北京",
        sub: [
          {
            name: "北京",
            sub: [
              { name: "東城區" },
              { name: "西城區" },
              { name: "南城區" },
              { name: "北城區" }
            ]
          }
        ]
      },
      {
        name: "廣東",
        sub: [
          {
            name: "廣州",
            sub: [{ name: "越秀區" }, { name: "白雲區" }, { name: "海珠區" }]
          },
          {
            name: "深圳",
            sub: [{ name: "蛇口區" }, { name: "保安區" }, { name: "鹽田區" }]
          }
        ]
      },
      {
        name: "湖北",
        sub: [
          {
            name: "武漢",
            sub: [{ name: "江夏區" }, { name: "洪山區" }, { name: "江漢區" }]
          },
          {
            name: "天門市",
            sub: [{ name: "精靈" }, { name: "小班" }, { name: "打扮" }]
          }
        ]
      }
    ];
   //關鍵部分
    let _data = function(data) {
      return data.map(v => {
        if (v.sub) {        // 遍歷數據,將菜單數據賦值給 ui框架指定屬性名
          v.label = v.name;
          v.children = v.sub;
          return _data(v.sub);
        }
        if (v.name && !v.sub) {
          v.label = v.name;
          return 123;
        }
      });
    };
    _data(data);
    return {
      data: JSON.parse(JSON.stringify(data)),  //將處理過的數據進行深刻隆
      data: JSON.parse(JSON.stringify(data))
    };
  },

  methods: {
    append(data) {
      const newChild = { id: id++, label: "testtest", children: [] };
      if (!data.children) {
        this.$set(data, "children", []);
      }
      data.children.push(newChild);
    },

    remove(node, data) {
      const parent = node.parent;
      const children = parent.data.children || parent.data;
      const index = children.findIndex(d => d.id === data.id);
      children.splice(index, 1);
    },

    renderContent(h, { node, data, store }) {
      return (
        <span class="custom-tree-node">
          <span>{node.label}</span>
          <span>
            <el-button
              size="mini"
              type="text"
              on-click={() => this.append(data)}
            >
              Append
            </el-button>
            <el-button
              size="mini"
              type="text"
              on-click={() => this.remove(node, data)}
            >
              Delete
            </el-button>
          </span>
        </span>
      );
    },
    getAdd() {
      this.$store.commit("increment");
      console.log("我是加法" + this.$store.state.count);
    },
    getjian() {
      this.$store.commit("jian", 2);
      console.log("我是加Z法" + this.$store.state.count);
    }
  },
  mounted() {
    console.log(this.$store.state.count);
  }
};
</script>

<style>
.custom-tree-node {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: space-between;
  font-size: 14px;
  padding-right: 8px;
}
</style>

 js  實現無極限目錄樹完整版

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        div {
            margin-left: 20px;
            cursor: pointer;
        }
        .has-child {
            position: relative;
        }
        .has-child::before {
            content: '+';
            position: absolute;
            left: -20px;
        }
        .has-child > div {
            display: none;
        }
        .has-child.expend > div {
            display: block;
        }
        .has-child.expend::before {
            content: '-';
        }
    </style>
</head>
<body>
    <div id="demo"></div>
    <script>
        var data = [{
            name: '一級標題1',
            children: [{
                name: '二級標題1',
                children: [{
                    name: '三級標題'
                }]
            }, {
                name: '二級標題2',
                children: [{
                    name: '三級標題'
                }]
            }]
        }, {
            name: '一級標題2',
            children: [{
                name: '二級標題1',
                children: [{
                    name: '三級標題',
                    children: {
                        name: '四級標題'
                    }
                }]
            }]
        }, {
            name: '一級標題3',
            children: [{
                name: '二級標題1',
                children: [{
                    name: '三級標題'
                }]
            }, {
                name: '二級標題2',
                children: [{
                    name: '三級標題'
                }]
            }, {
                name: '二級標題3'
            }]
        }]
        
        // 用來創建目錄樹結構的函數
        /**
        * data 目錄樹的數據
        * parentNode:   將目錄樹結構插入到哪個節點下
        */
        function dTree(data, parentNode) {
            // 做一個兼容  如果當前沒有傳遞父節點則創建一個父節點
            if (!parentNode) {
                parentNode = document.createElement('div');
                parentNode.className = 'root';
            }
            // 遍歷數據中的每一項創建目錄樹的結構
            data.forEach(function (item) {
                var node = document.createElement('div');
                node.innerText = item.name;
                // 阻止事件冒泡
                node.onclick = function (e) {
                    e.stopPropagation();
                }
                // 判斷當前標題下面是否含有子標題如果含有的話繼續創建標題結構
                if(item.children && item.children.length > 0) {
                    // 如果含有子標題則添加一個has-child的類名
                    node.className = 'has-child';
                    // 如果含有子標題則當前的標題可以點擊展開
                    node.onclick = function (e) {
                        e.stopPropagation();
                        if (this.classList.contains('expend')) {
                            this.classList.remove('expend')
                        } else {
                            this.classList.add('expend')
                        }
                        
                    }
                    // 如果有子標題的話 需要繼續創建子標題的結構
                    dTree(item.children, node);
                }
                parentNode.appendChild(node);
            })
            // 當前函數直接返回創建出來的目錄樹結構  由於所有的結構都插入到了父節點當中,因此可以返回父節點
            return parentNode
        }

        var demo = document.getElementById('demo');

        dTree(data, demo);

    </script>
</body>
</html>

...


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM