knockout.js实现一个级联下拉框

之前面试时碰上一道knockout.js的面试题,网上没有太多的相关资料,所以这里就记录一下。

题目

用knockout实现一个级联下拉,数据受前面的下拉控制(注意:下拉可能不是级联中下拉(可能是文本框)控制)。数据可以直接写死在页面中。下拉层级不少于4级。
A是完全级联例如(总公司-分公司-部门----人员姓名),B是其中有不是属于级联的 例如(总公司-分公司-部门、入职年份(非级联中字段) ---控制 人员姓名)

原方法

根据之前所按照网上原有的方法进行修改后:
完全级联A:
完全级联A

不完全级联B:
不完全级联B

代码如下:

完全级联A:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>document</title>
  </head>
  <body>
    <div>
      <div style="width:500px;"></div>
      <div>
        <span>完全级联</span>
        <div style="display: inline-block">
          <span id="container">
            <select
              data-bind="options:ch_array, optionsText:'name', optionsValue:'id',optionsCaption: 'Choose...',value:selected,event:{change:OnSelected}"
            ></select>
            <!--ko if:typeof ch_array ==='function' && ch_array().length>0-->
            <span
              data-bind="template: { name: 'ko_ch_array',foreach:ch_array }"
            ></span>
            <!--/ko-->
          </span>
          <script type="text/html" id="ko_ch_array">
            <!--ko if:id()==$parentContext.$data.selected() && typeof ch_array ==='function' && ch_array().length>0-->
            <select
              data-bind="options:ch_array, optionsText:'name', optionsValue:'id',optionsCaption: 'Choose...',value:selected"
            ></select>
            <!--ko if:ch_array().length>0-->
            <span
              data-bind="template: { name: 'ko_ch_array',foreach:ch_array }"
            ></span>
            <!--/ko-->
            <!--/ko-->
          </script>
        </div>
      </div>
    </div>
  </body>
</html>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.5.0/knockout-min.js"></script>
<script src="./knockout.mapping-latest.js"></script>
<script type="text/javascript">
  // 初始化数据
  function initData() {
    let create_comps_data = [
      {
        id: 1,
        name: "教育总公司",
        ch_array: [
          {
            id: 2,
            name: "教育总公司重庆分公司",
            ch_array: [
              {
                id: 3,
                name: "教育总公司重庆分公司采购部门",
                ch_array: [
                  { name: "王大锤1111111", id: 4 },
                  { name: "吴大勇111111", id: 5 },
                  { name: "圣埃蒂安11111111", id: 6 },
                ],
              },
              {
                id: 7,
                name: "教育总公司重庆分公司研发部门",
                ch_array: [
                  { name: "阿斯顿当过官发", id: 8 },
                  { name: "的发射点", id: 9 },
                  { name: "是德国商代给", id: 10 },
                  { name: "阿三大苏打撒旦法发是", id: 111 },
                  { name: "啊实打实打算人", id: 101 },
                  { name: "月台与用途很广泛", id: 1011 },
                ],
              },
            ],
          },
          {
            id: 11,
            name: "教育总公司深圳分公司",
            ch_array: [
              {
                id: 12,
                name: "教育总公司深圳分公司采购部门",
                ch_array: [
                  { name: "分割符号", id: 13 },
                  { name: "啊实打实的", id: 14 },
                  { name: "嘀咕嘀咕附件", id: 15 },
                ],
              },
              {
                id: 16,
                name: "教育总公司深圳分公司研发部门",
                ch_array: [
                  { name: "的说法是大哥", id: 17 },
                  { name: "快乐和快乐", id: 18 },
                  { name: "啊实打实啊实打实", id: 191 },
                  { name: "啊实打实阿斯弗", id: 192 },
                  { name: "啊实打实地方", id: 193 },
                  { name: "啊实打实大师傅", id: 194 },
                ],
              },
            ],
          },
          {
            id: 20,
            name: "教育总公司北京分公司",
            ch_array: [],
          },
        ],
      },
      {
        id: 21,

        name: "食品总公司",
        ch_array: [
          {
            id: 22,
            name: "食品总公司深圳分公司",
            ch_array: [],
          },
          {
            id: 23,
            name: "食品总公司重庆分公司",
            ch_array: [],
          },
          {
            id: 24,
            name: "食品总公司北京分公司",
            ch_array: [],
          },
        ],
      },
      {
        id: 25,
        name: "科技总公司",
        ch_array: [
          {
            id: 26,
            name: "科技总公司深圳分公司",
            ch_array: [],
          },
          {
            id: 27,
            name: "科技总公司重庆分公司",
            ch_array: [],
          },
        ],
      },
    ];
    let data = {};
    let model = create_comps_data;
    initSelected(model); 
    data.ch_array = model;
    data.selected = "";
    return data;
  }
  function initSelected(list) {
    for (var i = 0, n = list.length; i < n; i++) {
      list[i].selected = "";
      list[i].id = Math.random(100000); // 随机赋值 防止id相同出现bug
      if (
        list[i].ch_array &&
        list[i].ch_array instanceof Array &&
        list[i].ch_array.length > 0
      )
        initSelected(list[i].ch_array);
    }
  }


  var VM = ko.mapping.fromJS(initData());
  ko.applyBindings(VM, document.getElementById("container"));

  function OnSelected($data, event) {
    cleanUpSelected($data.ch_array());
  }
 // 还原初始化selected
  function cleanUpSelected(item) {
    item.forEach(function (value, index, array) {
      if (typeof value.selected === "function") value.selected("");
      if (typeof value.ch_array === "function")
        cleanUpSelected(value.ch_array());
    });
  }
