|
@@ -0,0 +1,237 @@
|
|
|
+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);
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|