Добавление рабочего листа в файл excel с помощью javascript

Просто предположим, что у меня 3 таблицы. Я хочу вставить таблицы в 3 рабочих листа (на одну страницу) в один файл excel (без необходимости использования ActiveXObject)

enter image description here

Я пробовал следующий код, но создавал только один рабочий лист

var tableToExcel = (function () {
    var uri = 'data:application/vnd.ms-excel;base64,',
        template = '<html xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:x="urn:schemas-microsoft-com:office:excel" xmlns="http://www.w3.org/TR/REC-html40"><head><!--[if gte mso 9]><xml><x:ExcelWorkbook><x:ExcelWorksheets><x:ExcelWorksheet><x:Name>{worksheet}</x:Name><x:WorksheetOptions><x:DisplayGridlines/></x:WorksheetOptions></x:ExcelWorksheet></x:ExcelWorksheets></x:ExcelWorkbook></xml><![endif]--></head><body><table>{table}</table><table>{table}</table></body></html>',
        base64 = function (s) {
            return window.btoa(unescape(encodeURIComponent(s)))
        }, format = function (s, c) {
            return s.replace(/{(\w+)}/g, function (m, p) {
                return c[p];
            })
        }
    return function (table, name) {
        if (!table.nodeType) table = document.getElementById(table)
        var ctx = {
            worksheet: name || 'Worksheet',
            table: table.innerHTML
        }
        window.location.href = uri + base64(format(template, ctx))
    }
})();

Ответ 1

В предложенном вами подходе используется смесь XML-таблицы и HTML-таблицы. С этой смесью невозможно заполнить несколько листов. Для этого нам нужно использовать только XML-таблицу. Это связано с тем, что только XML может описывать несколько рабочих листов. Данные таблицы HTML не связаны ни с какими листами, кроме активного листа.

Чтобы использовать только XML-таблицу, возможно, но мы должны тщательно работать с типами данных. Если Excel импортирует HTML, он пытается определить типы данных, как если бы пользователь вводил значения в ячейки вручную. С XML это не так. Он берет данные данных из XML. Если они не подходят, то это приводит к ошибкам. Поэтому в моем примере я использую "data-" attríbutes для описания типа данных, типа данных и значения данных. Таким образом, значение данных может отличаться от представления данных в ячейке таблицы HTML (TD). Поскольку HTML является форматом представления данных, а не для обмена данными, это также хорошая практика, на мой взгляд.

Для таблицы XML см. http://msdn.microsoft.com/en-us/library/aa140066.aspx

В примере используется URI данных как ссылка для загрузки, поэтому он работает только с браузерами, которые это поддерживают. Он не будет работать с Microsoft Internet Explorer.

Пример:

