angular各版本通用,xls之类等各excel导出插件都不需要引用,原生js,json导出excel解决方案

一、前言

问题由来,接手公司旧版本的 angular项目进行维护,该项目还是 极低的angular1原始版本,然后要进行新的功能添加导出excel,网上找各类都是 angular4之类的插件不适用,好吧只能凭着之前纯js方法写Excel导出功能,这样其实也就同样可以适用于 vue/react等前端框架,当然各框架本身都有比较好 功能齐全的 Excel导出插件。

二、思路

大致是 前端向后端条件查询 返回 json数据,前端js通过 一份数据参数对照表 将json数据 filter转化及 构造成excel数据结构(html-table)dom模板,然后创建a标签,将模板以 Blob对象 插入a标签href,然后点击导出excel

三、实现

1. excel导出功能-主体代码

// 导出excel功能
      $scope.exportExcelClick = function () {
        var str = '?';
        var param = {
          orderCode: $scope.condition.orderCode,
          orderStatus: $scope.condition.orderStatus,
          vin: $scope.condition.vin,
          beginDate: $scope.condition.beginDate === ''?"": typeof ($scope.condition.beginDate) === 'string'?$scope.condition.beginDate : $scope.condition.beginDate.format("yyyy-MM-dd 00:00:00"),
          endDate: $scope.condition.endDate === ''?"": typeof ($scope.condition.endDate) === 'string'?$scope.condition.endDate : $scope.condition.endDate.format("yyyy-MM-dd 23:59:59")
        }
        if ($scope.checkDateSelect(param)) {
          // 发请求
          WaybillStatusService.exportExcelClickExport(param).success(function (result) {
            if (result.success) {
              const jsonData = result.data.data
              // 参数对照表,filter 数据状态转化,例: 10-'发运' 20-'未发运' 30-'已运抵'
              const now_tableContent = [
                { prop: 'orderCode', label: '指令号', filter: null },
                { prop: 'vin', label: '车架号', filter: null },
                { prop: 'waybillType', label: '运单类型', filter: 'waybillAgainPieWaybillType' },
                { prop: 'name', label: '司机', filter: null },
                { prop: 'tmsDriverNo', label: '送车证号', filter: null },
                { prop: 'departureProvince', label: '起运省', filter: null },
                { prop: 'departureCity', label: '起运市', filter: null },
                { prop: 'destProvince', label: '目的省', filter: null },
                { prop: 'destCity', label: '目的市', filter: null },
                { prop: 'orderStatus', label: '订单状态', filter: null }, // filter: 'waybillStatusListOrderStatus'
                { prop: 'tmsCreateTime', label: '创建时间', filter: null }
              ];
              // 创建 table_content demo-数据拼接
              const table_content_str = $scope.jsonToTable(jsonData, now_tableContent)
              // Worksheet名
              var worksheetName = '订单状态列表'
              var now_url = 'data:application/vnd.ms-excel;base64,'; // xls
              //下载的表格模板数据
              var template = '<html xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:x="urn:schemas-microsoft-com:office:excel"> '
              template += '<head><!--[if gte mso 9]><xml><x:ExcelWorkbook><x:ExcelWorksheets><x:ExcelWorksheet>'
              template += '<x:Name>' + worksheetName + '</x:Name>'
              template += '<x:WorksheetOptions><x:DisplayGridlines/></x:WorksheetOptions></x:ExcelWorksheet>'
              template += '</x:ExcelWorksheets></x:ExcelWorkbook></xml><![endif]-->'
              template += '</head><body><table>' + table_content_str + '</table></body></html>'
              // 下载模板
              // 方法一 
              // 数据量较小可用,数据量 比如几千条数据时候,执行会导致失败,chrome浏览器会报 下载失败,网络错误,故放弃此方法 
              // var myA = document.createElement('a');
              // myA.setAttribute('href', now_url + $scope.base64(template))
              // myA.setAttribute('download', worksheetName +".xls")
              // myA.click()

              // 方法二 blob对象
              var blob = new Blob([template], {type: "application/vnd.ms-excel;base64"});
              var objectUrl = URL.createObjectURL(blob);
              var a = document.createElement('a');
              document.body.appendChild(a);
              a.setAttribute('style', 'display:none');
              a.setAttribute('href', objectUrl);
              a.setAttribute('download', worksheetName);
              a.click();
              URL.revokeObjectURL(objectUrl);
            } else {
              $rootScope.hycadmin.toast({
                title: result.message,
                timeOut: 3000
              });
            }
          });
        }
      };

2.根据table参数对照表 json数据 进入返回 table的内容 table_content_demo

// jsonToTable => excel demo ,根据 json 和table参数对照表返回 table的内容 table_content_demo
      $scope.jsonToTable = function(json, tableContent) {
        const jsonData = json
        const tableContentData = tableContent
        var all_tr_str = ''
        var tr_str = ''
        const all_th_str = $scope.returnTableThDemo(tableContent)
        for(var i = 0 ; i < jsonData.length ; i++ ){
          tr_str = '<tr>'
          for (var b = 0; b < tableContentData.length; b++) {
            tr_str += $scope.returnTableTrDemo(b, jsonData[i], tableContentData[b].prop, tableContentData[b].filter)
          }
          tr_str += '</tr>'
          // console.log('tr_str', tr_str)
          all_tr_str += tr_str
        }
        return all_th_str + all_tr_str
      };

