Browse Source

统计分析,期计量对比、台账汇总、期计量汇总

MaiXinRong 5 years ago
parent
commit
3a58ad5aa5

+ 165 - 20
app/controller/spss_controller.js

@@ -9,6 +9,7 @@
  */
 
 const measureType = require('../const/tender').measureType;
+const status = require('../const/audit').stage.status;
 
 module.exports = app => {
 
@@ -23,7 +24,81 @@ module.exports = app => {
         constructor(ctx) {
             super(ctx);
             ctx.showProject = true;
-        }        
+        }
+
+        async _getTzData(tid, includePos = false) {
+            console.log(tid);
+            const tender = await this.ctx.service.tender.getTender(tid);
+            if (!tender || tender.project_id !== this.ctx.session.sessionProject.id) {
+                throw '不存在该标段';
+            }
+            const bills = await this.ctx.service.ledger.getData(tid);
+            const pos = tender.measure_type === measureType.tz.value || includePos
+                ? await this.ctx.service.pos.getPosData({tid: tid})
+                : [];
+
+            return { id: tid, name: tender.name, bills: bills, pos: pos };
+        }
+
+        async _checkStage(tid, sorder) {
+            const _ = this.ctx.helper._;
+            const stage = await this.service.stage.getDataByCondition({ tid: tid, order: sorder });
+            if (!stage) throw '期数据错误';
+
+            // 读取原报、审核人数据
+            stage.auditors = await this.service.stageAudit.getAuditors(stage.id, stage.times);
+            stage.curAuditor = await this.service.stageAudit.getCurAuditor(stage.id, stage.times);
+            const accountId = this.ctx.session.sessionUser.accountId, auditorIds = _.map(stage.auditors, 'aid'), shareIds = [];
+            const permission = this.ctx.session.sessionUser.permission;
+
+            if (accountId === stage.user_id) { // 原报
+                stage.curTimes = stage.times;
+                if (stage.status === status.uncheck || stage.status === status.checkNo) {
+                    stage.curOrder = 0;
+                } else if (stage.status === status.checked) {
+                    stage.curOrder = _.max(_.map(stage.auditors, 'order'));
+                } else {
+                    stage.curOrder = stage.curAuditor.aid === accountId ? stage.curAuditor.order : stage.curAuditor.order - 1;
+                }
+            } else if (auditorIds.indexOf(accountId) !== -1) { // 审批人
+                if (stage.status === status.uncheck) {
+                    throw '您无权查看该数据';
+                }
+                stage.curTimes = stage.status === status.checkNo ? stage.times - 1 : stage.times;
+                if (stage.status === status.checked) {
+                    stage.curOrder = _.max(_.map(stage.auditors, 'order'));
+                } else if (stage.status === status.checkNo) {
+                    const audit = await this.service.stageAudit.getDataByCondition({
+                        sid: stage.id, times: stage.times - 1, status: status.checkNo
+                    });
+                    stage.curOrder = audit.order;
+                } else {
+                    stage.curOrder = accountId === stage.curAuditor.aid ? stage.curAuditor.order : stage.curAuditor.order - 1;
+                }
+            } else if (shareIds.indexOf(accountId) !== -1 || (permission !== null && permission.tender !== undefined && permission.tender.indexOf('2') !== -1)) { // 分享人
+                if (stage.status === status.uncheck) {
+                    throw '您无权查看该数据';
+                }
+                stage.curTimes = stage.status === status.checkNo ? stage.times - 1 : stage.times;
+                stage.curOrder = stage.status === status.checked ? _.max(_.map(stage.auditors, 'order')) : stage.curAuditor.order - 1;
+            } else { // 其他不可见
+                throw '您无权查看该数据';
+            }
+            return stage;
+        }
+
+        async _getStageData(tid, sorder) {
+            console.log(tid);
+            const data = await this._getTzData(tid, true);
+            const stage = await this._checkStage(tid, sorder);
+            const bills = await this.ctx.service.stageBills.getAuditorStageData(tid, stage.id, stage.curTimes, stage.curOrder);
+            const pos = await this.ctx.service.stagePos.getAuditorStageData(tid, stage.id, stage.curTimes, stage.curOrder);
+            data.stage = {
+                sid: stage.id, sorder: stage.order, curTimes: stage.curTimes, curOrder: stage.curOrder,
+                bills: bills, pos: pos
+            };
+            return data;
+        }
 
         /**
          * 台账 对比 页面
@@ -35,32 +110,38 @@ module.exports = app => {
             try {
                 const renderData = {
                     jsFiles: this.app.jsFiles.common.concat(this.app.jsFiles.compare.tz)
-                }
+                };
                 await this.layout('spss/compare_tz.ejs', renderData, 'spss/compare_tz_modal.ejs');
             } catch (err) {
                 ctx.helper.log(err);
             }
         }
-
+        /**
+         * 获取 台账 对比 数据(Ajax)
+         * @param ctx
+         * @returns {Promise<void>}
+         */
         async loadCompareTz(ctx) {
             try {
                 const data = JSON.parse(ctx.request.body.data);
                 const responseData = {err: 0, msg: '', data: {}};
 
-                const tender1 = await ctx.service.tender.getTender(data.tid1);
-                responseData.data.tender1 = {
-                    name: tender1.name,
-                    bills: await ctx.service.ledger.getData(data.tid1),
-                    pos: tender1.measure_type === measureType.tz.value 
-                        ? await ctx.service.pos.getPosData({tid: data.tid1}) : []             
-                };
-                const tender2 = await ctx.service.tender.getTender(data.tid2);
-                responseData.data.tender2 = {
-                    name: tender2.name,
-                    bills: await ctx.service.ledger.getData(data.tid2),
-                    pos: tender2.measure_type === measureType.tz.value 
-                        ? await ctx.service.pos.getPosData({tid: data.tid2}) : []             
-                };
+                // const tender1 = await ctx.service.tender.getTender(data.tid1);
+                // responseData.data.tender1 = {
+                //     name: tender1.name,
+                //     bills: await ctx.service.ledger.getData(data.tid1),
+                //     pos: tender1.measure_type === measureType.tz.value
+                //         ? await ctx.service.pos.getPosData({tid: data.tid1}) : []
+                // };
+                // const tender2 = await ctx.service.tender.getTender(data.tid2);
+                // responseData.data.tender2 = {
+                //     name: tender2.name,
+                //     bills: await ctx.service.ledger.getData(data.tid2),
+                //     pos: tender2.measure_type === measureType.tz.value
+                //         ? await ctx.service.pos.getPosData({tid: data.tid2}) : []
+                // };
+                responseData.data.tender1 = await this._getTzData(data.tid1);
+                responseData.data.tender2 = await this._getTzData(data.tid2);
                 ctx.body = responseData;
             } catch (err) {
                 ctx.helper.log(err);
@@ -77,11 +158,32 @@ module.exports = app => {
          */
         async compareStage(ctx) {
             try {
-                await this.layout('spss/compare_stage.ejs', {});
+                const renderData = {
+                    jsFiles: this.app.jsFiles.common.concat(this.app.jsFiles.compare.stage)
+                };
+                await this.layout('spss/compare_tz.ejs', renderData, 'spss/compare_stage_modal.ejs');
             } catch (err) {
                 ctx.helper.log(err);
             }
         }
+        /**
+         * 获取 期计量 对比 数据(Ajax)
+         * @param ctx
+         * @returns {Promise<void>}
+         */
+        async loadCompareStage(ctx) {
+            try {
+                const data = JSON.parse(ctx.request.body.data);
+                console.log(data);
+                const responseData = {err: 0, msg: '', data: {}};
+                responseData.data.tender1 = await this._getStageData(data.tid1, data.sorder1);
+                responseData.data.tender2 = await this._getStageData(data.tid2, data.sorder2);
+                ctx.body = responseData;
+            } catch (err) {
+                ctx.helper.log(err);
+                ctx.body = this.ajaxErrorBody(err, '查询数据错误');
+            }
+        }
 
 
         /**
@@ -92,12 +194,33 @@ module.exports = app => {
          */
         async gatherTz(ctx) {
             try {
-                await this.layout('spss/compare_stage.ejs', {});
+                const renderData = {
+                    jsFiles: this.app.jsFiles.common.concat(this.app.jsFiles.gather.tz)
+                };
+                await this.layout('spss/compare_tz.ejs', renderData, 'spss/gather_tz_modal.ejs');
             } catch (err) {
                 ctx.helper.log(err);
             }
         }
+        /**
+         * 获取 台账 汇总 数据(Ajax)
+         * @param ctx
+         * @returns {Promise<void>}
+         */
+        async loadGatherTz(ctx) {
+            try {
+                const data = JSON.parse(ctx.request.body.data);
+                const responseData = {err: 0, msg: '', data: []};
 
+                for (const t of data.tenders) {
+                    responseData.data.push(await this._getTzData(t.id));
+                }
+                ctx.body = responseData;
+            } catch (err) {
+                ctx.helper.log(err);
+                ctx.body = this.ajaxErrorBody(err, '查询数据错误');
+            }
+        }
 
         /**
          * 期计量 汇总 页面
@@ -107,11 +230,33 @@ module.exports = app => {
          */
         async gatherStage(ctx) {
             try {
-                await this.layout('spss/compare_stage.ejs', {});
+                const renderData = {
+                    jsFiles: this.app.jsFiles.common.concat(this.app.jsFiles.gather.stage)
+                };
+                await this.layout('spss/compare_tz.ejs', renderData, 'spss/gather_stage_modal.ejs');
             } catch (err) {
                 ctx.helper.log(err);
             }
         }
+        /**
+         * 获取 期计量 汇总 数据(Ajax)
+         * @param ctx
+         * @returns {Promise<void>}
+         */
+        async loadGatherStage(ctx) {
+            try {
+                const data = JSON.parse(ctx.request.body.data);
+                const responseData = {err: 0, msg: '', data: []};
+
+                for (const t of data.tenders) {
+                    responseData.data.push(await this._getStageData(t.id, t.sorder));
+                }
+                ctx.body = responseData;
+            } catch (err) {
+                ctx.helper.log(err);
+                ctx.body = this.ajaxErrorBody(err, '查询数据错误');
+            }
+        }
     }
 
     return SpssController;

+ 4 - 0
app/controller/stage_controller.js

@@ -1521,6 +1521,10 @@ module.exports = app => {
 
             ctx.body = responseData;
         }
+
+        async differ(ctx) {
+
+        }
     }
 
     return StageController;

+ 237 - 0
app/public/js/compare_stage.js

@@ -0,0 +1,237 @@
+'use strict';
+
+/**
+ *
+ *
+ * @author Mai
+ * @date
+ * @version
+ */
+
+$(document).ready(function () {
+    autoFlashHeight();
+    // 根据设置整理Spread设置
+    const ledgerSpreadSetting = {
+        cols: [
+            { title: '项目节编号', colSpan: '1', rowSpan: '2', field: 'code', hAlign: 0, width: 150, formatter: '@', cellType: 'tree' },
+            { title: '清单编号', colSpan: '1', rowSpan: '2', field: 'b_code', hAlign: 0, width: 80, formatter: '@' },
+            { title: '名称', colSpan: '1', rowSpan: '2', field: 'name', hAlign: 0, width: 230, formatter: '@' },
+            { title: '单位', colSpan: '1', rowSpan: '2', field: 'unit', hAlign: 1, width: 50, formatter: '@', cellType: 'unit' },
+            { title: '单价', colSpan: '1', rowSpan: '2', field: 'unit_price', hAlign: 2, width: 60, type: 'Number' },
+            { title: '标段1|数量', colSpan: '2|1', rowSpan: '1|1', field: 'qty_1', hAlign: 2, width: 60, type: 'Number' },
+            { title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'tp_1', hAlign: 2, width: 60, type: 'Number' },
+            { title: '标段2|数量', colSpan: '2|1', rowSpan: '1|1', field: 'qty_2', hAlign: 2, width: 60, type: 'Number' },
+            { title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'tp_2', hAlign: 2, width: 60, type: 'Number' },
+            { title: '标段1-标段2|数量', colSpan: '2|1', rowSpan: '1|1', field: 'qty_differ', hAlign: 2, width: 60, type: 'Number' },
+            { title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'tp_differ', hAlign: 2, width: 60, type: 'Number' },
+        ],
+        emptyRows: 2,
+        headRows: 2,
+        headRowHeight: [35, 25],
+        defaultRowHeight: 21,
+        headerFont: '12px 微软雅黑',
+        font: '12px 微软雅黑',
+        readOnly: true,
+        getColor: function (sheet, data, row, col, defaultColor) {
+            function checkDiffer(data) {
+                return !checkZero(data.qty_differ) && !checkZero(data.tp_differ);
+            }
+            return data && checkDiffer(data) ? '#F2DEDE' : defaultColor;
+        }
+    };
+    const posSpreadSetting = {
+        cols: [
+            { title: '计量单元', colSpan: '1', rowSpan: '2', field: 'name', hAlign: 0, width: 230, formatter: '@' },
+            { title: '数量|标段1', colSpan: '3|1', rowSpan: '1|1', field: 'qty_1', hAlign: 2, width: 80, type: 'Number' },
+            { title: '|标段2', colSpan: '|1', rowSpan: '|1', field: 'qty_2', hAlign: 2, width: 80, type: 'Number' },
+            { title: '|标段1-标段2', colSpan: '|1', rowSpan: '|1', field: 'qty_differ', hAlign: 2, width: 80, type: 'Number' },
+        ],
+        emptyRows: 3,
+        headRows: 2,
+        headRowHeight: [25, 25],
+        defaultRowHeight: 21,
+        headerFont: '12px 微软雅黑',
+        font: '12px 微软雅黑',
+        readOnly: true,
+        getColor: function (sheet, data, row, col, defaultColor) {
+            function checkDiffer(data) {
+                const fieldSufs = sheet.zh_setting.fieldSufs;
+                if (fieldSufs.length <= 1) return false;
+                const base = data['gather_qty' + fieldSufs[0]];
+                for (let i = 1; i < fieldSufs.length; i++) {
+                    const compare = data['gather_qty' + fieldSufs[i]];
+                    if ((base || compare) && (compare !== base)) return true;
+                }
+            }
+            return checkDiffer(data) ? '#F2DEDE' : defaultColor;
+        }
+    };
+    // 初始化台账
+    const billsSpread = SpreadJsObj.createNewSpread($('#bills-spread')[0]);
+    const billsSheet = billsSpread.getActiveSheet();
+    SpreadJsObj.initSheet(billsSheet, ledgerSpreadSetting);
+    // 初始化部位
+    const posSpread = SpreadJsObj.createNewSpread($('#pos-spread')[0]);
+    const posSheet = posSpread.getActiveSheet();
+    SpreadJsObj.initSheet(posSheet, posSpreadSetting);
+
+    const billsTree = createNewPathTree('compare', {
+        id: 'id',
+        pid: 'pid',
+        order: 'order',
+        level: 'level',
+        rootId: -1,
+        loadInfo1: function (node, source) {
+            node.qty_1 = ZhCalc.add(source.contract_qty, source.qc_qty);
+            node.tp_1 = ZhCalc.add(source.contract_tp, source.qc_tp);
+        },
+        loadInfo2: function (node, source) {
+            node.qty_2 = ZhCalc.add(source.contract_qty, source.qc_qty);
+            node.tp_2 = ZhCalc.add(source.contract_tp, source.qc_tp);
+        },
+        calcDiffer: function (node) {
+            node.qty_differ = ZhCalc.sub(node.qty_1, node.qty_2);
+            node.tp_differ = ZhCalc.sub(node.tp_1, node.tp_2);
+        },
+    });
+
+    // // 获取部位明细数据
+    // function loadPosData(iRow) {
+    //     const node = billsSheet.zh_tree.nodes[iRow];
+    //     if (node) {
+    //         SpreadJsObj.loadSheetData(posSheet, SpreadJsObj.DataType.Data, node.pos);
+    //     } else {
+    //         SpreadJsObj.loadSheetData(posSheet, SpreadJsObj.DataType.Data, []);
+    //     }
+    //     SpreadJsObj.resetTopAndSelect(posSheet);
+    // }
+    // // 切换清单行,读取所属项目节数据
+    // billsSheet.bind(spreadNS.Events.SelectionChanged, function (e, info) {
+    //     if (info.newSelections) {
+    //         const iNewRow = info.newSelections[0].row;
+    //         if (info.oldSelections) {
+    //             const iOldRow = info.oldSelections[0].row;
+    //             if (iNewRow !== iOldRow) {
+    //                 loadPosData(iNewRow);
+    //             }
+    //         } else {
+    //             loadPosData(iNewRow);
+    //         }
+    //     }
+    // });
+
+    // 选择
+    $('#select-ok').click(function () {
+        let data = {};
+        try {
+            data.tid1 = parseInt($('input[name=id1]').val());
+            data.sorder1 = parseInt($('input[name=sorder1]').val());
+            data.tid2 = parseInt($('input[name=id2]').val());
+            data.sorder2 = parseInt($('input[name=sorder2]').val());
+
+            if (data.tid1 > 0 && data.sorder1 > 0 && data.tid2 > 0 && data.sorder2 > 0) {
+                postData(window.location.pathname + '/load', data, function (result) {
+                    // 初始化基础数据分析
+                    const tenderTreeSetting = {
+                        id: 'ledger_id',
+                        pid: 'ledger_pid',
+                        order: 'order',
+                        level: 'level',
+                        rootId: -1,
+                        keys: ['id', 'tender_id', 'ledger_id'],
+                        calcFields: ['deal_tp', 'sgfh_tp', 'sjcl_tp', 'qtcl_tp', 'total_price', 'contract_tp', 'qc_tp'],
+                        stageId: 'id',
+                        updateFields: ['contract_qty', 'contract_tp', 'qc_qty', 'qc_tp'],
+                    };
+                    const tender1 = {
+                        billsTree: createNewPathTree('stage', tenderTreeSetting),
+                    };
+                    tender1.billsTree.loadDatas(result.tender1.bills);
+                    tender1.billsTree.loadCurStageData(result.tender1.stage.bills);
+                    treeCalc.calculateAll(tender1.billsTree);
+                    const col1 = ledgerSpreadSetting.cols.find(function (x) {
+                        return x.field === 'qty_1';
+                    });
+                    col1.title = result.tender1.name + '\n' + '第' + result.tender1.stage.sorder + '期' + '|数量';
+
+                    const tender2 = {
+                        billsTree: createNewPathTree('stage', tenderTreeSetting),
+                    };
+                    tender2.billsTree.loadDatas(result.tender2.bills);
+                    tender2.billsTree.loadCurStageData(result.tender2.stage.bills);
+                    treeCalc.calculateAll(tender2.billsTree);
+                    const col2 = ledgerSpreadSetting.cols.find(function (x) {
+                        return x.field === 'qty_2';
+                    });
+                    col2.title = result.tender2.name + '\n' + '第' + result.tender2.stage.sorder + '期' + '|数量';
+
+                    SpreadJsObj.initSheet(billsSheet, ledgerSpreadSetting);
+                    billsTree.loadCompareData(tender1, tender2);
+                    SpreadJsObj.loadSheetData(billsSheet, SpreadJsObj.DataType.Tree, billsTree);
+                    $('#data-select').modal('hide');
+                });
+            }
+        } catch (err) {
+            toastr.error('输入的标段ID非法');
+        }
+    });
+    // 显示层次
+    (function (select, sheet) {
+        $(select).click(function () {
+            if (!sheet.zh_tree) return;
+            const tag = $(this).attr('tag');
+            const tree = sheet.zh_tree;
+            switch (tag) {
+                case "1":
+                case "2":
+                case "3":
+                case "4":
+                case "5":
+                    tree.expandByLevel(parseInt(tag));
+                    SpreadJsObj.refreshTreeRowVisible(sheet);
+                    break;
+                case "last":
+                    tree.expandByCustom(() => { return true; });
+                    SpreadJsObj.refreshTreeRowVisible(sheet);
+                    break;
+                case "leafXmj":
+                    tree.expandToLeafXmj();
+                    SpreadJsObj.refreshTreeRowVisible(sheet);
+                    break;
+                case "curMeasure":
+                    tree.expandByCalcFields();
+                    SpreadJsObj.refreshTreeRowVisible(sheet);
+                    break;
+            }
+        });
+    })('a[name=showLevel]', billsSheet);
+    // 导航栏
+    $.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();
+            billsSpread.refresh();
+            posSpread.refresh();
+        }
+    });
+    // 上下窗口resizer
+    $.divResizer({
+        select: '#main-resize',
+        callback: function () {
+            billsSpread.refresh();
+            let bcontent = $(".bcontent-wrap").length > 0 ? $(".bcontent-wrap").height() : 0;
+            $(".sp-wrap").height(bcontent - 30);
+            posSpread.refresh();
+        }
+    });
+});

