KevinChen 发表于 2020-5-26 16:42:33

【自定义编辑器系列】FlexGrid中如何使用dropdowntree组件


我们知道,在FlexGrid中默认双击单元格时,只有普通的输入框可供用户键盘输入内容,
能否把功能丰富的Input组件引入到FlexGrid中呢?答案是肯定的,这个示例告诉我们没什么不可能:
https://demo.grapecity.com.cn/wijmo/demos/Grid/CustomCells/CustomEditors/purejs

自定义编辑器,实际上就是利用了FlexGrid的beginningEdit等事件,替换掉原生的input元素,
引入自定义的元素来实现原生input所不具备的功能。示例中演示了数字、下拉、日期三种input组件,
但实际应用中,我们的数据类型可能是多种多样的,比如今天讲到的,DropdownTree组件,如图:



想在FlexGrid中加入这个组件,面临的第一个问题是,WijmoJS本身并没有这个组件的实现,不过没关系,
关于这个问题,我直接给出解决方案,大家可以直接下载附件的“DropDownTree.js”,这个js文件扩展了wijmo的功能,
实现了这个功能,大家可以直接引用到项目中即可,如下:

new wijmo.input.DropDownTree()

好了,有了这个组件,第二步就是把它集成到FlexGrid中。我们利用“自定义编辑器”这个示例的基本结构,
通过一些修改,来实现集成DropDownTree的功能,如下:

<!DOCTYPE html>
<html>
        <head>
                <meta charset="UTF-8">
                <title></title>
                <link href="css/wijmo.min.css"rel="stylesheet"/>
                <script src="js/wijmo.min.js"></script>
                <script src="js/wijmo.nav.min.js"></script>
                <script src="js/wijmo.grid.min.js"></script>
                <script src="js/wijmo.grid.filter.min.js"></script>
                <script src="js/wijmo.input.min.js"></script>
                <script src="js/wijmo.culture.zh.min.js"></script>
                <script src="js/DropDownTree.js"></script>
                <style>
                        body {
                          margin-bottom: 24px;
                        }

                        .wj-flexgrid {
                          max-height: 300px;
                        }
                </style>
        </head>
        <body>
    <div class="container-fluid">
      <div id="theGrid">
      </div>
    </div>
                <script>
class CustomGridEditor {
    /**
   * Initializes a new instance of a CustomGridEditor.
   */
    constructor(flex,binding, edtClass, options) {
                // console.log(binding, edtClass, options)

      // save references
      this._grid = flex;
      // this._col = flex.columns.getColumn(binding);
                this._col = flex.rows;
               
                this._binding = flex.columns;
               
                console.log( flex.rows )
                console.log(this._col )

      // create editor
      this._ctl = new edtClass(document.createElement('div'), options);
      // connect grid events
      flex.beginningEdit.addHandler(this._beginningEdit, this);
      flex.sortingColumn.addHandler(() => {
            this._commitRowEdits();
      });
      flex.scrollPositionChanged.addHandler(() => {
            if (this._ctl.containsFocus()) {
                flex.focus();
            }
      });
      flex.selectionChanging.addHandler((s, e) => {
            if (e.row != s.selection.row) {
                this._commitRowEdits();
            }
      });
      // connect editor events
      this._ctl.addEventListener(this._ctl.hostElement, 'keydown', (e) => {
            switch (e.keyCode) {
                case wijmo.Key.Tab:
                case wijmo.Key.Enter:
                  e.preventDefault(); // TFS 255685
                  this._closeEditor(true);
                  this._grid.focus();
                  // forward event to the grid so it will move the selection
                  var evt = document.createEvent('HTMLEvents');
                  evt.initEvent('keydown', true, true);
                  'altKey,metaKey,ctrlKey,shiftKey,keyCode'.split(',').forEach((prop) => {
                        evt = e;
                  });
                  this._grid.hostElement.dispatchEvent(evt);
                  break;
                case wijmo.Key.Escape:
                  this._closeEditor(false);
                  this._grid.focus();
                  break;
            }
      });
      // close the editor when it loses focus
      this._ctl.lostFocus.addHandler(() => {
            setTimeout(() => {
                if (!this._ctl.containsFocus()) {
                  this._closeEditor(true); // apply edits and close editor
                  this._grid.onLostFocus(); // commit item edits if the grid lost focus
                }
            });
      });
      // commit edits when grid loses focus
      this._grid.lostFocus.addHandler(() => {
            setTimeout(() => {
                if (!this._grid.containsFocus() && !CustomGridEditor._isEditing) {
                  this._commitRowEdits();
                }
            });
      });
      // open drop-down on f4/alt-down
      this._grid.addEventListener(this._grid.hostElement, 'keydown', (e) => {
            // open drop-down on f4/alt-down
            this._openDropDown = false;
            if (e.keyCode == wijmo.Key.F4 ||
                (e.altKey && (e.keyCode == wijmo.Key.Down || e.keyCode == wijmo.Key.Up))) {
                var colIndex = this._grid.selection.col;
                                console.log( this._grid.selection.col)
                if (colIndex > -1 && this._grid.rows == this._col) {
                  this._openDropDown = true;
                  this._grid.startEditing(true);
                  e.preventDefault();
                }
            }
            // commit edits on Enter (in case we're at the last row, TFS 268944)
            if (e.keyCode == wijmo.Key.Enter) {
                this._commitRowEdits();
            }
      }, true);
      // close editor when user resizes the window
      // REVIEW: hides editor when soft keyboard pops up (TFS 326875)
      window.addEventListener('resize', () => {
            if (this._ctl.containsFocus()) {
                this._closeEditor(true);
                this._grid.focus();
            }
      });
    }
    // gets an instance of the control being hosted by this grid editor
    get control() {
      return this._ctl;
    };
       
