找回密码
 立即注册

QQ登录

只需一步,快速开始

KevinChen 讲师达人认证 悬赏达人认证 SpreadJS 开发认证
论坛元老   /  发表于:2020-5-26 16:42  /   查看:2716  /  回复:0

我们知道,在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的功能,
实现了这个功能,大家可以直接引用到项目中即可,如下:

  1. new wijmo.input.DropDownTree()
复制代码


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

  1. <!DOCTYPE html>
  2. <html>
  3.         <head>
  4.                 <meta charset="UTF-8">
  5.                 <title></title>
  6.                 <link href="css/wijmo.min.css"  rel="stylesheet"  />
  7.                 <script src="js/wijmo.min.js"></script>
  8.                 <script src="js/wijmo.nav.min.js"></script>
  9.                 <script src="js/wijmo.grid.min.js"></script>
  10.                 <script src="js/wijmo.grid.filter.min.js"></script>
  11.                 <script src="js/wijmo.input.min.js"></script>
  12.                 <script src="js/wijmo.culture.zh.min.js"></script>
  13.                 <script src="js/DropDownTree.js"></script>
  14.                 <style>
  15.                         body {
  16.                             margin-bottom: 24px;
  17.                         }

  18.                         .wj-flexgrid {
  19.                             max-height: 300px;
  20.                         }
  21.                 </style>
  22.         </head>
  23.         <body>
  24.     <div class="container-fluid">
  25.       <div id="theGrid">
  26.       </div>
  27.     </div>
  28.                 <script>
  29. class CustomGridEditor {
  30.     /**
  31.      * Initializes a new instance of a CustomGridEditor.
  32.      */
  33.     constructor(flex,binding, edtClass, options) {
  34.                 // console.log(binding, edtClass, options)

  35.         // save references
  36.         this._grid = flex;
  37.         // this._col = flex.columns.getColumn(binding);
  38.                 this._col = flex.rows[binding];
  39.                
  40.                 this._binding = flex.columns[binding];
  41.                
  42.                 console.log( flex.rows )
  43.                 console.log(  this._col )

  44.         // create editor
  45.         this._ctl = new edtClass(document.createElement('div'), options);
  46.         // connect grid events
  47.         flex.beginningEdit.addHandler(this._beginningEdit, this);
  48.         flex.sortingColumn.addHandler(() => {
  49.             this._commitRowEdits();
  50.         });
  51.         flex.scrollPositionChanged.addHandler(() => {
  52.             if (this._ctl.containsFocus()) {
  53.                 flex.focus();
  54.             }
  55.         });
  56.         flex.selectionChanging.addHandler((s, e) => {
  57.             if (e.row != s.selection.row) {
  58.                 this._commitRowEdits();
  59.             }
  60.         });
  61.         // connect editor events
  62.         this._ctl.addEventListener(this._ctl.hostElement, 'keydown', (e) => {
  63.             switch (e.keyCode) {
  64.                 case wijmo.Key.Tab:
  65.                 case wijmo.Key.Enter:
  66.                     e.preventDefault(); // TFS 255685
  67.                     this._closeEditor(true);
  68.                     this._grid.focus();
  69.                     // forward event to the grid so it will move the selection
  70.                     var evt = document.createEvent('HTMLEvents');
  71.                     evt.initEvent('keydown', true, true);
  72.                     'altKey,metaKey,ctrlKey,shiftKey,keyCode'.split(',').forEach((prop) => {
  73.                         evt[prop] = e[prop];
  74.                     });
  75.                     this._grid.hostElement.dispatchEvent(evt);
  76.                     break;
  77.                 case wijmo.Key.Escape:
  78.                     this._closeEditor(false);
  79.                     this._grid.focus();
  80.                     break;
  81.             }
  82.         });
  83.         // close the editor when it loses focus
  84.         this._ctl.lostFocus.addHandler(() => {
  85.             setTimeout(() => {
  86.                 if (!this._ctl.containsFocus()) {
  87.                     this._closeEditor(true); // apply edits and close editor
  88.                     this._grid.onLostFocus(); // commit item edits if the grid lost focus
  89.                 }
  90.             });
  91.         });
  92.         // commit edits when grid loses focus
  93.         this._grid.lostFocus.addHandler(() => {
  94.             setTimeout(() => {
  95.                 if (!this._grid.containsFocus() && !CustomGridEditor._isEditing) {
  96.                     this._commitRowEdits();
  97.                 }
  98.             });
  99.         });
  100.         // open drop-down on f4/alt-down
  101.         this._grid.addEventListener(this._grid.hostElement, 'keydown', (e) => {
  102.             // open drop-down on f4/alt-down
  103.             this._openDropDown = false;
  104.             if (e.keyCode == wijmo.Key.F4 ||
  105.                 (e.altKey && (e.keyCode == wijmo.Key.Down || e.keyCode == wijmo.Key.Up))) {
  106.                 var colIndex = this._grid.selection.col;
  107.                                 console.log( this._grid.selection.col)
  108.                 if (colIndex > -1 && this._grid.rows[colIndex] == this._col) {
  109.                     this._openDropDown = true;
  110.                     this._grid.startEditing(true);
  111.                     e.preventDefault();
  112.                 }
  113.             }
  114.             // commit edits on Enter (in case we're at the last row, TFS 268944)
  115.             if (e.keyCode == wijmo.Key.Enter) {
  116.                 this._commitRowEdits();
  117.             }
  118.         }, true);
  119.         // close editor when user resizes the window
  120.         // REVIEW: hides editor when soft keyboard pops up (TFS 326875)
  121.         window.addEventListener('resize', () => {
  122.             if (this._ctl.containsFocus()) {
  123.                 this._closeEditor(true);
  124.                 this._grid.focus();
  125.             }
  126.         });
  127.     }
  128.     // gets an instance of the control being hosted by this grid editor
  129.     get control() {
  130.         return this._ctl;
  131.     };
  132.        
  133.         // 遍历查找树节点
  134.         _findNode(treeNode, key, val, nodeArr){
  135.                 if(treeNode.dataItem && treeNode.dataItem[key] == val){
  136.                         nodeArr.push(treeNode);
  137.                 }else if(treeNode.nodes && treeNode.nodes.length>0){
  138.                         for(let i=0; i<treeNode.nodes.length; i++){
  139.                                 if(treeNode.nodes[i].dataItem[key] == val){
  140.                                         nodeArr.push(treeNode.nodes[i]);
  141.                                 }else{
  142.                                         this._findNode(treeNode.nodes[i], key, val, nodeArr);
  143.                                 }
  144.                         }
  145.                 }
  146.         };
  147.        
  148.     // handle the grid's beginningEdit event by canceling the built-in editor,
  149.     // initializing the custom editor and giving it the focus.
  150.     _beginningEdit(grid, args) {
  151.         // check that this is our column
  152.                 // console.log(args)

  153.         if (grid.columns[args.col] != this._binding) {
  154.                         // console.log(grid.columns[args.col])/
  155.             return;
  156.         }
  157.         // check that this is not the Delete key
  158.         // (which is used to clear cells and should not be messed with)
  159.         var evt = args.data;
  160.         if (evt && evt.keyCode == wijmo.Key.Delete) {
  161.             return;
  162.         }
  163.         // cancel built-in editor
  164.         args.cancel = true;
  165.         // save cell being edited
  166.         this._rng = args.range;
  167.         CustomGridEditor._isEditing = true;
  168.         // initialize editor host
  169.         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' : '';
  170.         wijmo.setCss(this._ctl.hostElement, {
  171.             position: 'absolute',
  172.             left: rcCell.left - 1 + ptOffset.x+8,
  173.             top: rcCell.top - 1 + ptOffset.y+8,
  174.             width: rcCell.width + 1,
  175.             height: grid.rows[args.row].renderHeight + 1,
  176.             borderRadius: '0px',
  177.             zIndex: zIndex,
  178.         });
  179.         // initialize editor content
  180.                 /**
  181.                         这句代码是负责在展开下拉前更新下拉状态的
  182.                         在此处清空勾选状态即可。
  183.                 */
  184.         if (!wijmo.isUndefined(this._ctl['checkedItems']) && this._ctl['checkedItems'].length > 0) {
  185.             this._ctl['checkedItems'] = [];
  186.         }
  187.                
  188.                
  189.                 // 这里设置回填数据
  190.                 var cellVal = grid.getCellData(args.row, args.col);
  191.                 var valArr = null;
  192.                 if(cellVal && cellVal.length > 0){
  193.                         valArr = cellVal.split(",");
  194.                 }
  195.                 if(valArr){
  196.                         var treeData = this._treeData;
  197.                         var findNode = this._findNode;
  198.                         var _this = this;
  199.                         var checkedNodes = [];
  200.                         for(let i=0; i<valArr.length; i++){
  201.                                 this._ctl.nodes.forEach(function(node){
  202.                                         findNode.call(_this, node, "header", valArr[i], checkedNodes);
  203.                                 });
  204.                         }
  205.                         checkedNodes.forEach(function(node){
  206.                                 node.isChecked = true;
  207.                         });
  208.                 }
  209.                
  210.                 /*
  211.         else {
  212.             throw 'Can\'t set editor value/text...';
  213.         }
  214.                 */
  215.         // start editing item
  216.         var ecv = grid.editableCollectionView, item = grid.rows[args.row].dataItem;
  217.         if (ecv && item && item != ecv.currentEditItem) {
  218.             setTimeout(function () {
  219.                 grid.onRowEditStarting(args);
  220.                 ecv.editItem(item);
  221.                 grid.onRowEditStarted(args);
  222.             }, 50); // wait for the grid to commit edits after losing focus
  223.         }
  224.         // activate editor
  225.         document.body.appendChild(this._ctl.hostElement);
  226.         this._ctl.focus();
  227.         setTimeout(() => {
  228.                
  229.             // get the key that triggered the editor
  230.             var key = (evt && evt.charCode > 32)
  231.                 ? String.fromCharCode(evt.charCode)
  232.                 : null;
  233.             // get input element in the control
  234.             var input = this._ctl.hostElement.querySelector('input');
  235.             // send key to editor
  236.             if (input) {
  237.                 if (key) {
  238.                     input.value = key;
  239.                     wijmo.setSelectionRange(input, key.length, key.length);
  240.                     var evtInput = document.createEvent('HTMLEvents');
  241.                     evtInput.initEvent('input', true, false);
  242.                     input.dispatchEvent(evtInput);
  243.                 }
  244.                 else {
  245.                     input.select();
  246.                 }
  247.             }
  248.             // give the control focus
  249.             if (!input && !this._openDropDown) {
  250.                 this._ctl.focus();
  251.             }
  252.             // open drop-down on F4/alt-down
  253.             if (this._openDropDown && this._ctl instanceof wijmo.input.DropDown) {
  254.                 this._ctl.isDroppedDown = true;
  255.                 this._ctl.dropDown.focus();
  256.             }
  257.         }, 50);
  258.                
  259.     }
  260.     // close the custom editor, optionally saving the edits back to the grid
  261.     _closeEditor(saveEdits) {
  262.         if (this._rng) {
  263.             var flexGrid = this._grid, ctl = this._ctl, host = ctl.hostElement;
  264.             // raise grid's cellEditEnding event
  265.             var e = new wijmo.grid.CellEditEndingEventArgs(flexGrid.cells, this._rng);
  266.             flexGrid.onCellEditEnding(e);
  267.             // save editor value into grid
  268.             if (saveEdits) {
  269.                                 /**
  270.                                         这里加一个判断,实现下拉tree回填逻辑
  271.                                 */
  272.                                 if(!wijmo.isUndefined(ctl['checkedItems'])){
  273.                                         var vals = ctl['checkedItems'];
  274.                                         if(vals.length > 0){
  275.                                                 var value = "";
  276.                                                 vals.forEach(function(item){
  277.                                                         if(item.header){
  278.                                                                 value += item.header + ","
  279.                                                         }
  280.                                                 })
  281.                                                 if(value.length > 0){
  282.                                                         value = value.substring(0, value.length-1);
  283.                                                 }
  284.                                                 this._grid.setCellData(this._rng.row, this._rng.col, value);
  285.                                         }else{
  286.                                                 this._grid.setCellData(this._rng.row, this._rng.col, "");
  287.                                         }
  288.                 }else if (!wijmo.isUndefined(ctl['value'])) {
  289.                     this._grid.setCellData(this._rng.row, this._rng.col, ctl['value']);
  290.                 }
  291.                 else if (!wijmo.isUndefined(ctl['text'])) {
  292.                     this._grid.setCellData(this._rng.row, this._rng.col, ctl['text']);
  293.                 }
  294.                 else {
  295.                     throw 'Can\'t get editor value/text...';
  296.                 }
  297.                 this._grid.invalidate();
  298.             }
  299.             // close editor and remove it from the DOM
  300.             if (ctl instanceof wijmo.input.DropDown) {
  301.                 ctl.isDroppedDown = false;
  302.             }
  303.             host.parentElement.removeChild(host);
  304.             this._rng = null;
  305.             CustomGridEditor._isEditing = false;
  306.             // raise grid's cellEditEnded event
  307.             flexGrid.onCellEditEnded(e);
  308.         }
  309.     }
  310.     // commit row edits, fire row edit end events (TFS 339615)
  311.     _commitRowEdits() {
  312.         var flexGrid = this._grid, ecv = flexGrid.editableCollectionView;
  313.         this._closeEditor(true);
  314.         if (ecv && ecv.currentEditItem) {
  315.             var e = new wijmo.grid.CellEditEndingEventArgs(flexGrid.cells, flexGrid.selection);
  316.             ecv.commitEdit();
  317.             setTimeout(() => {
  318.                 flexGrid.onRowEditEnding(e);
  319.                 flexGrid.onRowEditEnded(e);
  320.                 flexGrid.invalidate();
  321.             });
  322.         }
  323.     }
  324. }
  325. //
  326. document.readyState === 'complete' ? init() : window.onload = init;
  327. //
  328. function init() {
  329.     //
  330.     // create some random data
  331.     var countries = [
  332.         { header: 'Electronics', img: 'resources/electronics.png', items: [
  333.             { header: 'Trimmers/Shavers' },
  334.             { header: 'Tablets' },
  335.             { header: 'Phones', img: 'resources/phones.png', items: [
  336.                 { header: 'Apple' },
  337.                 { header: 'Motorola' },
  338.                 { header: 'Nokia' },
  339.                 { header: 'Samsung' }
  340.             ]},
  341.             { header: 'Speakers' },
  342.             { header: 'Monitors' }
  343.         ]}
  344.         ];
  345.        
  346.     var products = [
  347.         { id: 0, name: 'Widget', unitPrice: 23.43 },
  348.         { id: 1, name: 'Gadget', unitPrice: 12.33 },
  349.         { id: 2, name: 'Doohickey', unitPrice: 53.07 }
  350.     ];
  351.     var data = [];
  352.     var dt = new Date();
  353.     for (var i = 0; i < 10; i++) {
  354.         data.push({
  355.             id: i,
  356.             date: new Date(dt.getFullYear(), i % 12, 25, i % 24, i % 60, i % 60),
  357.             time: new Date(dt.getFullYear(), i % 12, 25, i % 24, i % 60, i % 60),
  358.             country: countries[0].header,
  359.             product: products[Math.floor(Math.random() * products.length)].name,
  360.             amount: Math.random() * 10000 - 5000,
  361.             discount: Math.random() / 4
  362.         });
  363.     }
  364.     //
  365.     // grid with custom editors
  366.     theGrid = new wijmo.grid.FlexGrid('#theGrid', {
  367.         keyActionTab: 'CycleOut',
  368.         autoGenerateColumns: false,
  369.         itemsSource: data,
  370.         columns: [
  371.             { header: 'ID', binding: 'id', width: 40, isReadOnly: true },
  372.             { header: 'Date', binding: 'date', format: 'd' ,"align":"right"},
  373.             { header: 'Time', binding: 'time', format: 't' },
  374.             { header: 'Country', binding: 'country' },
  375.             { header: 'Product', binding: 'product' },
  376.             { header: 'Amount', binding: 'amount', format: 'n2' }
  377.         ],
  378.                 beginningEdit: function (s, e) {
  379.             if (e.data.type == 'keypress' &&
  380.                 !document.getElementById('quickEdit').checked) {
  381.                 e.cancel = true;
  382.             }
  383.         },
  384.                  itemFormatter: function (panel, r, c, cell) {//单元格的GridPanel,从0开始——单元格的行、列索引、单元格的HTML元素
  385.                             var editRange = panel.grid.editRange;
  386.                             // if (panel.cellType == wijmo.grid.CellType.Cell && editRange && editRange.row === r && editRange.col === c) {
  387.                               // if(r==0){
  388.                 //                            // if (grid.columns[c].binding == 'country') {
  389.                 //

  390.                                                                 // console.log(panel.cellType == wijmo.grid.CellType.Cell)
  391.                                                         // }
  392.                                         // }
  393.                 }
  394.     });
  395.        
  396.     new CustomGridEditor(theGrid, 3, wijmo.input.DropDownTree, {
  397.         itemsSource: countries
  398.     });
  399.         new CustomGridEditor(theGrid, 4, wijmo.input.DropDownTree, {
  400.             itemsSource: countries
  401.         });
  402.     // new CustomGridEditor(theGrid, 'amount', wijmo.input.InputNumber, {
  403.     //     format: 'n2',
  404.     //     step: 10
  405.     // });
  406.     //
  407.     // create an editor based on a ComboBox
  408.     // var multiColumnEditor = new CustomGridEditor(theGrid, 'product', wijmo.input.ComboBox, {
  409.     //     headerPath: 'name',
  410.     //     displayMemberPath: 'name',
  411.     //     itemsSource: products
  412.     // });
  413.     //
  414.     // customize the ComboBox to show multiple columns
  415.     // var combo = multiColumnEditor.control;
  416.     // combo.listBox.formatItem.addHandler(function (s, e) {
  417.     //     e.item.innerHTML = '<table><tr>' +
  418.     //         '<td style="width:30px;text-align:right;padding-right:6px">' + e.data.id + '</td>' +
  419.     //         '<td style="width:100px;padding-right:6px"><b>' + e.data.name + '</b></td>' +
  420.     //         '<td style="width:80px;text-align:right;padding-right:6px">' +
  421.     //         wijmo.Globalize.format(e.data.unitPrice, 'c') +
  422.     //         '</td>' +
  423.     //         '</tr></table>';
  424.     // });
  425. }
  426.                 </script>
  427.         </body>
  428. </html>
复制代码


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

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x

0 个回复

您需要登录后才可以回帖 登录 | 立即注册
返回顶部