+ 5 - 5
app/public/js/compare_tz.js

@@ -38,7 +38,7 @@ $(document).ready(function () {
             }
             return data && checkDiffer(data) ? '#F2DEDE' : defaultColor;
         }
-    }
+    };
     const posSpreadSetting = {
         cols: [
             { title: '计量单元', colSpan: '1', rowSpan: '2', field: 'name', hAlign: 0, width: 230, formatter: '@' },
@@ -140,7 +140,7 @@ $(document).ready(function () {
     // });
 
     // 选择
-    $('#compare-ok').click(function () {
+    $('#select-ok').click(function () {
         let id1, id2;
         try {
             id1 = parseInt($('input[name=id1]').val());
@@ -165,7 +165,7 @@ $(document).ready(function () {
                     treeCalc.calculateAll(tender1.billsTree);
                     const col1 = ledgerSpreadSetting.cols.find(function (x) {
                         return x.field === 'qty_1';
-                    })
+                    });
                     col1.title = result.tender1.name + '|数量';
 
                     const tender2 = {
@@ -175,13 +175,13 @@ $(document).ready(function () {
                     treeCalc.calculateAll(tender2.billsTree);
                     const col2 = ledgerSpreadSetting.cols.find(function (x) {
                         return x.field === 'qty_2';
-                    })
+                    });
                     col2.title = result.tender2.name + '|数量';
                     
                     SpreadJsObj.initSheet(billsSheet, ledgerSpreadSetting);
                     billsTree.loadCompareData(tender1, tender2);
                     SpreadJsObj.loadSheetData(billsSheet, SpreadJsObj.DataType.Tree, billsTree);
-                    $('#compare-select').modal('hide');
+                    $('#data-select').modal('hide');
                 });
             }
         } catch (err) {

+ 283 - 0
app/public/js/gather_stage.js

@@ -0,0 +1,283 @@
+'use strict';
+
+/**
+ *
+ *
+ * @author Mai
+ * @date
+ * @version
+ */
+
+function initSpreadSetting(settings, gatherTender) {
+    function setSpreadSettingCols(setting, fieldSufs, Roles) {
+        function addExtraCols(fieldSuf, Role) {
+            for (const ec of setting.extraCols) {
+                const col = JSON.parse(JSON.stringify(ec));
+                col.title = _.replace(col.title, '%s', Role);
+                col.field = _.replace(col.field, '%s', fieldSuf);
+                setting.cols.push(col);
+            }
+        }
+        setting.cols = [];
+        for (const col of setting.baseCols) {
+            setting.cols.push(col);
+        }
+        for (const index in fieldSufs) {
+            addExtraCols(fieldSufs[index], Roles[index]);
+        }
+    }
+    const fieldSufs = [], names = [];
+    for (let [i, t] of gatherTender.entries()) {
+        fieldSufs.push((i + 1) + '');
+        names.push(t.name);
+    }
+    fieldSufs.push('sum');
+    names.push('汇总');
+    for (const s of settings) {
+        s.fieldSufs = fieldSufs;
+        setSpreadSettingCols(s, fieldSufs, names);
+    }
+}
+
+$(document).ready(function () {
+    autoFlashHeight();
+    // 根据设置整理Spread设置
+    const ledgerSpreadSetting = {
+        baseCols: [
+            { title: '项目节编号', colSpan: '1', rowSpan: '2', field: 'code', hAlign: 0, width: 150, formatter: '@', cellType: 'tree' },
+            { title: '清单编号', colSpan: '1', rowSpan: '2', field: 'b_code', hAlign: 0, width: 80, formatter: '@' },
+            { title: '名称', colSpan: '1', rowSpan: '2', field: 'name', hAlign: 0, width: 230, formatter: '@' },
+            { title: '单位', colSpan: '1', rowSpan: '2', field: 'unit', hAlign: 1, width: 50, formatter: '@', cellType: 'unit' },
+            { title: '单价', colSpan: '1', rowSpan: '2', field: 'unit_price', hAlign: 2, width: 60, type: 'Number' },
+        ],
+        extraCols: [
+            { title: '%s|数量', colSpan: '2|1', rowSpan: '1|1', field: 'qty_%s', hAlign: 2, width: 60, type: 'Number' },
+            { title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'tp_%s', hAlign: 2, width: 60, type: 'Number' },
+        ],
+        emptyRows: 2,
+        headRows: 2,
+        headRowHeight: [35, 25],
+        defaultRowHeight: 21,
+        headerFont: '12px 微软雅黑',
+        font: '12px 微软雅黑',
+        readOnly: true,
+        getColor: function (sheet, data, row, col, defaultColor) {
+            function checkDiffer(data) {
+                return !checkZero(data.qty_differ) && !checkZero(data.tp_differ);
+            }
+            return data && checkDiffer(data) ? '#F2DEDE' : defaultColor;
+        }
+    };
+    const posSpreadSetting = {
+        baseCols: [
+            { title: '计量单元', colSpan: '1', rowSpan: '2', field: 'name', hAlign: 0, width: 230, formatter: '@' },
+        ],
+        extraCols: [
+            { title: '%s|数量', colSpan: '1|1', rowSpan: '1|1', field: 'qty_%s', hAlign: 2, width: 80, type: 'Number' },
+        ],
+        emptyRows: 3,
+        headRows: 2,
+        headRowHeight: [25, 25],
+        defaultRowHeight: 21,
+        headerFont: '12px 微软雅黑',
+        font: '12px 微软雅黑',
+        readOnly: true,
+        getColor: function (sheet, data, row, col, defaultColor) {
+            function checkDiffer(data) {
+                const fieldSufs = sheet.zh_setting.fieldSufs;
+                if (fieldSufs.length <= 1) return false;
+                const base = data['gather_qty' + fieldSufs[0]];
+                for (let i = 1; i < fieldSufs.length; i++) {
+                    const compare = data['gather_qty' + fieldSufs[i]];
+                    if ((base || compare) && (compare !== base)) return true;
+                }
+            }
+            return checkDiffer(data) ? '#F2DEDE' : defaultColor;
+        }
+    };
+    initSpreadSetting([ledgerSpreadSetting, posSpreadSetting], []);
+    // 初始化台账
+    const billsSpread = SpreadJsObj.createNewSpread($('#bills-spread')[0]);
+    const billsSheet = billsSpread.getActiveSheet();
+    SpreadJsObj.initSheet(billsSheet, ledgerSpreadSetting);
+    // 初始化部位
+    const posSpread = SpreadJsObj.createNewSpread($('#pos-spread')[0]);
+    const posSheet = posSpread.getActiveSheet();
+    SpreadJsObj.initSheet(posSheet, posSpreadSetting);
+
+    const billsTree = createNewPathTree('tree-gather', {
+        id: 'id',
+        pid: 'pid',
+        order: 'order',
+        level: 'level',
+        rootId: -1,
+        loadInfo: function (node, source, index) {
+            const endfix = '_' + index;
+            node['qty' + endfix] = ZhCalc.add(source.contract_qty, source.qc_qty);
+            node['tp' + endfix] = ZhCalc.add(source.contract_tp, source.qc_tp);
+        },
+        calcSum: function (node, count) {
+            node.qty_sum = 0;
+            node.tp_sum = 0;
+            for (let i = 1; i<= count; i++) {
+                node.qty_sum = ZhCalc.add(node.qty_sum, node['qty_' + i]);
+                node.tp_sum = ZhCalc.add(node.tp_sum, node['tp_' + 1]);
+            }
+        },
+    });
+
+    // // 获取部位明细数据
+    // function loadPosData(iRow) {
+    //     const node = billsSheet.zh_tree.nodes[iRow];
+    //     if (node) {
+    //         SpreadJsObj.loadSheetData(posSheet, SpreadJsObj.DataType.Data, node.pos);
+    //     } else {
+    //         SpreadJsObj.loadSheetData(posSheet, SpreadJsObj.DataType.Data, []);
+    //     }
+    //     SpreadJsObj.resetTopAndSelect(posSheet);
+    // }
+    // // 切换清单行,读取所属项目节数据
+    // billsSheet.bind(spreadNS.Events.SelectionChanged, function (e, info) {
+    //     if (info.newSelections) {
+    //         const iNewRow = info.newSelections[0].row;
+    //         if (info.oldSelections) {
+    //             const iOldRow = info.oldSelections[0].row;
+    //             if (iNewRow !== iOldRow) {
+    //                 loadPosData(iNewRow);
+    //             }
+    //         } else {
+    //             loadPosData(iNewRow);
+    //         }
+    //     }
+    // });
+
+    // 选择
+    $('#select-ok').click(function () {
+        let data = {tenders: []};
+        try {
+            const trs = $('tr[tid]');
+            for (const tr of trs) {
+                const id = parseInt($('td[tid]', tr).attr('tid'));
+                const sorder = parseInt($('td[sorder]', tr).attr('sorder'));
+                data.tenders.push({ id: id, sorder: sorder });
+            }
+            if (data.tenders.length === 0) return;
+
+            postData(window.location.pathname + '/load', data, function (result) {
+                initSpreadSetting([ledgerSpreadSetting, posSpreadSetting], result);
+                SpreadJsObj.initSheet(billsSheet, ledgerSpreadSetting);
+                const tenderDatas = [];
+                const tenderTreeSetting = {
+                    id: 'ledger_id',
+                    pid: 'ledger_pid',
+                    order: 'order',
+                    level: 'level',
+                    rootId: -1,
+                    keys: ['id', 'tender_id', 'ledger_id'],
+                    calcFields: ['deal_tp', 'sgfh_tp', 'sjcl_tp', 'qtcl_tp', 'total_price', 'contract_tp', 'qc_tp'],
+                    stageId: 'id',
+                    updateFields: ['contract_qty', 'contract_tp', 'qc_qty', 'qc_tp'],
+                };
+                for (const [i, tender] of result.entries()) {
+                    const tenderData = {
+                        billsTree: createNewPathTree('stage', tenderTreeSetting),
+                    };
+                    tenderData.billsTree.loadDatas(tender.bills);
+                    tenderData.billsTree.loadCurStageData(tender.stage.bills);
+                    treeCalc.calculateAll(tenderData.billsTree);
+                    tenderDatas.push(tenderData);
+                }
+                billsTree.loadGatherData(tenderDatas);
+                SpreadJsObj.loadSheetData(billsSheet, SpreadJsObj.DataType.Tree, billsTree);
+                $('#data-select').modal('hide');
+            });
+        } catch (err) {
+            toastr.error('输入的标段ID非法');
+        }
+    });
+    // 显示层次
+    (function (select, sheet) {
+        $(select).click(function () {
+            if (!sheet.zh_tree) return;
+            const tag = $(this).attr('tag');
+            const tree = sheet.zh_tree;
+            switch (tag) {
+                case "1":
+                case "2":
+                case "3":
+                case "4":
+                case "5":
+                    tree.expandByLevel(parseInt(tag));
+                    SpreadJsObj.refreshTreeRowVisible(sheet);
+                    break;
+                case "last":
+                    tree.expandByCustom(() => { return true; });
+                    SpreadJsObj.refreshTreeRowVisible(sheet);
+                    break;
+                case "leafXmj":
+                    tree.expandToLeafXmj();
+                    SpreadJsObj.refreshTreeRowVisible(sheet);
+                    break;
+                case "curMeasure":
+                    tree.expandByCalcFields();
+                    SpreadJsObj.refreshTreeRowVisible(sheet);
+                    break;
+            }
+        });
+    })('a[name=showLevel]', billsSheet);
+    // 导航栏
+    $.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();
+            billsSpread.refresh();
+            posSpread.refresh();
+        }
+    });
+    // 上下窗口resizer
+    $.divResizer({
+        select: '#main-resize',
+        callback: function () {
+            billsSpread.refresh();
+            let bcontent = $(".bcontent-wrap").length > 0 ? $(".bcontent-wrap").height() : 0;
+            $(".sp-wrap").height(bcontent - 30);
+            posSpread.refresh();
+        }
+    });
+    $('#add-t').click(function () {
+        const id = $('input[name=tid]').val();
+        if (!id || id === '') {
+            toastr.warning('请先输入标段id,再添加');
+            return;
+        }
+        const tr = $('tr[tid=' + id + ']');
+        if (tr && tr.length > 0) {
+            toastr.warning('请勿重复添加同一个标段');
+            return;
+        }
+        const sorder = $('input[name= sorder]').val();
+        if (!sorder || sorder === '') {
+            toastr.warning('请先输入期序号,再添加');
+            return;
+        }
+        const html = '<tr tid="' + id + '">' +
+            '<td tid="' + id + '">' + id + '</td>' +
+            '<td sorder="' + sorder + '">' + sorder + '</td>' +
+            '<td><a href="javascript: void(0);" name="delete-t" class="btn btn-sm" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="删除"><i class="fa fa-remove text-danger" aria-hidden="true"></i></a></td>' +
+            '</tr>';
+        $('tbody').append(html);
+    });
+    $('body').on('click', 'a[name=delete-t]', function () {
+        $(this).parent().parent().remove();
+    });
+});

+ 269 - 0
app/public/js/gather_tz.js

@@ -0,0 +1,269 @@
+'use strict';
+
+/**
+ *
+ *
+ * @author Mai
+ * @date
+ * @version
+ */
+
+function initSpreadSetting(settings, gatherTender) {
+    function setSpreadSettingCols(setting, fieldSufs, Roles) {
+        function addExtraCols(fieldSuf, Role) {
+            for (const ec of setting.extraCols) {
+                const col = JSON.parse(JSON.stringify(ec));
+                col.title = _.replace(col.title, '%s', Role);
+                col.field = _.replace(col.field, '%s', fieldSuf);
+                setting.cols.push(col);
+            }
+        }
+        setting.cols = [];
+        for (const col of setting.baseCols) {
+            setting.cols.push(col);
+        }
+        for (const index in fieldSufs) {
+            addExtraCols(fieldSufs[index], Roles[index]);
+        }
+    }
+    const fieldSufs = [], names = [];
+    for (let [i, t] of gatherTender.entries()) {
+        fieldSufs.push((i + 1) + '');
+        names.push(t.name);
+    }
+    fieldSufs.push('s');
+    names.push('汇总');
+    for (const s of settings) {
+        s.fieldSufs = fieldSufs;
+        setSpreadSettingCols(s, fieldSufs, names);
+    }
+}
+
+$(document).ready(function () {
+    autoFlashHeight();
+    // 根据设置整理Spread设置
+    const ledgerSpreadSetting = {
+        baseCols: [
+            { title: '项目节编号', colSpan: '1', rowSpan: '2', field: 'code', hAlign: 0, width: 150, formatter: '@', cellType: 'tree' },
+            { title: '清单编号', colSpan: '1', rowSpan: '2', field: 'b_code', hAlign: 0, width: 80, formatter: '@' },
+            { title: '名称', colSpan: '1', rowSpan: '2', field: 'name', hAlign: 0, width: 230, formatter: '@' },
+            { title: '单位', colSpan: '1', rowSpan: '2', field: 'unit', hAlign: 1, width: 50, formatter: '@', cellType: 'unit' },
+            { title: '单价', colSpan: '1', rowSpan: '2', field: 'unit_price', hAlign: 2, width: 60, type: 'Number' },
+        ],
+        extraCols: [
+            { title: '%s|数量', colSpan: '2|1', rowSpan: '1|1', field: 'qty_%s', hAlign: 2, width: 60, type: 'Number' },
+            { title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'tp_%s', hAlign: 2, width: 60, type: 'Number' },
+        ],
+        emptyRows: 2,
+        headRows: 2,
+        headRowHeight: [35, 25],
+        defaultRowHeight: 21,
+        headerFont: '12px 微软雅黑',
+        font: '12px 微软雅黑',
+        readOnly: true,
+        getColor: function (sheet, data, row, col, defaultColor) {
+            function checkDiffer(data) {
+                return !checkZero(data.qty_differ) && !checkZero(data.tp_differ);
+            }
+            return data && checkDiffer(data) ? '#F2DEDE' : defaultColor;
+        }
+    };
+    const posSpreadSetting = {
+        baseCols: [
+            { title: '计量单元', colSpan: '1', rowSpan: '2', field: 'name', hAlign: 0, width: 230, formatter: '@' },
+        ],
+        extraCols: [
+            { title: '%s|数量', colSpan: '1|1', rowSpan: '1|1', field: 'qty_%s', hAlign: 2, width: 80, type: 'Number' },
+        ],
+        emptyRows: 3,
+        headRows: 2,
+        headRowHeight: [25, 25],
+        defaultRowHeight: 21,
+        headerFont: '12px 微软雅黑',
+        font: '12px 微软雅黑',
+        readOnly: true,
+        getColor: function (sheet, data, row, col, defaultColor) {
+            function checkDiffer(data) {
+                const fieldSufs = sheet.zh_setting.fieldSufs;
+                if (fieldSufs.length <= 1) return false;
+                const base = data['gather_qty' + fieldSufs[0]];
+                for (let i = 1; i < fieldSufs.length; i++) {
+                    const compare = data['gather_qty' + fieldSufs[i]];
+                    if ((base || compare) && (compare !== base)) return true;
+                }
+            }
+            return checkDiffer(data) ? '#F2DEDE' : defaultColor;
+        }
+    };
+    initSpreadSetting([ledgerSpreadSetting, posSpreadSetting], []);
+    // 初始化台账
+    const billsSpread = SpreadJsObj.createNewSpread($('#bills-spread')[0]);
+    const billsSheet = billsSpread.getActiveSheet();
+    SpreadJsObj.initSheet(billsSheet, ledgerSpreadSetting);
+    // 初始化部位
+    const posSpread = SpreadJsObj.createNewSpread($('#pos-spread')[0]);
+    const posSheet = posSpread.getActiveSheet();
+    SpreadJsObj.initSheet(posSheet, posSpreadSetting);
+
+    const billsTree = createNewPathTree('tree-gather', {
+        id: 'id',
+        pid: 'pid',
+        order: 'order',
+        level: 'level',
+        rootId: -1,
+        loadInfo: function (node, source, index) {
+            const endfix = '_' + index;
+            node['qty' + endfix] = source.quantity;
+            node['tp' + endfix] = source.total_price;
+        },
+        calcGather: function (node, count) {
+            node.qty_differ = 0;
+            for (let i = 1; i<= count; i++) {
+                node.qty_sum = ZhCalc.add(node.qty_sum, node['qty_' + i]);
+                node.tp_sum = ZhCalc.add(node.tp_sum, node['tp_' + 1]);
+            }
+        },
+    });
+
+    // // 获取部位明细数据
+    // function loadPosData(iRow) {
+    //     const node = billsSheet.zh_tree.nodes[iRow];
+    //     if (node) {
+    //         SpreadJsObj.loadSheetData(posSheet, SpreadJsObj.DataType.Data, node.pos);
+    //     } else {
+    //         SpreadJsObj.loadSheetData(posSheet, SpreadJsObj.DataType.Data, []);
+    //     }
+    //     SpreadJsObj.resetTopAndSelect(posSheet);
+    // }
+    // // 切换清单行,读取所属项目节数据
+    // billsSheet.bind(spreadNS.Events.SelectionChanged, function (e, info) {
+    //     if (info.newSelections) {
+    //         const iNewRow = info.newSelections[0].row;
+    //         if (info.oldSelections) {
+    //             const iOldRow = info.oldSelections[0].row;
+    //             if (iNewRow !== iOldRow) {
+    //                 loadPosData(iNewRow);
+    //             }
+    //         } else {
+    //             loadPosData(iNewRow);
+    //         }
+    //     }
+    // });
+
+    // 选择
+    $('#select-ok').click(function () {
+        let data = {tenders: []};
+        try {
+            const trs = $('tr[tid]');
+            for (const tr of trs) {
+                data.tenders.push({
+                    id: parseInt(tr.attributes['tid'].value)
+                });
+            }
+            if (data.tenders.length === 0) return;
+
+            postData(window.location.pathname + '/load', data, function (result) {
+                initSpreadSetting([ledgerSpreadSetting, posSpreadSetting], result);
+                SpreadJsObj.initSheet(billsSheet, ledgerSpreadSetting);
+                const tenderDatas = [];
+                const tenderTreeSetting = {
+                    id: 'ledger_id',
+                    pid: 'ledger_pid',
+                    order: 'order',
+                    level: 'level',
+                    rootId: -1,
+                    keys: ['id', 'tender_id', 'ledger_id'],
+                    calcFields: ['deal_tp', 'sgfh_tp', 'sjcl_tp', 'qtcl_tp', 'total_price'],
+                };
+                for (const [i, tender] of result.entries()) {
+                    const tenderData = {
+                        billsTree: createNewPathTree('ledger', tenderTreeSetting),
+                    };
+                    tenderData.billsTree.loadDatas(tender.bills);
+                    treeCalc.calculateAll(tenderData.billsTree);
+                    tenderDatas.push(tenderData);
+                }
+                billsTree.loadGatherData(tenderDatas);
+                SpreadJsObj.loadSheetData(billsSheet, SpreadJsObj.DataType.Tree, billsTree);
+                $('#data-select').modal('hide');
+            });
+        } catch (err) {
+            toastr.error('输入的标段ID非法');
+        }
+    });
+    // 显示层次
+    (function (select, sheet) {
+        $(select).click(function () {
+            if (!sheet.zh_tree) return;
+            const tag = $(this).attr('tag');
+            const tree = sheet.zh_tree;
+            switch (tag) {
+                case "1":
+                case "2":
+                case "3":
+                case "4":
+                case "5":
+                    tree.expandByLevel(parseInt(tag));
+                    SpreadJsObj.refreshTreeRowVisible(sheet);
+                    break;
+                case "last":
+                    tree.expandByCustom(() => { return true; });
+                    SpreadJsObj.refreshTreeRowVisible(sheet);
+                    break;
+                case "leafXmj":
+                    tree.expandToLeafXmj();
+                    SpreadJsObj.refreshTreeRowVisible(sheet);
+                    break;
+                case "curMeasure":
+                    tree.expandByCalcFields();
+                    SpreadJsObj.refreshTreeRowVisible(sheet);
+                    break;
+            }
+        });
+    })('a[name=showLevel]', billsSheet);
+    // 导航栏
+    $.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();
+            billsSpread.refresh();
+            posSpread.refresh();
+        }
+    });
+    // 上下窗口resizer
+    $.divResizer({
+        select: '#main-resize',
+        callback: function () {
+            billsSpread.refresh();
+            let bcontent = $(".bcontent-wrap").length > 0 ? $(".bcontent-wrap").height() : 0;
+            $(".sp-wrap").height(bcontent - 30);
+            posSpread.refresh();
+        }
+    });
+    $('#add-t').click(function () {
+        const id = $('input[name=tid]').val();
+        const tr = $('tr[tid=' + id + ']');
+        if (tr && tr.length > 0) {
+            toastr.warning('请勿重复添加同一个标段');
+            return;
+        }
+        const html = '<tr tid="' + id + '">' +
+            '<td>' + id + '</td>' +
+            '<td><a href="javascript: void(0);" name="delete-t" class="btn btn-sm" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="删除"><i class="fa fa-remove text-danger" aria-hidden="true"></i></a></td>' +
+            '</tr>';
+        $('tbody').append(html);
+    });
+    $('body').on('click', 'a[name=delete-t]', function () {
+        $(this).parent().parent().remove();
+    });
+});

+ 86 - 0
app/public/js/path_tree.js

@@ -1446,6 +1446,90 @@ const createNewPathTree = function (type, setting) {
         }
     }
 
