• ngTbale假分页实现排序、搜索、导出CSV等功能


    一. ngTable功能简化

      使用ngTable经常有分页,排序,过滤等功能,实现诸多功能较为麻烦。为了方便开发过程,可以抽取一些table共同点写一个公有方法。

      注意:

        1. 由于很多特别的需求,可能表格不一定适用于自己的项目,部分地方需要自己调试到适合自己的项目。

        2. 部分功能没有使用ngTable自带的功能,只因为PM需求与之不符合 

      代码如下:

      1. ListTableEX代码:

      1 angular.module('newApp')
      2   .factory('ListTableEX', ['ngTableParams', 'getStorage', function (ngTableParams, getStorage) {
      3     var store = getStorage('localStorage');
      4 
      5     /**
      6      * 创建表格
      7      * @param options1
      8      * @param options2
      9      * @returns {ngTableParams|*}
     10      */
     11     function createListTable(options1, options2) {
     12       var listTable = new ngTableParams(options1, {
     13         counts: options2.counts,
     14         getData: function ($defer, params) {
     15           // 如果不需要强制刷新数据且有缓存数据,那么直接使用缓存数据
     16           // Note: 由于api可能返回空数据或者出错,因此不能通过判断
     17           // 缓存数组容量是否为0来判断有无缓存,而是通过判断该数组是否为
     18           // undefined
     19           if (!listTable.needForceReload && params.items) {
     20             // 复制items中的数据,以免shownItems的更改影响到items
     21             params.shownItems = [];
     22             for (var index in params.items) {
     23               params.shownItems.push(params.items[index]);
     24             }
     25             var itemsFilter = options2.itemsFilter;
     26             if (itemsFilter) {
     27               params.shownItems = itemsFilter(params.shownItems);
     28             }
     29             sortTable(listTable);
     30             listTable.calculatePages();
     31             var itemsCount = params.shownItems.length;
     32             var itemCountPerPage = listTable.count();
     33             var currentPage = listTable.page();
     34             params.total(itemsCount);
     35             $defer.resolve(subArray(params.shownItems, (currentPage - 1) * itemCountPerPage, itemCountPerPage));
     36             return;
     37           }
     38           listTable.needForceReload = false;
     39           var apiParams = {
     40             limit: 20000000,
     41             index: 1
     42           };
     43           options2.getData(apiParams).then(function (data) {
     44             listTable.resetSortParams();
     45             listTable.resetCheckboxes();
     46             var resData = data.data.data;
     47             var onDataResult = options2.onDataResult;
     48             if(onDataResult) {
     49               onDataResult(resData);
     50             }
     51             var items = resData.items;
     52             var dataPreprocessFunc = options2.dataPreprocessFunc;
     53             if (dataPreprocessFunc) {
     54               dataPreprocessFunc(items);
     55             }
     56             // 即使api没有返回数据,也应该设置缓存数组,否则会导致无限刷新
     57             params.items = items ? items : [];
     58             // 由于已经获取到了数据,那么重新reload一次,复用有缓存数据时的处理逻辑
     59             listTable.reload();
     60           }, function () {
     61           });
     62         }
     63       });
     64       listTable.tableName = options1.tableName;
     65       listTable.titles = options1.titles;
     66       listTable.needForceReload = false;
     67       listTable.getItemId = options2.getItemId;
     68       listTable.forceReload = function () {
     69         listTable.needForceReload = true;
     70         listTable.reload();
     71       }
     72       return listTable;
     73     };
     74 
     75     /**
     76      * 初始化排序功能
     77      * @param listTable
     78      */
     79     function initSort(listTable) {
     80       listTable.nextSortType = function () {
     81         return listTable.sortParams.type == 'asc' ? 'desc' : 'asc';
     82       };
     83       listTable.isColumnSorting = function (key) {
     84         return listTable.sortParams.key == key;
     85       };
     86       listTable.isColumnSortingByASC = function (key) {
     87         return listTable.isColumnSorting(key) && listTable.sortParams.type == 'asc';
     88       };
     89       listTable.isColumnSortingByDESC = function (key) {
     90         return listTable.isColumnSorting(key) && listTable.sortParams.type == 'desc';
     91       };
     92       listTable.resetSortParams = function () {
     93         listTable.sortParams = {key: '', type: ""};
     94       }
     95       listTable.resetSortParams();
     96     };
     97 
     98     /**
     99      * 初始化条目选择框
    100      * @param listTable
    101      * @param withCheckboxes
    102      * @returns {*}
    103      */
    104     function initCheckboxes(listTable, withCheckboxes) {
    105       if (withCheckboxes) {
    106         listTable.toggleCheckAll = function () {
    107           var checkedCount = listTable.checkedIds().length;
    108           if (checkedCount == listTable.shownItems.length) {
    109             listTable.deselectAll();
    110           } else {
    111             listTable.selectAll();
    112           }
    113         };
    114         listTable.selectAll = function () {
    115           listTable.resetCheckboxes();
    116           listTable.checkboxes.selectAll = true;
    117           for (var index in listTable.shownItems) {
    118             listTable.checkboxes.items[listTable.getItemId(listTable.shownItems[index])] = true;
    119           }
    120         };
    121         listTable.deselectAll = function () {
    122           listTable.resetCheckboxes();
    123           listTable.checkboxes.selectAll = false;
    124           for (var index in listTable.shownItems) {
    125             listTable.checkboxes.items[listTable.getItemId(listTable.shownItems[index])] = false;
    126           }
    127         };
    128         listTable.checkedIds = function () {
    129           var ids = [];
    130           for (var index in listTable.shownItems) {
    131             var id = listTable.getItemId(listTable.shownItems[index]);
    132             if (listTable.checkboxes.items[id]) {
    133               ids.push(id);
    134             }
    135           }
    136           listTable.checkboxes.selectAll = listTable.shownItems ? (listTable.shownItems.length == ids.length) : false;
    137           return ids;
    138         }
    139         listTable.resetCheckboxes = function () {
    140           listTable.checkboxes = {
    141             selectAll: false,
    142             items: {}
    143           };
    144         };
    145       } else {
    146         listTable.resetCheckboxes = function () {
    147         }
    148       }
    149       listTable.resetCheckboxes();
    150       return listTable;
    151     };
    152 
    153     /**
    154      * 初始话分页功能
    155      * @param listTable 表格
    156      * @private
    157      */
    158     function initPagination(listTable) {
    159       var STORAGE_PAGESIZE_KEY = getKeyForStoragePageSize(listTable);
    160 
    161       if (store.get(STORAGE_PAGESIZE_KEY)) {
    162         listTable.count(store.get(STORAGE_PAGESIZE_KEY)['value']);
    163       }
    164       listTable.editCount = listTable.count();
    165 
    166       listTable.prePage = function () {
    167         var page = listTable.page();
    168         if (page > 1) {
    169           listTable.page(page - 1);
    170         }
    171       };
    172       listTable.nextPage = function () {
    173         var page = listTable.page();
    174         if (page < Math.ceil(1.0 * listTable.total() / listTable.count())) {
    175           listTable.page(page + 1);
    176         }
    177       };
    178       listTable.calculatePages = function () {
    179         var itemsCount = listTable.shownItems.length;
    180         // 计算页导航条数据
    181         var itemCountPerPage = listTable.count();
    182         store.set(STORAGE_PAGESIZE_KEY, {
    183           'value': itemCountPerPage
    184         });
    185         var totalPage = Math.ceil(1.0 * itemsCount / itemCountPerPage);
    186         // 当前页最大为页面总数,最小为1
    187         var currentPage = Math.max(1, Math.min(listTable.page(), totalPage));
    188         listTable.page(currentPage);
    189         listTable.pages = calculateTablePages(currentPage, totalPage);
    190       }
    191     };
    192 
    193     function getKeyForStoragePageSize(listTable) {
    194       return 'list_table_page_size@' + listTable.tableName;
    195     };
    196 
    197     /**
    198      * 从原始数组中分割出一段连续的数组,该操作不会影响原始数组
    199      * @param array 原始数组
    200      * @param startIndex 分割起始位置
    201      * @param length 分割数量
    202      * @returns {Array} 分割结果
    203      */
    204     function subArray(array, startIndex, length) {
    205       var result = [];
    206       if (array && startIndex > -1 && length > 0) {
    207         var maxIndex = Math.min(startIndex + length, array.length);
    208         for (var i = startIndex; i < maxIndex; i++) {
    209           result.push(array[i]);
    210         }
    211       }
    212       return result;
    213     };
    214 
    215     /**
    216      * 对数据进行排序,该操作将直接操作传入的数组顺序
    217      * @param items 数据数组
    218      * @param key 数据对象的排序关键字
    219      * @param sortType 排序类型,'asc'或者'desc'
    220      */
    221     function sortItems(items, key, sortType) {
    222       if (!items || items.length < 1 || !key || key.length < 1) {
    223         return;
    224       }
    225       items.sort(
    226         function compareFunction(param1, param2) {
    227           var value1 = param1[key];
    228           var value2 = param2[key];
    229           if (typeof(value1) === 'number' && typeof(value2) === 'number') {
    230             return sortType * (value1 - value2);
    231           }
    232           return sortType * ((value1 + '').localeCompare(value2 + ''));
    233         }
    234       );
    235     };
    236 
    237     function calculateTablePages(currentPage, totalPage) {
    238       if (totalPage == 0) {
    239         return [];
    240       }
    241       var end = Math.min(9, totalPage);
    242       var pages = [currentPage];
    243       while (pages.length < end) {
    244         var pre = pages[0] - 1;
    245         var next = pages[pages.length - 1] + 1;
    246         if (pre >= 1) {
    247           pages.unshift(pre);
    248         }
    249         if (next <= totalPage) {
    250           pages.push(next);
    251         }
    252       }
    253       return pages;
    254     };
    255 
    256     function sortTable(listTable) {
    257       var sortParams = listTable.sorting();
    258       var sortKey;
    259       var sortType;
    260       for (var key in sortParams) {
    261         sortKey = key;
    262         sortType = sortParams[key];
    263       }
    264       if (!sortType || sortType.length < 1) {
    265         sortType = 'asc';
    266       }
    267       listTable.sortParams.key = sortKey;
    268       listTable.sortParams.type = sortType;
    269       if (sortKey && sortKey.length > 0) {
    270         sortItems(listTable.shownItems, sortKey, sortType == 'asc' ? 1 : -1);
    271       }
    272     };
    273 
    274     function init(options1, options2) {
    275       var listTable = createListTable(options1, options2);
    276       initSort(listTable);
    277       initCheckboxes(listTable, options1.withCheckboxes);
    278       initPagination(listTable);
    279       return listTable;
    280     };
    281 
    282     return {
    283       init: init,
    284     };
    285   }]);

      2. ListTableEX分页代码:

     1 <div class="ng-cloak dahuo-pagination" ng-if="params.pages.length > 0">
     2   <div class="col-lg-6">
     3     <div class="dataTables_info" role="status" aria-live="polite">
     4       {{ params.count() * (params.page() - 1) + 1 }}~
     5       {{ params.count() * (params.page() - 1) + params.data.length }}条,共{{
     6       params.total() }}条记录
     7     </div>
     8   </div>
     9   <div class="col-lg-6 pagination2" style="margin-bottom: 40px">
    10     <div class="dataTables_paginate paging_simple_numbers">
    11       <ul class="pagination ng-table-pagination">
    12         <li><a href="" ng-click="params.prePage()">上一页</a></li>
    13         <li ng-repeat="page in params.pages"
    14             ng-class="{'active' : page == params.page()}">
    15           <a href="" ng-click="params.page(page)">{{ page }}</a>
    16         </li>
    17         <li><a href="" ng-click="params.nextPage()">下一页</a></li>
    18       </ul>
    19     </div>
    20     <div class="col-lg-3 pull-right">
    21       {{params.testOK}}
    22       <select class="select form-control" ng-model="params.editCount" ng-change="params.count(params.editCount)"
    23               style="padding: 6px; margin-top: 2px;min- 80px;margin-right: 10px"
    24               ng-options="size for size in params.settings().counts">
    25       </select>
    26     </div>
    27   </div>
    28 </div>

      由于PM的需求变更较大,使用方法可能各不相同。

      基本使用方法如下:

      1. HTML

        本表格是一个有checkBox的表格,具有编辑,新建,上线,下线等功能。

     1 <div>
     2   <div class="row">
     3     <div class="col-lg-12 portlets">
     4       <div class="panel">
     5         <div class="panel-content">
     6           <div class="row dahuo-table-toolbar">
     7             <div class="col-xs-6">
     8               <button type="button" class="btn btn-default" ng-click="activityupload()" ng-disabled="checkedCount() < 1">发布</button>
     9               <button type="button" class="btn btn-default" ng-click="activityoffline()" ng-disabled="checkedCount() < 1">下线</button>
    10               {{ checkedCount() }}个被选中
    11             </div>
    12             <div class="col-xs-6">
    13               <div class="pull-right">
    14                 <a href="#operation-activityadd">
    15                   <button type="button" class="btn btn-primary">+ 新建活动</button>
    16                 </a>
    17               </div>
    18             </div>
    19           </div>
    20           <div class="table-responsive">
    21             <table ng-table="activityTable" class="table trans-table table-hover text-left dahuo-table"
    22                      template-pagination="layouts/pagination_v2.html" width="100%">
    23               <thead>
    24                 <tr>
    25                   <th width="5%" class="dahuo-table-header-th text-center" header="'ng-table/headers/checkbox.html'">
    26                     <span class="list-checkbox">
    27                       <input type="checkbox" ng-model="activityTable.checkboxes.selectAll" ng-disabled="activityTable.items.length < 1" ng-click="activityTable.toggleCheckAll()"/>
    28                       <span></span>
    29                     </span>
    30                   </th>
    31                   <th width="{{title.width}}" ng-repeat="title in activityTable.titles" ng-class="{'sort': title.sortable}" ng-click="title.sortable ? activityTable.sorting(title.key, activityTable.nextSortType()) : 0">{{ title.name }}
    32                     <li class="fa pull-right" ng-if="title.sortable" ng-class="{'fa-sort-asc': activityTable.isColumnSortingByASC(title.key), 'fa-sort-desc': activityTable.isColumnSortingByDESC(title.key), 'fa-unsorted': !activityTable.isColumnSorting(title.key)}" aria-hidden="true" ng-style="{'color' : (!activityTable.isColumnSorting(title.key) ? '#c9c9c9' : '')}"></li>
    33                   </th>
    34                 </tr>
    35               </thead>
    36               <tbody>
    37                 <tr ng-repeat="item in $data">
    38                   <td class="text-center" header="'ng-table/headers/checkbox.html'">
    39                     <span class="list-checkbox">
    40                       <input type="checkbox" ng-model="activityTable.checkboxes.items[item.id]"/>
    41                       <span></span>
    42                     </span>
    43                   </td>
    44                   <td><a href="#operation-activityedit/{{item.id}}">{{自定义绑值}}</a></td>
    45                   <td>{{自定义绑值}}</td>
    46                   <td>{{自定义绑值}}</td>
    47                   <td>{{自定义绑值}}</td>
    48                   <td>{{自定义绑值}}</td>
    49                 </tr>
    50                 <tr style="height: 100px" ng-if="$data.length < 1">
    51                   <td colspan="6" class="text-center">暂时没有数据</td>
    52                 </tr>
    53               </tbody>
    54             </table>
    55           </div>
    56         </div>
    57       </div>
    58     </div>
    59   </div>
    60 </div>

      2. JS

      字段解释:

        (1).tableName:table名称,用于缓存数据时使用的localStorage的key

        (2).titles:table表头控制项目,key是绑定数据的key,name是显示名称,sortable是否可排序

        (3).withCheckboxes:表格是否包含checkboxes的列

        (4).page:当前页

        (5).count:每页显示条目数量

        (6).sorting:默认排序的key和升降序

        (7).counts:每页显示条数数量更改项

        (8).getData:获取表格的数据,一般是发送API。这里有一个坑,后续会详细说明。

        (9).getItemId:对应checkBoxes的itemId,复制即可。

      依赖项:

        DS:API请求依赖

        logger:提示信息依赖

     1 angular.module('newApp')
     2   .controller('ActivityListCtrl', ['$scope', 'ds.activity', 'logger', 'ListTableEX',
     3     function($scope, DS, logger, ListTableEX) {
     4       $scope.activityTable = new ListTableEX.init(
     5         {
     6           tableName:'operation.activity.activity.list',
     7           titles:[
     8             {key: '', name: "活动ID", sortable: true,  '10%'},
     9             {key: '', name: "活动名称", sortable: true,  '35%'},
    10             {key: '', name: "开始时间", sortable: true,  '20%'},
    11             {key: '', name: "结束时间", sortable: true,  '20%'},
    12             {key: '', name: "状态", sortable: true,  '10%'},
    13           ],
    14           withCheckboxes: true,
    15           page: 1,
    16           count: 25,
    17           sorting: {'id': 'desc'},
    18         },
    19         {
    20           counts: [10, 25, 50, 100],
    21           getData: function(apiParams) {
    22             return DS.listActivity(apiParams);
    23           },
    24           getItemId: function(item) {
    25             return item.id;
    26           }
    27         }
    28       );
    29 
    30       $scope.checkedCount = function() {
    31         return $scope.activityTable.checkedIds().length;
    32       }
    33 
    34       $scope.activityupload = function() {
    35         var checkedIds = $scope.activityTable.checkedIds();
    36         if (checkedIds.length < 1) {
    37           logger.warning('请选择操作对象');
    38           return;
    39         }
    40 
    41         DS.activityUpload(checkedIds)
    42           .then(function() {
    43             $scope.activityTable.forceReload();
    44             logger.success('上线成功');
    45           }, function(error) {
    46             logger.error('上线失败');
    47           });
    48       };
    49 
    50       $scope.activityoffline = function() {
    51         var checkedIds = $scope.activityTable.checkedIds();
    52         if (checkedIds.length < 1) {
    53           logger.warning('请选择操作对象');
    54           return;
    55         }
    56 
    57         DS.activityOffline(checkedIds)
    58           .then(function() {
    59             $scope.activityTable.forceReload();
    60             logger.success('下线成功');
    61           }, function(error) {
    62             logger.error('下线失败');
    63           });
    64       };
    65     }
    66   ]);

    二. 使用中注意的问题:

      1. 需要自己更改适应服务端返回数据的处理代码:

        对应代码段时listTableEX的“var resData = data.data.data”

      2. 使用中可能Table数据不是来自于API:

        需要使用如下代码:

     1 getData: function(apiParams) {
     2   return {then: function(callback){
     3     var data = {
     4       data:{
     5         data:{
     6           items: [],
     7         }
     8       }
     9     }
    10     callback(data);
    11   }}
    12 }

        然后强行加载数据:

    1 $scope.ActivityTable.items = items;
    2 $scope.ActivityTable.page(1);
    3 $scope.ActivityTable.reload();

      3. 可能数据来自API,但是触发时机不是Table new出来时:

        New出来的代码更改如下:

     1 getData: function(apiParams) {
     2   if (满足API发送条件){
     3     var apiParams = {
     4         (自定义)
     5     };
     6     return DS.activityList(apiParams);
     7   }
     8   return {then: function(callback){
     9     var data = {
    10       data:{
    11         data:{
    12           items: [],
    13         }
    14       }
    15     }
    16     callback(data);
    17   }}
    18 }

         然后发送API获取数据:

    1 $scope.ActivityTable.page(1)
    2 $scope.ActivityTable.forceReload();

     三. 搜索,过滤,导出CSV插件

      代码都有较长的注释,这里不做过多的介绍。

      导出CSV依赖插件FileSaver.js。

    angular.module('newApp')
      .factory('ListTableEXExtend', function (getStorage) {
        /*
        First method:
          ListTableEXExtend.searchFilter(Array, String, Boolean);
          Input:
            Array: ['a', 'b', 'c', 'aa', 'ab', 'cc']
            String: 'a'
            Boolean: true
          Output:
            Array: ['a', 'aa', 'ab']
    
        Second method:
          ListTableEXExtend.searchFilter(ObjectArray, String, Boolean, Array)
          Input:
            ObjectArray: [
              {id:111, name:'aaa', ex1:'sss', ex2: 'sss'},
              {id:222, name:'11aa', ex1:'sss', ex2: 'sss'},
              {id:333, name:'a1a1', ex1:'sss', ex2: 'sss'},
              {id:444, name:'bbbb', ex1:'sss', ex2: 'sss'}
            ]
            String: 1 or '1'
            Boolean: true
            Array: ['id', 'name']
          Output:
            ObjectArray: [
              {id:111, name:'aaa', ex1:'sss', ex2: 'sss'},
              {id:222, name:'11aa', ex1:'sss', ex2: 'sss'},
              {id:333, name:'a1a1', ex1:'sss', ex2: 'sss'}
            ]
          Others: return items
    
          Note: if has too much data, Please use method: delay reload Table
        */
        function searchFilter(items, keyword, isIgnoreUpper, regArr) {
          if (!items) {
            console.log("Items must be defined");
            return;
          }
          if (typeof(items) !== "object" || items.constructor !== Array) {
            console.log("Items must be a array");
            return items;
          }
          if (items.length === 0) {
            return items;
          }
          if (!keyword && keyword !== 0 && keyword !== '0') {
            return items;
          }
          /* Copy Items*/
          var newItems = items.concat();
          if (isIgnoreUpper){
            var keyword = keyword.toString().toLowerCase();
          }else {
            var keyword = keyword.toString();
          }
          if (!regArr) {
            var resultItem = [];
            for (var index in items) {
              try {
                if (isIgnoreUpper){
                  var newItem = newItems[index].toString().toLowerCase();
                }else {
                  var newItem = newItems[index].toString();
                }
              } catch (e) {
                console.log(e.name + ": " + e.message);
              }
              if (newItem.indexOf(keyword) !== -1) {
                resultItem.push(items[index]);
              }
            }
            return resultItem;
          }
          if (typeof(regArr) !== "object" || regArr.constructor !== Array) {
            console.log("regArr must be a array");
            return items;
          }
          if (regArr.length === 0) {
            return items;
          }
          var newRegArr = regArr.concat();
          for (var index in newRegArr) {
            try {
              newRegArr[index].toString();
            } catch (e) {
              console.log(e.name + ": " + e.message);
            }
          }
          var resultItem = [];
          for (var index in items) {
            try {
              var newItem = newItems[index];
              if (typeof(newItem) !== "object" || newItem.constructor !== Object) {
                console.log("Items must be a object array");
                return items;
              }
              for (var index2 in newRegArr) {
                if (!newItem[newRegArr[index2]] && newItem[newRegArr[index2]] !== 0 && newItem[newRegArr[index2]] !== '0') {
                  continue;
                }
                if (isIgnoreUpper){
                  var newItemToStr = newItem[newRegArr[index2]].toString().toLowerCase();
                }else {
                  var newItemToStr = newItem[newRegArr[index2]].toString();
                }
                if (newItemToStr.indexOf(keyword) !== -1) {
                  resultItem.push(items[index]);
                  break;
                }
              }
            } catch (e) {
              console.log(e.name + ": " + e.message);
            }
          }
          return resultItem;
        }
    
        /*
          Method:
            ListTableEXExtend.selectFilter(ObjectArray, Key, Value)
            Input:
              ObjectArray: [
                {id:111, name:'aaa', product_id:'123', ex: 'sss'},
                {id:222, name:'11aa', product_id:'113', ex: 'sss'},
                {id:333, name:'a1a1', product_id:'123', ex: 'sss'},
                {id:444, name:'bbbb', product_id:'113', ex: 'sss'},
              ]
              Key: product_id
              Value: 123
            Output:
              ObjectArray: [
                {id:111, name:'aaa', product_id:'123', ex: 'sss'},
                {id:333, name:'a1a1', product_id:'123', ex: 'sss'}
              ]
            Others: return items
        */
        function selectFilter(items, key, value) {
          if (!items) {
            console.log("Items must be defined");
            return;
          }
          if (!key || !value) {
            console.log("Key, value must be defined");
            return items;
          }
          if (typeof(items) !== "object" || items.constructor !== Array) {
            console.log("Items must be a array");
            return items;
          }
          if (items.length === 0) {
            return items;
          }
          key = key.toString();
          var resultItem = []
          for (var index in items) {
            try {
              var item = items[index];
              if (typeof(item) !== "object" || item.constructor !== Object) {
                console.log("Items must be a object array");
                return items;
              }
              if (item[key] == undefined || item[key] == null || item[key] === '') {
                continue;
              }
              if (value === 'defined'){
                resultItem.push(items[index]);
                continue;
              }
              if (item[key] == value) {
                resultItem.push(items[index]);
              }
            } catch (e) {
              console.log(e.name + ": " + e.message);
            }
          }
          return resultItem;
        }
    
        function _getDateTime(date){
          if (angular.isDate(date)){
            return date.getTime();
          }else {
            return new Date(date).getTime();
          }
        }
    
        function timeFilter(items, start, end, key){
          if (!items) {
            console.log("Items must be defined");
            return;
          }
          if (!angular.isArray(items)) {
            console.log("Items must be a array");
            return items;
          }
          var startTime = _getDateTime(start);
          var endTime = _getDateTime(end);
          var newItems = items.concat();
          var resultItems = [];
          var newKey = key.toString()
          for (var index in items){
            var newItem = items[index];
            if (!angular.isObject(newItem)) {
              console.log("Items must be a object array");
              return items;
            }
            if (!newItem[newKey]) {
              continue;
            }
            var newItemDate = _getDateTime(newItem[newKey]);
            if (newItemDate >= startTime && newItemDate <= endTime){
              resultItems.push(newItem);
            }
          }
          return resultItems;
        }
    
        function editItem(items, item, primaryKey, isCreated){
          if (!items) {
            console.log("Items must be defined");
            return;
          }
          if (!item || !primaryKey) {
            console.log("Item, primaryKey must be defined");
            return items;
          }
          if (!angular.isArray(items)) {
            console.log("Items must be a array");
            return items;
          }
          if (!angular.isObject(item)) {
            console.log("Item must be a object");
            return items;
          }
          primaryKey = primaryKey.toString();
          /* Copy Items*/
          var newItems = items.concat();
          for (var index in newItems) {
            try {
              var newItem = newItems[index];
              if (!angular.isObject(newItem)) {
                console.log("Items must be a object array");
                return items;
              }
              if (!newItem[primaryKey] && newItem[primaryKey] != 0 && !item[primaryKey] && item[primaryKey] != 0) {
                continue;
              }else {
                if (newItem[primaryKey] == item[primaryKey]){
                  newItems[index] = item;
                  isCreated = false;
                }
              }
            } catch (e) {
              console.log(e.name + ": " + e.message);
              return items;
            }
          }
          if (isCreated){
            newItems.push(item);
          }
          return newItems;
        }
    
        /*
          Usage:
          var csv = new ListTableEXExtend.Csv({
            --titles(Option):
              ObjectArray.
              just lick listtableex.titles, 'key' and 'name' is needed.
              This is used for defining Csv col.
            --items(Option):
              ObjectArray.
              just lick listtableex.items.
            --filterData(Option):
              FuncObject.
              This is used for switch data.
              For example:
                {id(Must be titles'key): function(input){return input + 'Test'}}.
            --defaultChar(Option):
              String or Object.
              String: All table's default char.
              Object: One col default char(Must be titles'key).
              This is used for set default char when <td></td> is like this.
            --handleData(Option):
              Func.
              Must return Data.
              Data is the fileData.
            --fileName(Option):
              String.
            --delimiter(Option):
              String.
              This is used for maping <td> and another <td> with 'char'.
              Default is ',' used for Csv.
              });
          csv has to public method:
            --setItems(Option)
              Note: The items maybe is undefined because API are not repond this time.
              So you need to set items again.
            --setTitles(Option)
              Note: The titles maybe changed.
              So you need to set titles again.
            --exportCsv(Necessary, Main)
            Others are private.
            If you have to get proto, you can add public method.
        */
        function Csv(options) {
          var items = options.items;
          var titles = options.titles;
          var filterData = options.filterData;
          var handleData = options.handleData;
          var defaultChar = options.defaultChar;
          var type = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8";
          var fileName = options.fileName ? options.fileName : 'download.csv';
          var delimiter = options.delimiter ? options.delimiter : ',';
          var data = '';
    
          function _download(data, fileName) {
            var blob = new Blob(["\ufeff" + data], {
              type: type
            });
            saveAs(blob, fileName);
          }
    
          function _getData(items, titles) {
            if (!items || !titles || typeof(items) !== "object" || items.constructor !== Array || typeof(titles) !== "object" || titles.constructor !== Array) {
              console.log("Usage:\n\titems: ObjectArray\n\ttitles: ObjectArray\n");
              return false;
            }
            return true;
          }
    
          function _handleData(items, titles, delimiter, filterData, handleData, defaultChar) {
            data = '';
            if (handleData) {
              data = handleData(items, titles);
            } else {
              var rowData = '';
              for (var index in titles) {
                rowData += titles[index].name + delimiter;
              }
              rowData = rowData.slice(0, rowData.length - 1);
              data += rowData + '\n';
              for (var i in items) {
                var item = items[i];
                var rowData = '';
                for (var j in titles) {
                  var title = titles[j];
                  if (!title.key) {
                    return '';
                  } else if (item[title.key] === undefined || item[title.key] === '') {
                    if (defaultChar) {
                      if (angular.isObject(defaultChar)) {
                        if (defaultChar[title.key] !== undefined) {
                          rowData += defaultChar[title.key] + delimiter;
                        } else {
                          rowData += delimiter;
                        }
                      } else {
                        rowData += defaultChar + delimiter;
                      }
                    } else {
                      rowData += delimiter;
                    }
                  } else {
                    if (filterData) {
                      if (title.filterKey && filterData[title.filterKey]) {
                        var filterResultData = filterData[title.filterKey](item[title.key]);
                        rowData += filterResultData + delimiter;
                      } else if (filterData[title.key]) {
                        var filterResultData = filterData[title.key](item[title.key]);
                        rowData += filterResultData + delimiter;
                      } else {
                        rowData += item[title.key] + delimiter;
                      }
                    } else {
                      rowData += item[title.key] + delimiter;
                    }
                  }
                }
                rowData = rowData.slice(0, rowData.length - 1);
                data += rowData + '\n';
              }
            }
            return data;
          }
    
          this.setItems = function (value) {
            items = value;
          }
    
          this.setTitles = function (value) {
            titles = value;
          }
    
          this.exportCsv = function () {
            if (!_getData(items, titles)) {
              return data;
            }
            data = _handleData(items, titles, delimiter, filterData, handleData, defaultChar);
            _download(data, fileName);
          }
        }
    
        return {
          searchFilter: searchFilter,
          selectFilter: selectFilter,
          timeFilter: timeFilter,
          editItem: editItem,
          Csv: Csv
        }
      })

      以上导出CSV方法是导出所有分页的数据,当表结构并非以[{...}, {...}]这种结构返回则比较难以实现通用性。对于没有分页的表,可以解析html来导出当页的数据到CSV。

    angular.module('newApp')
      .directive('exportCsv', ['$parse', '$timeout', function ($parse, $timeout) {
        /*
          This is used for export ShownTable, Only current page.
          You do not need to handle data.
    
          For example:
            if ngTable:
              <a class="btn btn-primary" ng-click='csv.generate($event, "FileName", "ngTableBindname")' href=''>export</a>
              <table ng-table="ngTableBindname" class="table trans-table table-hover dahuo-table" width="100%" export-csv="csv">
            else:
              <a class="btn btn-primary" ng-click='csv.generate($event, "FileName", "tableId")' href=''>export</a>
              <table id="tableId" class="table trans-table table-hover dahuo-table" width="100%" export-csv="csv">
        */
        var delimiter = ',';
        var tables = {};
        var type = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8";
        return {
          restrict: 'A',
          scope: false,
          link: function(scope, element, attrs) {
            var data = '';
            if (attrs.ngTable){
              tables[attrs.ngTable] = element;
            }else {
              tables[attrs.id] = element;
            }
            function stringify(str) {
              return '"' + str.replace(/^\s\s*/, '').replace(/\s*\s$/, '').replace(/"/g,'""') + '"';
            }
            function parseTable(table) {
              data = '';
              if (tables[table]){
                var rows = tables[table].find('tr');
              }else {
                return data
              }
              angular.forEach(rows, function(row, i) {
                var tr = angular.element(row),
                  tds = tr.find('th'),
                  rowData = '';
                if (tr.hasClass('ng-table-filters')) {
                  return;
                }
                if (tds.length === 0) {
                  tds = tr.find('td');
                }
                angular.forEach(tds, function(td) {
                  rowData += stringify(angular.element(td).text()) + Array.apply(null, Array(td.colSpan)).map(function () { return delimiter; }).join('');
                });
                rowData = rowData.slice(0, rowData.length - 1);
                data += rowData + '\n';
              });
            }
    
            function download(data, filename) {
              var blob = new Blob(["\ufeff" + data], {
                type: type
              });
              saveAs(blob, filename);
            }
    
            var csv = {
              generate: function(event, filename, table) {
                if (table){
                  parseTable(table);
                  download(data, filename);
                } else {
                  parseTable();
                  download(data, filename);
                }
              }
            };
            $parse(attrs.exportCsv).assign(scope.$parent, csv);
          }
        }
      }])
  • 相关阅读:
    leetcode 5414 收藏清单
    leetcode 714 买卖股票的最佳时机含手续费(dp)
    春招的一个记录
    leetcode 560 和为k的子数组(前缀和 + map)
    机器学习--激活函数篇
    leetcode 回文链表(O(1)空间复杂度)
    Leetcode 659 分割数组为连续子序列 (贪心)
    论文笔记:MeLU: Meta-Learned User Preference Estimator for Cold-Start Recommendation
    jni.h头文件详解一
    JNI笔记
  • 原文地址:https://www.cnblogs.com/ccblogs/p/4917230.html
Copyright © 2020-2023  润新知