1/*
2 * Copyright (C) 2022 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16const htmlStr = (): unknown => {
17  const html_start =
18    '<html xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:x="urn:schemas-microsoft-com:office:excel" xmlns="http://www.w3.org/TR/REC-html40">';
19  return {
20    uri: 'data:application/vnd.ms-excel;base64,',
21    template_ExcelWorksheet:
22      '<x:ExcelWorksheet><x:Name>{SheetName}</x:Name><x:WorksheetSource HRef="sheet{SheetIndex}.htm"/><x:WorksheetOptions><x:DisplayGridlines/></x:WorksheetOptions></x:ExcelWorksheet>',
23    template_ListWorksheet: '<o:File HRef="sheet{SheetIndex}.htm"/>',
24    template_WorkBook:
25      `MIME-Version: 1.0
26X-Document-Type: Workbook
27Content-Type: multipart/related; boundary="----=_NextPart_dummy"
28
29------=_NextPart_dummy
30Content-Location: WorkBook.htm
31Content-Type: text/html; charset=windows-1252
32
33` +
34      html_start +
35      `
36<head>
37<meta name="Excel Workbook Frameset">
38<meta http-equiv="Content-Type" charset="UTF-8" content="text/html; charset=windows-1252">
39<link rel="File-List" href="filelist.xml">
40<!--[if gte mso 9]><xml>
41 <x:ExcelWorkbook>
42    <x:ExcelWorksheets>{ExcelWorksheets}</x:ExcelWorksheets>
43    <x:ActiveSheet>0</x:ActiveSheet>
44 </x:ExcelWorkbook>
45</xml><![endif]-->
46</head>
47<frameset>
48    <frame src="sheet0.htm" name="frSheet">
49    <noframes><body><p>This page uses frames, but your browser does not support them.</p></body></noframes>
50</frameset>
51</html>
52{HTMLWorksheets}
53Content-Location: filelist.xml
54Content-Type: text/xml; charset="utf-8"
55
56<xml xmlns:o="urn:schemas-microsoft-com:office:office">
57    <o:MainFile HRef="../WorkBook.htm"/>
58    {ListWorksheets}
59    <o:File HRef="filelist.xml"/>
60</xml>
61------=_NextPart_dummy--
62`,
63  };
64};
65
66export class ExcelFormater {
67  static tmplCellXML = '<Cell{attributeStyleID}{attributeFormula}><Data ss:Type="{nameType}">{data}</Data></Cell>';
68  static base64 = function (s: unknown): string {
69    //@ts-ignore
70    return window.btoa(unescape(encodeURIComponent(s)));
71  };
72
73  static format(s: unknown, c: unknown): string {
74    //@ts-ignore
75    return s.replace(/{(\w+)}/g, function (m: unknown, p: unknown) {
76      //@ts-ignore
77      return c[p];
78    });
79  }
80
81  static createExcelRow(columns: unknown[], data: unknown): string {
82    let rowsXML = '';
83    rowsXML += '<Row>';
84    for (let k = 0; k < columns.length; k++) {
85      //@ts-ignore
86      let dataIndex = columns[k].getAttribute('data-index'); //@ts-ignore
87      let columnName = columns[k].getAttribute('title');
88      if (columnName === '') {
89        columnName = dataIndex;
90      }
91      let ctx = {
92        attributeStyleID: '',
93        nameType: 'String', //@ts-ignore
94        data: data ? data[dataIndex] || '' : columnName,
95        attributeFormula: '',
96      };
97      rowsXML += this.format(this.tmplCellXML, ctx);
98    }
99    rowsXML += '</Row>'; //@ts-ignore
100    if (data && data.children !== undefined && data.children.length > 0) {
101      //@ts-ignore
102      data.children.forEach((child: unknown) => {
103        rowsXML += this.createExcelRow(columns, child);
104      });
105    }
106    return rowsXML;
107  }
108
109  static addImage(baseStr: string): string {
110    return `<Row>${this.format(this.tmplCellXML, {
111      attributeStyleID: '',
112      nameType: 'String',
113      data: `<div><img src="${baseStr}"></img></div>`,
114      attributeFormula: '',
115    })}</Row>`;
116  }
117
118  static testExport(
119    dataSource: { columns: unknown[]; tables: unknown[]; sheetName: string }[],
120    fileName: string
121  ): void {
122    this.tablesToHtmlExcelMultipleSheet(dataSource, fileName);
123  }
124
125  static tablesToHtmlExcelMultipleSheet(
126    dataSource: { columns: unknown[]; tables: unknown[]; sheetName: string }[],
127    fileName: string,
128    image?: string
129  ): void {
130    let sheets: unknown[] = [];
131    dataSource.forEach((data): void => {
132      sheets.push(this.createTableData(data.columns, data.tables, image));
133    });
134    this.tablesToExcelTestSheet(sheets, fileName, dataSource);
135  }
136
137  static createTableData(columns: unknown[], dataSource: unknown[], image?: string): string {
138    let tableData = '';
139    let columnDatas = columns.map((column) => {
140      //@ts-ignore
141      let dataIndex = column.getAttribute('data-index'); //@ts-ignore
142      let columnName = column.getAttribute('title');
143      if (columnName === '') {
144        columnName = dataIndex;
145      }
146      return {
147        columnName: columnName,
148        dataIndex: dataIndex,
149      };
150    });
151    tableData += this.createTHead(
152      columnDatas.map((item) => {
153        return item.columnName;
154      })
155    );
156    let columnDataIndexes = columnDatas.map((item) => item.dataIndex);
157    dataSource.forEach((data, index) => {
158      if (index === 0 && image) {
159        tableData += this.createTableRow(columnDataIndexes, data, image);
160      } else {
161        tableData += this.createTableRow(columnDataIndexes, data);
162      }
163    });
164    return tableData;
165  }
166
167  static createTHead(columns: unknown[]): string {
168    let header = '<thead>';
169    columns.forEach((column) => {
170      header += `<td>${column}</td>`;
171    });
172    header += '</thrad>';
173    return header;
174  }
175
176  static createTableRow(columns: unknown[], data: unknown, image?: unknown): string {
177    let childrenData = ''; //@ts-ignore
178    if (data.children !== undefined) {
179      //@ts-ignore
180      data.children.forEach((child: unknown) => {
181        if (child) {
182          childrenData += this.createTableRow(columns, child);
183        }
184      });
185    }
186    return `<tr>${columns
187      .map((column) => {
188        //@ts-ignore
189        return `<td>${(data[column] + '').replace('μ', 'u')}</td>` || '';
190      })
191      .join('')}${image ? `<td><div><img src="${image}"></img></div></td>` : ''}</tr>${childrenData}`;
192  }
193
194  static tablesToExcelTestSheet(
195    tables: unknown[],
196    filename: string,
197    dataSource: { columns: unknown[]; tables: unknown[]; sheetName: string }[]
198  ): void {
199    const html_start =
200      '<html xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:x="urn:schemas-microsoft-com:office:excel" xmlns="http://www.w3.org/TR/REC-html40">'; //@ts-ignore
201    let { uri, template_ExcelWorksheet, template_ListWorksheet, template_WorkBook } = htmlStr();
202    let template_HTMLWorksheet =
203      `
204------=_NextPart_dummy
205Content-Location: sheet{SheetIndex}.htm
206Content-Type: text/html; charset=windows-1252
207
208` +
209      html_start +
210      `
211<head>
212    <meta http-equiv="Content-Type" charset="UTF-8" content="text/html; charset=windows-1252">
213    <link id="Main-File" rel="Main-File" href="../WorkBook.htm">
214    <link rel="File-List" href="filelist.xml">
215</head>
216<body><table>{SheetContent}</table></body>
217</html>`;
218    let context_WorkBook = {
219      ExcelWorksheets: '',
220      HTMLWorksheets: '',
221      ListWorksheets: '',
222    };
223    tables.forEach((item, sheetIndex) => {
224      context_WorkBook.ExcelWorksheets += this.format(template_ExcelWorksheet, {
225        SheetIndex: sheetIndex,
226        SheetName: dataSource[sheetIndex].sheetName,
227      });
228      context_WorkBook.HTMLWorksheets += this.format(template_HTMLWorksheet, {
229        SheetIndex: sheetIndex,
230        SheetContent: item,
231      });
232      context_WorkBook.ListWorksheets += this.format(template_ListWorksheet, {
233        SheetIndex: sheetIndex,
234      });
235    });
236    let link = document.createElement('a');
237    link.href = uri + this.base64(this.format(template_WorkBook, context_WorkBook));
238    link.download = filename + '.xls';
239    link.target = '_blank';
240    link.click();
241  }
242}
243