+    class TreeGatherTree extends FxTree {
+
+        constructor(setting) {
+            super(setting);
+            this._newId = 1;
+        }
+
+        get newId() {
+            return this._newId++;
+        }
+
+        loadGatherNode(node, parent, index, loadFun) {
+            const siblings = parent ? parent.children : this.children;
+            let cur = siblings.find(function (x) {
+                return node.b_code
+                    ? x.b_code === node.b_code && x.name === node.name && x.unit === node.unit && x.unit_price === node.unit_price
+                    : x.code === node.code && x.name === node.name;
+            });
+            if (!cur) {
+                const id = this.newId;
+                cur = {
+                    id: id,
+                    pid: parent ? parent.id : this.setting.rootId,
+                    full_path: parent ? parent.full_path + '-' + id : '' + id,
+                    level: parent ? parent.level + 1 : 1,
+                    order: siblings.length + 1,
+                    children: [],
+                    code: node.code, b_code: node.b_code, name: node.name,
+                    unit: node.unit, unit_price: node.unit_price,
+                };
+                siblings.push(cur);
+                this.datas.push(cur);
+            }
+            loadFun(cur, node, index);
+            for (const c of node.children) {
+                this.loadGatherNode(c, cur, index, loadFun);
+            }
+        }
+
+        generateSortNodes() {
+            const self = this;
+            const addSortNode = function (node) {
+                self.nodes.push(node);
+                for (const c of node.children) {
+                    addSortNode(c);
+                }
+            }
+            this.nodes = [];
+            for (const n of this.children) {
+                addSortNode(n);
+            }
+        }
+
+        loadGatherTree(data, index, loadFun) {
+            for (const c of data.billsTree.children) {
+                this.loadGatherNode(c, null, index, loadFun);
+            }
+            // todo load Pos Data;
+        }
+
+        calculateSum() {
+            if (this.setting.calcSum) {
+                for (const d of this.datas) {
+                    this.setting.calcSum(d, this.count);
+                }
+            }
+        }
+
+        loadGatherData(datas) {
+            this.count = datas.length;
+            for (const [i, data] of datas.entries()) {
+                this.loadGatherTree(data, i+1, this.setting.loadInfo);
+            }
+            for (const d of this.datas) {
+                d.is_leaf = d.children.length === 0;
+                d.expanded = true;
+                d.visible = true;
+                this.items[itemsPre + d[this.setting.id]] = d;
+            }
+            this.generateSortNodes();
+            this.calculateSum();
+        }
+    }
+
     if (type === 'base') {
         return new BaseTree(setting);
     } else if (type === 'fx') {
@@ -1466,6 +1550,8 @@ const createNewPathTree = function (type, setting) {
         return new GatherTree(setting);
     } else if (type === 'compare') {
         return new CompareTree(setting);
+    } else if (type === 'tree-gather') {
+        return new TreeGatherTree(setting);
     }
 };
 