3.根据table参数对照表 表头集合th_demo

// 根据 参数对照表 返回 表头集合th_demo
      $scope.returnTableThDemo = function(tableContent) {
        var th_str ='<th>';
        var th_content = ''
        for (var b = 0; b < tableContent.length; b++) {
          th_content = th_content + '<td>' + tableContent[b].label + '</td>';
        }
        th_str = th_content + '</th>';
        return th_str
      };
      

4.根据 参数对照表 返回 每行tr_demo

重点 now_td_str = $filter(filter)(v)    // 根据 filter 数据状态 转换 字符串名称显示

$filter 为 controller 挂载进来,需另外配置 filter文件

.controller('WaybillStatusCtrl', ['$rootScope', '$scope', '$modal', '$filter', 'WaybillService', 'WaybillStatusService',
  function ($rootScope, $scope, $modal, $filter, WaybillService, WaybillStatusService){
  .....
})
// 根据 参数对照表 返回 每行tr_demo
      $scope.returnTableTrDemo = function(index, item, props, filter) {
        var tr_content_str = ''
        for(var prop in item){
          if (props === prop) {
            // 如果值 非null 或 为 0  则 正常显示值
            var now_td_str = ''
            var new_str = ''
            if (filter) {
              if (item[prop]|| (item[prop] === 0)) {
                now_td_str = $filter(filter)(item[prop]) // 根据 filter 数据状态 转换 字符串名称显示
                tr_content_str += "<td>" + now_td_str + "</td>";
              } else {
                tr_content_str += "<td>\t</td>";
              }
            } else {
              if (item[prop] || (item[prop] === 0)) {
                tr_content_str += "<td>" + item[prop] + "\t</td>";
              } else {
                // 如果值 为null 则 显示空
                tr_content_str += "<td>\t</td>";
              }
            }
          }
        }
        return tr_content_str
      };

5. 自行配置各自的 angular-filter.js

/**
 * Created by ziHou on 2019/1/17.
 */
'use strict';
var adminFilter = angular.module('admin.filter', [])
     // const that = this
    .filter('parseTime',function() {
      return function(time, cFormat) {
        if (time === '' || time === null) {
          return ''
        }
        // 没有定义过滤格式
        if (!cFormat) {
          if ((time + '').length === 13 || (/000$/).test(time)) {
            time = parseInt(time)
            cFormat = '{y}-{m}-{d}'
          }
        }
        time = new Date(time)
        if (arguments.length === 0) {
          return null
        }

        if ((time + '').length === 10) {
          time = +time * 1000
        }

        const format = cFormat || '{y}-{m}-{d} {h}:{i}:{s}'
        var date
        if (typeof time === 'object') {
          date = time
        } else {
          date = new Date(parseInt(time))
        }
        const formatObj = {
          y: date.getFullYear(),
          m: date.getMonth() + 1,
          d: date.getDate(),
          h: date.getHours(),
          i: date.getMinutes(),
          s: date.getSeconds(),
          a: date.getDay()
        }
        const time_str = format.replace(/{(y|m|d|h|i|s|a)+}/g, function (result, key) {
          var value = formatObj[key]
          if (key === 'a') return ['一', '二', '三', '四', '五', '六', '日'][value - 1]
        if (result.length > 0 && value < 10) {
          value = '0' + value
        }
        return value || 0
      })
        return time_str
      }
    })
    .filter('waybillAgainPieWaybillType',function() {
      return function (status) {
        const v = parseInt(status)
        if (v === 10) {
          return "普通运单";
        } else if (v === 20) {
          return "急发单";
        } else if (v === 30) {
          return "未配钥匙";
        }
      }
    })
   
    .filter('waybillAgainPieWaybillStatus',function() {
     return function (status) {
       if (status) {
         return "已派单";
       } else {
         return "未派单";
       }
     }
    })
 

6. 输出base64  ,方法二 不使用 base64

$scope.base64 = function(s) { return window.btoa(unescape(encodeURIComponent(s))) };

四、结言

中间难点 其一 $filter 各数据状态 转化显示 表义信息,这个问题困扰我很久其实主要还是项目angular版本低 filter使用引入限制,翻阅各类前端filter使用及其详解才最后解决,本来是用的笨办法 直接 每个 filter 单独写个过滤 当然那很蠢.....

其二 Blob对象 使用 避免 json数据量过大时 转化报错 亲测2W条数据excel都可以导出,

其三 就是 demo-table 整个结构拼接 需要些html 逻辑拼接 就是脑海里要 有整个table-demo拆分的结构思维,其实最简单的就是 一点点 细分 一点点从外到内 到最细 慢慢拆分 将table-demo结构拆分成一点点,这样最后再 拼接构成整个execl-demo

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值