        // 遍历查找树节点
        _findNode(treeNode, key, val, nodeArr){
                if(treeNode.dataItem && treeNode.dataItem == val){
                        nodeArr.push(treeNode);
                }else if(treeNode.nodes && treeNode.nodes.length>0){
                        for(let i=0; i<treeNode.nodes.length; i++){
                                if(treeNode.nodes.dataItem == val){
                                        nodeArr.push(treeNode.nodes);
                                }else{
                                        this._findNode(treeNode.nodes, key, val, nodeArr);
                                }
                        }
                }
        };
       
    // handle the grid's beginningEdit event by canceling the built-in editor,
    // initializing the custom editor and giving it the focus.
    _beginningEdit(grid, args) {
      // check that this is our column
                // console.log(args)

      if (grid.columns != this._binding) {
                        // console.log(grid.columns)/
            return;
      }
      // check that this is not the Delete key
      // (which is used to clear cells and should not be messed with)
      var evt = args.data;
      if (evt && evt.keyCode == wijmo.Key.Delete) {
            return;
      }
      // cancel built-in editor
      args.cancel = true;
      // save cell being edited
      this._rng = args.range;
      CustomGridEditor._isEditing = true;
      // initialize editor host
      var rcCell = grid.getCellBoundingRect(args.row, args.col), rcBody = document.body.getBoundingClientRect(), ptOffset = new wijmo.Point(-rcBody.left, -rcBody.top), zIndex = (args.row < grid.frozenRows || args.col < grid.frozenColumns) ? '3' : '';
      wijmo.setCss(this._ctl.hostElement, {
            position: 'absolute',
            left: rcCell.left - 1 + ptOffset.x+8,
            top: rcCell.top - 1 + ptOffset.y+8,
            width: rcCell.width + 1,
            height: grid.rows.renderHeight + 1,
            borderRadius: '0px',
            zIndex: zIndex,
      });
      // initialize editor content
                /**
                        这句代码是负责在展开下拉前更新下拉状态的
                        在此处清空勾选状态即可。
                */
      if (!wijmo.isUndefined(this._ctl['checkedItems']) && this._ctl['checkedItems'].length > 0) {
            this._ctl['checkedItems'] = [];
      }
               
               
                // 这里设置回填数据
                var cellVal = grid.getCellData(args.row, args.col);
                var valArr = null;
                if(cellVal && cellVal.length > 0){
                        valArr = cellVal.split(",");
                }
                if(valArr){
                        var treeData = this._treeData;
                        var findNode = this._findNode;
                        var _this = this;
                        var checkedNodes = [];
                        for(let i=0; i<valArr.length; i++){
                                this._ctl.nodes.forEach(function(node){
                                        findNode.call(_this, node, "header", valArr, checkedNodes);
                                });
                        }
                        checkedNodes.forEach(function(node){
                                node.isChecked = true;
                        });
                }
               
                /*
      else {
            throw 'Can\'t set editor value/text...';
      }
                */
      // start editing item
      var ecv = grid.editableCollectionView, item = grid.rows.dataItem;
      if (ecv && item && item != ecv.currentEditItem) {
            setTimeout(function () {
                grid.onRowEditStarting(args);
                ecv.editItem(item);
                grid.onRowEditStarted(args);
            }, 50); // wait for the grid to commit edits after losing focus
      }
      // activate editor
      document.body.appendChild(this._ctl.hostElement);
      this._ctl.focus();
      setTimeout(() => {
               
            // get the key that triggered the editor
            var key = (evt && evt.charCode > 32)
                ? String.fromCharCode(evt.charCode)
                : null;
            // get input element in the control
            var input = this._ctl.hostElement.querySelector('input');
            // send key to editor
            if (input) {
                if (key) {
                  input.value = key;
                  wijmo.setSelectionRange(input, key.length, key.length);
                  var evtInput = document.createEvent('HTMLEvents');
                  evtInput.initEvent('input', true, false);
                  input.dispatchEvent(evtInput);
                }
                else {
                  input.select();
                }
            }
            // give the control focus
            if (!input && !this._openDropDown) {
                this._ctl.focus();
            }
            // open drop-down on F4/alt-down
            if (this._openDropDown && this._ctl instanceof wijmo.input.DropDown) {
                this._ctl.isDroppedDown = true;
                this._ctl.dropDown.focus();
            }
      }, 50);
               
    }
    // close the custom editor, optionally saving the edits back to the grid
    _closeEditor(saveEdits) {
      if (this._rng) {
            var flexGrid = this._grid, ctl = this._ctl, host = ctl.hostElement;
            // raise grid's cellEditEnding event
            var e = new wijmo.grid.CellEditEndingEventArgs(flexGrid.cells, this._rng);
            flexGrid.onCellEditEnding(e);
            // save editor value into grid
            if (saveEdits) {
                                /**
                                        这里加一个判断,实现下拉tree回填逻辑
                                */
                                if(!wijmo.isUndefined(ctl['checkedItems'])){
                                        var vals = ctl['checkedItems'];
                                        if(vals.length > 0){
                                                var value = "";
                                                vals.forEach(function(item){
                                                        if(item.header){
                                                                value += item.header + ","
                                                        }
                                                })
                                                if(value.length > 0){
                                                        value = value.substring(0, value.length-1);
                                                }
                                                this._grid.setCellData(this._rng.row, this._rng.col, value);
                                        }else{
                                                this._grid.setCellData(this._rng.row, this._rng.col, "");
                                        }
                }else if (!wijmo.isUndefined(ctl['value'])) {
                  this._grid.setCellData(this._rng.row, this._rng.col, ctl['value']);
                }
                else if (!wijmo.isUndefined(ctl['text'])) {
                  this._grid.setCellData(this._rng.row, this._rng.col, ctl['text']);
                }
                else {
                  throw 'Can\'t get editor value/text...';
                }
                this._grid.invalidate();
            }
            // close editor and remove it from the DOM
            if (ctl instanceof wijmo.input.DropDown) {
                ctl.isDroppedDown = false;
            }
            host.parentElement.removeChild(host);
            this._rng = null;
            CustomGridEditor._isEditing = false;
            // raise grid's cellEditEnded event
            flexGrid.onCellEditEnded(e);
      }
    }
    // commit row edits, fire row edit end events (TFS 339615)
    _commitRowEdits() {
      var flexGrid = this._grid, ecv = flexGrid.editableCollectionView;
      this._closeEditor(true);
      if (ecv && ecv.currentEditItem) {
            var e = new wijmo.grid.CellEditEndingEventArgs(flexGrid.cells, flexGrid.selection);
            ecv.commitEdit();
            setTimeout(() => {
                flexGrid.onRowEditEnding(e);
                flexGrid.onRowEditEnded(e);
                flexGrid.invalidate();
            });
      }
    }
}
//
document.readyState === 'complete' ? init() : window.onload = init;
//
function init() {
    //
    // create some random data
    var countries = [
      { header: 'Electronics', img: 'resources/electronics.png', items: [
            { header: 'Trimmers/Shavers' },
            { header: 'Tablets' },
            { header: 'Phones', img: 'resources/phones.png', items: [
                { header: 'Apple' },
                { header: 'Motorola' },
                { header: 'Nokia' },
                { header: 'Samsung' }
            ]},
            { header: 'Speakers' },
            { header: 'Monitors' }
      ]}
        ];
       
    var products = [
      { id: 0, name: 'Widget', unitPrice: 23.43 },
      { id: 1, name: 'Gadget', unitPrice: 12.33 },
      { id: 2, name: 'Doohickey', unitPrice: 53.07 }
    ];
    var data = [];
    var dt = new Date();
    for (var i = 0; i < 10; i++) {
      data.push({
            id: i,
            date: new Date(dt.getFullYear(), i % 12, 25, i % 24, i % 60, i % 60),
            time: new Date(dt.getFullYear(), i % 12, 25, i % 24, i % 60, i % 60),
            country: countries.header,
            product: products.name,
            amount: Math.random() * 10000 - 5000,
            discount: Math.random() / 4
      });
    }
    //
    // grid with custom editors
    theGrid = new wijmo.grid.FlexGrid('#theGrid', {
      keyActionTab: 'CycleOut',
      autoGenerateColumns: false,
      itemsSource: data,
      columns: [
            { header: 'ID', binding: 'id', width: 40, isReadOnly: true },
            { header: 'Date', binding: 'date', format: 'd' ,"align":"right"},
            { header: 'Time', binding: 'time', format: 't' },
            { header: 'Country', binding: 'country' },
            { header: 'Product', binding: 'product' },
            { header: 'Amount', binding: 'amount', format: 'n2' }
      ],
                beginningEdit: function (s, e) {
            if (e.data.type == 'keypress' &&
                !document.getElementById('quickEdit').checked) {
                e.cancel = true;
            }
      },
               itemFormatter: function (panel, r, c, cell) {//单元格的GridPanel,从0开始——单元格的行、列索引、单元格的HTML元素
                            var editRange = panel.grid.editRange;
                            // if (panel.cellType == wijmo.grid.CellType.Cell && editRange && editRange.row === r && editRange.col === c) {
                              // if(r==0){
                //                          // if (grid.columns.binding == 'country') {
                //

                                                                // console.log(panel.cellType == wijmo.grid.CellType.Cell)
                                                        // }
                                        // }
                }
    });
       
    new CustomGridEditor(theGrid, 3, wijmo.input.DropDownTree, {
      itemsSource: countries
    });
        new CustomGridEditor(theGrid, 4, wijmo.input.DropDownTree, {
          itemsSource: countries
        });
    // new CustomGridEditor(theGrid, 'amount', wijmo.input.InputNumber, {
    //   format: 'n2',
    //   step: 10
    // });
    //
    // create an editor based on a ComboBox
    // var multiColumnEditor = new CustomGridEditor(theGrid, 'product', wijmo.input.ComboBox, {
    //   headerPath: 'name',
    //   displayMemberPath: 'name',
    //   itemsSource: products
    // });
    //
    // customize the ComboBox to show multiple columns
    // var combo = multiColumnEditor.control;
    // combo.listBox.formatItem.addHandler(function (s, e) {
    //   e.item.innerHTML = '<table><tr>' +
    //         '<td style="width:30px;text-align:right;padding-right:6px">' + e.data.id + '</td>' +
    //         '<td style="width:100px;padding-right:6px"><b>' + e.data.name + '</b></td>' +
    //         '<td style="width:80px;text-align:right;padding-right:6px">' +
    //         wijmo.Globalize.format(e.data.unitPrice, 'c') +
    //         '</td>' +
    //         '</tr></table>';
    // });
}
                </script>
        </body>
</html>


完整的实现,参考附件打包Demo。

页: [1]
查看完整版本: 【自定义编辑器系列】FlexGrid中如何使用dropdowntree组件