</script>

不完全级联B:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>document</title>
  </head>
  <body>
    <div>
      <div style="width: 500px"></div>
      <div>
        <span>不完全级联</span>
        <div style="display: inline-block">
          <span id="container">
            <select
              data-bind="options:ch_array, optionsText:'name', optionsValue:'id',optionsCaption: 'Choose...',value:selected,event:{onblur:OnSelected}"
            ></select>
            <!--ko if:typeof ch_array ==='function' && ch_array().length>0-->
            <span
              data-bind="template: { name: 'ko_ch_array',foreach:ch_array }"
            ></span>
            <!--/ko-->
          </span>
          <script type="text/html" id="ko_ch_array">
            <!--ko if:id()==$parentContext.$data.selected() && typeof ch_array ==='function' && ch_array().length>0-->
            <!--ko if:ch_array()[0].time-->
            <input
              type="number"
              data-bind="value:$data.selectedyear,event:{change:inputChange($data,$root,$parent)}"
            />

            <select
              data-bind="visible:$root.show,options:$root.search, optionsText:'name', optionsValue:'id',optionsCaption: 'Choose...',value:selected"
            ></select>

            <!--/ko-->
            <!--ko ifnot:ch_array()[0].time-->
            <select
              data-bind="options:ch_array, optionsText:'name', optionsValue:'id',optionsCaption: 'Choose...',value:selected"
            ></select>
            <!--/ko-->
            <!--ko if:ch_array().length>0-->
            <span
              data-bind="template: { name: 'ko_ch_array',foreach:ch_array }"
            ></span>
            <!--/ko-->

            <!--/ko-->
          </script>
        </div>
      </div>
    </div>
  </body>
