통합 문서 뷰어 (Angular)

이 샘플은 xlsx 파일 내용을 로드하고 페이지에 표시하는 간단한 Excel 통합 문서 뷰어를 구현합니다.

샘플은 통합 문서 인스턴스를 만들고, Workbook.load 메서드를 사용하여 xlsx 파일 내용을 통합 문서 인스턴스에 로드합니다. 그런 다음, WorkSheet 클래스에서 제공하는 현재 통합 문서 시트의 콘텐츠를 검색하고 콘텐츠를 시각화하는 HTML 표를 만듭니다.

통합 문서 시트 간에 전환할 수 있도록 탭도 만듭니다. Workbook.sheets 컬렉션을 반복하는 ng-repeat 지시문이 적용된 <li> 요소의 스타일 지정과 비슷한 BootstrapJS 탭을 사용하여 탭을 구현합니다.

이 데모는 Angular를 기반으로합니다.

import 'bootstrap.css'; import '@grapecity/wijmo.styles/wijmo.css'; import './styles.css'; // import '@angular/compiler'; import { Component, enableProdMode, NgModule } from '@angular/core'; import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; import { BrowserModule } from '@angular/platform-browser'; import { FormsModule } from '@angular/forms'; // import * as wjcCore from '@grapecity/wijmo'; import * as wjcXlsx from '@grapecity/wijmo.xlsx'; // @Component({ selector: 'app-component', templateUrl: 'src/app.component.html' }) export class AppComponent { workbook: wjcXlsx.Workbook; sheetIndex: number; // ngAfterViewInit() { document.getElementById('importFile').addEventListener('change', () => { this._loadWorkbook(); }); } // tabClicked(e: MouseEvent, index: number) { e.preventDefault(); this._drawSheet(index); } // private _loadWorkbook() { let reader = new FileReader(); // reader.onload = (e) => { let workbook = new wjcXlsx.Workbook(); workbook.loadAsync(<string>reader.result, (result: wjcXlsx.Workbook) => { this.workbook = result; this._drawSheet(this.workbook.activeWorksheet || 0); }); }; // let file = (<HTMLInputElement>document.getElementById('importFile')).files[0]; if (file) { reader.readAsDataURL(file); } } // private _drawSheet(sheetIndex: number) { let drawRoot = document.getElementById('tableHost'); drawRoot.textContent = ''; this.sheetIndex = sheetIndex; this._drawWorksheet(this.workbook, sheetIndex, drawRoot, 200, 100); } // private _drawWorksheet(workbook: wjcXlsx.IWorkbook, sheetIndex: number, rootElement: HTMLElement, maxRows: number, maxColumns: number) { //NOTES: //Empty cells' values are numeric NaN, format is "General" // //Excessive empty properties: //fill.color = undefined // // netFormat should return '' for ''. What is 'General'? // font.color should start with '#'? // Column/row styles are applied to each cell style, this is convenient, but Column/row style info should be kept, // for column/row level styling // formats conversion is incorrect - dates and virtually everything; netFormat - return array of formats? // ?row heights - see hello.xlsx if (!workbook || !workbook.sheets || sheetIndex < 0 || workbook.sheets.length == 0) { return; } // sheetIndex = Math.min(sheetIndex, workbook.sheets.length - 1); // if (maxRows == null) { maxRows = 200; } // if (maxColumns == null) { maxColumns = 100; } // // Namespace and XlsxConverter shortcuts. let sheet = workbook.sheets[sheetIndex], defaultRowHeight = 20, defaultColumnWidth = 60, tableEl = document.createElement('table'); // tableEl.border = '1'; tableEl.style.borderCollapse = 'collapse'; // let maxRowCells = 0; for (let r = 0; sheet.rows && r < sheet.rows.length; r++) { if (sheet.rows[r] && sheet.rows[r].cells) { maxRowCells = Math.max(maxRowCells, sheet.rows[r].cells.length); } } // // add columns let columns = sheet.columns || [], invisColCnt = columns.filter(col => col.visible === false).length; // if (sheet.columns) { maxRowCells = Math.min(Math.max(maxRowCells, columns.length), maxColumns); // for (let c = 0; c < maxRowCells; c++) { let col = columns[c]; // if (col && !col.visible) { continue; } // let colEl = document.createElement('col'); tableEl.appendChild(colEl); let colWidth = defaultColumnWidth + 'px'; if (col) { this._importStyle(colEl.style, col.style); if (col.autoWidth) { colWidth = ''; } else if (col.width != null) { colWidth = col.width + 'px'; } } colEl.style.width = colWidth; } } // // generate rows let rowCount = Math.min(maxRows, sheet.rows.length); for (let r = 0; sheet.rows && r < rowCount; r++) { let row = sheet.rows[r], cellsCnt = 0; // including colspan // if (row && !row.visible) { continue; } // let rowEl = document.createElement('tr'); tableEl.appendChild(rowEl); // if (row) { this._importStyle(rowEl.style, row.style); if (row.height != null) { rowEl.style.height = row.height + 'px'; } // for (let c = 0; row.cells && c < row.cells.length; c++) { let cell = row.cells[c], cellEl = document.createElement('td'), col = columns[c]; // if (col && !col.visible) { continue; } // cellsCnt++; // rowEl.appendChild(cellEl); if (cell) { this._importStyle(cellEl.style, cell.style); let value = cell.value; // if (!(value == null || value !== value)) { // TBD: check for NaN should be eliminated if (wjcCore.isString(value) && value.charAt(0) == "'") { value = value.substr(1); } let netFormat = ''; if (cell.style && cell.style.format) { netFormat = wjcXlsx.Workbook.fromXlsxFormat(cell.style.format)[0]; } let fmtValue = netFormat ? wjcCore.Globalize.format(value, netFormat) : value; cellEl.innerHTML = wjcCore.escapeHtml(fmtValue); } // if (cell.colSpan && cell.colSpan > 1) { cellEl.colSpan = this._getVisColSpan(columns, c, cell.colSpan); cellsCnt += cellEl.colSpan - 1; c += cell.colSpan - 1; } // if (cell.note) { wjcCore.addClass(cellEl, 'cell-note'); cellEl.title = cell.note.text; } } } } // // pad with empty cells let padCellsCount = maxRowCells - cellsCnt - invisColCnt; for (let i = 0; i < padCellsCount; i++) { rowEl.appendChild(document.createElement('td')); } // if (!rowEl.style.height) { rowEl.style.height = defaultRowHeight + 'px'; } } // // do it at the end for performance rootElement.appendChild(tableEl); } // private _getVisColSpan(columns: wjcXlsx.IWorkbookColumn[], startFrom: number, colSpan: number) { let res = colSpan; // for (let i = startFrom; i < columns.length && i < startFrom + colSpan; i++) { let col = columns[i]; if (col && !col.visible) { res--; } } // return res; } // private _importStyle(cssStyle: CSSStyleDeclaration, xlsxStyle: wjcXlsx.IWorkbookStyle) { if (!xlsxStyle) { return; } // if (xlsxStyle.fill) { if (xlsxStyle.fill.color) { cssStyle.backgroundColor = xlsxStyle.fill.color; } } // if (xlsxStyle.hAlign && xlsxStyle.hAlign != wjcXlsx.HAlign.Fill) { cssStyle.textAlign = wjcXlsx.HAlign[xlsxStyle.hAlign].toLowerCase(); } // let font = xlsxStyle.font; if (font) { if (font.family) { cssStyle.fontFamily = font.family; } if (font.bold) { cssStyle.fontWeight = 'bold'; } if (font.italic) { cssStyle.fontStyle = 'italic'; } if (font.size != null) { cssStyle.fontSize = font.size + 'px'; } if (font.underline) { cssStyle.textDecoration = 'underline'; } if (font.color) { cssStyle.color = font.color; } } } } // @NgModule({ imports: [BrowserModule, FormsModule], declarations: [AppComponent], bootstrap: [AppComponent] }) export class AppModule { } // enableProdMode(); // Bootstrap application with hash style navigation and global services. platformBrowserDynamic().bootstrapModule(AppModule);
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>MESCIUS Wijmo Workbook Viewer</title> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <!-- Polyfills --> <script src="node_modules/core-js/client/shim.min.js"></script> <script src="node_modules/zone.js/fesm2015/zone.min.js"></script> <!-- SystemJS --> <script src="node_modules/systemjs/dist/system.src.js"></script> <script src="systemjs.config.js"></script> <script> // workaround to load 'rxjs/operators' from the rxjs bundle System.import('rxjs').then(function (m) { System.set(SystemJS.resolveSync('rxjs/operators'), System.newModule(m.operators)); System.import('./src/app.component'); }); </script> </head> <body> <app-component></app-component> </body> </html>
<div class="container-fluid"> <div class="row"> <input type="file" class="form-control" id="importFile" accept="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/vnd.ms-excel.sheet.macroEnabled.12" style="width:300px" /> </div> <!-- Sheet tabs --> <ul *ngIf="workbook" class="nav nav-tabs" style="margin-top:40px"> <li role="presentation" *ngFor="let sheet of workbook.sheets; let i = index" [ngClass]="{ active: i == sheetIndex }"> <a href="#" (click)="tabClicked($event, i)">{{sheet.name}}</a> </li> </ul> <!-- Current sheet view --> <div id="tableHost"></div> </div>
.cell-note { position: relative; } .cell-note:after { content: ""; position: absolute; display: block; top: 0; right: 0; border-left: 7px solid transparent; border-top: 7px solid red; }
(function (global) { SystemJS.config({ transpiler: './plugin-typescript.js', typescriptOptions: { "target": "ES2022", "module": "system", "emitDecoratorMetadata": true, "experimentalDecorators": true, }, baseURL: 'node_modules/', meta: { 'typescript': { "exports": "ts" }, '*.css': { loader: 'systemjs-plugin-css' } }, paths: { // paths serve as alias 'npm:': '' }, packageConfigPaths: [ '/node_modules/*/package.json', "/node_modules/@angular/*/package.json", "/node_modules/@grapecity/*/package.json" ], map: { 'core-js': 'https://cdn.jsdelivr.net/npm/core-js@2.6.12/client/shim.min.js', 'typescript': 'https://cdnjs.cloudflare.com/ajax/libs/typescript/5.2.2/typescript.min.js', "rxjs": "https://cdnjs.cloudflare.com/ajax/libs/rxjs/7.8.1/rxjs.umd.min.js", 'systemjs-plugin-css': 'https://cdn.jsdelivr.net/npm/systemjs-plugin-css@0.1.37/css.js', '@grapecity/wijmo': 'npm:@grapecity/wijmo/index.js', '@grapecity/wijmo.input': 'npm:@grapecity/wijmo.input/index.js', '@grapecity/wijmo.styles': 'npm:@grapecity/wijmo.styles', '@grapecity/wijmo.cultures': 'npm:@grapecity/wijmo.cultures', '@grapecity/wijmo.chart': 'npm:@grapecity/wijmo.chart/index.js', '@grapecity/wijmo.chart.analytics': 'npm:@grapecity/wijmo.chart.analytics/index.js', '@grapecity/wijmo.chart.animation': 'npm:@grapecity/wijmo.chart.animation/index.js', '@grapecity/wijmo.chart.annotation': 'npm:@grapecity/wijmo.chart.annotation/index.js', '@grapecity/wijmo.chart.finance': 'npm:@grapecity/wijmo.chart.finance/index.js', '@grapecity/wijmo.chart.finance.analytics': 'npm:@grapecity/wijmo.chart.finance.analytics/index.js', '@grapecity/wijmo.chart.hierarchical': 'npm:@grapecity/wijmo.chart.hierarchical/index.js', '@grapecity/wijmo.chart.interaction': 'npm:@grapecity/wijmo.chart.interaction/index.js', '@grapecity/wijmo.chart.radar': 'npm:@grapecity/wijmo.chart.radar/index.js', '@grapecity/wijmo.chart.render': 'npm:@grapecity/wijmo.chart.render/index.js', '@grapecity/wijmo.chart.webgl': 'npm:@grapecity/wijmo.chart.webgl/index.js', '@grapecity/wijmo.chart.map': 'npm:@grapecity/wijmo.chart.map/index.js', '@grapecity/wijmo.gauge': 'npm:@grapecity/wijmo.gauge/index.js', '@grapecity/wijmo.grid': 'npm:@grapecity/wijmo.grid/index.js', '@grapecity/wijmo.grid.detail': 'npm:@grapecity/wijmo.grid.detail/index.js', '@grapecity/wijmo.grid.filter': 'npm:@grapecity/wijmo.grid.filter/index.js', '@grapecity/wijmo.grid.search': 'npm:@grapecity/wijmo.grid.search/index.js', '@grapecity/wijmo.grid.grouppanel': 'npm:@grapecity/wijmo.grid.grouppanel/index.js', '@grapecity/wijmo.grid.multirow': 'npm:@grapecity/wijmo.grid.multirow/index.js', '@grapecity/wijmo.grid.transposed': 'npm:@grapecity/wijmo.grid.transposed/index.js', '@grapecity/wijmo.grid.transposedmultirow': 'npm:@grapecity/wijmo.grid.transposedmultirow/index.js', '@grapecity/wijmo.grid.pdf': 'npm:@grapecity/wijmo.grid.pdf/index.js', '@grapecity/wijmo.grid.sheet': 'npm:@grapecity/wijmo.grid.sheet/index.js', '@grapecity/wijmo.grid.xlsx': 'npm:@grapecity/wijmo.grid.xlsx/index.js', '@grapecity/wijmo.grid.selector': 'npm:@grapecity/wijmo.grid.selector/index.js', '@grapecity/wijmo.grid.cellmaker': 'npm:@grapecity/wijmo.grid.cellmaker/index.js', '@grapecity/wijmo.nav': 'npm:@grapecity/wijmo.nav/index.js', '@grapecity/wijmo.odata': 'npm:@grapecity/wijmo.odata/index.js', '@grapecity/wijmo.olap': 'npm:@grapecity/wijmo.olap/index.js', '@grapecity/wijmo.rest': 'npm:@grapecity/wijmo.rest/index.js', '@grapecity/wijmo.pdf': 'npm:@grapecity/wijmo.pdf/index.js', '@grapecity/wijmo.pdf.security': 'npm:@grapecity/wijmo.pdf.security/index.js', '@grapecity/wijmo.viewer': 'npm:@grapecity/wijmo.viewer/index.js', '@grapecity/wijmo.xlsx': 'npm:@grapecity/wijmo.xlsx/index.js', '@grapecity/wijmo.undo': 'npm:@grapecity/wijmo.undo/index.js', '@grapecity/wijmo.interop.grid': 'npm:@grapecity/wijmo.interop.grid/index.js', '@grapecity/wijmo.touch': 'npm:@grapecity/wijmo.touch/index.js', '@grapecity/wijmo.cloud': 'npm:@grapecity/wijmo.cloud/index.js', '@grapecity/wijmo.barcode': 'npm:@grapecity/wijmo.barcode/index.js', '@grapecity/wijmo.barcode.common': 'npm:@grapecity/wijmo.barcode.common/index.js', '@grapecity/wijmo.barcode.composite': 'npm:@grapecity/wijmo.barcode.composite/index.js', '@grapecity/wijmo.barcode.specialized': 'npm:@grapecity/wijmo.barcode.specialized/index.js', "@grapecity/wijmo.angular2.chart.analytics": "npm:@grapecity/wijmo.angular2.chart.analytics/index.js", "@grapecity/wijmo.angular2.chart.animation": "npm:@grapecity/wijmo.angular2.chart.animation/index.js", "@grapecity/wijmo.angular2.chart.annotation": "npm:@grapecity/wijmo.angular2.chart.annotation/index.js", "@grapecity/wijmo.angular2.chart.finance.analytics": "npm:@grapecity/wijmo.angular2.chart.finance.analytics/index.js", "@grapecity/wijmo.angular2.chart.finance": "npm:@grapecity/wijmo.angular2.chart.finance/index.js", "@grapecity/wijmo.angular2.chart.hierarchical": "npm:@grapecity/wijmo.angular2.chart.hierarchical/index.js", "@grapecity/wijmo.angular2.chart.interaction": "npm:@grapecity/wijmo.angular2.chart.interaction/index.js", "@grapecity/wijmo.angular2.chart.radar": "npm:@grapecity/wijmo.angular2.chart.radar/index.js", '@grapecity/wijmo.angular2.chart.map': 'npm:@grapecity/wijmo.angular2.chart.map/index.js', "@grapecity/wijmo.angular2.chart": "npm:@grapecity/wijmo.angular2.chart/index.js", "@grapecity/wijmo.angular2.core": "npm:@grapecity/wijmo.angular2.core/index.js", "@grapecity/wijmo.angular2.gauge": "npm:@grapecity/wijmo.angular2.gauge/index.js", "@grapecity/wijmo.angular2.grid.detail": "npm:@grapecity/wijmo.angular2.grid.detail/index.js", "@grapecity/wijmo.angular2.grid.filter": "npm:@grapecity/wijmo.angular2.grid.filter/index.js", "@grapecity/wijmo.angular2.grid.grouppanel": "npm:@grapecity/wijmo.angular2.grid.grouppanel/index.js", "@grapecity/wijmo.angular2.grid.search": "npm:@grapecity/wijmo.angular2.grid.search/index.js", "@grapecity/wijmo.angular2.grid.multirow": "npm:@grapecity/wijmo.angular2.grid.multirow/index.js", "@grapecity/wijmo.angular2.grid.sheet": "npm:@grapecity/wijmo.angular2.grid.sheet/index.js", '@grapecity/wijmo.angular2.grid.transposed': 'npm:@grapecity/wijmo.angular2.grid.transposed/index.js', '@grapecity/wijmo.angular2.grid.transposedmultirow': 'npm:@grapecity/wijmo.angular2.grid.transposedmultirow/index.js', "@grapecity/wijmo.angular2.grid": "npm:@grapecity/wijmo.angular2.grid/index.js", "@grapecity/wijmo.angular2.input": "npm:@grapecity/wijmo.angular2.input/index.js", "@grapecity/wijmo.angular2.olap": "npm:@grapecity/wijmo.angular2.olap/index.js", "@grapecity/wijmo.angular2.viewer": "npm:@grapecity/wijmo.angular2.viewer/index.js", "@grapecity/wijmo.angular2.nav": "npm:@grapecity/wijmo.angular2.nav/index.js", "@grapecity/wijmo.angular2.directivebase": "npm:@grapecity/wijmo.angular2.directivebase/index.js", '@grapecity/wijmo.angular2.barcode.common': 'npm:@grapecity/wijmo.angular2.barcode.common/index.js', '@grapecity/wijmo.angular2.barcode.composite': 'npm:@grapecity/wijmo.angular2.barcode.composite/index.js', '@grapecity/wijmo.angular2.barcode.specialized': 'npm:@grapecity/wijmo.angular2.barcode.specialized/index.js', 'bootstrap.css': 'npm:bootstrap/dist/css/bootstrap.min.css', 'jszip': 'https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js', "@angular/common/http": "https://cdn.jsdelivr.net/npm/@angular/common@16.2.6/fesm2022/http.mjs", "@angular/core": "https://cdn.jsdelivr.net/npm/@angular/core@16.2.6/fesm2022/core.mjs", "@angular/platform-browser": "https://cdn.jsdelivr.net/npm/@angular/platform-browser@16.2.6/fesm2022/platform-browser.mjs", "@angular/common": "https://cdn.jsdelivr.net/npm/@angular/common@16.2.6/fesm2022/common.mjs", "@angular/compiler": "https://cdn.jsdelivr.net/npm/@angular/compiler@16.2.6/fesm2022/compiler.mjs", "@angular/forms": "https://cdn.jsdelivr.net/npm/@angular/forms@16.2.6/fesm2022/forms.mjs", "@angular/localize": "https://cdn.jsdelivr.net/npm/@angular/localize@16.2.6/fesm2022/localize.mjs", "@angular/platform-browser-dynamic": "https://cdn.jsdelivr.net/npm/@angular/platform-browser-dynamic@16.2.6/fesm2022/platform-browser-dynamic.mjs", }, // packages tells the System loader how to load when no filename and/or no extension packages: { "./src": { defaultExtension: 'ts' }, "node_modules": { defaultExtension: 'js' }, wijmo: { defaultExtension: 'js', } } }); })(this);