+ 3 - 0
app/router.js

@@ -287,6 +287,9 @@ module.exports = app => {
     app.get('/compare/tz', sessionAuth, 'spssController.compareTz');
     app.post('/compare/tz/load', sessionAuth, 'spssController.loadCompareTz');
     app.get('/compare/stage', sessionAuth, 'spssController.compareStage');
+    app.post('/compare/stage/load', sessionAuth, 'spssController.loadCompareStage');
     app.get('/gather/tz', sessionAuth, 'spssController.gatherTz');
+    app.post('/gather/tz/load', sessionAuth, 'spssController.loadGatherTz');
     app.get('/gather/stage', sessionAuth, 'spssController.gatherStage');
+    app.post('/gather/stage/load', sessionAuth, 'spssController.loadGatherStage');
 };

+ 0 - 16
app/view/spss/compare_stage.ejs

@@ -1,16 +0,0 @@
-<% 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="content-wrap">
-        <div class="c-header p-0"></div>
-        <div class="c-body">
-            暂无该功能...<br>
-            会不会做,什么时候做...<br>
-            小编太忙了,如急需,请联系...<br>
-        </div>
-    </div>
-</div>

+ 38 - 0
app/view/spss/compare_stage_modal.ejs

@@ -0,0 +1,38 @@
+<!--弹出对比标段-->
+<div class="modal fade" id="data-select" data-backdrop="static">
+    <div class="modal-dialog" role="document">
+        <div class="modal-content">
+            <div class="modal-header">
+                <h5 class="modal-title">选择对比标段</h5>
+            </div>
+            <div class="modal-body">
+                <div class="form-group">
+                    <label>对比标段1<b class="text-danger">*</b></label>
+                    <div class="row ml-1 mr-1">
+                        <div class="col-6">
+                            <input class="form-control form-control-sm"  placeholder="输入标段1的ID" type="text" name="id1">
+                        </div>
+                        <div class="col-6">
+                            <input class="form-control form-control-sm"  placeholder="输入标段1的对比期序号" type="text" name="sorder1">
+                        </div>
+                    </div>
+                </div>
+                <div class="form-group">
+                    <label>对比标段2<b class="text-danger">*</b></label>
+                    <div class="row ml-1 mr-1">
+                        <div class="col-6">
+                            <input class="form-control form-control-sm"  placeholder="输入标段2的ID" type="text" name="id2">
+                        </div>
+                        <div class="col-6">
+                            <input class="form-control form-control-sm"  placeholder="输入标段2的对比期序号" type="text" name="sorder2">
+                        </div>
+                    </div>
+                </div>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-secondary btn-sm" data-dismiss="modal">关闭</button>
+                <button type="button" class="btn btn-primary btn-sm" id="select-ok">确定</button>
+            </div>
+        </div>
+    </div>
+</div>

