Browse Source

Merge branch 'master' of http://192.168.1.41:3000/maixinrong/Calculation

TonyKang 5 years ago
parent
commit
e2fce9429c

+ 10 - 4
app/controller/ledger_controller.js

@@ -22,6 +22,7 @@ const fs = require('fs');
 const LzString = require('lz-string');
 const accountGroup = require('../const/account_group').group;
 const path = require('path');
+const exportExcel = require('../lib/export_excel');
 
 module.exports = app => {
 
@@ -513,12 +514,17 @@ module.exports = app => {
                     let fileName;
                     if (file === '导入分项清单EXCEL格式.xls') {
                         fileName = path.join(this.app.baseDir, 'app', 'public', 'files', 'template', 'ledger', '导入分项清单EXCEL格式.xls');
-                    } else if (file === 'ledger') {
+                        ctx.body = await fs.readFileSync(fileName);
+                    } else if (file === '台账分解.xlsx') {
+                        console.log(file);
                         const create_time = Date.parse(new Date()) / 1000;
-                        fileName = this.app.baseDir + '/app/public/files/downloads/ledger/' + ctx.tender.id + '-' + create_time + '.xlsx';
-                        // todo 导出台账清单Excel
+                        fileName = this.app.baseDir + '/app/public/files/ledger' + ctx.tender.id + '-' + create_time + '.xlsx';
+                        const exportor = new exportExcel.exportLedger2Excel(ctx);
+                        await exportor.export2File(fileName);
+                        ctx.body = await fs.readFileSync(fileName);
+                        // 输出文件后删除
+                        fs.unlinkSync(fileName);
                     }
-                    ctx.body = await fs.readFileSync(fileName);
                 } catch (err) {
                     this.log(err);
                     this.setMessage(err.toString(), this.messageType.ERROR);

+ 10 - 6
app/extend/helper.js

@@ -870,7 +870,7 @@ module.exports = {
      */
     simpleXlsxSheetData (setting, data) {
         const headerStyle = {
-            font: {bold: true},
+            font: { sz: 10, bold: true },
             alignment: {horizontal: 'center'},
         };
         const sHeader = setting.header
@@ -878,8 +878,8 @@ module.exports = {
             .reduce((prev, next) => Object.assign({}, prev, {[next.position]: {v: next.v, s: next.s}}), {});
         const sData = data
             .map((v, i) => v.map((k, j) => Object.assign({}, {
-                v: k,
-                s: {alignment: {horizontal: setting.hAlign[j]}},
+                v: k ? k : '',
+                s: { font: { sz: 10 }, alignment: {horizontal: setting.hAlign[j]}},
                 position: String.fromCharCode(65+j) + (i+2) })))
             .reduce((prev, next) => prev.concat(next))
             .reduce((prev, next) => Object.assign({}, prev, {[next.position]: {v: next.v, s: next.s}}), {});
@@ -911,10 +911,14 @@ module.exports = {
      * @param {String}key
      * @param {*}data
      */
-    addDebugInfo(key, data) {
+    addDebugInfo(key, ...data) {
         if (!this.ctx.debugInfo) {
-            this.ctx.debugInfo = {};
+            this.ctx.debugInfo = { key: {}, other: [] };
+        }
+        if (key) {
+             this.ctx.debugInfo.key[key] = data;
+        } else {
+            this.ctx.debugInfo.other.push(data);
         }
-        this.ctx.debugInfo[key] = data;
     }
 };

+ 103 - 0
app/lib/export_excel.js

@@ -0,0 +1,103 @@
+'use strict';
+
+/**
+ *
+ *
+ * @author Mai
+ * @date
+ * @version
+ */
+const LedgerTree = require('./ledger');
+const xlsx = require('js-xlsx');
+const measureType = require('../const/tender').measureType;
+
+class exportExcel {
+
+    constructor(ctx) {
+        this.ctx = ctx;
+        this.setting = null;
+        this.xlsxData = {
+            SheetNames: [],
+            Sheets: {}
+        };
+    }
+
+    export2Sheet(xlsxData, name, data) {
+        if (xlsxData.SheetNames.indexOf(name) >= 0) throw '存在同名Sheet';
+
+        if (this.setting) {
+            xlsxData.SheetNames.push(name);
+            xlsxData.Sheets[name] = this.ctx.helper.simpleXlsxSheetData(this.setting, data);
+        }
+    }
+
+    async export2File(fileName) {
+        this.export2Sheet(this.xlsxData, 'Sheet1', []);
+        xlsx.writeFile(this.xlsxData, fileName);
+    }
+}
+
+class exportLedger2Excel extends exportExcel {
+    constructor(ctx) {
+        super(ctx);
+        this.setting = {
+            header: ['项目节编号', '清单子目号', '部位明细', '名称', '单位', '清单数量', '设计数量1', '设计数量2', '单价', '合价', '图号', '备注'],
+            width: [100, 70, 70, 300, 60, 80, 80, 80, 80, 80, 100, 100],
+            hAlign: ['left', 'left', 'left', 'left','center', 'right', 'right', 'right', 'right', 'right', 'left', 'left'],
+        };
+    }
+
+    async sortLedgerData(billsTree, pos) {
+        const result = [];
+        for (const node of billsTree.nodes) {
+            result.push([node.code, node.b_code, '', node.name, node.unit,
+                node.quantity, node.dgn_qty1, node.dgn_qty2, node.unit_price, node.total_price,
+                node.drawing_code, node.memo
+            ]);
+            const posRange = pos.getLedgerPos(node.id);
+            if (posRange && posRange.length > 0) {
+                for (const [i, p] of posRange.entries()) {
+                    result.push([
+                        '', '', (i + 1) + '', p.name, '',
+                        p.quantity, null, null, null, null,
+                        p.drawing_code, p.memo
+                    ]);
+                }
+            }
+        }
+        return result;
+    }
+
+    async getLedgerMapData() {
+        const billsData = await this.ctx.service.ledger.getData(this.ctx.tender.id);
+        const billsTree = new LedgerTree.billsTree(this.ctx, {
+            id: 'ledger_id',
+            pid: 'ledger_pid',
+            order: 'order',
+            level: 'level',
+            rootId: -1,
+            keys: ['id', 'tender_id', 'ledger_id'],
+            stageId: 'id',
+            calcFields: ['deal_tp', 'total_price']
+        });
+        billsTree.loadDatas(billsData);
+        billsTree.calculateAll();
+
+        const posData = this.ctx.tender.data.measure_type === measureType.tz.value
+            ? await this.ctx.service.pos.getPosData({tid: this.ctx.tender.id}) : [];
+        const pos = new LedgerTree.pos({id: 'id', ledgerId: 'lid'});
+        pos.loadDatas(posData);
+
+        return this.sortLedgerData(billsTree, pos);
+    }
+
+    async export2File(fileName) {
+        const ledgerData = await this.getLedgerMapData();
+        this.export2Sheet(this.xlsxData, '台账分解', ledgerData);
+        xlsx.writeFile(this.xlsxData, fileName);
+    }
+}
+
+module.exports = {
+    exportLedger2Excel
+};

BIN
app/public/deal_bills/template.xls


BIN
app/public/files/template/ledger/导入工程量清单EXCEL格式.xls


+ 70 - 6
app/public/js/ledger.js

@@ -1250,13 +1250,8 @@ $(document).ready(function() {
                     name: '导出表格数据',
                     icon: 'fa-file-excel-o',
                     callback: function (key, opt) {
-                        const excelIo = new GC.Spread.Excel.IO();
-                        const date = new Date();
                         const fileName = $('.text-truncate').attr('data-original-title') + '.xlsx';
-                        const sJson = JSON.stringify(ledgerSpread.toJSON({columnHeadersAsFrozenRows: true, rowHeadersAsFrozenColumns: true}));
-                        excelIo.save(sJson, function(blob) {
-                            saveAs(blob, fileName);
-                        });
+                        SpreadExcelObj.exportSpread2XlsxWithHeader(ledgerSpread, fileName);
                     },
                     visible: function (key, opt) {
                         try {
@@ -2345,6 +2340,75 @@ $(document).ready(function() {
             }
         });
     })('a[name=showLevel]', ledgerSpread.getActiveSheet());
+
+    // $('#exportLedger').click(function () {
+    //     const data = [];
+    //     const setting = {
+    //         header: ['项目节编号', '清单子目号', '部位明细', '名称', '单位', '清单数量', '设计数量1', '设计数量2', '单价', '合价', '图号', '备注'],
+    //         width: [100, 70, 70, 300, 60, 80, 80, 80, 80, 80, 100, 100],
+    //         hAlign: ['left', 'left', 'left', 'left','center', 'right', 'right', 'right', 'right', 'right', 'left', 'left'],
+    //     };
+    //     for (const node of ledgerTree.nodes) {
+    //         data.push([node.code, node.b_code, '', node.name, node.unit,
+    //             node.quantity, node.dgn_qty1, node.dgn_qty2, node.unit_price, node.total_price,
+    //             node.drawing_code, node.memo
+    //         ]);
+    //         const posRange = pos.getLedgerPos(node.id);
+    //         if (posRange && posRange.length > 0) {
+    //             for (const [i, p] of posRange.entries()) {
+    //                 data.push(['', '', (i + 1) + '', p.name, '',
+    //                     p.quantity, '', '', '', '',
+    //                     p.drawing_code, p.memo
+    //                 ]);
+    //             }
+    //         }
+    //     }
+    //
+    //     XLSXObj.exportXlsxSheet(setting, data, "台账分解.xlsx");
+    // });
+    $('#exportLedger').click(function () {
+        const data = [];
+        const setting = {
+            cols: [
+                {title: '项目节编号', colSpan: '1', rowSpan: '1', field: 'code', hAlign: 0, width: 100, formatter: '@'},
+                {title: '清单子目号', colSpan: '1', rowSpan: '1', field: 'b_code', hAlign: 0, width: 70, formatter: '@'},
+                {title: '部位明细', colSpan: '1', rowSpan: '1', field: 'pos_code', hAlign: 1, width: 70, formatter: '@'},
+                {title: '名称', colSpan: '1', rowSpan: '1', field: 'name', hAlign: 0, width: 300, formatter: '@'},
+                {title: '单位', colSpan: '1', rowSpan: '1', field: 'unit', hAlign: 1, width: 60, formatter: '@'},
+                {title: '清单数量', colSpan: '1', rowSpan: '1', field: 'quantity', hAlign: 2, width: 80, type: 'Number'},
+                {title: '设计数量1', colSpan: '1', rowSpan: '1', field: 'dgn_qty1', hAlign: 2, width: 80, type: 'Number'},
+                {title: '设计数量2', colSpan: '1', rowSpan: '1', field: 'dgn_qty2', hAlign: 2, width: 80, type: 'Number'},
+                {title: '单价', colSpan: '1', rowSpan: '1', field: 'unit_price', hAlign: 2, width: 80, type: 'Number'},
+                {title: '合价', colSpan: '1', rowSpan: '1', field: 'total_price', hAlign: 2, width: 80, type: 'Number'},
+                {title: '图号', colSpan: '1', rowSpan: '1', field: 'drawing_code', hAlign: 0, width: 100, formatter: '@'},
+                {title: '备注', colSpan: '1', rowSpan: '1', field: 'memo', hAlign: 0, width: 100, formatter: '@'},
+            ],
+            headRows: 1,
+            headRowHeight: [32],
+            defaultRowHeight: 21,
+            headerFont: 'bold 10px 微软雅黑',
+            font: '10px 微软雅黑'
+        };
+        for (const node of ledgerTree.nodes) {
+            data.push({
+                code: node.code, b_code: node.b_code, name: node.name, unit: node.unit,
+                unit_price: node.unit_price, quantity: node.quantity, total_price: node.total_price,
+                dgn_qty1: node.dgn_qty1, dgn_qty2: node.dgn_qty2,
+                drawing_code: node.drawing_code, memo: node.memo
+            });
+            const posRange = pos.getLedgerPos(node.id);
+            if (posRange && posRange.length > 0) {
+                for (const [i, p] of posRange.entries()) {
+                    data.push({
+                        pos_code: (i + 1) + '', name: p.name,
+                        quantity: p.quantity, drawing_code: p.drawing_code, memo: p.memo
+                    });
+                }
+            }
+        }
+
+        SpreadExcelObj.exportSimpleXlsxSheet(setting, data, "台账分解.xlsx");
+    });
 });
 
 // 检查上报情况

+ 124 - 0
app/public/js/shares/export_excel.js

@@ -0,0 +1,124 @@
+'use strict';
+
+/**
+ *
+ *
+ * @author Mai
+ * @date
+ * @version
+ */
+
+const SpreadExcelObj = (function() {
+    const _createHideSpread = function () {
+        const div = document.createElement("div");
+        div.setAttribute('id', 'exportExcelSpread');
+        div.style.display = 'none';
+        return div;
+    };
+
+    const _removeHideSpread = function (div) {
+        document.body.removeChild(div);
+    };
+
+    const exportSpread2XlsxWithHeader = function (spread, file) {
+        const excelIo = new GC.Spread.Excel.IO();
+        const sJson = JSON.stringify(spread.toJSON({columnHeadersAsFrozenRows: true, rowHeadersAsFrozenColumns: true}));
+        excelIo.save(sJson, function(blob) {
+            saveAs(blob, file);
+            document.body.removeChild(div);
+        });
+    };
+
+    const exportSimpleXlsxSheet = function (setting, data, file) {
+        const div = _createHideSpread();
+
+        const spread = SpreadJsObj.createNewSpread(div);
+        const sheet = spread.getActiveSheet();
+        sheet.setColumnCount(setting.cols.length);
+        sheet.setRowCount(setting.headRows + data.length);
+
+        for (let iRow = 0; iRow < setting.headRowHeight.length; iRow++) {
+            sheet.setRowHeight(iRow, setting.headRowHeight[iRow]);
+        }
+        for (let iCol = 0; iCol < setting.cols.length; iCol++) {
+            const col = setting.cols[iCol];
+            const title = col.title.split('|');
+            const colSpan = col.colSpan ? col.colSpan.split('|'): ['1'], rowSpan = col.rowSpan ? col.rowSpan.split('|'): ['1'];
+            for (let i = 0; i < title.length; i++) {
+                const cell = sheet.getCell(i, iCol);
+                cell.text(title[i]).wordWrap(true).hAlign(1).vAlign(1).font(setting.headerFont);
+                if ((colSpan[i] !== '' && colSpan[i] !== '1') || (rowSpan[i] !== '' && rowSpan[i] !== '1')) {
+                    sheet.addSpan(i, iCol, parseInt(rowSpan[i]), parseInt(colSpan[i]));
+                }
+            }
+            sheet.setColumnWidth(iCol, col.width);
+            if (col.visible !== undefined && col.visible !== null) {
+                sheet.setColumnVisible(iCol, col.visible);
+            }
+        }
+
+        for (let iRow = 0; iRow < data.length; iRow++) {
+            const curRow = setting.headRows + iRow;
+            const d = data[iRow];
+            for (let iCol = 0; iCol < setting.cols.length; iCol++) {
+                const cell = sheet.getCell(curRow, iCol);
+                const col = setting.cols[iCol];
+                if (col.field !== '' && d[col.field]) {
+                    cell.value(d[col.field]);
+                    if (typeof d[col.field] === 'string') {
+                        cell.formatter('@');
+                    }
+                }
+                if (col.font) {
+                    cell.font(col.font);
+                } else if (setting.font) {
+                    cell.font(setting.font);
+                }
+                cell.hAlign(col.hAlign);
+            }
+        }
+
+        const excelIo = new GC.Spread.Excel.IO();
+        const sJson = JSON.stringify(spread.toJSON());
+        excelIo.save(sJson, function(blob) {
+            saveAs(blob, file);
+            _removeHideSpread(div);
+        });
+    };
+
+    return {exportSimpleXlsxSheet, exportSpread2XlsxWithHeader}
+})();
+
+const XLSXObj = (function () {
+    const exportXlsxSheet = function (setting, data, file) {
+        const headerStyle = {
+            font: { sz: 10, bold: true },
+            alignment: {horizontal: 'center'},
+        };
+        const sHeader = setting.header
+            .map((v, i) => Object.assign({}, {v: v, s: headerStyle, position: String.fromCharCode(65+i) + 1 }))
+            .reduce((prev, next) => Object.assign({}, prev, {[next.position]: {v: next.v, s: next.s}}), {});
+        const sData = data
+            .map((v, i) => v.map((k, j) => Object.assign({}, {
+                v: k ? k : '',
+                s: { font: { sz: 10 }, alignment: {horizontal: setting.hAlign[j]}},
+                position: String.fromCharCode(65+j) + (i+2) })))
+            .reduce((prev, next) => prev.concat(next))
+            .reduce((prev, next) => Object.assign({}, prev, {[next.position]: {v: next.v, s: next.s}}), {});
+        const output = Object.assign({}, sHeader, sData);
+        const outputPos = Object.keys(output);
+        const result = Object.assign({}, output,
+            {'!ref': outputPos[0] + ':' + outputPos[outputPos.length - 1]},
+            {'!cols': setting.width.map((w) => Object.assign({}, {wpx: w}))});
+        const xlsxData = {
+            SheetNames: ['Sheet1'],
+            Sheets: {
+                'Sheet1': result
+            }
+        };
+        const blob = xlsxUtils.format2Blob(xlsxData);
+        saveAs(blob, file);
+    }
+
+    return {exportXlsxSheet}
+});

+ 1 - 1
app/public/js/stage.js

@@ -2593,7 +2593,7 @@ $(document).ready(() => {
                 return false;
             }
             const filesize = file.size;
-            if (filesize > 10 * 1024 * 1024) {
+            if (filesize > 30 * 1024 * 1024) {
                 toastr.error('存在上传文件大小过大!');
                 return false;
             }

+ 1 - 1
app/public/js/stage_pay.js

@@ -1132,7 +1132,7 @@ $(document).ready(() => {
                 return false;
             }
             const filesize = file.size;
-            if (filesize > 10 * 1024 * 1024) {
+            if (filesize > 30 * 1024 * 1024) {
                 toast('存在上传文件大小过大!', 'error');
                 return false;
             }

File diff suppressed because it is too large
+ 62703 - 0
app/public/js/xlsx-populate/xlsx-populate.js


File diff suppressed because it is too large
+ 2 - 0
app/public/js/xlsx-populate/xlsx-populate.min.js


+ 9 - 1
app/public/report/js/rpt_main.js

@@ -329,7 +329,15 @@ let zTreeOprObj = {
                     JpcCanvasOutput.drawPageBorder(me.currentRptPageRst, canvas, getScreenDPI());
                 }
                 if (is_debug) {
-                    console.log(result.debugInfo);
+                    console.log('含有key的debug信息:');
+                    for (const k in result.debugInfo.key) {
+                        console.log(k + ':', ...result.debugInfo.key[k]);
+                    }
+                    //console.log(result.debugInfo.key);
+                    console.log('其他debug信息:');
+                    for (const di of result.debugInfo.other) {
+                        console.log(...di);
+                    }
                 }
             }, function(err){
                 // hintBox.unWaitBox();

+ 4 - 0
app/view/ledger/explode.ejs

@@ -39,6 +39,10 @@
                         <input type="text" class="form-control form-control-sm m-0" id="bills-expr" readonly="">
                     </div>
                 </div>
+                <div class="d-inline-block ml-3">
+                    <!--<a id="exportLedger" class="btn btn-primary btn-sm" href="/tender/<%- ctx.tender.id %>/ledger/download/台账分解.xlsx">下载台账Excel</a>-->
+                    <a id="exportLedger" class="btn btn-primary btn-sm" href="javascript: void(0)">下载台账Excel</a>
+                </div>
             </div>
             <div class="ml-auto">
                 <% if (tender.ledger_status === auditConst.status.checkNo) { %>

+ 1 - 1
app/view/stage/modal.ejs

@@ -206,7 +206,7 @@
             </div>
             <div class="modal-body">
                 <div class="form-group">
-                    <label for="formGroupExampleInput">大小限制:10MB,支持<span data-toggle="tooltip" data-placement="bottom" title="" data-original-title="doc,docx,xls,xlsx,ppt,pptx,pdf">office等文档格式</span>、<span data-toggle="tooltip" data-placement="bottom" title="" data-original-title="jpg,png,bmp">图片格式</span>、<span data-toggle="tooltip" data-placement="bottom" title="" data-original-title="rar,zip">压缩包格式</span></label>
+                    <label for="formGroupExampleInput">大小限制:30MB,支持<span data-toggle="tooltip" data-placement="bottom" title="" data-original-title="doc,docx,xls,xlsx,ppt,pptx,pdf">office等文档格式</span>、<span data-toggle="tooltip" data-placement="bottom" title="" data-original-title="jpg,png,bmp">图片格式</span>、<span data-toggle="tooltip" data-placement="bottom" title="" data-original-title="rar,zip">压缩包格式</span></label>
                     <input type="file" class="" id="upload-file" multiple>
                 </div>
             </div>

+ 1 - 1
app/view/stage/pay_modal.ejs

@@ -77,7 +77,7 @@
             <div class="modal-body">
                 <% if (uploadPermission) { %>
                 <div class="form-group">
-                    <label for="formGroupExampleInput">大小限制:10MB,支持<span data-toggle="tooltip" data-placement="bottom" title="doc,docx,xls,xlsx,ppt,pptx,pdf">office等文档格式</span>、<span data-toggle="tooltip" data-placement="bottom" title="jpg,png,bmp">图片格式</span>、<span data-toggle="tooltip" data-placement="bottom" title="rar,zip">压缩包格式</span></label>
+                    <label for="formGroupExampleInput">大小限制:30MB,支持<span data-toggle="tooltip" data-placement="bottom" title="doc,docx,xls,xlsx,ppt,pptx,pdf">office等文档格式</span>、<span data-toggle="tooltip" data-placement="bottom" title="jpg,png,bmp">图片格式</span>、<span data-toggle="tooltip" data-placement="bottom" title="rar,zip">压缩包格式</span></label>
                     <input type="file" class="" id="upload-file" multiple>
                 </div>
                 <% } %>

+ 1 - 0
config/web.js

@@ -127,6 +127,7 @@ const JsFiles = {
                     "/public/js/decimal.min.js",
                     "/public/js/math.min.js",
                     "/public/js/file-saver/FileSaver.js",
+                    "/public/js/shares/export_excel.js",
                 ],
                 mergeFiles: [
                     "/public/js/sub_menu.js",