</html>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.5.0/knockout-min.js"></script>
<script src="./knockout.mapping-latest.js"></script>
<script type="text/javascript">
  // 初始化数据
  function initData() {
    let create_comps_data = [
      {
        id: 1,
        name: "教育总公司",
        ch_array: [
          {
            id: 2,
            name: "教育总公司重庆分公司",
            ch_array: [
              {
                id: 3,
                name: "教育总公司重庆分公司采购部门",
                ch_array: [
                  { name: "王大锤", id: 4, time: 1991 },
                  { name: "吴大勇", id: 5, time: 1991 },
                  { name: "大苏打", id: 6, time: 1991 },
                  { name: "刘大力", id: 7, time: 1995 },
                  { name: "阿萨大大", id: 8, time: 1997 },
                  { name: "阿斯顿", id: 9, time: 1997 }
                ],
              },
              {
                id: 10,
                name: "教育总公司重庆分公司研发部门",
                ch_array: [
                  { name: "王大锤1111111", id: 8, time: 1991 },
                  { name: "吴大勇222222222", id: 9, time: 1991 },
                  { name: "圣埃蒂安33333333333", id: 10, time: 1991 },
                ],
              },
            ],
          },
          {
            id: 11,
            name: "教育总公司深圳分公司",
            ch_array: [
              {
                id: 12,
                name: "教育总公司深圳分公司采购部门",
                ch_array: [
                  { name: "王大爷44444444", id: 13, time: 1990 },
                  { name: "张大侠1111111111", id: 14, time: 1995 },
                  { name: "刘老铁22222222222", id: 15, time: 1991 },
                ],
              },
              {
                id: 16,
                name: "教育总公司深圳分公司研发部门",
                ch_array: [
                  { name: "深圳王大锤44", id: 17, time: 2000 },
                  { name: "深圳吴大勇22", id: 18, time: 2001 },
                  { name: "深圳圣埃蒂安44", id: 19, time: 2002 },
                ],
              },
            ],
          },
          {
            id: 20,
            name: "教育总公司北京分公司",
            ch_array: [ {
                id: 3,
                name: "教育总公司北京分公司采购部门",
                ch_array: [
                  { name: "王大锤21", id: 4, time: 1991 },
                  { name: "吴大勇12", id: 5, time: 1991 },
                  { name: "大苏打212", id: 6, time: 1991 },
                  { name: "刘大力", id: 7, time: 1995 },
                  { name: "阿萨大大12", id: 8, time: 1997 },
                  { name: "阿斯顿12", id: 9, time: 1997 }
                ],
              },
              {
                id: 10,
                name: "教育总公司北京分公司研发部门",
                ch_array: [
                  { name: "王大锤123", id: 8, time: 1991 },
                  { name: "吴大勇123", id: 9, time: 1991 },
                  { name: "圣埃蒂安123", id: 10, time: 1991 },
                ],
              },],
          },
        ],
      },
      {
        id: 21,

        name: "食品总公司",
        ch_array: [
          {
            id: 22,
            name: "食品总公司深圳分公司",
            ch_array: [],
          },
          {
            id: 23,
            name: "食品总公司重庆分公司",
            ch_array: [],
          },
          {
            id: 24,
            name: "食品总公司北京分公司",
            ch_array: [],
          },
        ],
      },
      {
        id: 25,
        name: "科技总公司",
        ch_array: [
          {
            id: 26,
            name: "科技总公司深圳分公司",
            ch_array: [],
          },
          {
            id: 27,
            name: "科技总公司重庆分公司",
            ch_array: [],
          },
        ],
      },
    ];
    let data = {};
    let model = create_comps_data;
    initSelected(model);
    data.ch_array = model; 
    data.selected = "";
    data.show=false; // 隐藏下拉框
    data.search=[];   // 用于显示查找对应年份的人员
    return data;
  }
  // 初始化
  function initSelected(list) {
    for (var i = 0, n = list.length; i < n; i++) {
      list[i].selected = "";
      list[i].id = Math.random(100000); // 随机赋值 防止id相同出现bug
      if (
        list[i].ch_array &&
        list[i].ch_array instanceof Array &&
        list[i].ch_array.length > 0
      )
        initSelected(list[i].ch_array);
    }
  }
  
  var VM = ko.mapping.fromJS(initData());
  ko.applyBindings(VM, document.getElementById("container"));

  function OnSelected($data, event) {
    cleanUpSelected($data.ch_array());
  }
  // 还原初始化selected
  function cleanUpSelected(item) {
    item.forEach(function (value, index, array) {
      if (typeof value.selected === "function") value.selected("");
      if (typeof value.ch_array === "function")
        cleanUpSelected(value.ch_array());
    });
  }
  function inputChange(data,root,parent) {
    let that = this;
    let year = data.selectedyear;
    console.log(year);
    if (year != "") {
      let array = data.ch_array();
      // console.log(array)
      // console.log($data)
      // console.log($root)
      // console.log(parent)
      let z = array.filter((item) => { // 查找相同属性
        // console.log(item)
        return item.time() === parseInt(year);
      });
      // console.log(z);
      if (z.length > 0) {
        ko.mapping.fromJS(z, {}, root.search); // 重新渲染
        ko.mapping.fromJS(true, {}, root.show);
      }else{
        ko.mapping.fromJS(false, {}, root.show);
      }
    } else {
      ko.mapping.fromJS(false, {}, root.show);
    }
  }
</script>

后续改进

将题目的解题逻辑重写了,将级联的层级固定为四级级联

重写完全级联A