+ 3 - 1
app/view/spss/compare_tz.ejs

@@ -21,7 +21,9 @@
                     </div>
                 </div>
                 <div class="d-inline-block">
-                    <button href="#compare-select" class="btn btn-sm btn-light text-primary" data-toggle="modal" data-target="#compare-select"><i class="fa fa-clone"></i> 选择比较标段</button>
+                    <button href="#data-select" class="btn btn-sm btn-light text-primary" data-toggle="modal" data-target="#data-select"><i class="fa fa-clone"></i>
+                        选择<%- ctx.url.indexOf('compare') > 0 ? '对比' : '汇总' %>标段
+                    </button>
                 </div>
             </div>
             <div class="ml-auto"></div>

+ 2 - 4
app/view/spss/compare_tz_modal.ejs

@@ -1,5 +1,5 @@
 <!--弹出对比标段-->
-<div class="modal fade" id="compare-select" data-backdrop="static">
+<div class="modal fade" id="data-select" data-backdrop="static">
     <div class="modal-dialog" role="document">
         <div class="modal-content">
             <div class="modal-header">
@@ -9,17 +9,15 @@
                 <div class="form-group">
                     <label>对比标段1<b class="text-danger">*</b></label>
                     <input class="form-control form-control-sm"  placeholder="输入标段1的ID" type="text" name="id1">
