Browse Source

台账修订对比1.0

MaiXinRong 4 years ago
parent
commit
d4d4f25e98

+ 63 - 0
app/controller/revise_controller.js

@@ -889,6 +889,69 @@ module.exports = app => {
                 ctx.redirect('/tender/' + ctx.tender.id + '/revise/info');
             }
         }
+
+        async compare(ctx) {
+            const revise = await ctx.service.ledgerRevise.getLastestRevise(ctx.tender.id);
+            if (!revise) throw '台账修订数据有误';
+
+            const renderData = {
+                revise,
+                measureType,
+                jsFiles: this.app.jsFiles.common.concat(this.app.jsFiles.revise.compare),
+            };
+            await this.layout('revise/compare.ejs', renderData);
+        }
+
+        async _loadDataByFilter(ctx, filter) {
+            switch(filter) {
+                case 'bills': return await ctx.service.ledger.getAllDataByCondition({where: {tender_id: ctx.tender.id} });
+                case 'pos': return await ctx.service.pos.getAllDataByCondition({where: {tid: ctx.tender.id} });
+                case 'reviseBills': return await ctx.service.reviseBills.getAllDataByCondition({where: {tender_id: ctx.tender.id}});
+                case 'revisePos': return await ctx.service.revisePos.getAllDataByCondition({where: {tid: ctx.tender.id}});
+            }
+        }
+
+        async loadData(ctx) {
+            try {
+                const revise = await ctx.service.ledgerRevise.getLastestRevise(ctx.tender.id);
+                if (!revise) throw '台账修订数据有误';
+
+                const data = JSON.parse(ctx.request.body.data);
+                const filter = data.filter ? data.filter.split(';') : [];
+
+                const responseData = { err: 0, msg: '', data: {} };
+                for (const f of filter) {
+                    if (!f) continue;
+                    responseData.data[f] = await this._loadDataByFilter(ctx, f);
+                }
+                ctx.body = responseData;
+            } catch (err) {
+                this.ctx.helper.log(err);
+                this.ajaxErrorBody(err, '加载数据错误,请刷新页面重试');
+            }
+        }
+
+        async gather(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),
+            };
+            await this.layout('revise/gather.ejs', renderData);
+        }
+
+        async bwtz(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.bwtz),
+            };
+            await this.layout('revise/bwtz.ejs', renderData);
+        }
     }
 
     return ReviseController;

+ 21 - 11
app/public/js/path_tree.js

@@ -1460,25 +1460,34 @@ const createNewPathTree = function (type, setting) {
             return this._newId++;
         }
 
+        findCompareNode(node, parent) {
+            if (this.setting.findNode) {
+                return this.setting.findNode(this, node, parent);
+            } else {
+                const siblings = parent ? parent.children : this.children;
+                return 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;
+                });
+            }
+        }
+
         loadCompareNode(node, parent, 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;
-            });
+            let cur = this.findCompareNode(node, parent);
             if (!cur) {
+                const siblings = parent ? parent.children : this.children;
                 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,
                 };
+                cur[this.setting.id] = id;
+                cur[this.setting.pid] = parent ? parent[this.setting.id] : this.setting.rootId;
+                cur[this.setting.full_path] = parent ? parent[this.setting.full_path] + '-' + id : '' + id;
+                cur[this.setting.level] = parent ? parent[this.setting.level] + 1 : 1;
+                cur[this.setting.order] = siblings.length + 1;
                 siblings.push(cur);
                 this.datas.push(cur);
             }
@@ -1528,6 +1537,7 @@ const createNewPathTree = function (type, setting) {
             }
             this.generateSortNodes();
             this.calculateDiffer();
+            if (this.setting.afterLoad) this.setting.afterLoad(this);
         }
     }
 

+ 320 - 0
app/public/js/revise_compare.js