重写完全级联A

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <div style="text-align: center; margin-top: 50px" id="container">
      <div>完全级联</div>
      <div>
        <div>
          <span>总公司</span>
          <!-- value是要绑定在哪个数值变量 optionsValue是将其值传给value  -->
          <select
            data-bind="options:monitor_head_comp_array, optionsText:'head_comp_name',optionsValue:$data,optionsCaption: '选择总公司',value:head_selected"
          ></select>
        </div>
        <div>
          <span>分公司</span>
          <select
            data-bind="options:monitor_breach_comp_array, optionsText:'breach_comp_name',optionsValue:$data,optionsCaption: '选择分公司',value:breach_selected"
          ></select>
        </div>
        <div>
          <span>各部门</span>
          <select
            data-bind="options:monitor_department_comp_array, optionsText:'depart_name',optionsValue:$data,optionsCaption: '选择部门',value:depart_selected"
          ></select>
        </div>
        <div>
          <span>游戏类</span>
          <select
            data-bind="options:monitor_game_name_array, optionsText:'name',optionsValue:$data,optionsCaption: '选择游戏/员工',value:game_selected"
          ></select>
        </div>
      </div>
    </div>
  </body>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.5.0/knockout-min.js"></script>
  <script type="text/javascript">
    // 初始化数据

    var create_game = [
      {
        id: 1,
        head_comp_name: "XX总公司",
        data_array: [
          {
            id: 2,
            breach_comp_name: "XX总公司重庆分公司",
            data_array: [
              {
                id: 3,
                depart_name: "XX总公司重庆分公司采购部门",
                data_array: [
                  { name: "彩虹六号", id: 4, time: 1991 },
                  { name: "地下城与勇士", id: 5, time: 1991 },
                  { name: "穿越火线", id: 6, time: 1991 },
                  { name: "QQ飞车", id: 7, time: 1995 },
                  { name: "虐杀原形1", id: 8, time: 1997 },
                  { name: "虐杀原形2", id: 9, time: 1997 },
                ],
              },
              {
                id: 10,
                depart_name: "XX总公司重庆分公司研发部门",
                data_array: [
                  { name: "黑暗之魂1", id: 8, time: 1991 },
                  { name: "黑暗之魂2", id: 9, time: 1991 },
                  { name: "黑暗之魂3", id: 10, time: 1991 },
                  { name: "只狼", id: 10, time: 1991 },
                ],
              },
            ],
          },
          {
            id: 2,
            breach_comp_name: "XX总公司湖南分公司",
            data_array: [
              {
                id: 3,
                depart_name: "XX总公司湖南分公司采购部门",
                data_array: [
                  { name: "赛尔号", id: 4, time: 1991 },
                  { name: "奥比岛", id: 5, time: 1991 },
                  { name: "功夫派", id: 6, time: 1991 },
                  { name: "QQ炫舞", id: 7, time: 1995 },
                  { name: "剑灵", id: 8, time: 1997 },
                  { name: "欢乐斗地主", id: 9, time: 1997 },
                ],
              },
              {
                id: 10,
                depart_name: "XX总公司湖南分公司研发部门",
                data_array: [
                  { name: "蝙蝠侠阿甘之城", id: 8, time: 1991 },
                  { name: "蝙蝠侠阿卡姆骑士", id: 9, time: 1991 },
                  { name: "侠影之谜", id: 10, time: 1991 },
                  { name: "闪电侠", id: 10, time: 1991 },
                ],
              },
            ],
          },
          {
            id: 2,
            breach_comp_name: "XX总公司成都分公司",
            data_array: [
              {
                id: 3,
                depart_name: "XX总公司成都分公司采购部门",
                data_array: [
                  { name: "天使爱美丽", id: 4, time: 1991 },
                  { name: "猪猪侠", id: 5, time: 1991 },
                  { name: "小灰灰", id: 6, time: 1991 },
                  { name: "哆啦A梦", id: 7, time: 1995 },
                  { name: "蜡笔小新", id: 8, time: 1997 },
                  { name: "海绵宝宝", id: 9, time: 1997 },
                ],
              },
              {
                id: 10,
                depart_name: "XX总公司成都分公司研发部门",
                data_array: [
                  { name: "乐高蝙蝠侠1", id: 8, time: 1991 },
                  { name: "乐高蝙蝠侠2", id: 9, time: 1991 },
                  { name: "乐高蝙蝠侠3", id: 10, time: 1991 },
                  { name: "乐高漫威超级英雄1", id: 10, time: 1991 },
                  { name: "乐高漫威超级英雄2", id: 10, time: 1991 },
                ],
              },
            ],
          },
        ],
      },

      {
        id: 1,
        head_comp_name: "YY总公司",
        data_array: [
          {
            id: 2,
            breach_comp_name: "YY总公司重庆分公司",
            data_array: [
              {
                id: 3,
                depart_name: "YY总公司重庆分公司游戏部门",
                data_array: [
                  { name: "艾尔登法环", id: 4, time: 1991 },
                  { name: "switch", id: 5, time: 1991 },
                  { name: "孤岛危机1", id: 6, time: 1991 },
                  { name: "孤岛危机2", id: 7, time: 1995 },
                  { name: "孤岛危机3", id: 8, time: 1997 },
                  { name: "光环", id: 9, time: 1997 },
                ],
              },
              {
                id: 10,
                depart_name: "YY总公司重庆分公司研发部门",
                data_array: [
                  { name: "GTA5", id: 8, time: 1991 },
                  { name: "侠盗猎车手", id: 9, time: 1991 },
                  { name: "幽浮1未知敌人", id: 10, time: 1991 },
                  { name: "幽浮1内部敌人", id: 10, time: 1991 },
                  { name: "幽浮2天选者之战", id: 10, time: 1991 },
                ],
              },
            ],
          },
        ],
      },

      {
        id: 1,
        head_comp_name: "娱乐总公司",
        data_array: [
          {
            id: 2,
            breach_comp_name: "娱乐总公司重庆分公司",
            data_array: [
              {
                id: 3,
                depart_name: "娱乐总公司重庆分公司采购部门",
                data_array: [
                  { name: "凤凰点", id: 4, time: 1991 },
                  { name: "蜘蛛侠1", id: 5, time: 1991 },
                  { name: "蜘蛛侠2", id: 6, time: 1991 },
                  { name: "蜘蛛侠3", id: 7, time: 1995 },
                  { name: "超凡蜘蛛侠1", id: 8, time: 1997 },
                  { name: "超凡蜘蛛侠2", id: 9, time: 1997 },
                  { name: "超凡蜘蛛侠3", id: 9, time: 1997 },
                ],
              },
              {
                id: 10,
                depart_name: "娱乐总公司重庆分公司宣发部门",
                data_array: [
                  { name: "怪物猎人世界", id: 8, time: 1991 },
                  { name: "怪物猎人崛起", id: 9, time: 1991 },
                  { name: "明日方舟", id: 10, time: 1991 },
                ],
              },
            ],
          },
        ],
      },

      {
        id: 1,
        head_comp_name: "暴雪总公司",
        data_array: [
          {
            id: 2,
            breach_comp_name: "暴雪总公司重庆分公司",
            data_array: [
              {
                id: 3,
                depart_name: "暴雪总公司重庆分公司采购部门",
                data_array: [
                  { name: "魔兽世界", id: 4, time: 1991 },
                  { name: "魔兽争霸3冰封王座", id: 5, time: 1991 },
                  { name: "星际争霸1", id: 6, time: 1991 },
                  { name: "星际争霸2", id: 7, time: 1995 },
                  { name: "守望先锋1", id: 8, time: 1997 },
                  { name: "守望先锋2", id: 9, time: 1997 },
                  { name: "暗黑破坏神", id: 9, time: 1997 },
                  { name: "炉石传说", id: 9, time: 1997 },
                ],
              },
              {
                id: 10,
                depart_name: "娱乐总公司重庆分公司宣发部门",
                data_array: [
                  { name: "怪物猎人世界", id: 8, time: 1991 },
                  { name: "怪物猎人崛起", id: 9, time: 1991 },
                  { name: "明日方舟", id: 10, time: 1991 },
                ],
              },
              {
                id: 10,
                depart_name: "娱乐总公司重庆分公司代理部门",
                data_array: [
                  { name: "文明6", id: 8, time: 1991 },
                  { name: "欧陆风云2", id: 9, time: 1991 },
                  { name: "维多利亚2", id: 10, time: 1991 },
                  { name: "群星", id: 10, time: 1991 },
                  { name: "仙剑奇侠传1", id: 10, time: 1991 },
                  { name: "仙剑奇侠传2", id: 10, time: 1991 },
                  { name: "仙剑奇侠传3", id: 10, time: 1991 },
                  { name: "仙剑奇侠传4", id: 10, time: 1991 },
                  { name: "仙剑奇侠传5", id: 10, time: 1991 },
                  { name: "仙剑奇侠传6", id: 10, time: 1991 },
                  { name: "仙剑奇侠传7", id: 10, time: 1991 },
                ],
              },
            ],
          },
        ],
      },
    ];
    let games = {};
    let model = create_game;
    setNewAttrInTraverse(model, "id", "data_array");
    games["data_array"] = model;

    initData();
    this.monitor["monitor_head_comp_array"](
      searchAttr("head_comp_name", model)
    ); // 初始化总公司
    setListener("head_selected", "monitor_breach_comp_array", 1, this);
    setListener("breach_selected", "monitor_department_comp_array", 2, this);
    setListener("depart_selected", "monitor_game_name_array", 3, this);

    function initData() {
      var self = this;
      self.monitor = {
        monitor_head_comp_array: ko.observable([]),
        monitor_breach_comp_array: ko.observable([]),
        monitor_department_comp_array: ko.observable([]),
        monitor_game_name_array: ko.observable([]),
        head_selected: ko.observable(),
        breach_selected: ko.observable(),
        depart_selected: ko.observable(),
        game_selected: ko.observable(),
      };
      ko.applyBindings(monitor, document.getElementById("container"));
    }

    function searchAttr(attr, list) {
      let array = [];
      for (let i = 0; i < list.length; i++) {
        if (list[i].hasOwnProperty(attr)) {
          array.push(list[i]);
        }
      }
      return array;
    }

    // 添加数值变化监听事件
    // monitor_name 要监听的属性名
    // _name_array // 要修改的监听数组名
    // state 重新点击第几层级
    // _self  // 全局作用域
    function setListener(monitor_name, _name_array, state, _self) {
      _self.monitor[monitor_name].subscribe(function (val) {
        // 当总公司发生改变
        console.log(val);
        cleanUpSelected(state, _self);
        if (val&&val.data_array) {
        //   cleanUpSelected(state, _self);
          _self.monitor[_name_array](val["data_array"]);
        }
      }); // 监测值变化
    }

    // 遍历json格式对象 给每一层添加或修改属性
    // list 数组
    // id 属性值 string类型
    // array 属性值 string类型
    function setNewAttrInTraverse(list, id, array) {
      for (let i = 0; i < list.length; i++) {
        list[i][id] = Math.random(10000);
        if (
          list[i][array] &&
          list[i][array] instanceof Array &&
          list[i][array].length > 0
        ) {
          // 如果子数列存在
          setNewAttrInTraverse(list[i][array], id, array);
        }
      }
    }

    // 用于还原后面层级的初始状态
    function cleanUpSelected(state, _self) {
      switch (state) {
        case 3:
          //   _self.monitor["game_selected"]({});
          _self.monitor["game_selected"]({});
          _self.monitor["monitor_game_name_array"]([]);
          break;
        case 2:
          //   _self.monitor["monitor_department_comp_array"]([]);
        //   _self.monitor["monitor_breach_comp_array"]([]);
          _self.monitor["monitor_department_comp_array"]([]);
          _self.monitor["monitor_game_name_array"]([]);
        //   _self.monitor["breach_selected"]({});
          _self.monitor["depart_selected"]({});
          _self.monitor["game_selected"]({});
          break;
        case 1:
            // _self.monitor["monitor_head_comp_array"]([]);
            _self.monitor["monitor_breach_comp_array"]([]);
          _self.monitor["monitor_department_comp_array"]([]);
          _self.monitor["monitor_game_name_array"]([]);
          //   _self.monitor["head_selected"]({});
          _self.monitor["breach_selected"]({});
          _self.monitor["depart_selected"]({});
          _self.monitor["game_selected"]({});
          break;
        default:
      }
    }
  </script>
  <style type="text/css">
    select {width: 300px;}
    p {margin-left: 20px}
    </style>