-                    <input class="form-control form-control-sm"  placeholder="输入标段1的对比期序号" type="text" name="stage1" style="display: none;">
                 </div>               
                 <div class="form-group">
                     <label>对比标段2<b class="text-danger">*</b></label>
                     <input class="form-control form-control-sm"  placeholder="输入标段2的ID" type="text" name="id2">
-                    <input class="form-control form-control-sm"  placeholder="输入标段2的对比期序号" type="text" name="stage1" style="display: none;">
                 </div>               
             </div>
             <div class="modal-footer">
                 <button type="button" class="btn btn-secondary btn-sm" data-dismiss="modal">关闭</button>
-                <button type="button" class="btn btn-primary btn-sm" id="compare-ok">确定</button>
+                <button type="button" class="btn btn-primary btn-sm" id="select-ok">确定</button>
             </div>
         </div>
     </div>

+ 35 - 0
app/view/spss/gather_stage_modal.ejs

@@ -0,0 +1,35 @@
+<!--弹出汇总标段-->
+<div class="modal fade" id="data-select" data-backdrop="static">
+    <div class="modal-dialog" role="document">
+        <div class="modal-content">
+            <div class="modal-header">
+                <h5 class="modal-title">选择汇总标段</h5>
+            </div>
+            <div class="modal-body">
+                <div class="form-group">
+                    <div class="row">
+                        <div class="col-10 d-inline-flex">
+                            <input class="form-control form-control-sm"  placeholder="输入需要汇总的标段ID" type="text" name="tid">
+                            <input class="form-control form-control-sm ml-1"  placeholder="输入需要汇总的标段的期序号" type="text" name="sorder">
+                            <a href="javascript: void(0);" id="add-t" class="btn btn-sm" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="新增"><i class="fa fa-plus text-primary" aria-hidden="true"></i></a>
+                        </div>
+                    </div>
+                </div>
+                <div class="form-group">
+                    <label>汇总标段列表<b class="text-danger">*</b></label>
+                    <table class="table table-bordered table-sm mt-1">
+                        <thead>
+                        <th style="width: 40%">标段id</th><th style="width: 40%">第几期</th><th></th>
+                        </thead>
+                        <tbody>
+                        </tbody>
+                    </table>
+                </div>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-secondary btn-sm" data-dismiss="modal">关闭</button>
+                <button type="button" class="btn btn-primary btn-sm" id="select-ok">确定</button>
+            </div>
+        </div>
+    </div>
+</div>