@@ -0,0 +1,320 @@
+'use strict';
+
+/**
+ * 台账修订页面js
+ *
+ * @author Mai
+ * @date
+ * @version
+ */
+
+const billsCompareField = [
+    'code', 'b_code', 'name', 'unit', 'unit_price', 'dgn_qty1', 'dgn_qty2', 'dgn_price',
+    'sgfh_qty', 'sgfh_tp', 'sjcl_qty', 'sjcl_tp', 'qtcl_qty', 'qtcl_tp', 'quantity', 'total_price',
+    'deal_qty', 'deal_tp'
+];
+const posCompareField = [
+    'name', 'position', 'sgfh_qty', 'sjcl_qty', 'qtcl_qty', 'quantity'
+];
+const compareColor = {
+    add: '#D9EDF7'
+}
+
+$(document).ready(() => {
+    let searchLedger;
+    autoFlashHeight();
+    // 初始化spread
+    const billsSpread = SpreadJsObj.createNewSpread($('#bills-spread')[0]);
+    const billsSheet = billsSpread.getActiveSheet();
+    sjsSettingObj.setFxTreeStyle(billsSpreadSetting, sjsSettingObj.FxTreeStyle.jz);
+    if (thousandth) sjsSettingObj.setTpThousandthFormat(billsSpreadSetting);
+    billsSpreadSetting.getColor = function (sheet, data, row, col, defaultColor) {
+        // 增
+        if (data.differ.indexOf('add') >= 0) return '#cce5ff';
+        // 删
+        if (data.differ.indexOf('del') >= 0) return '#DCDCDC';
+        // 结构变动
+        if (data.differ.indexOf('tree') >= 0) return '#d0f6fd';
+        // 修改计算或文字
+        if (data.differ.indexOf('calc') >= 0) return '#f8d7da';
+        if (data.differ.indexOf('info') >= 0) return '#d4edda';
+        // 层次结构
+        if (data.level === 2) {
+            return '#C4CAFB';
+        } else if ((!data.b_code || data.b_code === '') && data.level > 2) {
+            return '#DFE8F9';
+        } else {
+            return defaultColor;
+        }
+    };
+    SpreadJsObj.initSheet(billsSheet, billsSpreadSetting);
+
+    const posSpread = SpreadJsObj.createNewSpread($('#pos-spread')[0]);
+    const posSheet = posSpread.getActiveSheet();
+    sjsSettingObj.setGridSelectStyle(posSpreadSetting);
+    if (thousandth) sjsSettingObj.setTpThousandthFormat(posSpreadSetting);
+    SpreadJsObj.initSheet(posSheet, posSpreadSetting);
+
+    const posSearch = $.posSearch({selector: '#pos-search', searchSpread: posSpread});
+
+    // 初始化 节点树结构
+    const treeSetting = {
+        id: 'ledger_id',
+        pid: 'ledger_pid',
+        order: 'order',
+        level: 'level',
+        full_path: 'full_path',
+        rootId: -1,
+        keys: ['id', 'tender_id', 'ledger_id'],
+        calcFields: ['sgfh_tp', 'sjcl_tp', 'qtcl_tp', 'total_price'],
+        findNode: function (tree, node, parent) {
+            const sameId = tree.datas.find(x => {return x.id === node.id});
+            if (sameId) {
+                sameId.org_pid = parent ? parent[tree.setting.id] : tree.setting.rootId;
+                if (sameId[tree.setting.pid] !== sameId.org_pid) {
+                    sameId.changeParent = true;
+                }
+                return sameId;
+            } else {
+                const siblings = parent ? parent.children : tree.children;
+                return 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;
+                });
+            }
+        },
+        loadInfo1: function (node, source) {
+            for (const f of billsCompareField) {
+                node['new_' + f] = source[f];
+            }
+            node.id = source.id;
+            node.isNew = true;
+        },
+        loadInfo2: function (node, source) {
+            for (const f of billsCompareField) {
+                node['org_' + f] = source[f];
+            }
+            node.isOrg = true;
+        },
+        afterLoad: function (tree) {
+            for (const data of tree.datas) {
+                data.differ = [];
+                data.differ_str = [];
+                if (data.isNew && !data.isOrg) {
+                    data.differ.push('add');
+                    data.differ_str.push('增');
+                } else if (!data.isNew && data.isOrg) {
+                    data.differ.push('del');
+                    data.differ_str.push('删');
+                } else {
+                    if (data.changeParent) data.differ.push('tree');
+                    if (!data.children || data.children.length === 0) {
+                        const orgCalc = getCompare(data, compareFields.leafCalc, 'org_', 0);
+                        const newCalc = getCompare(data, compareFields.leafCalc, 'new_', 0);
+                        if (!_.isMatch(newCalc, orgCalc)) data.differ.push('calc');
+                    } else {
+                        const orgCalc = getCompare(data, compareFields.parentCalc, 'org_', 0);
+                        const newCalc = getCompare(data, compareFields.parentCalc, 'new_', 0);
+                        if (!_.isMatch(newCalc, orgCalc)) data.push('calc');
+                    }
+                    const orgInfo = getCompare(data, compareFields.info, 'org_', '');
+                    const newInfo = getCompare(data, compareFields.info, 'new_', '');
+                    if (!_.isMatch(newInfo, orgInfo)) data.differ.push('info');
+                    if (data.differ.length > 0) data.differ_str.push('改');
+                }
+            }
+        }
+    };
+    if (!isTz) {
+        treeSetting.calcFields.push('deal_tp');
+    }
+    treeSetting.calcFun = function (node) {
+        node.dgn_price = ZhCalc.round(ZhCalc.div(node.total_price, node.dgn_qty1), 2);
+    };
+    const billsTree = createNewPathTree('compare', treeSetting);
+    // 初始化 计量单元
+    const pos = new PosData({ id: 'id', ledgerId: 'lid' });
+
+    // 清单 相关方法&绑定spreadjs事件
+    const billsTreeSpreadObj = {
+        selectionChanged: function (e, info) {
+            if (info.newSelections) {
+                if (!info.oldSelections || info.newSelections[0].row !== info.oldSelections[0].row) {
+                    posSpreadObj.loadCurPosData();
+                    posSearch.search($('#pos-keyword').val());
+                }
+            }
+        },
+    };
+    billsSpread.bind(spreadNS.Events.SelectionChanged, billsTreeSpreadObj.selectionChanged);
+
+    // 计量单元 相关方法&绑定spreadjs事件
+    const posSpreadObj = {
+        /**
+         * 加载计量单元 根据当前台账选择节点
+         */
+        loadCurPosData: function () {
+            const node = SpreadJsObj.getSelectObject(billsSheet);
+            if (node) {
+                const posData = pos.getLedgerPos(node.id) || [];
+                SpreadJsObj.loadSheetData(posSheet, 'data', posData);
+            } else {
+                SpreadJsObj.loadSheetData(posSheet, 'data', []);
+            }
+        },
+    };
+
+    // 加载清单&计量单元数据
+    postData('/tender/' + window.location.pathname.split('/')[2] + '/revise/load', {filter: 'bills;pos;reviseBills;revisePos'}, 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'],
+        };
+        const reviseLedger = {
+            billsTree: createNewPathTree('ledger', tenderTreeSetting),
+        };
+        reviseLedger.billsTree.loadDatas(result.reviseBills);
+        const orgLedger = {
+            billsTree: createNewPathTree('ledger', tenderTreeSetting),
+        };
+        202-orgLedger.billsTree.loadDatas(result.bills);
+
+        billsTree.loadCompareData(reviseLedger, orgLedger);
+        console.log(billsTree.nodes);
+        SpreadJsObj.loadSheetData(billsSheet, SpreadJsObj.DataType.Tree, billsTree);
+
+        pos.loadDatas(result.pos);
+        posSpreadObj.loadCurPosData();
+    }, null);
+    $.divResizer({
+        select: '#revise-resize',
+        callback: function () {
+            billsSpread.refresh();
+            let bcontent = $(".bcontent-wrap") ? $(".bcontent-wrap").height() : 0;
+            $(".sp-wrap").height(bcontent-30);
+            posSpread.refresh();
+        }
+    });
+
+    $.divResizer({
+        select: '#revise-right-spr',
+        callback: function () {
+            billsSpread.refresh();
+            if (posSpread) {
+                posSpread.refresh();
+            }
+            if (searchLedger) {
+                searchLedger.spread.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();
+            billsSpread.refresh();
+            if (posSpread) {
+                posSpread.refresh();
+            }
+            if (searchLedger) {
+                searchLedger.spread.refresh();
+            }
+        }
+    });
+
+    // 展开收起标准节点
+    $('a', '#side-menu').bind('click', function (e) {
+        e.preventDefault();
+        const tab = $(this), tabPanel = $(tab.attr('content'));
+        // 展开工具栏、切换标签
+        if (!tab.hasClass('active')) {
+            const close = $('.active', '#side-menu').length === 0;
+            $('a', '#side-menu').removeClass('active');
+            tab.addClass('active');
+            $('.tab-content .tab-pane').removeClass('active');
+            tabPanel.addClass('active');
+            showSideTools(tab.hasClass('active'));
+            if (tab.attr('content') === '#search') {
+                if (!searchLedger) {
+                    searchLedger = $.billsSearch({
+                        selector: '#search',
+                        searchSpread: billsSpread,
+                        resultSpreadSetting: {
+                            cols: [
+                                {title: '项目节编号', field: 'code', hAlign: 0, width: 90, formatter: '@', readOnly: true},
+                                {title: '清单编号', field: 'b_code', hAlign: 0, width: 80, formatter: '@', readOnly: true},
+                                {title: '名称', field: 'name', width: 150, hAlign: 0, formatter: '@', readOnly: true},
+                                {title: '单位', field: 'unit', width: 50, hAlign: 1, formatter: '@', readOnly: true},
+                            ],
+                            emptyRows: 0,
+                            headRows: 1,
+                            headRowHeight: [32],
+                            headColWidth: [30],
+                            defaultRowHeight: 21,
+                            headerFont: '12px 微软雅黑',
+                            font: '12px 微软雅黑',
+                            selectedBackColor: '#fffacd',
+                        },
+                        afterLocated: function () {
+                            posSpreadObj.loadCurPosData();
+                        }
+                    });
+                }
+                searchLedger.spread.refresh();
+            }
+        }
+        else {// 收起工具栏
+            tab.removeClass('active');
+            tabPanel.removeClass('active');
+            showSideTools(tab.hasClass('active'));
+        }
+        billsSpread.refresh();
+        if (posSpread) {
+            posSpread.refresh();
+        }
+    });
+
+    // 显示层次
+    (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;
+            }
+        });
+    })('a[name=showLevel]', billsSheet);
+});
+

