Переглянути джерело

Merge branch 'dev' of http://192.168.1.41:3000/maixinrong/Calculation into dev

TonyKang 4 роки тому
батько
коміт
3ae9cfb50d

+ 1 - 1
app/controller/advance_controller.js

@@ -423,7 +423,7 @@ module.exports = app => {
             try {
                 const { id } = JSON.parse(ctx.request.body.data);
                 const fileInfo = await ctx.service.advanceFile.getDataById(id);
-                if (fileInfo) {
+                if (fileInfo || Object.keys(fileInfo).length) {
                     // 先删除文件
                     await fs.unlinkSync(path.resolve(this.app.baseDir, './app', fileInfo.filepath));
                     // 再删除数据库

+ 3 - 0
app/controller/change_controller.js

@@ -790,6 +790,9 @@ module.exports = app => {
                 const data = JSON.parse(ctx.request.body.data);
                 const change = await ctx.service.change.getDataByCondition({ cid: ctx.params.cid });
                 const fileInfo = await ctx.service.changeAtt.getDataById(data.id);
+                if (!fileInfo || !Object.keys(fileInfo).length) {
+                    throw '该文件不存在';
+                }
                 if (!fileInfo.extra_upload && change.status === audit.flow.status.checked) {
                     throw '无权限删除';
                 }

+ 3 - 0
app/controller/material_controller.js

@@ -987,6 +987,9 @@ module.exports = app => {
                 const { data } = ctx.request.body;
                 const { id } = JSON.parse(data);
                 const fileInfo = await ctx.service.materialFile.getMaterialFileById(id);
+                if (!fileInfo || !Object.keys(fileInfo).length) {
+                    throw '该文件不存在';
+                }
                 if (!fileInfo.extra_upload && ctx.material.status === auditConst.status.checked) {
                     throw '无权限删除';
                 }

+ 6 - 6
app/controller/revise_controller.js

@@ -931,8 +931,8 @@ module.exports = app => {
                     }
                 } else {
                     const ncd = {id: pd.id};
-                    ncd[relaId] = cd[relaId];
-                    for (const f of fields) {
+                    ncd[relaId] = pd[relaId];
+                    for (const f of field) {
                         ncd[prefix + f] = pd[f];
                     }
                     curData.push(ncd);
@@ -985,6 +985,7 @@ module.exports = app => {
                     } else {
                         return await this._loadLastStagePosData(ctx);
                     }
+                case 'dealBills': return await ctx.service.dealBills.getAllDataByCondition({where: {tender_id: ctx.tender.id}});
             }
         }
 
@@ -997,7 +998,6 @@ module.exports = app => {
                 const filter = data.filter ? data.filter.split(';') : [];
 
                 const responseData = { err: 0, msg: '', data: {} };
-                console.log(filter);
                 for (const f of filter) {
                     if (!f) continue;
                     responseData.data[f] = await this._loadDataByFilter(ctx, f);
@@ -1009,15 +1009,15 @@ module.exports = app => {
             }
         }
 
-        async gather(ctx) {
+        async gclCompare(ctx) {
             const revise = await ctx.service.ledgerRevise.getLastestRevise(ctx.tender.id);
             if (!revise) throw '台账修订数据有误';
 
             const renderData = {
                 revise,
-                jsFiles: this.app.jsFiles.common.concat(this.app.jsFiles.revise.gather),
+                jsFiles: this.app.jsFiles.common.concat(this.app.jsFiles.revise.gclCompare),
             };
-            await this.layout('revise/gather.ejs', renderData);
+            await this.layout('revise/gcl_compare.ejs', renderData);
         }
 
         async bwtz(ctx) {

+ 10 - 5
app/controller/stage_controller.js

@@ -1453,6 +1453,9 @@ module.exports = app => {
 
                 const data = JSON.parse(ctx.request.body.data);
                 const fileInfo = await ctx.service.stageAtt.getDataById(data.id);
+                if (!fileInfo || !Object.keys(fileInfo).length) {
+                    throw '该文件不存在';
+                }
                 if (!fileInfo.extra_upload && ctx.stage.status === auditConst.status.checked) {
                     throw '无权限删除';
                 }
@@ -1561,11 +1564,13 @@ module.exports = app => {
                     'Content-Disposition': disposition,
                     'Content-Length': size,
                 });
-                ctx.body = fs.createReadStream(path.join(this.app.baseDir, zipPath));
-                // ctx.body = fs.readFileSync(path.resolve(this.app.baseDir, zipPath));
-                if (fs.existsSync(path.resolve(this.app.baseDir, zipPath))) {
-                    fs.unlinkSync(path.resolve(this.app.baseDir, zipPath));
-                }
+                const readStream = fs.createReadStream(path.join(this.app.baseDir, zipPath));
+                ctx.body = readStream;
+                readStream.on('close', () => {
+                    if (fs.existsSync(path.resolve(this.app.baseDir, zipPath))) {
+                        fs.unlinkSync(path.resolve(this.app.baseDir, zipPath));
+                    }
+                });
             } catch (error) {
                 this.log(error);
             }

+ 9 - 0
app/public/css/main.css

@@ -102,6 +102,9 @@ font-size: .875rem;
 .text-primary-50{
   color: #cce5ff;
 }
+.text-warning-50{
+  color: #ffddc5;
+}
 .text-secondary-50{
   color: #dcdcdc;
 }
@@ -117,6 +120,9 @@ font-size: .875rem;
 .bg-primary-50{
   background-color: #cce5ff;
 }
+.bg-warning-50{
+  background-color: #ffddc5;
+}
 .bg-secondary-50{
   background-color: #dcdcdc;
 }
@@ -132,6 +138,9 @@ font-size: .875rem;
 .border-primary-50{
   border:1px solid #b8daff;
 }
+.border-warning-50{
+  border:1px solid #fbc7a3;
+}
 .border-secondary-50{
   border:1px solid #ccc;
 }

+ 9 - 0
app/public/css/main_s.css

@@ -102,6 +102,9 @@ font-size: .875rem;
 .text-primary-50{
   color: #cce5ff;
 }
+.text-warning-50{
+  color: #ffddc5;
+}
 .text-secondary-50{
   color: #dcdcdc;
 }
@@ -117,6 +120,9 @@ font-size: .875rem;
 .bg-primary-50{
   background-color: #cce5ff;
 }
+.bg-warning-50{
+  background-color: #ffddc5;
+}
 .bg-secondary-50{
   background-color: #dcdcdc;
 }
@@ -132,6 +138,9 @@ font-size: .875rem;
 .border-primary-50{
   border:1px solid #b8daff;
 }
+.border-warning-50{
+  border:1px solid #fbc7a3;
+}
 .border-secondary-50{
   border:1px solid #ccc;
 }

+ 2 - 2
app/public/js/change_set.js

@@ -251,7 +251,7 @@ $(document).ready(() => {
             const newVal = $('#gr-search').val()
             let html = ''
             if (newVal && newVal === oldSearchVal) {
-                accountList.filter(item => item && cur_uid !== item.id && (item.name.indexOf(newVal) !== -1 || (item.mobile && item.mobile.indexOf(newVal) !== -1))).forEach(item => {
+                accountList.filter(item => item && changesUid !== item.id && (item.name.indexOf(newVal) !== -1 || (item.mobile && item.mobile.indexOf(newVal) !== -1))).forEach(item => {
                     html += `<dd class="border-bottom p-2 mb-0 " data-id="${item.id}" >
                         <p class="mb-0 d-flex"><span class="text-primary">${item.name}</span><span
                                 class="ml-auto">${item.mobile || ''}</span></p>
@@ -268,7 +268,7 @@ $(document).ready(() => {
                         </a> ${group.groupName}</dt>
                         <div class="dd-content" data-toggleid="${idx}">`
                         group.groupList.forEach(item => {
-                            if (item.id !== cur_uid) {
+                            if (item.id !== changesUid) {
                                 html += `<dd class="border-bottom p-2 mb-0 " data-id="${item.id}" >
                                     <p class="mb-0 d-flex"><span class="text-primary">${item.name}</span><span
                                             class="ml-auto">${item.mobile || ''}</span></p>

+ 3 - 2
app/public/js/material_exponent.js

@@ -20,7 +20,8 @@ function resetExTpTable() {
     $('#tp_set').find('td').eq(4).text(ZhCalc.round(ZhCalc.add(ex_pre_tp, ex_tp), 2));
     $('#rate_set').find('td').eq(3).text(bqhs !== 0 ? bqhs : '');
     $('#rate_set').find('td').eq(4).text(jzbqhs !== 0 ? jzbqhs : '');
-    $('#ex_expr').html(ex_expr);
+    // $('#ex_expr').html(ex_expr);
+    $('#ex_expr').attr('data-original-title', '本期价差:' + (ex_expr ? ex_expr : ''));
 }
 $(document).ready(() => {
     autoFlashHeight();
@@ -50,7 +51,7 @@ $(document).ready(() => {
     const materialExponentBase = {
         isUsed: function (data) {
             if (data.type === 2) {
-                return data.mid === materialID || data.basic_price === null;
+                return data.mid === materialID || data.basic_price === null || data.basic_price === 0;
             } else {
                 return false;
             }

+ 4 - 2
app/public/js/material_file.js

@@ -61,7 +61,6 @@ $(document).ready(function () {
             if ($('#file-list tr').length === 1) {
                 getAllList(curPageNo - 1);
             } else {
-
                 getAllList(curPageNo);
             }
             // self.parents('tr').remove();
@@ -124,7 +123,10 @@ $(document).ready(function () {
         // 总页数
         const pageNum = Math.ceil(total/pageCount);
         // 当前页附件内容
-        const currPageAttData = fileData && isCheckAll ? fileData.slice((currPageNum-1)*pageCount, currPageNum*pageCount) : filterFileData.map((v, index) => {
+        const currPageAttData = fileData && isCheckAll ? fileData.map((v, index) => {
+            return {...v, index }
+        }).slice((currPageNum-1)*pageCount, currPageNum*pageCount)
+        : filterFileData.map((v, index) => {
             return {...v, index }
         }).slice((currPageNum-1)*pageCount, currPageNum*pageCount);
         renderHtml(currPageAttData)

+ 2 - 2
app/public/js/revise_compare.js

@@ -30,7 +30,7 @@ $(document).ready(() => {
     }
     billsSpreadSetting.getColor = function (sheet, data, row, col, defaultColor) {
         // 增
-        if (data.differ.indexOf('add') >= 0) return '#cce5ff';
+        if (data.differ.indexOf('add') >= 0) return '#ffddc5';
         // 删
         if (data.differ.indexOf('del') >= 0) return '#DCDCDC';
         // 结构变动
@@ -59,7 +59,7 @@ $(document).ready(() => {
 
     posSpreadSetting.getColor = function (sheet, data, row, col, defaultColor) {
         // 增
-        if (data.differ.indexOf('add') >= 0) return '#cce5ff';
+        if (data.differ.indexOf('add') >= 0) return '#ffddc5';
         // 删
         if (data.differ.indexOf('del') >= 0) return '#DCDCDC';
         // 修改计算或文字

+ 220 - 0
app/public/js/revise_gcl_compare.js

@@ -0,0 +1,220 @@
+'use strict';
+
+/**
+ *
+ *
+ * @author Mai
+ * @date 2020/10/28
+ * @version
+ */
+const showTools = function (show) {
+    const left = $('#left-view'), right = $('#right-view'), parent = left.parent();
+    if (show) {
+        right.show();
+        autoFlashHeight();
+        /**
+         * right.show()后, parent被撑开成2倍left.height, 导致parent.width减少了10px
+         * 第一次left.width调整后,parent的缩回left.height, 此时parent.width又增加了10px
+         * 故需要通过最终的parent.width再计算一次left.width
+         *
+         * Q: 为什么不通过先计算left.width的宽度,以避免计算两次left.width?
+         * A: 右侧工具栏不一定显示,当右侧工具栏显示过一次后,就必须使用parent和right来计算left.width
+         *
+         */
+            //left.css('width', parent.width() - right.outerWidth());
+            //left.css('width', parent.width() - right.outerWidth());
+        const percent = 100 - right.outerWidth() /parent.width() * 100;
+        left.css('width', percent + '%');
+    } else {
+        right.hide();
+        left.css('width', '100%');
+    }
+};
+
+$(document).ready(() => {
+    showTools(true);
+    autoFlashHeight();
+    const gclSpread = SpreadJsObj.createNewSpread($('#gcl-spread')[0]);
+    const gclSpreadSetting = {
+        cols: [
+            {title: '清单编号', colSpan: '1', rowSpan: '2', field: 'b_code', hAlign: 0, width: 120, formatter: '@'},
+            {title: '名称', colSpan: '1', rowSpan: '2', field: 'name', hAlign: 0, width: 230, formatter: '@'},
+            {title: '单位', colSpan: '1', rowSpan: '2', field: 'unit', hAlign: 1, width: 60, formatter: '@', cellType: 'unit'},
+            {title: '单价', colSpan: '1', rowSpan: '2', field: 'unit_price', hAlign: 2, width: 80, type: 'Number'},
+            {title: '签约清单|数量', colSpan: '2|1', rowSpan: '1|1', field: 'deal_bills_qty', hAlign: 2, width: 80, type: 'Number'},
+            {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'deal_bills_tp', hAlign: 2, width: 80, type: 'Number'},
+            {title: '台账修订|数量', colSpan: '2|1', rowSpan: '1|1', field: 'new_quantity', hAlign: 2, width: 80, type: 'Number'},
+            {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'new_total_price', hAlign: 2, width: 80, type: 'Number'},
+            {title: '台账|数量', colSpan: '2|1', rowSpan: '1|1', field: 'org_quantity', hAlign: 2, width: 80, type: 'Number'},
+            {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'org_total_price', hAlign: 2, width: 80, type: 'Number'},
+        ],
+        emptyRows: 0,
+        headRows: 2,
+        headRowHeight: [25, 25],
+        headColWidth: [30],
+        defaultRowHeight: 21,
+        headerFont: '12px 微软雅黑',
+        font: '12px 微软雅黑',
+        readOnly: true,
+        localCache: {
+            key: 'ledger-gather-gcl',
+            colWidth: true,
+        },
+        getColor: function (sheet, data, row, col, defaultColor) {
+            return data
+                ? (data.differ ? '#FFE699' : '#ffffff')
+                : '#ffffff';
+        }
+    };
+    if (thousandth) sjsSettingObj.setTpThousandthFormat(gclSpreadSetting);
+    SpreadJsObj.initSheet(gclSpread.getActiveSheet(), gclSpreadSetting);
+    const gclSheet = gclSpread.getActiveSheet();
+    const leafXmjSpread = SpreadJsObj.createNewSpread($('#leaf-xmj-spread')[0]);
+    const leafXmjSpreadSetting = {
+        cols: [
+            {title: '项目节编号', colSpan: '1', rowSpan: '2', field: 'code', hAlign: 0, width: 120, formatter: '@'},
+            {title: '修订台账|数量', colSpan: '1', rowSpan: '1|1', field: 'new_quantity', hAlign: 2, width: 80, type: 'Number'},
+            {title: '原台账|数量', colSpan: '1', rowSpan: '1|1', field: 'org_quantity', hAlign: 2, width: 80, type: 'Number'},
+            {title: '单位工程', colSpan: '1', rowSpan: '2', field: 'dwgc', hAlign: 0, width: 100, formatter: '@'},
+            {title: '分部工程', colSpan: '1', rowSpan: '2', field: 'fbgc', hAlign: 0, width: 100, formatter: '@'},
+            {title: '分项工程', colSpan: '1', rowSpan: '2', field: 'fxgc', hAlign: 0, width: 100, formatter: '@'},
+            {title: '细目', colSpan: '1', rowSpan: '2', field: 'jldy', hAlign: 0, width: 100, formatter: '@'},
+            {title: '计量单元', colSpan: '1', rowSpan: '2', field: 'bwmx', hAlign: 0, width: 100, formatter: '@'},
+            {title: '图册号', colSpan: '1', rowSpan: '2', field: 'drawing_code', hAlign: 0, width: 120, formatter: '@'},
+        ],
+        emptyRows: 0,
+        headRows: 1,
+        headRowHeight: [25, 25],
+        headColWidth: [30],
+        defaultRowHeight: 21,
+        headerFont: '12px 微软雅黑',
+        font: '12px 微软雅黑',
+        readOnly: true,
+        localCache: {
+            key: 'ledger-gather-leafXmj',
+            colWidth: true,
+        },
+    };
+    if (thousandth) sjsSettingObj.setTpThousandthFormat(leafXmjSpreadSetting);
+    SpreadJsObj.initSheet(leafXmjSpread.getActiveSheet(), leafXmjSpreadSetting);
+    const leafXmjSheet = leafXmjSpread.getActiveSheet();
+
+    let gclData = [];
+    // 获取项目节数据
+    function loadLeafXmjData(iGclRow) {
+        const gcl = gclData[iGclRow];
+        SpreadJsObj.resetTopAndSelect(leafXmjSheet);
+        if (gcl) {
+            SpreadJsObj.loadSheetData(leafXmjSheet, SpreadJsObj.DataType.Data, gcl.leafXmjs);
+        } else {
+            SpreadJsObj.loadSheetData(leafXmjSheet, SpreadJsObj.DataType.Data, []);
+        }
+    }
+    // 切换清单行,读取所属项目节数据
+    gclSpread.getActiveSheet().bind(spreadNS.Events.SelectionChanged, function (e, info) {
+        const iNewRow = info.newSelections[0].row;
+        if (!info.oldSelections || iNewRow !== info.oldSelections[0].row) {
+            loadLeafXmjData(iNewRow);
+        }
+    });
+
+    function generateChapterHtml(data) {
+        const html = [];
+        if (data) {
+            for (const d of data) {
+                if (['1000', '1100', '1200', '1300'].indexOf(d.code) >= 0) {
+                    if (checkZero(d.total_price) && checkZero(d.deal_bills_tp)) {
+                        continue;
+                    }
+                }
+                html.push('<tr>');
+                if (d.code) {
+                    html.push('<td>', d.code, '</td>');
+                    html.push('<td>', d.name, '</td>');
+                } else {
+                    html.push('<td colspan="2">', d.name, '</td>');
+                }
+                html.push('<td class="text-right">', d.deal_bills_tp ? d.deal_bills_tp : '', '</td>');
+                html.push('<td class="text-right">', d.new_total_price ? d.new_total_price : '', '</td>');
+                html.push('<td class="text-right">', d.org_total_price ? d.org_total_price : '', '</td>');
+                html.push('</tr>');
+            }
+        }
+        $('#chapter-list').html(html.join(''));
+    }
+
+    postData('/tender/' + window.location.pathname.split('/')[2] + '/revise/load', {filter: 'bills;pos;reviseBills;revisePos;dealBills'}, function (data) {
+        const setting = {
+            tree: {
+                id: 'ledger_id',
+                pid: 'ledger_pid',
+                order: 'order',
+                level: 'level',
+                rootId: -1,
+                keys: ['id', 'tender_id', 'ledger_id'],
+                stageId: 'id',
+            },
+            pos: { id: 'id', ledgerId: 'lid' },
+            billsFields: ['quantity', 'total_price'],
+            posFields: ['quantity'],
+            chapterFields: ['total_price'],
+        };
+        gclCompareModel.init(gclData, chapter);
+        setting.prefix = 'new_';
+        gclCompareModel.gatherLedgerData(data.reviseBills, data.revisePos, setting);
+        setting.prefix = 'org_';
+        gclCompareModel.gatherLedgerData(data.bills, data.pos, setting);
+        gclCompareModel.gatherDealBills(data.dealBills);
+        gclCompareModel.checkDiffer();
+        SpreadJsObj.loadSheetData(gclSheet, SpreadJsObj.DataType.Data, gclData);
+        loadLeafXmjData(0);
+
+        const chapterData = gclCompareModel.chapterData();
+        generateChapterHtml(chapterData);
+    }, null, true);
+
+    // 展开收起附件
+    $('a', '.right-nav').bind('click', function () {
+        const tab = $(this), tabPanel = $(tab.attr('content'));
+        if (!tab.hasClass('active')) {
+            $('a', '.side-menu').removeClass('active');
+            $('.tab-content .tab-select-show').removeClass('active');
+            tab.addClass('active');
+            tabPanel.addClass('active');
+            showTools(tab.hasClass('active'));
+        } else {
+            tab.removeClass('active');
+            tabPanel.removeClass('active');
+            showTools(tab.hasClass('active'));
+        }
+        gclSpread.refresh();
+        leafXmjSpread.refresh();
+    });
+    $.subMenu({
+        menu: '#sub-menu', miniMenu: '#sub-mini-menu', miniMenuList: '#mini-menu-list',
+        toMenu: '#to-menu', toMiniMenu: '#to-mini-menu',
+        key: 'menu.1.0.0',
+        miniHint: '#sub-mini-hint', hintKey: 'menu.hint.1.0.1',
+        callback: function (info) {
+            if (info.mini) {
+                $('.panel-title').addClass('fluid');
+                $('#sub-menu').removeClass('panel-sidebar');
+            } else {
+                $('.panel-title').removeClass('fluid');
+                $('#sub-menu').addClass('panel-sidebar');
+            }
+            autoFlashHeight();
+            gclSpread.refresh();
+            leafXmjSpread.refresh();
+        }
+    });
+    $.divResizer({
+        select: '#main-resize',
+        callback: function () {
+            gclSpread.refresh();
+            let bcontent = $(".bcontent-wrap") ? $(".bcontent-wrap").height() : 0;
+            $(".sp-wrap").height(bcontent-30);
+            leafXmjSpread.refresh();
+        }
+    });
+});

+ 412 - 0
app/public/js/shares/gcl_gather_compare.js

@@ -0,0 +1,412 @@
+'use strict';
+
+/**
+ *
+ * 清单汇总对比(需使用 decimal.min.js, zh_calc.js, path_tree.js, lodash.js)
+ *
+ * @author Mai
+ * @date 2020/10/28
+ * @version
+ */
+
+const gclCompareModel = (function () {
+    const leafXmjs = [], mergeChar = ';';
+    let gclList, gclChapter, otherChapter;
+    let ledgerSetting, gsTree;
+
+    function gatherfields(obj, src, fields, prefix = '') {
+        if (obj && src) {
+            for (const f of fields) {
+                obj[prefix + f] = ZhCalc.add(obj[prefix + f], src[f]);
+            }
+        }
+    }
+    /**
+     * 新建 清单汇总节点
+     * @param node - 最底层 工程量清单节点
+     * @returns {obj}
+     */
+    function newGclNode(node) {
+        const gcl = {
+            b_code: node.b_code,
+            name: node.name,
+            unit: node.unit,
+            unit_price: node.unit_price,
+            leafXmjs: [],
+        };
+        gclList.push(gcl);
+        return gcl;
+    }
+
+    /**
+     * 获取清单汇总节点
+     *
+     * @param node - 最底层清单节点
+     * @returns {*}
+     */
+    function getGclNode(node) {
+        const gcl = gclList.find(function (g) {
+            return g.b_code === node.b_code &&
+                (g.name || node.name ? g.name === node.name : true) &&
+                (g.unit || node.unit ? g.unit === node.unit : true) &&
+                checkZero(ZhCalc.sub(g.unit_price, node.unit_price));
+        });
+        if (gcl) {
+            return gcl
+        } else {
+            return newGclNode(node);
+        }
+    }
+
+    /**
+     * 检查 text 是否是Peg
+     * e.g. K123+000(true) Kab+123(false) K123.234+234(false) K12+324.234(true)
+     *
+     * @param text
+     * @returns {*}
+     * @constructor
+     */
+    function CheckPeg(text) {
+        const pegReg = /[a-zA-Z]?[kK][0-9]+[++][0-9]{3}([.][0-9]+)?/;
+        return pegReg.test(text);
+    }
+
+    /**
+     * 获取 桩号节点
+     * @param node - 检索起始节点
+     * @returns {*}
+     */
+    function getPegNode (node) {
+        if (node) {
+            if (CheckPeg(node.name)) {
+                return node;
+            } else {
+                const parent = gsTree.getParent(node);
+                return parent ? getPegNode(parent) : null;
+            }
+        }
+    }
+
+    /**
+     * 获取节点的第N层父节点
+     *
+     * @param node - 节点(检索起点)
+     * @param level - 第N层
+     * @returns {*}
+     */
+    function getNodeByLevel(node, level) {
+        let cur = node;
+        while (cur && cur.level > level) {
+            cur = gsTree.getParent(cur);
+        }
+        return cur;
+    }
+
+    /**
+     * 获取 单位工程
+     *
+     * @param xmj - 计量单元(最底层项目节)
+     * @returns {string}
+     */
+    function getDwgc(peg, xmj) {
+        if (peg) {
+            return peg.name;
+        } else {
+            const node = getNodeByLevel(xmj, 2);
+            return node ? node.name : '';
+        }
+    }
+
+    /**
+     * 获取 分部工程
+     *
+     * @param peg - 桩号节点
+     * @param xmj - 计量单元(最底层项目节)
+     * @returns {string}
+     */
+    function getFbgc(peg, xmj) {
+        if (peg && peg.id !== xmj.id) {
+            const node = getNodeByLevel(xmj, peg.level + 1);
+            return node ? node.name : '';
+        } else {
+            const node = getNodeByLevel(xmj, 3);
+            return node ? node.name : '';
+        }
+    }
+
+    /**
+     * 获取 分项工程
+     *
+     * @param peg - 桩号节点
+     * @param xmj - 计量单元(最底层项目节)
+     * @returns {string}
+     */
+    function getFxgc(peg, xmj) {
+        if (!peg) {
+            const node = getNodeByLevel(xmj, 4);
+            return node ? node.name : '';
+        } else if (peg.id === xmj.id) {
+            if (xmj.level > 4) {
+                let value = '';
+                for (let level = 4; level < xmj.level; level++) {
+                    const node = getNodeByLevel(xmj, level);
+                    value = value === '' ? node.name : value + mergeChar + node.name;
+                }
+                return value;
+            } else {
+                return '';
+            }
+        } else {
+            if (peg.level + 2 < xmj.level) {
+                let value = '';
+                for (let level = peg.level + 2; level < xmj.level; level++) {
+                    const node = getNodeByLevel(xmj, level);
+                    value = value === '' ? node.name : value + mergeChar + node.name;
+                }
+                return value;
+            } else {
+                return '';
+            }
+        }
+    }
+
+    /**
+     * 新建 最底层项目节 缓存数据
+     * @param leafXmj
+     * @returns {{id, code: *|string[], jldy, fbgc: string, fxgc: string, dwgc: string, bwmx: string, drawing_code: string}}
+     */
+    function newCacheLeafXmj(leafXmj) {
+        const peg = getPegNode(leafXmj);
+        const cacheLX = {
+            id: leafXmj.id,
+            code: leafXmj.code,
+            jldy: leafXmj.name,
+            fbgc: getFbgc(peg, leafXmj),
+            fxgc: getFxgc(peg, leafXmj),
+            dwgc: getDwgc(peg, leafXmj),
+            drawing_code: leafXmj.drawing_code,
+        };
+        leafXmjs.push(cacheLX);
+        return cacheLX;
+    }
+
+    /**
+     * 获取缓存的最底层项目节数据
+     *
+     * @param leafXmj - 最底层项目节
+     * @returns {*}
+     */
+    function getCacheLeafXmj(leafXmj) {
+        const cacheLX = leafXmjs.find(function (lx) {
+            return lx.id === leafXmj.id;
+        });
+        if (!cacheLX) {
+            return newCacheLeafXmj(leafXmj);
+        } else {
+            return cacheLX;
+        }
+    }
+
+    /**
+     * 汇总节点
+     * @param node - 最底层 工程量清单 节点
+     * @param leafXmj - 所属 最底层 项目节
+     */
+    function loadGatherGclNode(node, leafXmj, gsPos) {
+        const gcl = getGclNode(node);
+        gatherfields(gcl, node, ledgerSetting.billsFields, ledgerSetting.prefix);
+        const cacheLeafXmj = getCacheLeafXmj(leafXmj);
+        const posRange = gsPos.getLedgerPos(node.id);
+        const detail = posRange && posRange.length > 0 ? posRange : [node];
+        for (const d of detail) {
+            const lx = gcl.leafXmjs.find(x => {return x.id === leafXmj.id && (x.mx_id === d.id || x.gcl_id === d.id)});
+            if (lx) {
+                gatherfields(lx, d, ledgerSetting.posFields, ledgerSetting.prefix);
+            } else {
+                const dx = _.assign({}, cacheLeafXmj);
+                gatherfields(dx, d, ledgerSetting.posFields, ledgerSetting.prefix);
+                dx.gcl_id = node.id;
+                if (d.name !== node.name) {
+                    dx.bwmx = d.name;
+                    dx.mx_id = d.id;
+                }
+                if (d.drawing_code) {
+                    dx.drawing_code = d.drawing_code;
+                }
+                gcl.leafXmjs.push(dx);
+            }
+        }
+    }
+
+    /**
+     * (递归)汇总树节点
+     * @param nodes - 汇总节点列表
+     * @param leafXmj - 汇总节点所属的底层项目节
+     */
+    function recursiveGatherGclData(nodes, leafXmj, gsPos) {
+        for (const node of nodes) {
+            if (node.b_code) {
+                if (node.children.length > 0) {
+                    recursiveGatherGclData(node.children, leafXmj, gsPos);
+                } else {
+                    loadGatherGclNode(node, leafXmj, gsPos);
+                }
+            } else if (node.children.length > 0) {
+                recursiveGatherGclData(node.children, node, gsPos);
+            }
+        }
+    }
+
+    function _getCalcChapter(chapter) {
+        const gclChapter = [], otherChapter = [];
+        let serialNo = 1;
+        for (const c of chapter) {
+            const cc = { code: c.code, name: c.name, cType: 1 };
+            cc.serialNo = serialNo++;
+            cc.filter = '^[^0-9]*' + c.code.substr(0, c.code.length - 2) + '[0-9]{2}-';
+            gclChapter.push(cc);
+        }
+        gclChapter.push({ name: '未计入章节清单合计', cType: 21, serialNo: serialNo++, });
+        otherChapter.push({ name: '清单小计(A)', cType: 11, serialNo: serialNo++ });
+        otherChapter.push({ name: '非清单项费用(B)', cType: 31, serialNo: serialNo++ });
+        otherChapter.push({ name: '合计(C=A+B)', cType: 41, serialNo: serialNo });
+        return [gclChapter, otherChapter];
+    }
+
+    function _gatherChapterFields(chapter, data, fields) {
+        for (const f of fields) {
+            chapter[f] = ZhCalc.add(chapter[f], data[f]);
+        }
+    }
+
+    function _getGclChapter(chapter, data) {
+        for (const c of chapter) {
+            if (c.filter) {
+                const reg = new RegExp(c.filter);
+                if (reg.test(data.b_code)) {
+                    return c;
+                }
+            } else {
+                return c;
+            }
+        }
+    }
+
+    function _gatherChapter() {
+        for (const d of gsTree.datas) {
+            if (d.children && d.children.length > 0) continue;
+
+            for (const c of otherChapter) {
+                if (c.cType === 41) {
+                    gatherfields(c, d, ledgerSetting.chapterFields, ledgerSetting.prefix);
+                } else if (c.cType === 31 && (!d.b_code || d.b_code === '')) {
+                    gatherfields(c, d, ledgerSetting.chapterFields, ledgerSetting.prefix);
+                } else if (c.cType === 11 && (d.b_code)) {
+                    gatherfields(c, d, ledgerSetting.chapterFields, ledgerSetting.prefix);
+                }
+            }
+            if (d.b_code) {
+                const c = _getGclChapter(gclChapter, d);
+                gatherfields(c, d, ledgerSetting.chapterFields, ledgerSetting.prefix);
+            }
+        }
+    }
+
+    function init (gclData, chapter) {
+        gclList = gclData;
+        [gclChapter, otherChapter] = _getCalcChapter(chapter);
+    }
+
+    /**
+     *
+     * @param bills - 项目节+清单数据
+     * @param pos - 计量单元数据
+     * @param setting 配置
+     *  e.g.
+     *  {
+     *      tree: {
+     *          id: 'ledger_id',
+     *          pid: 'ledger_pid',
+     *          order: 'order',
+     *          level: 'level',
+     *          rootId: -1,
+     *          keys: ['id', 'tender_id', 'ledger_id'],
+     *          stageId: 'id',
+     *      },
+     *      pos: { id: 'id', ledgerId: 'lid', },
+     *      billsFields: ['quantity', 'total_price', 'deal_qty', 'deal_tp'],
+     *      posFields: ['quantity'],
+     *      chapterFields: [new_total_price],
+     *      prefix: 'org' // 'new'
+     *  }
+     */
+    function gatherLedgerData(bills, pos, setting) {
+        ledgerSetting = setting;
+        try {
+            if (leafXmjs.length > 0) leafXmjs.length = 0;
+            gsTree = createNewPathTree('ledger', setting.tree);
+            gsTree.loadDatas(bills);
+            const gsPos = new PosData(setting.pos);
+            gsPos.loadDatas(pos);
+            recursiveGatherGclData(gsTree.children, null, gsPos);
+            _gatherChapter();
+        } catch(err) {
+            console.log(err);
+        }
+        ledgerSetting = null;
+    }
+
+    /**
+     *
+     * @param data {Array} - 签约清单数据
+     * @returns <void>
+     */
+    function gatherDealBills(data) {
+        if (data instanceof Array && data.length > 0) {
+            for (const node of data) {
+                node.b_code = node.code;
+                const gcl = getGclNode(node);
+                if (!d.quantity || !d.unit_price) continue;
+                gcl.deal_bills_qty = node.quantity || 0;
+                gcl.deal_bills_tp = node.total_price || 0;
+
+                for (const c of otherChapter) {
+                    if (c.cType === 41 || c.cType === 11) {
+                        c.deal_bills_tp = ZhCalc.add(c.deal_bills_tp, d.total_price);
+                    }
+                }
+                const c = _getGclChapter(gclChapter, d);
+                c.deal_bills_tp = ZhCalc.add(c.deal_bills_tp, d.total_price);
+            }
+        }
+    }
+
+    /**
+     * 检查汇总完的工程量清单中,同编号 & 不同名称/单位/单价 的清单并标记
+     */
+    function checkDiffer() {
+        for (const gcl of gclList) {
+            gcl.differ = false;
+        }
+        for (const [i, gcl] of gclList.entries()) {
+            if (i === gclList.length - 1) continue;
+            const next = gclList[i+1];
+            if (gcl.b_code === next.b_code) {
+                if (gcl.name !== next.name || gcl.unit !== next.unit || !checkZero(gcl.unit_price - next.unit_price)) {
+                    gcl.differ = true;
+                    next.differ = true;
+                }
+            }
+        }
+    }
+
+    function chapterData () {
+        return gclChapter.concat(otherChapter);
+    }
+
+    return {
+        init,
+        gatherLedgerData, gatherDealBills, checkDiffer,
+        chapterData
+    };
+})();

+ 24 - 15
app/public/js/stage.js

@@ -3209,8 +3209,10 @@ $(document).ready(() => {
         const tabPanel = $(this).attr('fujian-content');
         if (tabPanel !== 'syfujian') {
             $('#showPage').hide();
+            $('#bach-download').prop('type', 'curr');
         } else {
             $('#showPage').show();
+            $('#bach-download').prop('type', 'all')
         }
         $('#showAttachment').hide();
     });
@@ -3450,34 +3452,41 @@ $(document).ready(() => {
 
     // 批量下载
     $('#bach-download').click(function() {
-        const fileIds = []
-        $('.tab-pane.active.show .list-table .check-file:checked').each(function() {
-            const fileId = $(this).attr('file-id')
-            fileId && fileIds.push(fileId)
-        })
+        const fileIds = [];
+        const type = $(this).prop('type');
+        let node = ''
+        if (type === 'curr') {
+            node = '#nodelist-table .check-file:checked'
+        } else {
+            node = '#alllist-table .check-file:checked'
+        }
+        $(node).each(function() {
+            const fileId = $(this).attr('file-id');
+            fileId && fileIds.push(fileId);
+        });
 
         if (fileIds.length) {
-            const url = `/tender/${tender.id}/measure/stage/${stage.order}/download/compresse-file?fileIds=${JSON.stringify(fileIds)}`
-            $('#zipDown').attr('href', url)
-            $("#zipDown")[0].click()
+            const url = `/tender/${tender.id}/measure/stage/${stage.order}/download/compresse-file?fileIds=${JSON.stringify(fileIds)}`;
+            $('#zipDown').attr('href', url);
+            $("#zipDown")[0].click();
         }
     });
 
     // 监听附件check是否选中
     $('.list-table').on('click', '.check-file', function() {
-        const checkedList = $(this).parents('.list-table').children().find('input:checked')
-        const childs = $(this).parents('.list-table').children().length
-        const checkBox = $(this).parents('.list-table').parent().find('.check-all-file')
+        const checkedList = $(this).parents('.list-table').children().find('input:checked');
+        const childs = $(this).parents('.list-table').children().length;
+        const checkBox = $(this).parents('.list-table').parent().find('.check-all-file');
         if (checkedList.length === childs) {
-            checkBox.prop("checked", true)
+            checkBox.prop("checked", true);
         } else {
-            checkBox.prop("checked", false)
+            checkBox.prop("checked", false);
         }
     });
     $('.check-all-file').click(function() {
-        const isCheck = $(this).is(':checked')
+        const isCheck = $(this).is(':checked');
         $(this).parents('table').find('.list-table').each(function() {
-            $(this).find('input:checkbox').prop("checked", isCheck)
+            $(this).find('input:checkbox').prop("checked", isCheck);
         })
     });
 

+ 1 - 2
app/router.js

@@ -176,8 +176,7 @@ module.exports = app => {
     app.post('/tender/:id/revise/info/check', sessionAuth, tenderCheck, uncheckTenderCheck, 'reviseController.checkData');
 
     app.get('/tender/:id/revise/compare', sessionAuth, tenderCheck, uncheckTenderCheck, reviseAuditCheck, 'reviseController.compare');
-    // app.get('/tender/:id/revise/gather', sessionAuth, tenderCheck, uncheckTenderCheck, reviseAuditCheck, 'reviseController.gather');
-    // app.get('/tender/:id/revise/bwtz', sessionAuth, tenderCheck, uncheckTenderCheck, reviseAuditCheck, 'reviseController.bwtz');
+    app.get('/tender/:id/revise/gcl-compare', sessionAuth, tenderCheck, uncheckTenderCheck, reviseAuditCheck, 'reviseController.gclCompare');
     app.post('/tender/:id/revise/load', sessionAuth, tenderCheck, uncheckTenderCheck, reviseAuditCheck, 'reviseController.loadData');
 
     // 查看修订数据

+ 4 - 0
app/service/change_att.js

@@ -92,6 +92,10 @@ module.exports = app => {
                 // 每次开一个新的archiver
                 const ziparchiver = archiver('zip');
                 const outputPath = fs.createWriteStream(path.resolve(this.app.baseDir, zipPath));
+                outputPath.on('error', err => {
+                    reject(err);
+                });
+
                 ziparchiver.pipe(outputPath);
                 files.forEach(item => {
                     ziparchiver.file(path.resolve(this.app.baseDir, item.filepath), { name: item.filename + item.fileext });

+ 3 - 3
app/service/material_audit.js

@@ -213,8 +213,8 @@ module.exports = app => {
                 });
                 // 本期一些必要数据(如应耗数量和上期调差金额)插入到material_bills_history表里
                 const materialBillsData = await this.ctx.service.materialBills.getAllDataByCondition({ where: { tid: this.ctx.tender.id } });
-                if (materialBillsData.length === 0) {
-                    throw '调差工料不能为空';
+                if (materialBillsData.length === 0 && this.ctx.material.ex_expr === null) {
+                    throw '请至少使用一种调差方式';
                 }
                 const mbhList = [];
                 for (const mb of materialBillsData) {
@@ -240,7 +240,7 @@ module.exports = app => {
                     };
                     mbhList.push(newMbh);
                 }
-                await transaction.insert(this.ctx.service.materialBillsHistory.tableName, mbhList);
+                if(mbhList.length !== 0) await transaction.insert(this.ctx.service.materialBillsHistory.tableName, mbhList);
 
                 const materialExponentData = await this.ctx.service.materialExponent.getAllDataByCondition({ where: { tid: this.ctx.tender.id } });
                 const mehList = [];

+ 4 - 0
app/service/material_file.js

@@ -93,6 +93,10 @@ module.exports = app => {
                 // 每次开一个新的archiver
                 const ziparchiver = archiver('zip');
                 const outputPath = fs.createWriteStream(path.resolve(this.app.baseDir, zipPath));
+                outputPath.on('error', err => {
+                    reject(err);
+                });
+
                 ziparchiver.pipe(outputPath);
                 files.forEach(item => {
                     ziparchiver.file(path.resolve(this.app.baseDir, 'app', item.filepath), { name: item.file_name });

+ 4 - 0
app/service/stage_att.js

@@ -113,6 +113,10 @@ module.exports = app => {
                 // 每次开一个新的archiver
                 const ziparchiver = archiver('zip');
                 const outputPath = fs.createWriteStream(path.resolve(this.app.baseDir, zipPath));
+
+                outputPath.on('error', err => {
+                    reject(err);
+                });
                 ziparchiver.pipe(outputPath);
                 files.forEach(item => {
                     ziparchiver.file(item.filepath, { name: item.filename + item.fileext });

+ 23 - 21
app/service/tender_info.js

@@ -122,6 +122,24 @@ module.exports = app => {
             await this.db.update(this.tableName, data, { where: { tid: tenderId } });
         }
 
+        async _getLedgerService() {
+            try {
+                if (this.ctx.tender.data.ledger_status === auditConst.ledger.status.checked) {
+                    const stageCount = await this.ctx.service.stage.count({tid: this.ctx.tender.id});
+                    if (stageCount === 0) {
+                        const revise = await this.ctx.service.ledgerRevise.getLastestRevise(this.ctx.tender.id);
+                        if (revise.status === auditConst.revise.status.uncheck || revise.status === auditConst.revise.status.checkNo) {
+                            return [this.ctx.service.reviseBills, this.ctx.service.revisePos];
+                        }
+                    }
+                } else {
+                    return [this.ctx.service.ledger, this.ctx.service.pos];
+                }
+            } catch(err) {
+            }
+            return [];
+        }
+
         async savePrecision(tenderId, newPrecision, oldPrecision, decimal) {
             const changePrecision = [];
             const units = await this.ctx.service.ledger.getTenderUsedUnits(tenderId);
@@ -146,16 +164,9 @@ module.exports = app => {
             }
             changeUnits = this._.flatten(changeUnits);
 
-            let billsService, posService;
-            if (this.ctx.tender.data.status === auditConst.ledger.status.checked) {
-                billsService = this.ctx.service.ledger;
-                posService = this.ctx.service.pos;
-            } else {
-                billsService = this.ctx.service.reviseBills;
-                posService = this.ctx.service.revisePos;
-            }
+            const [billsService, posService] = await this._getLedgerService();
 
-            if (changeUnits.length > 0) {
+            if (changeUnits.length > 0 && billsService && posService) {
                 const bills = await billsService.getAllDataByCondition({
                     columns: ['id', 'unit', 'unit_price', 'sgfh_qty', 'sjcl_qty', 'qtcl_qty', 'deal_qty'],
                     where: { tender_id: tenderId, unit: changeUnits, is_leaf: true },
@@ -295,18 +306,9 @@ module.exports = app => {
                 }
             }
 
-            let billsService = null;
-            if (this.ctx.tender.data.ledger_status === auditConst.ledger.status.checked) {
-                const stageCount = this.ctx.service.stage.count({tid: tenderId});
-                if (stageCount === 0) {
-                    const revise = await ctx.service.ledgerRevise.getLastestRevise(tenderId);
-                    if (revise.status === auditConst.revise.status.uncheck || revise.status === auditConst.revise.status.checkNo) {
-                        billsService = this.ctx.service.reviseBills;
-                    }
-                }
-            } else {
-                billsService = this.ctx.service.ledger;
-            }
+            const [billsService] = await this._getLedgerService();
+            console.log(billsService);
+
             const changeBills = await this._reCalcLedger(tenderId, billsService, newDecimal, oldDecimal);
             const [changeSj, changeSb, changeSo] = await this._reCalcStageExtra(tenderId, newDecimal, oldDecimal);
             if (changeBills.length > 0 ||

+ 7 - 4
app/view/material/exponent.ejs

@@ -13,9 +13,9 @@
                 <div class="d-inline-block ml-3">
                     本期调差计量期:第<span class="mx-2"><%= material.s_order.split(',').join(',') %></span>期
                 </div>
-                <div class="d-inline-block ml-3">
-                    本期价差:<b id="ex_expr"><%- material.ex_expr %></b>
-                </div>
+                <!--<div class="d-inline-block ml-3" style="display: none!important;">-->
+                    <!--本期价差:<b id="ex_expr"><%- material.ex_expr %></b>-->
+                <!--</div>-->
             </div>
             <div class="ml-auto">
             </div>
@@ -51,7 +51,10 @@
                         <div class="col-7 p-0">
                             <table class="table table-sm table-bordered">
                                 <tr><th rowspan="2"></th><th colspan="2" class="text-center">信息价</th><th colspan="2" class="text-center">价格指数</th></tr>
-                                <tr class="text-center"><th>本期金额</th><th>截止本期金额</th><th>本期金额</th><th>截止本期金额</th></tr>
+                                <tr class="text-center"><th>本期金额</th><th>截止本期金额</th>
+                                    <th>本期金额
+                                        <a href="javascript:void(0);" id="ex_expr" data-toggle="tooltip" data-placement="bottom" title="本期价差:<%- material.ex_expr ? material.ex_expr : '' %>"><i class="fa fa-question-circle-o"></i></a></th>
+                                    <th>截止本期金额</th></tr>
                                 <tr id="tp_set"><td>材料价差费用</td>
                                     <td class="text-center"><%= material.m_tp !== null ? ctx.helper.round(material.m_tp, 2) : null %></td>
                                     <td class="text-center"><%= material.m_tp !== null || material.pre_tp !== null ? ctx.helper.round(ctx.helper.add(material.pre_tp, material.m_tp), 2) : null %></td>

+ 4 - 1
app/view/material/info.ejs

@@ -53,7 +53,10 @@
                         <div class="col-7 p-0">
                             <table class="table table-sm table-bordered">
                                 <tr><th rowspan="2"></th><th colspan="2" class="text-center">信息价</th><th colspan="2" class="text-center">价格指数</th></tr>
-                                <tr class="text-center"><th>本期金额</th><th>截止本期金额</th><th>本期金额</th><th>截止本期金额</th></tr>
+                                <tr class="text-center"><th>本期金额</th><th>截止本期金额</th>
+                                    <th>本期金额
+                                        <a href="javascript:void(0);" id="ex_expr" data-toggle="tooltip" data-placement="bottom" title="本期价差:<%- material.ex_expr ? material.ex_expr : '' %>"><i class="fa fa-question-circle-o"></i></a></th>
+                                    <th>截止本期金额</th></tr>
                                 <tr id="tp_set"><td>材料价差费用</td>
                                     <td class="text-center"><%= material.m_tp !== null ? ctx.helper.round(material.m_tp, 2) : null %></td>
                                     <td class="text-center"><%= material.m_tp !== null || material.pre_tp !== null ? ctx.helper.round(ctx.helper.add(material.pre_tp, material.m_tp), 2) : null %></td>

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

@@ -42,7 +42,7 @@
                     <tr>
                         <td>
                             <a href="<%- '/tender/' + ctx.tender.id + '/measure/stage/' + s.order %>" target="_blank">第 <%- s.order %> 期</a>
-                            <% if ((s.status !== auditConst.status.checked || (stages[i-1] && stages[i-1].status !== auditConst.status.checked)) && s.user_id === ctx.session.sessionUser.accountId) { %>
+                            <% if ((i === 0 || (stages[i-1] && stages[i-1].status !== auditConst.status.checked)) && s.user_id === ctx.session.sessionUser.accountId) { %>
                             <a href="#edit" class="edit-stage" data-index="<%- i %>" data-toggle="modal" data-target="#edit"><i class="fa fa-pencil-square-o "></i></a>
                             <% } %>
                         </td>

+ 2 - 2
app/view/measure/stage_modal.ejs

@@ -72,7 +72,7 @@
         </div>
     </div>
 </div>
-<% if (stages.length > 0 && stages[0].status !== auditConst.status.checked && stages[0].user_id === ctx.session.sessionUser.accountId) { %>
+<% if (stages.length > 0 && stages[0].user_id === ctx.session.sessionUser.accountId) { %>
 <!--设置-->
 <div class="modal fade" id="edit" data-backdrop="static">
     <div class="modal-dialog" role="document">
@@ -107,7 +107,7 @@
 <script src="/public/js/datepicker/datepicker.min.js"></script>
 <script src="/public/js/datepicker/datepicker.zh.js"></script>
 <script>
-    <% if (stages.length > 0 && stages[0].status !== auditConst.status.checked && stages[0].user_id === ctx.session.sessionUser.accountId) { %>
+    <% if (stages.length > 0 && stages[0].user_id === ctx.session.sessionUser.accountId) { %>
     $(function () {
        $('.edit-stage').on('click', function () {
            const index = parseInt($(this).data('index'));

+ 2 - 2
app/view/revise/compare.ejs

@@ -21,7 +21,7 @@
                 </div>
             </div>
             <div class="d-inline-block">
-                <span class=""><i class="fa fa-stop text-primary-50 border-primary-50 bg-primary-50"></i> 新增</span>
+                <span class=""><i class="fa fa-stop text-warning-50 border-warning-50 bg-primary-50"></i> 新增</span>
                 <span class="ml-2"><i class="fa fa-stop text-secondary-50 border-secondary-50 bg-secondary-50"></i> 删除</span>
                 <span class="ml-2"><i class="fa fa-stop text-info-50 border-info-50 bg-info-50"></i> 结构调整</span>
                 <span class="ml-2"><i class="fa fa-stop text-danger-50 border-danger-50 bg-danger-50"></i> 计算项修改</span>
@@ -53,7 +53,7 @@
                             </li>
                             <li>
                                 <div class="d-inline-block ml-2 mt-1">
-                                    <span class=""><i class="fa fa-stop text-primary-50 border-primary-50 bg-primary-50"></i> 新增</span>
+                                    <span class=""><i class="fa fa-stop text-warning-50 border-warning-50 bg-primary-50"></i> 新增</span>
                                     <span class="ml-2"><i class="fa fa-stop text-secondary-50 border-secondary-50 bg-secondary-50"></i> 删除</span>
                                     <span class="ml-2"><i class="fa fa-stop text-danger-50 border-danger-50 bg-danger-50"></i> 数量修改</span>
                                     <span class="ml-2"><i class="fa fa-stop text-success-50 border-success-50 bg-success-50"></i> 文字修改</span>

+ 66 - 0
app/view/revise/gcl_compare.ejs

@@ -0,0 +1,66 @@
+<% include ./sub_menu.ejs %>
+<div class="panel-content">
+    <div class="panel-title">
+        <div class="title-main d-flex">
+            <% include ./sub_mini_menu.ejs %>
+            <div>
+                工程量清单对比
+            </div>
+            <div class="ml-auto">
+            </div>
+        </div>
+    </div>
+    <div class="content-wrap row pr-46">
+        <div class="c-header p-0 col-12">
+        </div>
+        <div class="row w-100 sub-content">
+            <div class="c-body" id="left-view" style="width: 100%">
+                <div class="sjs-height-1" id="gcl-spread">
+                </div>
+                <div class="bcontent-wrap" id="main-bottom">
+                    <div id="main-resize" class="resize-y" r-Type="height" div1="#gcl-spread" div2="#main-bottom" store-id="ledger-gather" store-version="1.0.0" min="100"></div>
+                    <div class="bc-bar mb-1">
+                        <ul class="nav nav-tabs">
+                            <li class="nav-item">
+                                <a class="nav-link active" data-toggle="tab" href="#xmujie" role="tab">所属项目节</a>
+                            </li>
+                        </ul>
+                    </div>
+                    <div class="tab-content">
+                        <div class="tab-pane active" id="xmujie">
+                            <div class="sp-wrap" id="leaf-xmj-spread">
+                            </div>
+                        </div>
+                    </div>
+                </div>
+            </div>
+            <div class="c-body" id="right-view" style="display: none; width: 33%;">
+                <div class="resize-x" id="right-spr" r-Type="width" div1="#left-view" div2="#right-view" title="调整大小" a-type="percent"><!--调整左右高度条--></div>
+                <div class="tab-content">
+                    <div id="chapter" class="tab-pane active">
+                        <div class="side-bar-1"></div>
+                        <div class="sjs-sh-1">
+                            <table class="table table-bordered">
+                                <tr class="text-center"><th>章节</th><th>章节名称</th><th>签约金额</th><th>修订台账金额</th><th>原台账金额</th></tr>
+                                <tbody id="chapter-list"></tbody>
+                            </table>
+                        </div>
+                    </div>
+                </div>
+            </div>
+        </div>
+        <!--右侧菜单-->
+        <div class="side-menu">
+            <!--右侧菜单-->
+            <ul class="nav flex-column right-nav" id="side-menu">
+                <li class="nav-item">
+                    <a class="nav-link active" content="#chapter" href="javascript: void(0);">章节合计</a>
+                </li>
+            </ul>
+        </div>
+    </div>
+</div>
+<script>
+    const chapter = JSON.parse('<%- JSON.stringify(ctx.tender.info.chapter) %>');
+    const thousandth = <%- ctx.tender.info.display.thousandth %>;
+</script>

+ 1 - 2
app/view/revise/sub_menu_list.ejs

@@ -1,5 +1,4 @@
 <nav-menu title="返回" url="/tender/<%= ctx.tender.id %>/revise" tclass="text-primary" ml="1" icon="fa-chevron-left"></nav-menu>
 <nav-menu title="台账修订" url="/tender/<%= ctx.tender.id %>/revise/info" ml="3" active="<%= ctx.url.indexOf('revise/info') %>"></nav-menu>
 <nav-menu title="台账对比" url="/tender/<%= ctx.tender.id %>/revise/compare" ml="3" active="<%= ctx.url.indexOf('revise/compare') %>"></nav-menu>
-<!--<nav-menu title="清单汇总" url="/tender/<%= ctx.tender.id %>/revise/gather" ml="3" active="<%= ctx.url.indexOf('revise/gather') %>"></nav-menu>
-<nav-menu title="部位台账" url="/tender/<%= ctx.tender.id %>/revise/bwtz" ml="3" active="<%= ctx.url.indexOf('revise/bwtz') %>"></nav-menu>-->
+<nav-menu title="清单对比" url="/tender/<%= ctx.tender.id %>/revise/gcl-compare" ml="3" active="<%= ctx.url.indexOf('revise/gcl-compare') %>"></nav-menu>

+ 2 - 2
app/view/stage/index.ejs

@@ -267,9 +267,9 @@
                                     <a class="nav-link" data-toggle="tab" href="#syfujian" role="tab" fujian-content="syfujian">所有附件</a>
                                 </li>
                                 <li class="nav-item ml-auto pt-1">
-                                    <a href="javascript:void(0);" id="bach-download" class="btn btn-sm btn-primary mr-3">批量下载</a>
+                                    <a href="javascript:void(0);" id="bach-download" class="btn btn-sm btn-primary" type="curr">批量下载</a>
                                     <!--所有附件 翻页-->
-                                    <span id="showPage" style="display: none"><a href="javascript:void(0);" class="page-select" content="pre"><i class="fa fa-chevron-left"></i></a> <span id="currentPage">1</span>/<span id="totalPage">10</span> <a href="javascript:void(0);" class="page-select" content="next"><i class="fa fa-chevron-right"></i></a></span>
+                                    <span id="showPage" style="display: none"><a href="javascript:void(0);" class="page-select ml-3" content="pre"><i class="fa fa-chevron-left"></i></a> <span id="currentPage">1</span>/<span id="totalPage">10</span> <a href="javascript:void(0);" class="page-select  ml-3" content="next"><i class="fa fa-chevron-right"></i></a></span>
                                     <a href="#upload" data-toggle="modal" data-target="#upload"  class="btn btn-sm btn-outline-primary ml-3">上传</a>
                                 </li>
                             </ul>

+ 18 - 0
config/web.js

@@ -260,6 +260,24 @@ const JsFiles = {
                     '/public/js/revise_compare.js',
                 ],
                 mergeFile: 'revise_compare',
+            },
+            gclCompare: {
+                files: [
+                    '/public/js/spreadjs/sheets/v11/gc.spread.sheets.all.11.2.2.min.js',
+                    '/public/js/decimal.min.js',
+                    '/public/js/math.min.js',
+                    '/public/js/component/menu.js',
+                ],
+                mergeFiles: [
+                    '/public/js/sub_menu.js',
+                    '/public/js/div_resizer.js',
+                    '/public/js/spreadjs_rela/spreadjs_zh.js',
+                    '/public/js/zh_calc.js',
+                    '/public/js/path_tree.js',
+                    '/public/js/shares/gcl_gather_compare.js',
+                    '/public/js/revise_gcl_compare.js',
+                ],
+                mergeFile: 'revise_gcl_compare',
             }
         },
         stage: {