</html>

重写不完全级联B

重写不完全级联B

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <div style="text-align: center; margin-top: 50px" id="container">
      <div>不完全级联</div>
      <div>
        <div>
          <span>总公司</span>
          <!-- value是要绑定在哪个数值变量 optionsValue是将其值传给value  -->
          <select
            data-bind="options:monitor_head_comp_array, optionsText:'head_comp_name',optionsValue:$data,optionsCaption: '选择总公司',value:head_selected"
          ></select>
        </div>
        <div>
          <span>分公司</span>
          <select
            data-bind="options:monitor_breach_comp_array, optionsText:'breach_comp_name',optionsValue:$data,optionsCaption: '选择分公司',value:breach_selected"
          ></select>
        </div>
        <div>
          <span>各部门</span>
          <select
            data-bind="options:monitor_department_comp_array, optionsText:'depart_name',optionsValue:$data,optionsCaption: '选择部门',value:depart_selected"
          ></select>
        </div>

        <div>
          <span>出版年</span>
          <input
            type="number"
            placeholder="选择部门后输入搜索年份"
            data-bind="disable:disabled_power,value:monitor.selected_year,event:{change:inputChange(monitor.depart_selected(),monitor.selected_year(),$root)}"
          />
        </div>

        <div>
          <span>游戏类</span>
          <select
            data-bind="options:monitor_game_name_array, optionsText:function(item){return item.name+' 出版年:'+item.time},optionsValue:$data,optionsCaption: '选择游戏',value:game_selected"
          ></select>
        </div>
      </div>
    </div>
  </body>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.5.0/knockout-min.js"></script>
  <script type="text/javascript">
    // 初始化数据

    var create_game = [
      {
        id: 1,
        head_comp_name: "XX总公司",
        data_array: [
          {
            id: 2,
            breach_comp_name: "XX总公司重庆分公司",
            data_array: [
              {
                id: 3,
                depart_name: "XX总公司重庆分公司采购部门",
                data_array: [
                  { name: "彩虹六号", id: 4, time: 1991 },
                  { name: "地下城与勇士", id: 5, time: 1991 },
                  { name: "穿越火线", id: 6, time: 1991 },
                  { name: "QQ飞车", id: 7, time: 1995 },
                  { name: "虐杀原形1", id: 8, time: 1997 },
                  { name: "虐杀原形2", id: 9, time: 1997 },
                ],
              },
              {
                id: 10,
                depart_name: "XX总公司重庆分公司研发部门",
                data_array: [
                  { name: "黑暗之魂1", id: 8, time: 1991 },
                  { name: "黑暗之魂2", id: 9, time: 1991 },
                  { name: "黑暗之魂3", id: 10, time: 1991 },
                  { name: "只狼", id: 10, time: 1991 },
                ],
              },
            ],
          },
          {
            id: 2,
            breach_comp_name: "XX总公司湖南分公司",
            data_array: [
              {
                id: 3,
                depart_name: "XX总公司湖南分公司采购部门",
                data_array: [
                  { name: "赛尔号", id: 4, time: 1991 },
                  { name: "奥比岛", id: 5, time: 1991 },
                  { name: "功夫派", id: 6, time: 1991 },
                  { name: "QQ炫舞", id: 7, time: 1995 },
                  { name: "剑灵", id: 8, time: 1997 },
                  { name: "欢乐斗地主", id: 9, time: 1997 },
                ],
              },
              {
                id: 10,
                depart_name: "XX总公司湖南分公司研发部门",
                data_array: [
                  { name: "蝙蝠侠阿甘之城", id: 8, time: 1991 },
                  { name: "蝙蝠侠阿卡姆骑士", id: 9, time: 1991 },
                  { name: "侠影之谜", id: 10, time: 1991 },
                  { name: "闪电侠", id: 10, time: 1991 },
                ],
              },
            ],
          },
          {
            id: 2,
            breach_comp_name: "XX总公司成都分公司",
            data_array: [
              {
                id: 3,
                depart_name: "XX总公司成都分公司采购部门",
                data_array: [
                  { name: "天使爱美丽", id: 4, time: 1991 },
                  { name: "猪猪侠", id: 5, time: 1991 },
                  { name: "小灰灰", id: 6, time: 1991 },
                  { name: "哆啦A梦", id: 7, time: 1995 },
                  { name: "蜡笔小新", id: 8, time: 1997 },
                  { name: "海绵宝宝", id: 9, time: 1997 },
                ],
              },
              {
                id: 10,
                depart_name: "XX总公司成都分公司研发部门",
                data_array: [
                  { name: "乐高蝙蝠侠1", id: 8, time: 1991 },
                  { name: "乐高蝙蝠侠2", id: 9, time: 1991 },
                  { name: "乐高蝙蝠侠3", id: 10, time: 1991 },
                  { name: "乐高漫威超级英雄1", id: 10, time: 1991 },
                  { name: "乐高漫威超级英雄2", id: 10, time: 1991 },
                ],
              },
            ],
          },
        ],
      },

      {
        id: 1,
        head_comp_name: "YY总公司",
        data_array: [
          {
            id: 2,
            breach_comp_name: "YY总公司重庆分公司",
            data_array: [
              {
                id: 3,
                depart_name: "YY总公司重庆分公司游戏部门",
                data_array: [
                  { name: "艾尔登法环", id: 4, time: 1991 },
                  { name: "switch", id: 5, time: 1991 },
                  { name: "孤岛危机1", id: 6, time: 1991 },
                  { name: "孤岛危机2", id: 7, time: 1995 },
                  { name: "孤岛危机3", id: 8, time: 1997 },
                  { name: "光环", id: 9, time: 1997 },
                ],
              },
              {
                id: 10,
                depart_name: "YY总公司重庆分公司研发部门",
                data_array: [
                  { name: "GTA5", id: 8, time: 1991 },
                  { name: "侠盗猎车手", id: 9, time: 1991 },
                  { name: "幽浮1未知敌人", id: 10, time: 1991 },
                  { name: "幽浮1内部敌人", id: 10, time: 1991 },
                  { name: "幽浮2天选者之战", id: 10, time: 1991 },
                ],
              },
            ],
          },
        ],
      },

      {
        id: 1,
        head_comp_name: "娱乐总公司",
        data_array: [
          {
            id: 2,
            breach_comp_name: "娱乐总公司重庆分公司",
            data_array: [
              {
                id: 3,
                depart_name: "娱乐总公司重庆分公司采购部门",
                data_array: [
                  { name: "凤凰点", id: 4, time: 1991 },
                  { name: "蜘蛛侠1", id: 5, time: 1991 },
                  { name: "蜘蛛侠2", id: 6, time: 1991 },
                  { name: "蜘蛛侠3", id: 7, time: 1995 },
                  { name: "超凡蜘蛛侠1", id: 8, time: 1997 },
                  { name: "超凡蜘蛛侠2", id: 9, time: 1997 },
                  { name: "超凡蜘蛛侠3", id: 9, time: 1997 },
                ],
              },
              {
                id: 10,
                depart_name: "娱乐总公司重庆分公司宣发部门",
                data_array: [
                  { name: "怪物猎人世界", id: 8, time: 1991 },
                  { name: "怪物猎人崛起", id: 9, time: 1991 },
                  { name: "明日方舟", id: 10, time: 1991 },
                ],
              },
            ],
          },
        ],
      },

      {
        id: 1,
        head_comp_name: "暴雪总公司",
        data_array: [
          {
            id: 2,
            breach_comp_name: "暴雪总公司重庆分公司",
            data_array: [
              {
                id: 3,
                depart_name: "暴雪总公司重庆分公司采购部门",
                data_array: [
                  { name: "魔兽世界", id: 4, time: 1991 },
                  { name: "魔兽争霸3冰封王座", id: 5, time: 1991 },
                  { name: "星际争霸1", id: 6, time: 1991 },
                  { name: "星际争霸2", id: 7, time: 1995 },
                  { name: "守望先锋1", id: 8, time: 1997 },
                  { name: "守望先锋2", id: 9, time: 1997 },
                  { name: "暗黑破坏神", id: 9, time: 1997 },
                  { name: "炉石传说", id: 9, time: 1997 },
                ],
              },
              {
                id: 10,
                depart_name: "娱乐总公司重庆分公司宣发部门",
                data_array: [
                  { name: "怪物猎人世界", id: 8, time: 1991 },
                  { name: "怪物猎人崛起", id: 9, time: 1991 },
                  { name: "明日方舟", id: 10, time: 1991 },
                ],
              },
              {
                id: 10,
                depart_name: "娱乐总公司重庆分公司代理部门",
                data_array: [
                  { name: "文明6", id: 8, time: 1991 },
                  { name: "欧陆风云2", id: 9, time: 1991 },
                  { name: "维多利亚2", id: 10, time: 1991 },
                  { name: "群星", id: 10, time: 1991 },
                  { name: "仙剑奇侠传1", id: 10, time: 1991 },
                  { name: "仙剑奇侠传2", id: 10, time: 1991 },
                  { name: "仙剑奇侠传3", id: 10, time: 1991 },
                  { name: "仙剑奇侠传4", id: 10, time: 1991 },
                  { name: "仙剑奇侠传5", id: 10, time: 1991 },
                  { name: "仙剑奇侠传6", id: 10, time: 1991 },
                  { name: "仙剑奇侠传7", id: 10, time: 1991 },
                ],
              },
            ],
          },
        ],
      },
    ];
    let games = {};
    let model = create_game;
    setNewAttrInTraverse(model, "id", "data_array");
    games["data_array"] = model;

    initData();
    this.monitor["monitor_head_comp_array"](
      searchAttr("head_comp_name", model)
    ); // 初始化总公司
    setListener("head_selected", "monitor_breach_comp_array", 1, this);
    setListener("breach_selected", "monitor_department_comp_array", 2, this);
    setListener("depart_selected", "monitor_game_name_array", 3, this);

    function initData() {
      var self = this;
      self.monitor = {
        monitor_head_comp_array: ko.observable([]),
        monitor_breach_comp_array: ko.observable([]),
        monitor_department_comp_array: ko.observable([]),
        monitor_game_name_array: ko.observable([]),
        head_selected: ko.observable(),
        breach_selected: ko.observable(),
        depart_selected: ko.observable(),
        game_selected: ko.observable(), // 选择的游戏层级的option
        selected_year: ko.observable(), // 查找年份
        disabled_power:ko.observable(true),
        inputChange: function (data, year, root) {
          let that = this;
          if (
            data &&
            data["data_array"] &&
            data["data_array"] instanceof Array &&
            data["data_array"].length > 0
          ) {
            let ar = data["data_array"].filter((item) => {
              if (year === "") {
                return item.time;
              } else {
                // 查找相同属性
                console.log(item);
                return item.time === parseInt(year);
              }
            });
            root["monitor_game_name_array"](ar);
          }
        },
      };
      ko.applyBindings(monitor, document.getElementById("container"));
    }

    function searchAttr(attr, list) {
      let array = [];
      for (let i = 0; i < list.length; i++) {
        if (list[i].hasOwnProperty(attr)) {
          array.push(list[i]);
        }
      }
      return array;
    }

    // 添加数值变化监听事件
    // monitor_name 要监听的属性名
    // _name_array // 要修改的监听数组名
    // state 重新点击第几层级
    // _self  // 全局作用域
    function setListener(monitor_name, _name_array, state, _self) {
      _self.monitor[monitor_name].subscribe(function (val) {
        // 当总公司发生改变
        cleanUpSelected(state, _self);
        if (val && val.data_array) {
          _self.monitor[_name_array](val["data_array"]);
        }
        if(monitor_name ==='depart_selected'){
            if(val&&val["data_array"]&&val["data_array"].length>0){
              _self.monitor["disabled_power"](false);
            }else{
              _self.monitor["disabled_power"](true);
            }

          }
      }); // 监测值变化
    }

    // 遍历json格式对象 给每一层添加或修改属性
    // list 数组
    // id 属性值 string类型
    // array 属性值 string类型
    function setNewAttrInTraverse(list, id, array) {
      for (let i = 0; i < list.length; i++) {
        list[i][id] = Math.random(10000);
        if (
          list[i][array] &&
          list[i][array] instanceof Array &&
          list[i][array].length > 0
        ) {
          // 如果子数列存在
          setNewAttrInTraverse(list[i][array], id, array);
        }
      }
    }

    // 用于还原后面层级的初始状态
    function cleanUpSelected(state, _self) {
      switch (state) {
        case 3:
          //   _self.monitor["game_selected"]({});
          _self.monitor["game_selected"]({});
          _self.monitor["monitor_game_name_array"]([]);
          _self.monitor["selected_year"]("");
          // _self.monitor["disabled_power"](true);
          break;
        case 2:
          //   _self.monitor["monitor_department_comp_array"]([]);
          //   _self.monitor["monitor_breach_comp_array"]([]);
          _self.monitor["monitor_department_comp_array"]([]);
          _self.monitor["monitor_game_name_array"]([]);
          //   _self.monitor["breach_selected"]({});
          _self.monitor["depart_selected"]({});
          _self.monitor["game_selected"]({});
          _self.monitor["selected_year"]("");
          break;
        case 1:
          // _self.monitor["monitor_head_comp_array"]([]);
          _self.monitor["monitor_breach_comp_array"]([]);
          _self.monitor["monitor_department_comp_array"]([]);
          _self.monitor["monitor_game_name_array"]([]);
          //   _self.monitor["head_selected"]({});
          _self.monitor["breach_selected"]({});
          _self.monitor["depart_selected"]({});
          _self.monitor["game_selected"]({});
          _self.monitor["selected_year"]("");
          break;
        default:
      }

    }
  </script>
  <style type="text/css">
    select {
      width: 300px;
    }
    input {
      width: 290px;
    }
  </style>
</html>

posted @ 2022-08-18 20:44  DAmarkday  阅读(180)  评论(0编辑  收藏  举报