+ 34 - 0
app/view/spss/gather_tz_modal.ejs

@@ -0,0 +1,34 @@
+<!--弹出汇总标段-->
+<div class="modal fade" id="data-select" data-backdrop="static">
+    <div class="modal-dialog" role="document">
+        <div class="modal-content">
+            <div class="modal-header">
+                <h5 class="modal-title">选择汇总标段</h5>
+            </div>
+            <div class="modal-body">
+                <div class="form-group">
+                    <div class="row">
+                        <div class="col-6 d-inline-flex">
+                            <input class="form-control form-control-sm"  placeholder="输入需要汇总的标段ID" type="text" name="tid">
+                            <a href="javascript: void(0);" id="add-t" class="btn btn-sm" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="新增"><i class="fa fa-plus text-primary" aria-hidden="true"></i></a>
+                        </div>
+                    </div>
+                </div>
+                <div class="form-group">
+                    <label>汇总标段列表<b class="text-danger">*</b></label>
+                    <table class="table table-bordered table-sm mt-1">
+                        <thead>
+                        <th style="width: 80%">标段id</th><th></th>
+                        </thead>
+                        <tbody>
+                        </tbody>
+                    </table>
+                </div>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-secondary btn-sm" data-dismiss="modal">关闭</button>
+                <button type="button" class="btn btn-primary btn-sm" id="select-ok">确定</button>
+            </div>
+        </div>
+    </div>
+</div>

