class ExportExcel { workBook = null; sheet = null; border = new GC.Spread.Sheets.LineBorder('#000', GC.Spread.Sheets.LineStyle.thin); excelIo = new GC.Spread.Excel.IO(); $info = $('#excel-info'); // 表格当前画到的行 curRow = 0; billTree = null; curBillNode = null; libID = ''; // 表格块,类型为ExcelBlock实例 blocks = []; // 清单ID - 清单精灵映射 billIDElfMap = {}; // 叶子清单ID leafIDs = []; // 叶子清单总数量 total = 0; constructor(workBook, billTree, libID) { this.workBook = workBook; this.sheet = this.workBook.getSheet(0); this.initSheet(); this.billTree = billTree; this.libID = libID; this.leafIDs = billTree.items.filter(node => !node.children.length).map(node => node.data.ID); this.total = this.leafIDs.length; } // 导出 async export() { if (!this.workBook) { return; } const json = this.workBook.toJSON(); this.excelIo.save(json, function (blob) { saveAs(blob, '清单精灵排版.xlsx'); }, function (e) { // process error alert(e); console.log(e); }); } // 将数据画在表格上 async paintOnSheet() { this.updateProcessInfo(); // let i = 0; this.sheet.suspendPaint(); this.sheet.suspendEvent(); for (const billNode of this.billTree.items) { // 画标题 if (this.curRow >= this.sheet.getRowCount()) { this.sheet.addRows(this.curRow, 2); } this.sheet.setFormatter(this.curRow, 0, '@'); this.sheet.setFormatter(this.curRow, 1, '@'); this.sheet.setText(this.curRow, 0, billNode.data.code); this.sheet.setText(this.curRow++, 1, billNode.data.name); // 画表格 if (!billNode.children.length) { this.curRow++; // 空行 const elfTree = await this.getElfTree(billNode.data.ID); if (!elfTree) { continue; } const block = this.getBlock(elfTree); if (!block.length) { continue; } const blockRange = this.getBlockRange(elfTree); this.checkRange(blockRange); const range = this.sheet.getRange(block[0].row + this.curRow, block[0].col, blockRange.rowCount, blockRange.colCount) // 画单元格 console.log(billNode.data); console.log(block); this.drawCell(block); // 画边框 this.drawBorder(range) // i++; this.curRow += blockRange.rowCount + 1; } /* if (i === 100) { break; } */ } const range = this.sheet.getRange(0, 0, this.sheet.getRowCount(), this.sheet.getColumnCount()); range.vAlign(GC.Spread.Sheets.VerticalAlign.center); range.wordWrap(true); this.sheet.resumeEvent(); this.sheet.resumePaint(); if (this.total === this.curProcessCount) { this.afterPaint(); } } /* ========================================================以下为私有方法============================================== */ // 当前到多少条叶子清单 get curProcessCount () { return this.total - this.leafIDs.length; } // 画完表格后的处理 afterPaint() { $('#excel-dialog').width('800px'); $('#excel-spread').show(); this.workBook.refresh(); $('#export-excel-confirm').show(); } // 超出范围追加行列 checkRange(blockRange) { // 不够行数,追加行数 const needRows = this.curRow + blockRange.rowCount; const curRowCount = this.sheet.getRowCount(); if (curRowCount < needRows) { this.sheet.addRows(this.curRow, needRows - curRowCount + 5); } // 不够列数,追加列数 const curColCount = this.sheet.getColumnCount(); if (curColCount < blockRange.colCount) { this.sheet.addColumns(curColCount - 1, blockRange.colCount - curColCount); } } // 画单元格、合并单元格 drawCell(block) { block.forEach(item => { const row = item.row + this.curRow; this.sheet.addSpan(row, item.col, item.rowCount, item.colCount); this.sheet.setFormatter(row, item.col, '@'); this.sheet.setText(row, item.col, item.text); }); } // 画边框 drawBorder(range) { range.setBorder(this.border, { all: true }) } // 更新进度信息 updateProcessInfo() { this.$info.text(`导出清单中: ${this.curProcessCount} / ${this.total}`) } // 精灵树数据转换为表格块单元格数据 getBlock(elfTree) { return elfTree.items.map(node => { // rowCount、colCount标记合并单元格范围 const rowCount = node.posterityLeafCount() || 1; const parentRow = node.parent && node.parent.cellInfo ? node.parent.cellInfo.row : 0; // let prevRowCount = node.preSibling && node.preSibling.cellInfo ? node.preSibling.cellInfo.row + node.preSibling.cellInfo.rowCount - parentRow : 0; const prev = node.prevNode(); let prevRowCount = prev && prev.cellInfo ? prev.cellInfo.row + prev.cellInfo.rowCount - parentRow : 0; const row = parentRow + prevRowCount; const col = node.depth(); const name = node.data.type === itemType.ration ? node.data.name.split(' ')[0] : node.data.name; const text = `${isProcessNode(node) && node.data.require ? '* ' : ''}${name}` node.cellInfo = { row, col, rowCount, }; return { row, col, rowCount, text, colCount: 1, } }) } // 根据精灵树数据,获取表格块range getBlockRange(elfTree) { const rowCount = elfTree.roots.reduce((prev, node) => prev + (node.posterityLeafCount() || 1), 0); const a = Date.now(); const colCount = Math.max(...elfTree.items.map(node => node.depth())) + 1; console.log(Date.now() - a); return { rowCount, colCount } } // 获取清单的精灵树 async getElfTree(billID) { const items = await this.getElfItems(billID); if (!items || !items.length) { return null; } const tree = idTree.createNew({ id: 'ID', pid: 'ParentID', nid: 'NextSiblingID', rootId: -1, autoUpdate: true }); tree.loadDatas(items); return tree; } // 获取清单的精灵数据 async getElfItems(billID) { if (this.billIDElfMap[billID]) { return this.billIDElfMap[billID]; } if (!this.leafIDs.length) { return null; } const count = 20; // 每次拉取数据条数 const billIDs = this.leafIDs.splice(0, count) await setTimeoutSync(null, 100); const items = await ajaxPost('/billsGuidance/api/getItemsByBillIDs', { guidanceLibID: this.libID, billIDs }); this.updateProcessInfo(); items.forEach(item => { (this.billIDElfMap[item.billsID] || (this.billIDElfMap[item.billsID] = [])).push(item) }); return this.billIDElfMap[billID]; } initSheet() { this.sheet.name('清单精灵'); for (let col = 0; col < this.sheet.getColumnCount(); col++) { this.sheet.setColumnWidth(col, 100); } } }