+ 1 - 0
app/public/js/spreadjs_rela/spreadjs_zh.js

@@ -655,6 +655,7 @@ const SpreadJsObj = {
             });
             this.endMassOperation(sheet);
         } catch (err) {
+            console.log(err);
             this.endMassOperation(sheet);
         }
     },

+ 5 - 0
app/router.js

@@ -174,6 +174,11 @@ module.exports = app => {
     app.post('/tender/:id/revise/info/upload-excel/:ueType', sessionAuth, tenderCheck, uncheckTenderCheck, 'reviseController.uploadExcel');
     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.post('/tender/:id/revise/load', sessionAuth, tenderCheck, uncheckTenderCheck, reviseAuditCheck, 'reviseController.loadData');
+
     // 查看修订数据
     app.get('/tender/:id/revise/history', sessionAuth, tenderCheck, uncheckTenderCheck, 'reviseController.history');
     app.post('/tender/:id/revise/history/load', sessionAuth, tenderCheck, uncheckTenderCheck, 'reviseController.loadHistoryData');

+ 1 - 1
app/service/stage_pos.js

@@ -433,7 +433,7 @@ module.exports = app => {
             }
             const updateBillsStage = [], insertBillsStage = [], info = this.ctx.tender.info;
             for (const b of bills) {
-                const stageBills = await this.getLastestStageData(b.tender_id, this.ctx.stage.id, b.id);
+                const stageBills = await this.ctx.service.stageBills.getLastestStageData(b.tender_id, this.ctx.stage.id, b.id);
 
                 const posStage = await this.getLastestStageData2(b.tender_id, this.ctx.stage.id, {lid: b.id});
                 let contract_qty = 0;

+ 155 - 0
app/view/revise/compare.ejs

@@ -0,0 +1,155 @@
+<% include ./sub_menu.ejs %>
+<div class="panel-content">
+    <div class="panel-title">
+        <div class="title-main  d-flex">
+            <% include ./sub_mini_menu.ejs %>
+            <div class="d-inline-block">
+                <div class="dropdown">
+                    <button class="btn btn-sm btn-light dropdown-toggle text-primary" type="button" id="dropdownMenuButton" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
+                        <i class="fa fa-list-ol"></i> 显示层级
+                    </button>
+                    <div class="dropdown-menu" aria-labelledby="dropdownMenuButton">
+                        <a class="dropdown-item" name="showLevel" tag="1" href="javascirpt: void(0);">第一层</a>
+                        <a class="dropdown-item" name="showLevel" tag="2" href="javascirpt: void(0);">第二层</a>
+                        <a class="dropdown-item" name="showLevel" tag="3" href="javascirpt: void(0);">第三层</a>
+                        <a class="dropdown-item" name="showLevel" tag="4" href="javascirpt: void(0);">第四层</a>
+                        <a class="dropdown-item" name="showLevel" tag="5" href="javascirpt: void(0);">第五层</a>
+                        <a class="dropdown-item" name="showLevel" tag="last" href="javascirpt: void(0);">最底层</a>
+                        <a class="dropdown-item" name="showLevel" tag="leafXmj" href="javascirpt: void(0);">只显示项目节</a>
+                    </div>
+                </div>
+            </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%">
+                <!--0号台账模式-->
+                <div class="sjs-height-1" style="overflow: hidden" id="bills-spread">
+                </div>
+                <% if (ctx.tender.data.measure_type === measureType.tz.value) { %>
+                <div class="bcontent-wrap">
+                    <div id="revise-resize" class="resize-y" id="top-spr" r-Type="height" div1=".sjs-height-1" div2=".bcontent-wrap" title="调整大小"><!--调整上下高度条--></div>
+                    <div class="bc-bar mb-1">
+                        <ul class="nav nav-tabs">
+                            <li class="nav-item">
+                                <a class="nav-link active" href="javascript:void(0)">计量单元</a>
+                            </li>
+                            <li class="nav-item" id="pos-search">
+                            </li>
+                        </ul>
+                    </div>
+                    <div class="sp-wrap" id="pos-spread">
+                    </div>
+                </div>
+                <% } %>
+            </div>
+            <!--右栏-->
+            <div class="c-body" id="right-view" style="display: none; width: 33%;">
+                <div class="resize-x" id="revise-right-spr" r-Type="width" div1="#left-view" div2="#right-view" title="调整大小" a-type="percent"><!--调整左右高度条--></div>
+                <div class="tab-content">
+                    <div id="search" class="tab-pane">
+                    </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" content="#search" href="javascript: void(0);">查找定位</a>
+                </li>
+            </ul>
+        </div>
+    </div>
+</div>
+<script src="/public/js/moment/moment.min.js"></script>
+<script>
+    const isTz = <%- ctx.tender.data.measure_type === measureType.tz.value %>;
+    const thousandth = <%- ctx.tender.info.display.thousandth %>;
+    const decimal = JSON.parse('<%- JSON.stringify(ctx.tender.info.decimal) %>');
+    const compareFields = {
+        info: ['code', 'b_code', 'name', 'unit'],
+        leafCalc: ['unit_price', 'sgfh_qty', 'sgfh_tp', 'qtcl_qty', 'qtcl_tp', 'sjcl_qty', 'sjcl_tp', 'deal_qty', 'deal_tp', 'quantity', 'total_price'],
+        parentCalc: ['dgn_qty1', 'dgn_qty2',],
+    }
+    const getCompare = function (node, field, prefix, defaultValue) {
+        const result = {};
+        for (const f of field) {
+            result[f] = node[prefix + f] || defaultValue;
+        }
+        return result;
+    };
+    const billsSpreadSetting = {
+        cols: [
+            {title: '', colSpan: '1', rowSpan: '3', field: 'differ_str', hAlign: 1, width: 20, formatter: '@'},
+            {title: '修订台账|项目节编号', colSpan: '16|1', rowSpan: '1|2', field: 'new_code', hAlign: 0, width: 145, formatter: '@', cellType: 'tree'},
+            {title: '|清单编号', colSpan: '|1', rowSpan: '|2', field: 'new_b_code', hAlign: 0, width: 70, formatter: '@'},
+            {title: '|名称', colSpan: '|1', rowSpan: '|2', field: 'new_name', hAlign: 0, width: 185, formatter: '@'},
+            {title: '|单位', colSpan: '|1', rowSpan: '|2', field: 'new_unit', hAlign: 1, width: 50, formatter: '@'},
+            {title: '|单价', colSpan: '|1', rowSpan: '|2', field: 'new_unit_price', hAlign: 2, width: 60, type: 'Number'},
+            {title: '|项目节数量|数量1',  colSpan: '|2|1', rowSpan: '|1|1', field: 'new_dgn_qty1', hAlign: 2, width: 60, type: 'Number'},
+            {title: '||数量2',  colSpan: '||1', rowSpan: '||1', field: 'new_dgn_qty2', hAlign: 2, width: 60, type: 'Number'},
+            {title: '|经济指标',  colSpan: '|1', rowSpan: '|2', field: 'new_dgn_price', hAlign: 2, width: 60, type: 'Number'},
+            {title: '|设计量|数量', colSpan: '|2|1', rowSpan: '|1|1', field: 'new_sgfh_qty', hAlign: 2, width: 60, type: 'Number'},
+            {title: '||金额', colSpan: '||1', rowSpan: '||1', field: 'new_sgfh_tp', hAlign: 2, width: 60, type: 'Number'},
+            {title: '|设计错漏增减|数量', colSpan: '|2|1', rowSpan: '|1|1', field: 'new_sjcl_qty', hAlign: 2, width: 60, type: 'Number'},
+            {title: '||金额', colSpan: '||1', rowSpan: '||1', field: 'new_sjcl_tp', hAlign: 2, width: 60, type: 'Number'},
+            {title: '|其他错漏增减|数量', colSpan: '|2|1', rowSpan: '|1|1', field: 'new_qtcl_qty', hAlign: 2, width: 60, type: 'Number'},
+            {title: '||金额', colSpan: '||1', rowSpan: '||1', field: 'new_qtcl_tp', hAlign: 2, width: 60, type: 'Number'},
+            {title: '|台账小计|数量', colSpan: '|2|1', rowSpan: '|1|1', field: 'new_quantity', hAlign: 2, width: 60, type: 'Number'},
+            {title: '||金额', colSpan: '||1', rowSpan: '||1', field: 'new_total_price', hAlign: 2, width: 60, type: 'Number'},
+            {title: '原台账|项目节编号', colSpan: '16|1', rowSpan: '1|2', field: 'org_code', hAlign: 0, width: 145, formatter: '@', cellType: 'tree'},
+            {title: '|清单编号', colSpan: '|1', rowSpan: '|2', field: 'org_b_code', hAlign: 0, width: 70, formatter: '@'},
+            {title: '|名称', colSpan: '|1', rowSpan: '|2', field: 'org_name', hAlign: 0, width: 185, formatter: '@'},
+            {title: '|单位', colSpan: '|1', rowSpan: '|2', field: 'org_unit', hAlign: 1, width: 50, formatter: '@', cellType: 'unit'},
+            {title: '|单价', colSpan: '|1', rowSpan: '|2', field: 'org_unit_price', hAlign: 2, width: 60, type: 'Number'},
+            {title: '|项目节数量|数量1',  colSpan: '|2|1', rowSpan: '|1|1', field: 'org_dgn_qty1', hAlign: 2, width: 60, type: 'Number'},
+            {title: '||数量2',  colSpan: '||1', rowSpan: '||1', field: 'org_dgn_qty2', hAlign: 2, width: 60, type: 'Number'},
+            {title: '|经济指标',  colSpan: '|1', rowSpan: '|2', field: 'org_dgn_price', hAlign: 2, width: 60, type: 'Number'},
+            {title: '|设计量|数量', colSpan: '|2|1', rowSpan: '|1|1', field: 'org_sgfh_qty', hAlign: 2, width: 60, type: 'Number'},
+            {title: '||金额', colSpan: '||1', rowSpan: '||1', field: 'org_sgfh_tp', hAlign: 2, width: 60, type: 'Number'},
+            {title: '|设计错漏增减|数量', colSpan: '|2|1', rowSpan: '|1|1', field: 'org_sjcl_qty', hAlign: 2, width: 60, type: 'Number'},
+            {title: '||金额', colSpan: '||1', rowSpan: '||1', field: 'org_sjcl_tp', hAlign: 2, width: 60, type: 'Number'},
+            {title: '|其他错漏增减|数量', colSpan: '|2|1', rowSpan: '|1|1', field: 'org_qtcl_qty', hAlign: 2, width: 60, type: 'Number'},
+            {title: '||金额', colSpan: '||1', rowSpan: '||1', field: 'org_qtcl_tp', hAlign: 2, width: 60, type: 'Number'},
+            {title: '|台账小计|数量', colSpan: '|2|1', rowSpan: '|1|1', field: 'org_quantity', hAlign: 2, width: 60, type: 'Number'},
+            {title: '||金额', colSpan: '||1', rowSpan: '||1', field: 'org_total_price', hAlign: 2, width: 60, type: 'Number'},
+        ],
+        emptyRows: 3,
+        headRows: 3,
+        headRowHeight: [25, 25, 25],
+        defaultRowHeight: 21,
+        headerFont: '12px 微软雅黑',
+        font: '12px 微软雅黑',
+        readOnly: true,
+    };
+    const posSpreadSetting = {
+        cols: [
+            {title: '修订台账|计量单元', colSpan: '6|1', rowSpan: '1|2', field: 'org_name', hAlign: 0, width: 230, formatter: '@'},
+            {title: '|位置', colSpan: '|1', rowSpan: '|2', field: 'org_position', hAlign: 0, width: 60, formatter: '@'},
+            {title: '|台账数量|设计量', colSpan: '|4|1', rowSpan: '|1|1', field: 'org_sgfh_qty', hAlign: 2, width: 100, type: 'Number'},
+            {title: '||设计错漏增减', colSpan: '||1', rowSpan: '||1', field: 'org_sjcl_qty', hAlign: 2, width: 100, type: 'Number'},
+            {title: '||其他错漏增减', colSpan: '||1', rowSpan: '||1', field: 'org_qtcl_qty', hAlign: 2, width: 100, type: 'Number'},
+            {title: '||小计', colSpan: '||1', rowSpan: '||1', field: 'org_quantity', hAlign: 2, width: 60, type: 'Number'},
+            {title: '原台账|计量单元', colSpan: '6|1', rowSpan: '1|2', field: 'new_name', hAlign: 0, width: 230, formatter: '@'},
+            {title: '|位置', colSpan: '|1', rowSpan: '|2', field: 'new_position', hAlign: 0, width: 60, formatter: '@'},
+            {title: '|台账数量|设计量', colSpan: '|4|1', rowSpan: '|1|1', field: 'new_sgfh_qty', hAlign: 2, width: 100, type: 'Number'},
+            {title: '||设计错漏增减', colSpan: '||1', rowSpan: '||1', field: 'new_sjcl_qty', hAlign: 2, width: 100, type: 'Number'},
+            {title: '||其他错漏增减', colSpan: '||1', rowSpan: '||1', field: 'new_qtcl_qty', hAlign: 2, width: 100, type: 'Number'},
+            {title: '||小计', colSpan: '||1', rowSpan: '||1', field: 'new_quantity', hAlign: 2, width: 60, type: 'Number'},
+        ],
+        emptyRows: 3,
+        headRows: 3,
+        headRowHeight: [25, 25, 25],
+        headColWidth: [30],
+        defaultRowHeight: 21,
+        headerFont: '12px 微软雅黑',
+        font: '12px 微软雅黑',
+        readOnly: true,
+    }
+</script>

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

@@ -1,8 +1,8 @@
-<% include ../tender/tender_sub_menu.ejs %>
+<% include ./sub_menu.ejs %>
 <div class="panel-content">
     <div class="panel-title">
         <div class="title-main  d-flex">
-            <% include ../tender/tender_sub_mini_menu.ejs %>
+            <% include ./sub_mini_menu.ejs %>
             <!--工具-->
             <% if ((revise.status === audit.status.uncheck || revise.status === audit.status.checkNo) && revise.uid === ctx.session.sessionUser.accountId) { %>
             <div>

+ 12 - 0
app/view/revise/sub_menu.ejs

@@ -0,0 +1,12 @@
+<div class="panel-sidebar" id="sub-menu">
+    <div class="sidebar-title" data-toggle="tooltip" data-placement="right" data-original-title="<%- revise.corder %># 台账修订"><%- revise.corder %># 台账修订</div>
+    <div class="scrollbar-auto">
+        <% include ./sub_menu_list.ejs %>
+        <div class="side-fold"><a href="javascript: void(0)" data-toggle="tooltip" data-placement="top" data-original-title="折叠侧栏" id="to-mini-menu"><i class="fa fa-upload fa-rotate-270"></i></a></div>
+    </div>
+    <script>
+        new Vue({
+            el: '.scrollbar-auto',
+        });
+    </script>
+</div>

+ 5 - 0
app/view/revise/sub_menu_list.ejs

@@ -0,0 +1,5 @@
+<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>-->

+ 16 - 0
app/view/revise/sub_mini_menu.ejs

@@ -0,0 +1,16 @@
+<!--折起的菜单-->
+<div class="min-side" id="sub-mini-menu" style="display: none;">
+    <div id="sub-mini-hint" class="side-switch" data-container="body" data-toggle="popover" data-placement="bottom" data-content="这里打开收起的菜单栏"></div>
+    <div class="side-switch">
+        <i class="fa fa-bars"></i>
+    </div>
+    <div class="side-menu" id="mini-menu-list" style="display: none">
+        <% include ./sub_menu_list.ejs %>
+        <div class="side-fold"><a href="javascript: void(0);" data-toggle="tooltip" data-placement="top" data-original-title="展开侧栏" id="to-menu"><i class="fa fa-upload fa-rotate-90"></i></a></div>
+    </div>
+</div>
+<script>
+    new Vue({
+        el: '.side-menu',
+    });
+</script>

+ 20 - 0
config/web.js

@@ -212,6 +212,7 @@ const JsFiles = {
                     '/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',
@@ -241,6 +242,25 @@ const JsFiles = {
                 ],
                 mergeFile: 'revise_history',
             },
+            compare: {
+                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/shares/sjs_setting.js',
+                    '/public/js/shares/cs_tools.js',
+                    '/public/js/zh_calc.js',
+                    '/public/js/path_tree.js',
+                    '/public/js/revise_compare.js',
+                ],
+                mergeFile: 'revise_compare',
+            }
         },
         stage: {
             // 本期计量台账