<script type="text/javascript">
  var tablesToExcel = (function() {
    var uri = 'data:application/vnd.ms-excel;base64,'
    , tmplWorkbookXML = '<?xml version="1.0"?><?mso-application progid="Excel.Sheet"?><Workbook xmlns="urn:schemas-microsoft-com:office:spreadsheet" xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet">'
      + '<DocumentProperties xmlns="urn:schemas-microsoft-com:office:office"><Author>Axel Richter</Author><Created>{created}</Created></DocumentProperties>'
      + '<Styles>'
      + '<Style ss:ID="Currency"><NumberFormat ss:Format="Currency"></NumberFormat></Style>'
      + '<Style ss:ID="Date"><NumberFormat ss:Format="Medium Date"></NumberFormat></Style>'
      + '</Styles>' 
      + '{worksheets}</Workbook>'
    , tmplWorksheetXML = '<Worksheet ss:Name="{nameWS}"><Table>{rows}</Table></Worksheet>'
    , tmplCellXML = '<Cell{attributeStyleID}{attributeFormula}><Data ss:Type="{nameType}">{data}</Data></Cell>'
    , base64 = function(s) { return window.btoa(unescape(encodeURIComponent(s))) }
    , format = function(s, c) { return s.replace(/{(\w+)}/g, function(m, p) { return c[p]; }) }
    return function(tables, wsnames, wbname, appname) {
      var ctx = "";
      var workbookXML = "";
      var worksheetsXML = "";
      var rowsXML = "";

      for (var i = 0; i < tables.length; i++) {
        if (!tables[i].nodeType) tables[i] = document.getElementById(tables[i]);
        for (var j = 0; j < tables[i].rows.length; j++) {
          rowsXML += '<Row>'
          for (var k = 0; k < tables[i].rows[j].cells.length; k++) {
            var dataType = tables[i].rows[j].cells[k].getAttribute("data-type");
            var dataStyle = tables[i].rows[j].cells[k].getAttribute("data-style");
            var dataValue = tables[i].rows[j].cells[k].getAttribute("data-value");
            dataValue = (dataValue)?dataValue:tables[i].rows[j].cells[k].innerHTML;
            var dataFormula = tables[i].rows[j].cells[k].getAttribute("data-formula");
            dataFormula = (dataFormula)?dataFormula:(appname=='Calc' && dataType=='DateTime')?dataValue:null;
            ctx = {  attributeStyleID: (dataStyle=='Currency' || dataStyle=='Date')?' ss:StyleID="'+dataStyle+'"':''
                   , nameType: (dataType=='Number' || dataType=='DateTime' || dataType=='Boolean' || dataType=='Error')?dataType:'String'
                   , data: (dataFormula)?'':dataValue
                   , attributeFormula: (dataFormula)?' ss:Formula="'+dataFormula+'"':''
                  };
            rowsXML += format(tmplCellXML, ctx);
          }
          rowsXML += '</Row>'
        }
        ctx = {rows: rowsXML, nameWS: wsnames[i] || 'Sheet' + i};
        worksheetsXML += format(tmplWorksheetXML, ctx);
        rowsXML = "";
      }

      ctx = {created: (new Date()).getTime(), worksheets: worksheetsXML};
      workbookXML = format(tmplWorkbookXML, ctx);

console.log(workbookXML);

      var link = document.createElement("A");
      link.href = uri + base64(workbookXML);
      link.download = wbname || 'Workbook.xls';
      link.target = '_blank';
      document.body.appendChild(link);
      link.click();
      document.body.removeChild(link);
    }
  })();
</script> 

<table id="tbl1">
  <tr>
    <td>Name</td>
    <td>Birthday</td>
    <td>Amount</td>
    <td>Rebate (10%)</td>
  </tr>
  <tr>
    <td>Smith</td>
    <td data-type="DateTime" data-style="Date" data-value="1980-03-23">Mar 23 1980</td>
    <td data-type="Number" data-style="Currency" data-value="1234.56">$ 1,234.56</td>
    <td data-formula="=RC[-1]/10" data-type="Number" data-style="Currency">$ 123.45</td>
  </tr>
  <tr>
    <td>Doe</td>
    <td data-type="DateTime" data-style="Date" data-value="1978-11-05">Nov 05 1978</td>
    <td data-type="Number" data-style="Currency" data-value="2345.67">$ 2,345.67</td>
    <td data-formula="=RC[-1]/10" data-type="Number" data-style="Currency">$ 234.56</td>
  </tr>
</table>
<hr>
<table id="tbl2">
  <tr>
    <td>Product</td>
    <td>Price</td>
    <td>Available</td>
    <td>Count</td>
  </tr>
  <tr>
    <td>Bred</td>
    <td data-type="Number" data-style="Currency" data-value="1.89">$ 1.89</td>
    <td data-type="Boolean" data-value="1">yes</td>
    <td data-type="Number" data-value="123">123</td>
  </tr>
  <tr>
    <td>Butter</td>
    <td data-type="Number" data-style="Currency" data-value=".89">$ .89</td>
    <td data-type="Boolean" data-value="0">no</td>
    <td data-type="Number" data-value="0">0</td>
  </tr>
</table>


<button  onclick="tablesToExcel(['tbl1','tbl2'], ['Customers','Products'], 'TestBook.xls', 'Excel')">Export to Excel</button>
<button  onclick="tablesToExcel(['tbl1','tbl2'], ['Customers','Products'], 'TestBook.xls', 'Calc')">Export to Calc</button>

Fiddle: http://jsfiddle.net/qxLn3h86/

Привет

Аксель