+ 50 - 0
config/web.js

@@ -482,6 +482,56 @@ const JsFiles = {
                 ],
                 mergeFile: 'compare_tz',
             },
+            stage: {
+                files: [
+                    "/public/js/spreadjs/sheets/v11/gc.spread.sheets.all.11.2.2.min.js",
+                    "/public/js/decimal.min.js",
+                ],
+                mergeFiles: [
+                    "/public/js/sub_menu.js",
+                    "/public/js/div_resizer.js",
+                    "/public/js/spreadjs_rela/spreadjs_zh.js",
+                    "/public/js/shares/sjs_setting.js",
+                    "/public/js/zh_calc.js",
+                    "/public/js/path_tree.js",
+                    "/public/js/compare_stage.js",
+                ],
+                mergeFile: 'compare_stage',
+            },
+        },
+        gather: {
+            tz: {
+                files: [
+                    "/public/js/spreadjs/sheets/v11/gc.spread.sheets.all.11.2.2.min.js",
+                    "/public/js/decimal.min.js",
+                ],
+                mergeFiles: [
+                    "/public/js/sub_menu.js",
+                    "/public/js/div_resizer.js",
+                    "/public/js/spreadjs_rela/spreadjs_zh.js",
+                    "/public/js/shares/sjs_setting.js",
+                    "/public/js/zh_calc.js",
+                    "/public/js/path_tree.js",
+                    "/public/js/gather_tz.js",
+                ],
+                mergeFile: 'gather_tz',
+            },
+            stage: {
+                files: [
+                    "/public/js/spreadjs/sheets/v11/gc.spread.sheets.all.11.2.2.min.js",
+                    "/public/js/decimal.min.js",
+                ],
+                mergeFiles: [
+                    "/public/js/sub_menu.js",
+                    "/public/js/div_resizer.js",
+                    "/public/js/spreadjs_rela/spreadjs_zh.js",
+                    "/public/js/shares/sjs_setting.js",
+                    "/public/js/zh_calc.js",
+                    "/public/js/path_tree.js",
+                    "/public/js/gather_stage.js",
+                ],
+                mergeFile: 'gather_stage',
+            },
         }
     }