Bläddra i källkod

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

TonyKang 5 år sedan
förälder
incheckning
ab9e057ebf
59 ändrade filer med 1459 tillägg och 419 borttagningar
  1. 2 2
      app/const/spread.js
  2. 5 0
      app/controller/material_controller.js
  3. 1 1
      app/controller/measure_controller.js
  4. 2 1
      app/controller/stage_controller.js
  5. 26 5
      app/lib/bills_pos_convert.js
  6. 6 3
      app/lib/ledger.js
  7. 4 2
      app/lib/rpt_data_analysis.js
  8. 9 2
      app/middleware/tender_check.js
  9. 4 0
      app/public/js/global.js
  10. 18 8
      app/public/js/ledger.js
  11. 147 0
      app/public/js/ledger_bwtz.js
  12. 51 0
      app/public/js/ledger_gather.js
  13. 64 5
      app/public/js/ledger_search.js
  14. 9 1
      app/public/js/material.js
  15. 144 4
      app/public/js/material_list.js
  16. 16 0
      app/public/js/measure_compare.js
  17. 52 29
      app/public/js/revise.js
  18. 15 11
      app/public/js/se_bonus.js
  19. 4 0
      app/public/js/se_jgcl.js
  20. 4 0
      app/public/js/se_other.js
  21. 7 1
      app/public/js/shares/export_excel.js
  22. 47 0
      app/public/js/shares/tender_list_order.js
  23. 67 146
      app/public/js/stage.js
  24. 243 20
      app/public/js/stage_bwtz.js
  25. 17 0
      app/public/js/stage_compare.js
  26. 76 1
      app/public/js/stage_gather.js
  27. 10 8
      app/public/js/tender_list.js
  28. 15 15
      app/public/js/tender_list_info.js
  29. 10 9
      app/public/js/tender_list_manage.js
  30. 7 7
      app/public/js/tender_list_progress.js
  31. 5 4
      app/public/js/tender_showhide.js
  32. 6 2
      app/service/change.js
  33. 11 3
      app/service/change_audit.js
  34. 9 0
      app/service/material_audit.js
  35. 27 0
      app/service/material_list.js
  36. 9 0
      app/service/revise_audit.js
  37. 60 84
      app/service/rpt_gather_memory.js
  38. 1 1
      app/service/stage_att.js
  39. 4 4
      app/service/stage_bills.js
  40. 11 2
      app/service/stage_other.js
  41. 4 0
      app/service/tender.js
  42. 3 3
      app/view/dashboard/index.ejs
  43. 51 14
      app/view/ledger/bwtz.ejs
  44. 2 2
      app/view/ledger/explode.ejs
  45. 7 0
      app/view/ledger/explode_modal.ejs
  46. 1 1
      app/view/material/list.ejs
  47. 5 0
      app/view/measure/compare.ejs
  48. 1 1
      app/view/measure/stage_modal.ejs
  49. 7 0
      app/view/revise/info_modal.ejs
  50. 28 0
      app/view/shares/delete_hint_modal.ejs
  51. 64 15
      app/view/stage/bwtz.ejs
  52. 5 0
      app/view/stage/compare.ejs
  53. 5 0
      app/view/stage/gather.ejs
  54. 1 1
      app/view/stage/index.ejs
  55. 5 0
      app/view/stage_extra/bonus.ejs
  56. 5 0
      app/view/stage_extra/jgcl.ejs
  57. 5 0
      app/view/stage_extra/other.ejs
  58. 1 1
      builder_report_index_define.js
  59. 34 0
      config/web.js

+ 2 - 2
app/const/spread.js

@@ -378,8 +378,8 @@ const stageGather = {
             {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'end_qc_tp', hAlign: 2, width: 60, type: 'Number'},
             {title: '截止本期完成计量|数量', colSpan: '3|1', rowSpan: '1|1', field: 'end_gather_qty', hAlign: 2, width: 60, type: 'Number'},
             {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'end_gather_tp', hAlign: 2, width: 60, type: 'Number'},
-            {title: '|完成率(%)', colSpan: '1', rowSpan: '|1', field: 'end_gather_percent', hAlign: 2, width: 80, readOnly: true, type: 'Number'},
-            {title: '台账+变更令|数量', colSpan: '3|1', rowSpan: '1|1', field: 'end_final_qty', hAlign: 2, width: 60, type: 'Number'},
+            {title: '|完成率(%)', colSpan: '|1', rowSpan: '|1', field: 'end_gather_percent', hAlign: 2, width: 80, readOnly: true, type: 'Number'},
+            {title: '台账+变更令|数量', colSpan: '2|1', rowSpan: '1|1', field: 'end_final_qty', hAlign: 2, width: 60, type: 'Number'},
             {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'end_final_tp', hAlign: 2, width: 60, type: 'Number'},
         ],
         emptyRows: 0,

+ 5 - 0
app/controller/material_controller.js

@@ -327,6 +327,11 @@ module.exports = app => {
                     case 'notjoin':
                         responseData.data = await ctx.service.materialListNotjoin.add(data.select);
                         break;
+                    case 'paste':
+                        await ctx.service.materialList.saveDatas(data.updateData);
+                        // 取所有工料表
+                        responseData.data = await ctx.service.materialList.getMaterialData(ctx.tender.id, ctx.material.id);
+                        break;
                     default: throw '参数有误';
                 }
 

+ 1 - 1
app/controller/measure_controller.js

@@ -140,7 +140,7 @@ module.exports = app => {
                 if (!stage) {
                     throw '提交数据错误';
                 }
-                if (stage.status !== auditConst.status.uncheck) {
+                if (stage.status === auditConst.status.checked) {
                     throw '该计量期当前不可编辑';
                 }
                 if (ctx.session.sessionUser.accountId !== stage.user_id) {

+ 2 - 1
app/controller/stage_controller.js

@@ -1156,12 +1156,13 @@ module.exports = app => {
 
         async loadBwtz(ctx) {
             try {
+                const data = ctx.request.body.data ? JSON.parse(ctx.request.body.data) : {};
                 const ledgerData = await this._getStageLedgerData(ctx);
                 const posData = await this._getStagePosData(ctx);
                 const changeData = await this._getStageChangeData(ctx);
                 const convert = new billsPosConvert(ctx);
                 convert.loadData(ledgerData, posData, changeData);
-                const result = convert.convert();
+                const result = convert.convert(data.filter);
                 // const wbsCodeHis = await ctx.service.externalData.getExValue(ctx.tender.id, -1,
                 //     externalDataConst.FuLong.exType, externalDataConst.FuLong.exFields.wbsCode) || [];
                 // const [result, needUpdate] = convert.convertByWbsCode(wbsCodeHis);

+ 26 - 5
app/lib/bills_pos_convert.js

@@ -312,22 +312,43 @@ class BillsPosConvert {
         return needUpdate;
     }
 
-    _getResultData() {
-        const result = this.resultTree.getDefaultDatas();
+    _getResultData(filterFun) {
+        const result = this.resultTree.getDefaultDatas(filterFun);
         for (const r of result) {
             if (r.unitTree) {
-                r.unitTreeData = r.unitTree.getDefaultDatas();
+                r.unitTreeData = r.unitTree.getDefaultDatas(filterFun);
                 delete r.unitTree;
             }
         }
         return result;
     }
     // 转换数据
-    convert() {
+    convert(filter) {
         this._recursiveConvertNode(this.bpcTree.children);
         this._calculateAndSortResult();
         this._generateCodeByWbsCode([]);
-        return this._getResultData();
+        switch (filter) {
+            case 'cur':
+                return this._getResultData(function (node) {
+                    for (const field of ['contract_tp', 'qc_tp', 'gather_tp']) {
+                        if (node[field]) {
+                            return false;
+                        }
+                    }
+                    return true;
+                });
+            case 'end':
+                return this._getResultData(function (node) {
+                    for (const field of ['end_contract_tp', 'end_qc_tp', 'end_gather_tp']) {
+                        if (node[field]) {
+                            return false;
+                        }
+                    }
+                    return true;
+                });
+            default:
+                return this._getResultData();
+        }
     }
 
     /**

+ 6 - 3
app/lib/ledger.js

@@ -189,9 +189,12 @@ class baseTree {
      * @param fields
      * @returns {Array}
      */
-    getDatasWithout (fields) {
+    getDatasWithout (fields, filter) {
         const datas = [];
         for (const node of this.nodes) {
+            if (filter && filter(node)) {
+                continue;
+            }
             if (node.b_code && node.b_code !== '') node.chapter = this.ctx.helper.getChapterCode(node.b_code);
             const data = {};
             for (const field in node) {
@@ -207,8 +210,8 @@ class baseTree {
      * 获取默认数据 剔除一些树结构需要的缓存数据
      * @returns {Array}
      */
-    getDefaultDatas() {
-        return this.getDatasWithout(['expanded', 'visible', 'children', 'index']);
+    getDefaultDatas(filter) {
+        return this.getDatasWithout(['expanded', 'visible', 'children', 'index'], filter);
     }
 
     _mapTreeNode () {

+ 4 - 2
app/lib/rpt_data_analysis.js

@@ -1011,7 +1011,7 @@ const gatherSelectConverse = {
     defaultSetting: {
         table: ['mem_gather_stage_bills'],
     },
-    _stageBills: function (helper, data, count) {
+    _commonConverse: function (helper, data, count) {
         const result = [];
         const reg = new RegExp('^t_[0-9]+_');
         for (let i = 0; i < count; i++) {
@@ -1043,7 +1043,9 @@ const gatherSelectConverse = {
         for (const t of options.table) {
             switch (t) {
                 case 'mem_gather_stage_bills':
-                    data[t] = this._stageBills(ctx.helper, data[t], count);
+                case 'mem_gather_stage_pay':
+                case 'mem_gather_deal_bills':
+                    data[t] = this._commonConverse(ctx.helper, data[t], count);
                     break;
             }
         }

+ 9 - 2
app/middleware/tender_check.js

@@ -55,11 +55,18 @@ module.exports = options => {
                     const auditors = yield this.service.ledgerAudit.getAuditors(tender.id, times);
                     const auditorsId = this.helper._.map(auditors, 'audit_id');
                     const stageAuditors = yield this.service.stageAudit.getAllAuditors(tender.id);
-                    const stageAUditorsId = this.helper._.map(stageAuditors, 'aid');
+                    const stageAuditorsId = this.helper._.map(stageAuditors, 'aid');
+                    const changeAuditors = yield this.service.changeAudit.getAllAuditors(tender.id);
+                    const changeAuditorsId = this.helper._.map(changeAuditors, 'uid');
+                    const reviseAuditors = yield this.service.reviseAudit.getAllAuditors(tender.id);
+                    const reviseAuditorsId = this.helper._.map(reviseAuditors, 'audit_id');
+                    const materialAuditors = yield this.service.materialAudit.getAllAuditors(tender.id);
+                    const materialAuditorsId = this.helper._.map(materialAuditors, 'aid');
                     const tenderPermission = this.session.sessionUser.permission ? this.session.sessionUser.permission.tender : null;
                     if (auditorsId.indexOf(accountId) === -1 && tender.data.user_id !== accountId &&
                         (tenderPermission === null || tenderPermission === undefined || tenderPermission.indexOf('2') === -1) &&
-                        stageAUditorsId.indexOf(accountId) === -1) {
+                        stageAuditorsId.indexOf(accountId) === -1 && changeAuditorsId.indexOf(accountId) === -1 &&
+                        reviseAuditorsId.indexOf(accountId) === -1 && materialAuditorsId.indexOf(accountId) === -1) {
                         throw '您无权查看该项目';
                     }
                 }

+ 4 - 0
app/public/js/global.js

@@ -673,6 +673,10 @@ function trimInvalidChar(str) {
     return $.trim(str).replace(/\n/g, '').replace(/\r/g, '').replace(/\t/g, '');
 }
 
+function cleanSymbols(str) {
+    return $.trim(str).replace(/\\/g, '').replace(/\'/g, '').replace(/\"/g, '').replace(/\</g, '').replace(/\|/g, '');
+}
+
 jQuery.bootstrapLoading = {
     isLoading: function () {
         return $('#loadingPage').is(':visible');

+ 18 - 8
app/public/js/ledger.js

@@ -300,12 +300,14 @@ $(document).ready(function() {
                         count: count
                     }
                 };
-                postData(window.location.pathname + '/update', data, function (result) {
-                    const refreshNode = tree.loadPostData(result);
-                    for (const d of refreshNode.delete) pos.removeDatasByMasterId(d.id);
-                    self.refreshTree(sheet, refreshNode);
-                    self.refreshOperationValid(sheet);
-                    posOperationObj.loadCurPosData();
+                deleteAfterHint(function () {
+                    postData(window.location.pathname + '/update', data, function (result) {
+                        const refreshNode = tree.loadPostData(result);
+                        for (const d of refreshNode.delete) pos.removeDatasByMasterId(d.id);
+                        self.refreshTree(sheet, refreshNode);
+                        self.refreshOperationValid(sheet);
+                        posOperationObj.loadCurPosData();
+                    });
                 });
             }
         },
@@ -743,7 +745,7 @@ $(document).ready(function() {
             }, null, true);
         },
         selectionChanged: function (e, info) {
-            if (!info.oldSelections[0] || info.newSelections[0].row !== info.oldSelections[0].row) {
+            if (!info.oldSelections || !info.oldSelections[0] || info.newSelections[0].row !== info.oldSelections[0].row) {
                 posOperationObj.loadCurPosData();
                 SpreadJsObj.resetTopAndSelect(posSpread.getActiveSheet());
                 posSearch.search($('#pos-keyword').val());
@@ -2103,6 +2105,11 @@ $(document).ready(function() {
             this.obj = obj;
             this.billsCount = 6;
             this.posCount = 1000;
+            this.filter = getLocalCache('zh-calc-batch-filter');
+            if (!this.filter) {
+                this.filter = '1';
+            }
+            $('input[name=batch-filter]')[0].checked = this.filter && this.filter != '0';
             // 初始化 清单编号窗口 参数
             this.qdSpreadSetting = {
                 cols: [
@@ -2294,6 +2301,9 @@ $(document).ready(function() {
                     }
                 }
             });
+            $('input[name=batch-filter]').change(function () {
+                setLocalCache('zh-calc-batch-filter', this.checked ? 1 : 0);
+            });
         }
         // 初始化左侧表格
         initView () {
@@ -2339,7 +2349,6 @@ $(document).ready(function() {
                     price: _.toNumber(qdSheet.getText(iRow, 3)),
                     pos: [],
                 };
-                result.push(qd);
                 for (let iPosRow = 0; iPosRow < posSheet.getRowCount(); iPosRow++) {
                     const value = _.toNumber(posSheet.getText(iPosRow, iRow + 2));
                     if (value !== 0 && !isNaN(value)) {
@@ -2350,6 +2359,7 @@ $(document).ready(function() {
                         });
                     }
                 }
+                if (!$('input[name=batch-filter]')[0].checked || qd.pos.length > 0) result.push(qd);
             }
             return result;
         }

+ 147 - 0
app/public/js/ledger_bwtz.js

@@ -9,6 +9,7 @@
  */
 
 $(document).ready(() => {
+    let xmjSearch;
     autoFlashHeight();
     const xmjSpread = SpreadJsObj.createNewSpread($('#xmj-spread')[0]);
     const xmjSheet = xmjSpread.getActiveSheet();
@@ -82,4 +83,150 @@ $(document).ready(() => {
             unitSpread.refresh();
         }
     });
+    $.divResizer({
+        select: '#right-spr',
+        callback: function () {
+            xmjSpread.refresh();
+            unitSpread.refresh();
+            if (xmjSearch) {
+                xmjSearch.spread.refresh();
+            }
+        }
+    });
+
+    $('a', '.side-menu').bind('click', function (e) {
+        e.preventDefault();
+        const tab = $(this), tabPanel = $(tab.attr('content'));
+        const showSideTools = function (show) {
+            const left = $('#left-view'), right = $('#right-view'), parent = left.parent();
+            if (show) {
+                right.show();
+                autoFlashHeight();
+                /**
+                 * right.show()后, parent被撑开成2倍left.height, 导致parent.width减少了10px
+                 * 第一次left.width调整后,parent的缩回left.height, 此时parent.width又增加了10px
+                 * 故需要通过最终的parent.width再计算一次left.width
+                 *
+                 * Q: 为什么不通过先计算left.width的宽度,以避免计算两次left.width?
+                 * A: 右侧工具栏不一定显示,当右侧工具栏显示过一次后,就必须使用parent和right来计算left.width
+                 *
+                 */
+                    //left.css('width', parent.width() - right.outerWidth());
+                    //left.css('width', parent.width() - right.outerWidth());
+                const percent = 100 - right.outerWidth() /parent.width() * 100;
+                left.css('width', percent + '%');
+            } else {
+                left.width(parent.width());
+                right.hide();
+            }
+        };
+        // 展开工具栏、切换标签
+        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' && !xmjSearch) {
+                if (!xmjSearch) {
+                    xmjSearch = $.billsSearch({
+                        selector: '#search',
+                        searchSpread: xmjSpread,
+                        searchRangeStr: '项目节编号/名称',
+                        resultSpreadSetting: {
+                            cols: [
+                                {title: '项目节编号', field: 'code', hAlign: 0, width: 120, formatter: '@', readOnly: true},
+                                {title: '名称', field: 'name', width: 150, hAlign: 0, formatter: '@', readOnly: true},
+                                {title: '单位', field: 'unit', width: 50, hAlign: 1, formatter: '@', readOnly: true},
+                                {title: '台账金额', field: 'total_price', hAlign: 2, width: 80, readOnly: true},
+                            ],
+                            emptyRows: 0,
+                            headRows: 1,
+                            headRowHeight: [32],
+                            defaultRowHeight: 21,
+                            headerFont: '12px 微软雅黑',
+                            font: '12px 微软雅黑',
+                            selectedBackColor: '#fffacd',
+                        },
+                        afterLocated: function () {
+                            unitTreeObj.loadCurUnitData();
+                        }
+                    });
+                }
+                xmjSearch.spread.refresh();
+            }
+        } else { // 收起工具栏
+            tab.removeClass('active');
+            tabPanel.removeClass('active');
+            showSideTools(tab.hasClass('active'));
+        }
+        xmjSpread.refresh();
+        unitSpread.refresh();
+    });
+
+    // 显示层次
+    (function (select, sheet) {
+        $(select).click(function () {
+            const tag = $(this).attr('tag');
+            const tree = sheet.zh_tree;
+            if (!tree) return;
+            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;
+            }
+        });
+    })('a[name=showLevel]', xmjSheet);
+
+    $('#exportBwtz').click(function () {
+        const data = [];
+        const setting = {
+            cols: [
+                {title: '项目节编号', colSpan: '1', rowSpan: '2', field: 'code', hAlign: 0, width: 100, formatter: '@'},
+                {title: '清单编号', colSpan: '1', rowSpan: '2', field: 'b_code', hAlign: 0, width: 70, formatter: '@'},
+                {title: '名称', colSpan: '1', rowSpan: '2', field: 'name', hAlign: 0, width: 300, formatter: '@'},
+                {title: '单位', colSpan: '1', rowSpan: '2', field: 'unit', hAlign: 1, width: 60, formatter: '@'},
+                {title: '单价', colSpan: '1', rowSpan: '2', field: 'unit_price', hAlign: 2, width: 80, type: 'Number'},
+                {title: '台账|数量', colSpan: '2|1', rowSpan: '1|1', field: 'quantity', hAlign: 2, width: 80, type: 'Number'},
+                {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'total_price', hAlign: 2, width: 80, type: 'Number'},
+                {title: '图(册)号', colSpan: '1', rowSpan: '2', field: 'drawing_code', hAlign: 0, width: 100, formatter: '@'},
+                {title: '备注', colSpan: '1', rowSpan: '2', field: 'memo', hAlign: 0, width: 100, formatter: '@'},
+            ],
+            headRows: 2,
+            headRowHeight: [25, 25],
+            defaultRowHeight: 21,
+            headerFont: 'bold 10px 微软雅黑',
+            font: '10px 微软雅黑'
+        };
+        if (!xmjSheet.zh_tree) return;
+        for (const node of xmjSheet.zh_tree.nodes) {
+            data.push({
+                code: node.code, b_code: node.b_code, name: node.name, unit: node.unit,
+                unit_price: node.unit_price, quantity: node.quantity, total_price: node.total_price,
+                drawing_code: node.drawing_code, memo: node.memo
+            });
+            if (node.unitTree) {
+                for (const unitNode of node.unitTree.nodes) {
+                    data.push({
+                        code: unitNode.code, b_code: unitNode.b_code,
+                        name: unitNode.pos_name ? unitNode.pos_name : unitNode.name, unit: unitNode.unit,
+                        unit_price: unitNode.unit_price, quantity: unitNode.quantity, total_price: unitNode.total_price,
+                        drawing_code: unitNode.drawing_code, memo: unitNode.memo
+                    });
+                }
+            }
+        }
+
+        SpreadExcelObj.exportSimpleXlsxSheet(setting, data, $('h2')[0].innerHTML + "-部位台账.xlsx");
+    });
 });

+ 51 - 0
app/public/js/ledger_gather.js

@@ -222,4 +222,55 @@ $(document).ready(() => {
             leafXmjSpread.refresh();
         }
     });
+    $('#exportExcel').click(function () {
+        const data = [];
+        const setting = {
+            cols: [
+                {title: '清单编号', colSpan: '1', rowSpan: '2', field: 'b_code', hAlign: 0, width: 70, formatter: '@'},
+                {title: '名称', colSpan: '1', rowSpan: '2', field: 'name', hAlign: 0, width: 300, formatter: '@'},
+                {title: '单位', colSpan: '1', rowSpan: '2', field: 'unit', hAlign: 1, width: 60, formatter: '@'},
+                {title: '单价', colSpan: '1', rowSpan: '2', field: 'unit_price', hAlign: 2, width: 80, type: 'Number'},
+                {title: '项目节编号', colSpan: '1', rowSpan: '2', field: 'code', hAlign: 0, width: 100, formatter: '@'},
+                {title: '签约清单|数量', colSpan: '2|1', rowSpan: '1|1', field: 'deal_bills_qty', hAlign: 2, width: 80, type: 'Number'},
+                {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'deal_bills_tp', hAlign: 2, width: 80, type: 'Number'},
+                {title: '台账|数量', colSpan: '2|1', rowSpan: '1|1', field: 'quantity', hAlign: 2, width: 80, type: 'Number'},
+                {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'total_price', hAlign: 2, width: 80, type: 'Number'},
+                {title: '签约-台账|数量', colSpan: '2|1', rowSpan: '1|1', field: 'compare_qty', hAlign: 2, width: 80, type: 'Number'},
+                {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'compare_tp', hAlign: 2, width: 80, type: 'Number'},
+                {title: '单位工程', colSpan: '1', rowSpan: '2', field: 'dwgc', hAlign: 0, width: 80, formatter: '@'},
+                {title: '分部工程', colSpan: '1', rowSpan: '2', field: 'fbgc', hAlign: 0, width: 80, formatter: '@'},
+                {title: '分项工程', colSpan: '1', rowSpan: '2', field: 'fxgc', hAlign: 0, width: 80, formatter: '@'},
+                {title: '细目', colSpan: '1', rowSpan: '2', field: 'jldy', hAlign: 0, width: 80, formatter: '@'},
+                {title: '计量单元', colSpan: '1', rowSpan: '2', field: 'bwmx', hAlign: 0, width: 80, formatter: '@'},
+                {title: '图册号', colSpan: '1', rowSpan: '2', field: 'drawing_code', hAlign: 0, width: 80, formatter: '@'},
+            ],
+            headRows: 2,
+            headRowHeight: [25, 25],
+            defaultRowHeight: 21,
+            headerFont: 'bold 10px 微软雅黑',
+            font: '10px 微软雅黑'
+        };
+        if (!gclSheet.zh_data || gclSheet.zh_data.length === 0) return;
+        for (const gcl of gclSheet.zh_data) {
+            data.push({
+                b_code: gcl.b_code, name: gcl.name, unit: gcl.unit,
+                unit_price: gcl.unit_price,
+                deal_bills_qty: gcl.deal_bills_qty, deal_bills_tp: gcl.deal_bills_tp,
+                quantity: gcl.quantity, total_price: gcl.total_price,
+                compare_qty: gcl.compare_qty, compare_tp: gcl.compare_tp,
+            });
+            if (gcl.leafXmjs && gcl.leafXmjs.length > 0) {
+                for (const xmj of gcl.leafXmjs) {
+                    data.push({
+                        code: xmj.code,
+                        quantity: xmj.quantity,
+                        dwgc: xmj.dwgc, fbgc: xmj.fbgc, fxgc: xmj.fxgc,
+                        jldy: xmj.jldy, bwmx: xmj.bwmx, drawing_code: xmj.drawing_code,
+                    });
+                }
+            }
+        }
+
+        SpreadExcelObj.exportSimpleXlsxSheet(setting, data, $('h2')[0].innerHTML + "-清单汇总.xlsx");
+    });
 });

+ 64 - 5
app/public/js/ledger_search.js

@@ -1,7 +1,10 @@
 'use strict';
 
 /**
- * 台账搜索相关(多个页面均使用:台账分解、台账审批、台账修订)
+ * 台账搜索相关
+ * 多个页面均使用:
+ * 0好台账:台账分解、台账审批、台账修订、部位台账
+ * 期计量:计量台账、部位台账
  *
  * 搜索基于spreadjs,请放在gc.spread.sheets.all.10.0.1.min.js/spreadjs_zh.js之后
  *
@@ -105,15 +108,20 @@
             searchObj.locateNext();
         });
         return searchObj;
-    }
+    };
     $.billsSearch = function (setting) {
         if (!setting.selector || !setting.searchSpread || !setting.resultSpreadSetting) return;
+        if (!setting.searchRangeStr) setting.searchRangeStr = '项目节编号/清单编号/名称';
         const resultId = setting.id + '-search-result';
         const obj = $(setting.selector);
         obj.html(
             '                        <div class="sjs-bar">\n' +
             '                            <div class="input-group input-group-sm pb-1">\n' +
-            '                                <input id="searchKeyword" type="text" class="form-control" placeholder="可查找 项目节编号/清单编号/名称" aria-label="Recipient\'s username" aria-describedby="button-addon2">\n' +
+            '                                <div class="input-group-prepend">\n' +
+            (setting.searchOver ? '                                    <div class="input-group-text"><input type="radio" name="searchType" id="over"> 超计</div>\n' : '') +
+            (setting.searchEmpty ? '                                    <div class="input-group-text"><input type="radio" name="searchType" id="empty"> 漏计</div>\n' : '') +
+            '                                </div>' +
+            '                                <input id="searchKeyword" type="text" class="form-control" placeholder="可查找 ' + setting.searchRangeStr + '" aria-label="Recipient\'s username" aria-describedby="button-addon2">\n' +
             '                                <div class="input-group-append">\n' +
             '                                    <button class="btn btn-outline-secondary" type="button"">搜索</button>\n' +
             '                                </div>\n' +
@@ -129,7 +137,7 @@
         const searchSheet = setting.searchSpread.getActiveSheet();
         let searchResult = [];
         const searchBills = function () {
-            const keyword = $('input', obj).val();
+            const keyword = $('#searchKeyword', obj).val();
             searchResult = [];
             const sortData = SpreadJsObj.getSortData(searchSheet);
             for (const node of sortData) {
@@ -143,11 +151,62 @@
             }
             SpreadJsObj.loadSheetData(resultSpread.getActiveSheet(), 'data', searchResult);
         };
+        const calculateCompletePercent = function (searchResult) {
+            if (!searchResult) return;
+            for (const sr of searchResult) {
+                const base = ZhCalc.add(sr.total_price, sr.end_qc_tp);
+                sr.complete_percent = base !== 0 ? ZhCalc.mul(ZhCalc.div(sr.end_gather_tp, base), 100, 2) : 0;
+            }
+        };
+        const searchOver = function () {
+            searchResult = [];
+            const sortData = SpreadJsObj.getSortData(searchSheet);
+            for (const node of sortData) {
+                if (node.children && node.children.length > 0) continue;
+                if (setting.checkOver && setting.checkOver(node)) {
+                    const data = JSON.parse(JSON.stringify(node));
+                    data.visible = true;
+                    searchResult.push(data);
+                }
+            }
+            calculateCompletePercent(searchResult);
+            SpreadJsObj.loadSheetData(resultSpread.getActiveSheet(), 'data', searchResult);
+        };
+        const searchEmpty = function () {
+            searchResult = [];
+            const sortData = SpreadJsObj.getSortData(searchSheet);
+            for (const node of sortData) {
+                if (node.children && node.children.length > 0) continue;
+                if (setting.checkEmpty && setting.checkEmpty(node)) {
+                    const data = JSON.parse(JSON.stringify(node));
+                    data.visible = true;
+                    searchResult.push(data);
+                }
+            }
+            calculateCompletePercent(searchResult);
+            SpreadJsObj.loadSheetData(resultSpread.getActiveSheet(), 'data', searchResult);
+        };
+        const searchLess = function () {
+            searchResult = [];
+            const sortData = SpreadJsObj.getSortData(searchSheet);
+            for (const node of sortData) {
+                if (node.children && node.children.length > 0) continue;
+                if (setting.checkLess && setting.checkLess(node)) {
+                    const data = JSON.parse(JSON.stringify(node));
+                    data.visible = true;
+                    searchResult.push(data);
+                }
+            }
+            calculateCompletePercent(searchResult);
+            SpreadJsObj.loadSheetData(resultSpread.getActiveSheet(), 'data', searchResult);
+        };
 
         $('input', this.obj).bind('keydown', function (e) {
             if (e.keyCode == 13) searchBills();
         });
         $('button', this.obj).bind('click', () => {searchBills()});
+        $('#over', this.obj).bind('change', () => {searchOver()});
+        $('#empty', this.obj).bind('change', () => {searchLess()});
         resultSpread.getActiveSheet().bind(spreadNS.Events.CellDoubleClick, function (e, info) {
             const sheet = info.sheet;
             const data = sheet.zh_data;
@@ -162,5 +221,5 @@
             }
         });
         return {spread: resultSpread};
-    }
+    };
 })(jQuery);

+ 9 - 1
app/public/js/material.js

@@ -362,7 +362,15 @@ $(document).ready(() => {
             const sortData = info.sheet.zh_data || [];
             if (info.cellRange.row + info.cellRange.rowCount > sortData.length) {
                 toastMessageUniq(hint.cellError);
-                SpreadJsObj.loadSheetData(materialSpread.getActiveSheet(), SpreadJsObj.DataType.Data, materialBillsData);
+                // SpreadJsObj.loadSheetData(materialSpread.getActiveSheet(), SpreadJsObj.DataType.Data, materialBillsData);
+                SpreadJsObj.reLoadSheetHeader(materialSpread.getActiveSheet());
+                SpreadJsObj.reLoadSheetData(materialSpread.getActiveSheet());
+                return;
+            }
+            if (sortData.length > 0 && range.col + range.colCount > 18) {
+                toastMessageUniq(hint.cellError);
+                SpreadJsObj.reLoadSheetHeader(materialSpread.getActiveSheet());
+                SpreadJsObj.reLoadSheetData(materialSpread.getActiveSheet());
                 return;
             }
             const data = [];

+ 144 - 4
app/public/js/material_list.js

@@ -48,7 +48,59 @@ function calcOneBQJC(xmj) {
     return jiacha;
 }
 
+function getPasteHint (str, row = '') {
+    let returnObj = str;
+    if (row) {
+        returnObj.msg = '清单第' + (row+1) + '行' + str.msg;
+    }
+    return returnObj;
+}
+
+const is_numeric = (value) => {
+    if (typeof(value) === 'object') {
+        return false;
+    } else {
+        return !Number.isNaN(Number(value)) && value.toString().trim() !== '';
+    }
+};
+
 $(document).ready(() => {
+    function TipCellType()
+    {
+    }
+    TipCellType.prototype = new GC.Spread.Sheets.CellTypes.ColumnHeader();
+    TipCellType.prototype.getHitInfo = function (x, y, cellStyle, cellRect, context) {
+        return { x: x, y: y, row: context.row, col: context.col, cellRect: cellRect, sheetArea: context.sheetArea, sheet: context.sheet };
+    };
+    TipCellType.prototype.processMouseEnter = function (hitInfo){
+        if (!this._toolTipElement) {
+            var div = document.createElement("div");
+            $(div).css("position", "absolute")
+                .css("border", "1px #C0C0C0 solid")
+                .css("box-shadow", "1px 2px 5px rgba(0,0,0,0.4)")
+                .css("font", "9pt Arial")
+                .css("background", "#fff")
+                // .css("color", "#fff")
+                .css("z-index", "1000")
+                .css("padding", 5);
+            this._toolTipElement = div;
+        }
+        $(this._toolTipElement).text("单位数量:每一单位清单下所需工料消耗量。")
+            .css("top", hitInfo.y + 15)
+            .css("left", hitInfo.x - 15);
+        $(this._toolTipElement).hide();
+        // document.body.insertBefore(this._toolTipElement, null);
+        $('#material-spread-div').append(this._toolTipElement, null);
+        $(this._toolTipElement).show("fast");
+    };
+    TipCellType.prototype.processMouseLeave = function (hitInfo) {
+        if (this._toolTipElement) {
+            // document.body.removeChild(this._toolTipElement);
+            // $('#material-spread-div').removeChild(this._toolTipElement);
+            this._toolTipElement.remove();
+            this._toolTipElement = null;
+        }
+    };
     autoFlashHeight();
     // 清单table
     const ledgerSpread = SpreadJsObj.createNewSpread($('#ledger-spread')[0]);
@@ -86,7 +138,7 @@ $(document).ready(() => {
             // console.log(gcl.leafXmjs);
             SpreadJsObj.loadSheetData(leafXmjSpread.getActiveSheet(), SpreadJsObj.DataType.Data, gcl.leafXmjs);
             // 对清单调差工料table的单位数量进行改变
-            materialSpreadSetting.cols[materialSpreadSetting.cols.length - 1].title = '|' + gcl.unit + '数量';
+            materialSpreadSetting.cols[materialSpreadSetting.cols.length - 1].title = '|' + gcl.unit + '数量';
             SpreadJsObj.initSheet(materialSpread.getActiveSheet(), materialSpreadSetting);
         } else {
             SpreadJsObj.loadSheetData(leafXmjSpread.getActiveSheet(), SpreadJsObj.DataType.Data, []);
@@ -146,7 +198,7 @@ $(document).ready(() => {
             {title: '清单调差工料|编号', colSpan: '4|1', rowSpan: '1|1', field: 'code', hAlign: 0, width: 80, formatter: '@', readOnly: true},
             {title: '|名称', colSpan: '|1', rowSpan: '|1', field: 'name', hAlign: 0, width: 100, formatter: '@', readOnly: true},
             {title: '|单位', colSpan: '|1', rowSpan: '|1', field: 'unit', hAlign: 1, width: 60, formatter: '@', readOnly: true},
-            {title: '|数量', colSpan: '1', rowSpan: '|1', field: 'quantity', hAlign: 2, width: 60, type: 'Number', readOnly: 'readOnly.isEdit'},
+            {title: '|数量', colSpan: '1', rowSpan: '|1', field: 'quantity', hAlign: 2, width: 80, type: 'Number', readOnly: 'readOnly.isEdit'},
         ],
         emptyRows: 0,
         headRows: 2,
@@ -197,6 +249,10 @@ $(document).ready(() => {
 
     loadLeafXmjData(0);
     loadMaterialData(0, 0);
+    const sheet = materialSpread.getActiveSheet();
+    sheet.suspendPaint();
+    sheet.setCellType(1, 3, new TipCellType(), spreadNS.SheetArea.colHeader);
+    sheet.resumePaint();
     // 不参与调差数据值变灰
     function checkNotJoinMaterialData() {
         const sheet = ledgerSpread.getActiveSheet();
@@ -365,12 +421,16 @@ $(document).ready(() => {
                     SpreadJsObj.reLoadRowData(lsheet, iRow);
                 });
             },
+            deletePress: function (sheet) {
+                return;
+            },
             editEnded: function (e, info) {
                 if (info.sheet.zh_setting) {
                     const select = SpreadJsObj.getSelectObject(info.sheet);
                     const col = info.sheet.zh_setting.cols[info.col];
                     // 未改变值则不提交
-                    const validText = info.editingText ? (typeof(info.editingText) === 'String' ? info.editingText.replace('\n', '') : info.editingText) : null;
+                    // const validText = info.editingText ? (typeof(info.editingText) === 'String' ? info.editingText.replace('\n', '') : info.editingText) : null;
+                    const validText = is_numeric(info.editingText) ? parseFloat(info.editingText) : (info.editingText ? trimInvalidChar(info.editingText) : null);
                     const orgValue = select[col.field];
                     if (orgValue == validText || ((!orgValue || orgValue === '') && (validText === '' || validText === null))) {
                         SpreadJsObj.reLoadRowData(info.sheet, info.row);
@@ -404,9 +464,89 @@ $(document).ready(() => {
                         SpreadJsObj.reLoadRowData(info.sheet, info.row);
                     });
                 }
-            }
+            },
+            clipboardPasted(e, info) {
+                const hint = {
+                    cellError: {type: 'error', msg: '粘贴内容超出了表格范围'},
+                    numberExpr: {type: 'error', msg: '不能粘贴其它非数字类型字符'},
+                    numberCan: {type: 'error', msg: '请粘贴大于0并且小于6位小数的浮点数'},
+                };
+                const range = info.cellRange;
+                const sortData = info.sheet.zh_data || [];
+                if (range.row + range.rowCount > sortData.length) {
+                    toastMessageUniq(hint.cellError);
+                    SpreadJsObj.reLoadSheetHeader(materialSpread.getActiveSheet());
+                    SpreadJsObj.reLoadSheetData(materialSpread.getActiveSheet());
+                    return;
+                }
+                if (sortData.length > 0 && range.col + range.colCount > 4) {
+                    toastMessageUniq(hint.cellError);
+                    SpreadJsObj.reLoadSheetHeader(materialSpread.getActiveSheet());
+                    SpreadJsObj.reLoadSheetData(materialSpread.getActiveSheet());
+                    return;
+                }
+                const data = [];
+                for (let iRow = 0; iRow < range.rowCount; iRow++) {
+                    let bPaste = true;
+                    const curRow = range.row + iRow;
+                    const materialData = { id: sortData[curRow].id, mb_id: sortData[curRow].mb_id };
+                    const hintRow = range.rowCount > 1 ? curRow : '';
+                    let sameCol = 0;
+                    for (let iCol = 0; iCol < range.colCount; iCol++) {
+                        const curCol = range.col + iCol;
+                        const colSetting = info.sheet.zh_setting.cols[curCol];
+                        if (!colSetting) continue;
+
+                        let validText = info.sheet.getText(curRow, curCol);
+                        validText = is_numeric(validText) ? parseFloat(validText) : (validText ? trimInvalidChar(validText) : null);
+                        const orgValue = sortData[curRow][colSetting.field];
+                        if (orgValue == validText || ((!orgValue || orgValue === '') && (validText === ''))) {
+                            sameCol++;
+                            if (range.colCount === sameCol)  {
+                                bPaste = false;
+                            }
+                            continue;
+                        }
+                        if (colSetting.field === 'quantity') {
+                            if (isNaN(validText)) {
+                                toastMessageUniq(getPasteHint(hint.numberExpr, hintRow));
+                                bPaste = false;
+                                continue;
+                            }
+                            const num = parseFloat(validText);
+                            if (num < 0 || !/^\d+(\.\d{1,6})?$/.test(num)) {
+                                toastMessageUniq(getPasteHint(hint.numberCan, hintRow));
+                                bPaste = false;
+                                continue;
+                            }
+                        }
+                        materialData[colSetting.field] = validText;
+                    }
+                    if (bPaste) {
+                        data.push(materialData);
+                        // rowData.push(curRow);
+                    } else {
+                        SpreadJsObj.reLoadRowData(info.sheet, curRow);
+                    }
+                }
+                if (data.length === 0) {
+                    SpreadJsObj.reLoadRowData(info.sheet, info.cellRange.row, info.cellRange.rowCount);
+                    return;
+                }
+                // 更新至服务器
+                postData(window.location.pathname + '/save', { type:'paste', updateData: data }, function (result) {
+                    materialListData = result;
+                    const [iGclRow, iRow, sheet, lselect] = leafXmjSpreadObj.getSelect();
+                    gclGatherData[iGclRow].leafXmjs[iRow].jiacha = calcOneBQJC(lselect);
+                    SpreadJsObj.reLoadRowData(sheet, iRow);
+                }, function () {
+                    SpreadJsObj.reLoadRowData(info.sheet, info.cellRange.row, info.cellRange.rowCount);
+                });
+            },
         };
         materialSpread.bind(spreadNS.Events.EditEnded, materialSpreadObj.editEnded);
+        materialSpread.bind(spreadNS.Events.ClipboardPasted, materialSpreadObj.clipboardPasted);
+        SpreadJsObj.addDeleteBind(materialSpread, materialSpreadObj.deletePress);
         // 应用调差工料至其他清单明细
         $('#user_all_material').click(function () {
             const sheet = materialSpread.getActiveSheet();

+ 16 - 0
app/public/js/measure_compare.js

@@ -244,4 +244,20 @@ $(document).ready(() => {
             }
         });
     })('a[name=showLevel]', billsSheet);
+
+    $('#exportExcel').click(function () {
+        const data = [];
+        if (!billsSheet.zh_tree) return;
+        for (const node of billsSheet.zh_tree.nodes) {
+            data.push(node);
+            const posRange = cPos.getLedgerPos(node.id);
+            if (posRange && posRange.length > 0) {
+                for (const pr of posRange) {
+                    data.push(pr);
+                }
+            }
+        }
+
+        SpreadExcelObj.exportSimpleXlsxSheet(billsSpreadSetting, data, $('h2')[0].innerHTML + "-多期比较.xlsx");
+    });
 });

+ 52 - 29
app/public/js/revise.js

@@ -282,35 +282,50 @@ $(document).ready(() => {
                 }
             }
 
-            postData(window.location.pathname + '/update', {
-                postType: type,
-                postData: {
-                    id: node.ledger_id,
-                    count: type === 'add' ? addCount : count,
-                }
-            }, function (result) {
-                const refreshData = tree.loadPostData(result);
-                self.refreshTree(sheet, refreshData);
-                if (type === 'delete') {
-                    const sel = sheet.getSelections()[0];
-                    if (sel) {
-                        sheet.setSelection(sel.row, sel.col, 1, sel.colCount);
-                    }
-                } else if (['up-move', 'down-move'].indexOf(type) > -1) {
-                    const sel = sheet.getSelections()[0];
-                    if (sel) {
-                        sheet.setSelection(tree.nodes.indexOf(node), sel.col, sel.rowCount, sel.colCount);
-                        SpreadJsObj.reloadRowsBackColor(sheet, [sel.row, tree.nodes.indexOf(node)]);
+            if (type === 'delete') {
+                deleteAfterHint(function () {
+                    postData(window.location.pathname + '/update', {
+                        postType: type,
+                        postData: {
+                            id: node.ledger_id,
+                            count: type === 'add' ? addCount : count,
+                        }
+                    }, function (result) {
+                        const refreshData = tree.loadPostData(result);
+                        self.refreshTree(sheet, refreshData);
+                        const sel = sheet.getSelections()[0];
+                        if (sel) {
+                            sheet.setSelection(sel.row, sel.col, 1, sel.colCount);
+                        }
+                        self.refreshOperationValid(sheet);
+                    });
+                });
+            } else {
+                postData(window.location.pathname + '/update', {
+                    postType: type,
+                    postData: {
+                        id: node.ledger_id,
+                        count: type === 'add' ? addCount : count,
                     }
-                } else if (type === 'add') {
-                    const sel = sheet.getSelections()[0];
-                    if (sel) {
-                        sheet.setSelection(tree.nodes.indexOf(refreshData.create[0]), sel.col, sel.rowCount, sel.colCount);
-                        SpreadJsObj.reloadRowsBackColor(sheet, [sel.row, tree.nodes.indexOf(refreshData.create[0])]);
+                }, function (result) {
+                    const refreshData = tree.loadPostData(result);
+                    self.refreshTree(sheet, refreshData);
+                    if (['up-move', 'down-move'].indexOf(type) > -1) {
+                        const sel = sheet.getSelections()[0];
+                        if (sel) {
+                            sheet.setSelection(tree.nodes.indexOf(node), sel.col, sel.rowCount, sel.colCount);
+                            SpreadJsObj.reloadRowsBackColor(sheet, [sel.row, tree.nodes.indexOf(node)]);
+                        }
+                    } else if (type === 'add') {
+                        const sel = sheet.getSelections()[0];
+                        if (sel) {
+                            sheet.setSelection(tree.nodes.indexOf(refreshData.create[0]), sel.col, sel.rowCount, sel.colCount);
+                            SpreadJsObj.reloadRowsBackColor(sheet, [sel.row, tree.nodes.indexOf(refreshData.create[0])]);
+                        }
                     }
-                }
-                self.refreshOperationValid(sheet);
-            });
+                    self.refreshOperationValid(sheet);
+                });
+            }
         },
         /**
          * 编辑单元格响应事件
@@ -1573,6 +1588,11 @@ $(document).ready(() => {
             this.obj = obj;
             this.billsCount = 6;
             this.posCount = 1000;
+            this.filter = getLocalCache('zh-calc-batch-filter');
+            if (!this.filter) {
+                this.filter = '1';
+            }
+            $('input[name=batch-filter]')[0].checked = this.filter && this.filter != '0';
             // 初始化 清单编号窗口 参数
             this.qdSpreadSetting = {
                 cols: [
@@ -1715,6 +1735,9 @@ $(document).ready(() => {
                     }, null, true);
                 }
             });
+            $('input[name=batch-filter]').change(function () {
+                setLocalCache('zh-calc-batch-filter', this.checked ? 1 : 0);
+            });
 
             $.contextMenu({
                 selector: '.batch-l-t',
@@ -1800,17 +1823,17 @@ $(document).ready(() => {
                     price: _.toNumber(qdSheet.getText(iRow, 3)),
                     pos: [],
                 };
-                result.push(qd);
                 for (let iPosRow = 0; iPosRow < posSheet.getRowCount(); iPosRow++) {
                     const value = _.toNumber(posSheet.getText(iPosRow, iRow + 2));
                     if (value !== 0 && !isNaN(value)) {
                         qd.pos.push({
                             name: posSheet.getText(iPosRow, 0),
                             drawing_code: posSheet.getText(iPosRow, 1),
-                            quantity: value,
+                            quantity: value, porder: qd.pos.length + 1,
                         });
                     }
                 }
+                if (!$('input[name=batch-filter]')[0].checked || qd.pos.length > 0) result.push(qd);
             }
             return result;
         }

+ 15 - 11
app/public/js/se_bonus.js

@@ -54,17 +54,17 @@ $(document).ready(() => {
             },
             {title: '编号', colSpan: '1', rowSpan: '1', field: 'code', hAlign: 0, width: 150, formatter: '@', readOnly: isPre, },
             {title: '发文单位', colSpan: '1', rowSpan: '1', field: 'doc_co', hAlign: 0, width: 150, formatter: '@', readOnly: isPre},
-            // {
-            //     title: '依据材料证明', colSpan: '1', rowSpan: '1', field: 'proof_file', hAlign: 1, width: 80, formatter: '@',
-            //     readOnly: true, cellType: 'imageBtn', normalImg: '#rela-file-icon', hoverImg: '#rela-file-hover',
-            //     getValue: function (data) {
-            //         return data.proof_file ? data.proof_file.length : 0;
-            //     },
-            //     showImage: function (data) {
-            //         return data !== undefined && data !== null;
-            //     },
-            // },
-            {title: '依据材料证明', colSpan: '1', rowSpan: '1', field: 'proof', hAlign: 0, width: 150, formatter: '@', readOnly: isPre},
+            {
+                title: '依据材料证明', colSpan: '1', rowSpan: '1', field: 'proof_file', hAlign: 1, width: 80, formatter: '@',
+                readOnly: true, cellType: 'imageBtn', normalImg: '#rela-file-icon', hoverImg: '#rela-file-hover',
+                getValue: function (data) {
+                    return data.proof_file ? data.proof_file.length : 0;
+                },
+                showImage: function (data) {
+                    return data !== undefined && data !== null;
+                },
+            },
+            // {title: '依据材料证明', colSpan: '1', rowSpan: '1', field: 'proof', hAlign: 0, width: 150, formatter: '@', readOnly: isPre},
             {
                 title: '计量期', colSpan: '1', rowSpan: '1', field: 'sorder', hAlign: 1, width: 100, formatter: '@',
                 getValue: function (data) {
@@ -498,4 +498,8 @@ $(document).ready(() => {
             },
         });
     }
+
+    $('#exportExcel').click(function () {
+        SpreadExcelObj.exportSimpleXlsxSheet(spreadSetting, bonusObj.data, $('h2')[0].innerHTML + "-奖罚金.xlsx");
+    });
 });

+ 4 - 0
app/public/js/se_jgcl.js

@@ -460,4 +460,8 @@ $(document).ready(() => {
             },
         })
     }
+
+    $('#exportExcel').click(function () {
+        SpreadExcelObj.exportSimpleXlsxSheet(spreadSetting, jgclObj.data, $('h2')[0].innerHTML + "-甲供材料.xlsx");
+    });
 });

+ 4 - 0
app/public/js/se_other.js

@@ -460,4 +460,8 @@ $(document).ready(() => {
             },
         })
     }
+
+    $('#exportExcel').click(function () {
+        SpreadExcelObj.exportSimpleXlsxSheet(otherSpreadSetting, seOtherObj.data, $('h2')[0].innerHTML + "-其他.xlsx");
+    });
 });

+ 7 - 1
app/public/js/shares/export_excel.js

@@ -12,6 +12,8 @@ const SpreadExcelObj = (function() {
     const _createHideSpread = function () {
         const div = document.createElement("div");
         div.setAttribute('id', 'exportExcelSpread');
+        //$(div).css("position", "absolute").css('top', 0).css('left', 0).css('height', 300).css('width', 500).css('z-index', 999);
+        //document.body.insertBefore(div, null);
         div.style.display = 'none';
         return div;
     };
@@ -35,6 +37,8 @@ const SpreadExcelObj = (function() {
 
         const spread = SpreadJsObj.createNewSpread(div);
         const sheet = spread.getActiveSheet();
+
+        SpreadJsObj.beginMassOperation(sheet);
         sheet.options.isProtected = false;
         sheet.setColumnCount(setting.cols.length);
         sheet.setRowCount(setting.headRows + data.length);
@@ -45,7 +49,8 @@ const SpreadExcelObj = (function() {
         for (let iCol = 0; iCol < setting.cols.length; iCol++) {
             const col = setting.cols[iCol];
             const title = col.title.split('|');
-            const colSpan = col.colSpan ? col.colSpan.split('|'): ['1'], rowSpan = col.rowSpan ? col.rowSpan.split('|'): ['1'];
+            const colSpan = col.colSpan ? col.colSpan.split('|') : ['1'],
+                rowSpan = col.rowSpan ? col.rowSpan.split('|') : ['1'];
             for (let i = 0; i < title.length; i++) {
                 const cell = sheet.getCell(i, iCol);
                 cell.text(title[i]).wordWrap(true).hAlign(1).vAlign(1).font(setting.headerFont);
@@ -79,6 +84,7 @@ const SpreadExcelObj = (function() {
                 cell.hAlign(col.hAlign);
             }
         }
+        SpreadJsObj.endMassOperation(sheet);
 
         const excelIo = new GC.Spread.Excel.IO();
         const sJson = JSON.stringify(spread.toJSON());

+ 47 - 0
app/public/js/shares/tender_list_order.js

@@ -0,0 +1,47 @@
+'use strict';
+
+/**
+ *
+ *
+ * @author Mai
+ * @date
+ * @version
+ */
+
+const tenderListOrder = (function () {
+    let orderSetting = getLocalCache('zh-calc-tender-list-order');
+    if (!orderSetting) orderSetting = 'name|up';
+    function reOrderTenders (orderStr) {
+        if (orderStr) {
+            orderSetting = orderStr;
+            setLocalCache('zh-calc-tender-list-order', orderStr);
+        }
+        const orders = orderSetting.split('|');
+        if (orders[0] === 'name') {
+            tenders.sort(function (a, b) {
+                return orders[1] === 'up'
+                    ? a[orders[0]].localeCompare(b[orders[0]], 'zh-CN')
+                    : -a[orders[0]].localeCompare(b[orders[0]], 'zh-CN')
+            });
+        } else if (orders[0] === 'create_time') {
+            tenders.sort(function (a, b){
+                return orders[1] === 'up'
+                    ? Date.parse(a[orders[0]]) - Date.parse(b[orders[0]])
+                    : Date.parse(b[orders[0]]) - Date.parse(a[orders[0]]);
+            })
+        }
+        initTenderTree();
+        $('.c-body').html(getTenderTreeHtml());
+        localHideList();
+    }
+    function getOrderButton(field) {
+        const orders = orderSetting.split('|');
+        const button = field === orders[0]
+            ? (orders[1] === 'up'
+                ? '<i class="fa fa-sort-amount-asc" aria-hidden="true" onclick="tenderListOrder.reOrderTenders(\'' + field + '|down' + '\')"></i>'
+                : '<i class="fa fa-sort-amount-desc" aria-hidden="true" onclick="tenderListOrder.reOrderTenders(\'' + field + '|up' + '\')"></i>')
+            : '<i class="fa fa-sort" aria-hidden="true" onclick="tenderListOrder.reOrderTenders(\'' + field + '|up' + '\')"></i>';
+        return '<a href="javascript:void(0)" class="btn btn-sm ml-1">' + button + '</a>';
+    }
+    return { reOrderTenders, getOrderButton }
+})();

+ 67 - 146
app/public/js/stage.js

@@ -466,6 +466,12 @@ $(document).ready(() => {
     ledgerSpreadSetting.dgnUpFields = ['deal_dgn_qty1', 'deal_dgn_qty2', 'c_dgn_qty1', 'c_dgn_qty2'];
     ledgerSpreadSetting.getColor = function (sheet, data, row, col, defaultColor) {
         if (data) {
+            const posRange = stagePos.ledgerPos[itemsPre + data.id] || [];
+            if (posRange.length > 0) {
+                for (const p of posRange) {
+                    if (p.end_contract_qty > p.quantity) return '#f8d7da';
+                }
+            }
             if (data.is_tp) {
                 return data.end_contract_tp > data.total_price ? '#f8d7da' : defaultColor;
             } else {
@@ -1362,11 +1368,11 @@ $(document).ready(() => {
         // stageTree.loadCurStageData(curStageData);
         // stageTree.loadPreStageData(preStageData);
         treeCalc.calculateAll(stageTree);
-        SpreadJsObj.loadSheetData(slSpread.getActiveSheet(), 'tree', stageTree);
-        SpreadJsObj.loadTopAndSelect(slSpread.getActiveSheet(), ckBillsSpread);
         // 加载部位明细
         stagePos.loadDatas(result.posData);
         stagePos.calculateAll();
+        SpreadJsObj.loadSheetData(slSpread.getActiveSheet(), 'tree', stageTree);
+        SpreadJsObj.loadTopAndSelect(slSpread.getActiveSheet(), ckBillsSpread);
         stagePosSpreadObj.loadCurPosData();
         SpreadJsObj.resetTopAndSelect(spSpread.getActiveSheet());
         // 加载中间计量
@@ -1526,145 +1532,6 @@ $(document).ready(() => {
         $('#row-view').modal('hide');
     });
 
-    class SearchLedger {
-        constructor(obj, mainSpread) {
-            const self = this;
-            this.obj = obj;
-            this.mainSpread = mainSpread;
-            this.spreadSetting = {
-                cols: [
-                    {title: '项目节编号', field: 'code', hAlign: 0, width: 120, formatter: '@'},
-                    {title: '清单编号', field: 'b_code', hAlign: 0, width: 80, formatter: '@'},
-                    {title: '名称', field: 'name', width: 150, hAlign: 0, formatter: '@'},
-                    {title: '单位', field: 'unit', width: 50, hAlign: 1, formatter: '@'},
-                    {title: '单价', field: 'unit_price', hAlign: 2, width: 50},
-                    {title: '数量', field: 'quantity', hAlign: 2, width: 50},
-                    {title: '完成率(%)', field: 'complete_percent', hAlign: 2, width: 70},
-                ],
-                emptyRows: 0,
-                headRows: 1,
-                headRowHeight: [32],
-                defaultRowHeight: 21,
-                headerFont: '12px 微软雅黑',
-                font: '12px 微软雅黑',
-                readOnly: true,
-                selectedBackColor: '#fffacd',
-            };
-            this.spread = SpreadJsObj.createNewSpread($('#search-result', this.obj)[0]);
-            SpreadJsObj.initSheet(this.spread.getActiveSheet(), this.spreadSetting);
-            $('#keyword', this.obj).bind('keydown', function(e) {
-                if (e.keyCode == 13) self.searchText();
-            });
-            $('#searchLedger', this.obj).bind('click', () => {self.searchText()});
-            $('#over', this.obj).bind('change', () => {self.searchOver()});
-            $('#empty', this.obj).bind('change', () => {self.searchLess()});
-            this.spread.getActiveSheet().bind(GC.Spread.Sheets.Events.CellDoubleClick, function (e, info) {
-                const sheet = info.sheet;
-                const data = sheet.zh_data;
-                if (!data) { return }
-
-                const curBills = data[info.row];
-                if (!curBills) { return }
-
-                SpreadJsObj.locateTreeNode(self.mainSpread.getActiveSheet(), curBills.ledger_id, true);
-                stagePosSpreadObj.loadCurPosData();
-            });
-        }
-        calculateCompletePercent() {
-            if (!this.searchResult) return;
-            for (const sr of this.searchResult) {
-                const base = ZhCalc.add(sr.total_price, sr.end_qc_tp);
-                sr.complete_percent = base !== 0 ? ZhCalc.mul(ZhCalc.div(sr.end_gather_tp, base), 100, 2) : 0;
-            }
-        }
-        searchText() {
-            const keyword = $('#keyword', this.obj).val();
-            if (keyword !== '') {
-                $('#over', this.obj)[0].checked = false;
-                $('#empty', this.obj)[0].checked = false;
-                this.searchResult = [];
-                const sortData = SpreadJsObj.getSortData(this.mainSpread.getActiveSheet());
-                for (const node of sortData) {
-                    if ((node.code && node.code.indexOf(keyword) > -1) ||
-                        node.b_code && node.b_code.indexOf(keyword) > -1 ||
-                        node.name && node.name.indexOf(keyword) > -1) {
-                        const data = JSON.parse(JSON.stringify(node));
-                        data.visible = true;
-                        this.searchResult.push(data);
-                    }
-                }
-                this.calculateCompletePercent();
-                SpreadJsObj.loadSheetData(this.spread.getActiveSheet(), 'data', this.searchResult);
-            }
-        }
-        searchOver() {
-            this.searchResult = [];
-            const sortData = SpreadJsObj.getSortData(this.mainSpread.getActiveSheet());
-            for (const node of sortData) {
-                if (node.children && node.children.length > 0) continue;
-                if (node.end_gather_qty) {
-                    if (!node.quantity || Math.abs(node.end_gather_qty) > Math.abs(ZhCalc.add(node.quantity, node.end_qc_qty))) {
-                        const data = JSON.parse(JSON.stringify(node));
-                        data.visible = true;
-                        this.searchResult.push(data);
-                    }
-                } else if (node.end_gather_tp) {
-                    if (!node.total_price || Math.abs(node.end_gather_tp) > Math.abs(ZhCalc.add(node.total_price, node.end_qc_tp))) {
-                        const data = JSON.parse(JSON.stringify(node));
-                        data.visible = true;
-                        this.searchResult.push(data);
-                    }
-                }
-            }
-            this.calculateCompletePercent();
-            SpreadJsObj.loadSheetData(this.spread.getActiveSheet(), 'data', this.searchResult);
-        }
-        searchEmpty() {
-            this.searchResult = [];
-            const sortData = SpreadJsObj.getSortData(this.mainSpread.getActiveSheet());
-            for (const node of sortData) {
-                if (node.children && node.children.length > 0) continue;
-                if (node.quantity) {
-                    if (!node.end_gather_qty || checkZero(node.end_gather_qty)) {
-                        const data = JSON.parse(JSON.stringify(node));
-                        data.visible = true;
-                        this.searchResult.push(data);
-                    }
-                } else if (node.total_price) {
-                    if (!node.end_gather_tp || checkZero(node.end_gather_tp)) {
-                        const data = JSON.parse(JSON.stringify(node));
-                        data.visible = true;
-                        this.searchResult.push(data);
-                    }
-                }
-            }
-            this.calculateCompletePercent();
-            SpreadJsObj.loadSheetData(this.spread.getActiveSheet(), 'data', this.searchResult);
-        }
-        searchLess() {
-            this.searchResult = [];
-            const sortData = SpreadJsObj.getSortData(this.mainSpread.getActiveSheet());
-            for (const node of sortData) {
-                if (node.children && node.children.length > 0) continue;
-                if (node.quantity) {
-                    if (ZhCalc.sub(ZhCalc.add(node.quantity, node.end_qc_qty), node.end_gather_qty) > 0) {
-                        const data = JSON.parse(JSON.stringify(node));
-                        data.visible = true;
-                        this.searchResult.push(data);
-                    }
-                } else if (node.total_price) {
-                    if (ZhCalc.sub(ZhCalc.add(node.total_price, node.end_qc_tp), node.end_gather_tp) > 0) {
-                        const data = JSON.parse(JSON.stringify(node));
-                        data.visible = true;
-                        this.searchResult.push(data);
-                    }
-                }
-            }
-            this.calculateCompletePercent();
-            SpreadJsObj.loadSheetData(this.spread.getActiveSheet(), 'data', this.searchResult);
-        }
-    }
-
     const posSearch = (function () {
         let resultArr = [];
         const search = function () {
@@ -2678,7 +2545,61 @@ $(document).ready(() => {
             tabPanel.addClass('active');
             showTools(tab.hasClass('active'));
             if (tab.attr('content') === '#search' && !searchLedger) {
-                searchLedger = new SearchLedger($('#search'), slSpread);
+                searchLedger = $.billsSearch({
+                    selector: '#search',
+                    searchSpread: slSpread,
+                    searchRangeStr: '项目节编号/名称',
+                    searchOver: true,
+                    searchEmpty: true,
+                    resultSpreadSetting: {
+                        cols: [
+                            {title: '项目节编号', field: 'code', hAlign: 0, width: 120, formatter: '@'},
+                            {title: '清单编号', field: 'b_code', hAlign: 0, width: 80, formatter: '@'},
+                            {title: '名称', field: 'name', width: 150, hAlign: 0, formatter: '@'},
+                            {title: '单位', field: 'unit', width: 50, hAlign: 1, formatter: '@'},
+                            {title: '单价', field: 'unit_price', hAlign: 2, width: 50},
+                            {title: '数量', field: 'quantity', hAlign: 2, width: 50},
+                            {title: '完成率(%)', field: 'complete_percent', hAlign: 2, width: 70},
+                        ],
+                        emptyRows: 0,
+                        headRows: 1,
+                        headRowHeight: [32],
+                        defaultRowHeight: 21,
+                        headerFont: '12px 微软雅黑',
+                        font: '12px 微软雅黑',
+                        selectedBackColor: '#fffacd',
+                    },
+                    afterLocated: function () {
+                        stagePosSpreadObj.loadCurPosData();
+                    },
+                    checkLess: function (node) {
+                        if (node.quantity) {
+                            return ZhCalc.sub(ZhCalc.add(node.quantity, node.end_qc_qty), node.end_gather_qty) > 0;
+                        } else if (node.total_price) {
+                            return ZhCalc.sub(ZhCalc.add(node.total_price, node.end_qc_tp), node.end_gather_tp) > 0;
+                        }
+                    },
+                    checkOver: function (node) {
+                        const posRange = stagePos.ledgerPos[itemsPre + node.id] || [];
+                        if (posRange.length > 0) {
+                            for (const p of posRange) {
+                                if (p.end_contract_qty > p.quantity) return true;
+                            }
+                            return false;
+                        } else if (node.end_gather_qty) {
+                            return !node.quantity || Math.abs(node.end_gather_qty) > Math.abs(ZhCalc.add(node.quantity, node.end_qc_qty));
+                        } else if (node.end_gather_tp) {
+                            return !node.total_price || Math.abs(node.end_gather_tp) > Math.abs(ZhCalc.add(node.total_price, node.end_qc_tp));
+                        }
+                    },
+                    checkEmpty: function (node) {
+                        if (node.quantity) {
+                            return !node.end_gather_qty || checkZero(node.end_gather_qty);
+                        } else if (node.total_price) {
+                            return !node.end_gather_tp || checkZero(node.end_gather_tp);
+                        }
+                    },
+                });
                 searchLedger.spread.refresh();
             }
             if (tab.attr('content') === '#fujian') {
@@ -2902,14 +2823,14 @@ $(document).ready(() => {
 
     // 切换页数
     $('.page-select').on('click', function () {
-        const totalPageNum = $('#totalPage').text();
-        const lastPageNum = $('#currentPage').text();
+        const totalPageNum = parseInt($('#totalPage').text());
+        const lastPageNum = parseInt($('#currentPage').text());
         const status = $(this).attr('content');
         if (status === 'pre' && lastPageNum > 1) {
-            getAllList(parseInt(lastPageNum)-1);
+            getAllList(lastPageNum-1);
             $('#showAttachment').hide();
         } else if (status === 'next' && lastPageNum < totalPageNum) {
-            getAllList(parseInt(lastPageNum)+1);
+            getAllList(lastPageNum+1);
             $('#showAttachment').hide();
         }
     });

+ 243 - 20
app/public/js/stage_bwtz.js

@@ -8,7 +8,10 @@
  * @version
  */
 
+
+
 $(document).ready(() => {
+    let xmjSearch;
     const preUrl = window.location.pathname.split('/').slice(0, 6).join('/');
     autoFlashHeight();
     const xmjSpread = SpreadJsObj.createNewSpread($('#xmj-spread')[0]);
@@ -33,27 +36,29 @@ $(document).ready(() => {
     xmjSpread.bind(spreadNS.Events.SelectionChanged, function (e, info) {
         unitTreeObj.loadCurUnitData();
     });
-
-    postData(window.location.pathname + '/load', {}, function (result) {
-        const setting = {
-            id: 'ledger_id',
-            pid: 'ledger_pid',
-            order: 'order',
-            level: 'level',
-            rootId: -1,
-            fullPath: 'full_path',
-        };
-        const xmjTree = createNewPathTree('base', setting);
-        xmjTree.loadDatas(result);
-        for (const n of xmjTree.nodes) {
-            if (n.unitTreeData) {
-                n.unitTree = createNewPathTree('base', setting);
-                n.unitTree.loadDatas(n.unitTreeData);
+    const loadData = function (dataType) {
+        postData(window.location.pathname + '/load', {filter: dataType}, function (result) {
+            const setting = {
+                id: 'ledger_id',
+                pid: 'ledger_pid',
+                order: 'order',
+                level: 'level',
+                rootId: -1,
+                fullPath: 'full_path',
+            };
+            const xmjTree = createNewPathTree('base', setting);
+            xmjTree.loadDatas(result);
+            for (const n of xmjTree.nodes) {
+                if (n.unitTreeData) {
+                    n.unitTree = createNewPathTree('base', setting);
+                    n.unitTree.loadDatas(n.unitTreeData);
+                }
             }
-        }
-        SpreadJsObj.loadSheetData(xmjSheet, SpreadJsObj.DataType.Tree, xmjTree);
-        unitTreeObj.loadCurUnitData();
-    });
+            SpreadJsObj.loadSheetData(xmjSheet, SpreadJsObj.DataType.Tree, xmjTree);
+            unitTreeObj.loadCurUnitData();
+        });
+    };
+    loadData('all');
 
     $.subMenu({
         menu: '#sub-menu', miniMenu: '#sub-mini-menu', miniMenuList: '#mini-menu-list',
@@ -83,4 +88,222 @@ $(document).ready(() => {
             unitSpread.refresh();
         }
     });
+    $.divResizer({
+        select: '#right-spr',
+        callback: function () {
+            xmjSpread.refresh();
+            unitSpread.refresh();
+            if (xmjSearch) {
+                xmjSearch.spread.refresh();
+            }
+        }
+    });
+    $('a', '.side-menu').bind('click', function (e) {
+        e.preventDefault();
+        const tab = $(this), tabPanel = $(tab.attr('content'));
+        const showSideTools = function (show) {
+            const left = $('#left-view'), right = $('#right-view'), parent = left.parent();
+            if (show) {
+                right.show();
+                autoFlashHeight();
+                /**
+                 * right.show()后, parent被撑开成2倍left.height, 导致parent.width减少了10px
+                 * 第一次left.width调整后,parent的缩回left.height, 此时parent.width又增加了10px
+                 * 故需要通过最终的parent.width再计算一次left.width
+                 *
+                 * Q: 为什么不通过先计算left.width的宽度,以避免计算两次left.width?
+                 * A: 右侧工具栏不一定显示,当右侧工具栏显示过一次后,就必须使用parent和right来计算left.width
+                 *
+                 */
+                    //left.css('width', parent.width() - right.outerWidth());
+                    //left.css('width', parent.width() - right.outerWidth());
+                const percent = 100 - right.outerWidth() /parent.width() * 100;
+                left.css('width', percent + '%');
+            } else {
+                left.width(parent.width());
+                right.hide();
+            }
+        };
+        // 展开工具栏、切换标签
+        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' && !xmjSearch) {
+                if (!xmjSearch) {
+                    xmjSearch = $.billsSearch({
+                        selector: '#search',
+                        searchSpread: xmjSpread,
+                        searchRangeStr: '项目节编号/名称',
+                        searchOver: true,
+                        searchEmpty: true,
+                        resultSpreadSetting: {
+                            cols: [
+                                {title: '项目节编号', field: 'code', hAlign: 0, width: 120, formatter: '@', readOnly: true},
+                                {title: '名称', field: 'name', width: 150, hAlign: 0, formatter: '@', readOnly: true},
+                                {title: '单位', field: 'unit', width: 50, hAlign: 1, formatter: '@', readOnly: true},
+                                {title: '金额', field: 'total_price', hAlign: 2, width: 80, readOnly: true},
+                                {title: '完成率(%)', field: 'complete_percent', hAlign: 2, width: 70},
+                            ],
+                            emptyRows: 0,
+                            headRows: 1,
+                            headRowHeight: [32],
+                            defaultRowHeight: 21,
+                            headerFont: '12px 微软雅黑',
+                            font: '12px 微软雅黑',
+                            selectedBackColor: '#fffacd',
+                        },
+                        afterLocated: function () {
+                            unitTreeObj.loadCurUnitData();
+                        },
+                        checkLess: function (node) {
+                            if (node.quantity) {
+                                return ZhCalc.sub(ZhCalc.add(node.quantity, node.end_qc_qty), node.end_gather_qty) > 0;
+                            } else if (node.total_price) {
+                                return ZhCalc.sub(ZhCalc.add(node.total_price, node.end_qc_tp), node.end_gather_tp) > 0;
+                            }
+                        },
+                        checkOver: function (node) {
+                            if (node.unitTree) {
+                                for (const u of node.unitTree.nodes) {
+                                    if (u.end_gather_qty) {
+                                        if (ZhCalc.sub(u.end_contract_qty, u.quantity) > 0) return true;
+                                    } else if (u.end_gather_tp) {
+                                        if (ZhCalc.sub(u.end_contract_tp, u.total_price) > 0) return true;
+                                    }
+                                }
+                                return false;
+                            } else if (node.end_gather_qty) {
+                                return !node.quantity || Math.abs(node.end_gather_qty) > Math.abs(ZhCalc.add(node.quantity, node.end_qc_qty));
+                            } else if (node.end_gather_tp) {
+                                return !node.total_price || Math.abs(node.end_gather_tp) > Math.abs(ZhCalc.add(node.total_price, node.end_qc_tp));
+                            }
+                        },
+                        checkEmpty: function (node) {
+                            if (node.quantity) {
+                                return !node.end_gather_qty || checkZero(node.end_gather_qty);
+                            } else if (node.total_price) {
+                                return !node.end_gather_tp || checkZero(node.end_gather_tp);
+                            }
+                        },
+                    });
+                }
+                xmjSearch.spread.refresh();
+            }
+        } else { // 收起工具栏
+            tab.removeClass('active');
+            tabPanel.removeClass('active');
+            showSideTools(tab.hasClass('active'));
+        }
+        xmjSpread.refresh();
+        unitSpread.refresh();
+    });
+    // 显示层次
+    (function (select, sheet) {
+        $(select).click(function () {
+            const tag = $(this).attr('tag');
+            const tree = sheet.zh_tree;
+            if (!tree) return;
+            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 "curMeasure":
+                    tree.expandByCustom(function (node) {
+                        for (const field of ['contract_tp', 'qc_tp', 'gather_tp']) {
+                            if (node[field]) {
+                                return true;
+                            }
+                        }
+                        return false;
+                    });
+                    SpreadJsObj.refreshTreeRowVisible(sheet);
+                    break;
+            }
+        });
+    })('a[name=showLevel]', xmjSheet);
+    // 过滤数据
+    $('a[name=load-data]').click(function () {
+        const tag = $(this).attr('tag');
+        loadData(tag);
+    });
+
+    $('#exportBwtz').click(function () {
+        const data = [];
+        const setting = {
+            cols: [
+                {title: '项目节编号', colSpan: '1', rowSpan: '2', field: 'code', hAlign: 0, width: 100, formatter: '@'},
+                {title: '清单编号', colSpan: '1', rowSpan: '2', field: 'b_code', hAlign: 0, width: 70, formatter: '@'},
+                {title: '名称', colSpan: '1', rowSpan: '2', field: 'name', hAlign: 0, width: 300, formatter: '@'},
+                {title: '单位', colSpan: '1', rowSpan: '2', field: 'unit', hAlign: 1, width: 60, formatter: '@'},
+                {title: '单价', colSpan: '1', rowSpan: '2', field: 'unit_price', hAlign: 2, width: 80, type: 'Number'},
+                {title: '台账|数量', colSpan: '2|1', rowSpan: '1|1', field: 'quantity', hAlign: 2, width: 80, type: 'Number'},
+                {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'total_price', hAlign: 2, width: 80, type: 'Number'},
+                {title: '本期合同计量|数量', colSpan: '2|1', rowSpan: '1|1', field: 'contract_qty', hAlign: 2, width: 60, type: 'Number'},
+                {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'contract_tp', hAlign: 2, width: 60, type: 'Number'},
+                {title: '本期数量变更|数量', colSpan: '3|1', rowSpan: '1|1', field: 'qc_qty', hAlign: 2, width: 60, type: 'Number'},
+                {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'qc_tp', hAlign: 2, width: 60, type: 'Number'},
+                {title: '|变更令', colSpan: '|1', rowSpan: '|1', field: 'bgl_code', hAlign: 2, width: 60, formatter: '@'},
+                {title: '本期完成计量|数量', colSpan: '2|1', rowSpan: '1|1', field: 'gather_qty', hAlign: 2, width: 60, type: 'Number'},
+                {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'gather_tp', hAlign: 2, width: 60, type: 'Number'},
+                {title: '截止本期合同计量|数量', colSpan: '2|1', rowSpan: '1|1', field: 'end_contract_qty', hAlign: 2, width: 60, type: 'Number'},
+                {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'end_contract_tp', hAlign: 2, width: 60, type: 'Number'},
+                {title: '截止本期数量变更|数量', colSpan: '2|1', rowSpan: '1|1', field: 'end_qc_qty', hAlign: 2, width: 60, type: 'Number'},
+                {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'end_qc_tp', hAlign: 2, width: 60, type: 'Number'},
+                {title: '截止本期完成计量|数量', colSpan: '3|1', rowSpan: '1|1', field: 'end_gather_qty', hAlign: 2, width: 60, type: 'Number'},
+                {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'end_gather_tp', hAlign: 2, width: 60, type: 'Number'},
+                {title: '|完成率(%)', colSpan: '|1', rowSpan: '|1', field: 'end_gather_percent', hAlign: 2, width: 60, type: 'Number'},
+                {title: '图(册)号', colSpan: '1', rowSpan: '2', field: 'drawing_code', hAlign: 0, width: 100, formatter: '@'},
+                {title: '本期批注', colSpan: '1', rowSpan: '2', field: 'postil', hAlign: 0, width: 100, formatter: '@'},
+                {title: '备注', colSpan: '1', rowSpan: '2', field: 'memo', hAlign: 0, width: 100, formatter: '@'},
+            ],
+            headRows: 2,
+            headRowHeight: [25, 25],
+            defaultRowHeight: 21,
+            headerFont: 'bold 10px 微软雅黑',
+            font: '10px 微软雅黑'
+        };
+        if (!xmjSheet.zh_tree) return;
+        for (const node of xmjSheet.zh_tree.nodes) {
+            data.push({
+                code: node.code, b_code: node.b_code, name: node.name, unit: node.unit,
+                unit_price: node.unit_price, quantity: node.quantity, total_price: node.total_price,
+                contract_tp: node.contract_tp, qc_tp: node.qc_tp, gather_tp: node.gather_tp,
+                end_contract_tp: node.end_contract_tp, end_qc_tp: node.end_qc_tp, end_gather_tp: node.end_gather_tp,
+                end_gather_percent: node.end_gather_percent,
+                drawing_code: node.drawing_code, postil: node.postil, memo: node.memo
+            });
+            if (node.unitTree) {
+                for (const unitNode of node.unitTree.nodes) {
+                    data.push({
+                        code: unitNode.code, b_code: unitNode.b_code,
+                        name: unitNode.pos_name ? unitNode.pos_name : unitNode.name, unit: unitNode.unit,
+                        unit_price: unitNode.unit_price, quantity: unitNode.quantity, total_price: unitNode.total_price,
+                        contract_qty: unitNode.contract_qty, contract_tp: unitNode.contract_tp,
+                        qc_qty: unitNode.qc_qty, qc_tp: unitNode.qc_tp,
+                        gather_qty: unitNode.gather_qty, gather_tp: unitNode.gather_tp,
+                        end_contract_qty: unitNode.end_contract_qty, end_contract_tp: unitNode.end_contract_tp,
+                        end_qc_qty: unitNode.end_qc_qty, end_qc_tp: unitNode.end_qc_tp,
+                        end_gather_qty: unitNode.end_gather_qty, end_gather_tp: unitNode.end_gather_tp,
+                        end_gather_percent: unitNode.end_gather_percent,
+                        drawing_code: unitNode.drawing_code, postil: unitNode.postil_merge, memo: unitNode.memo
+                    });
+                }
+            }
+        }
+
+        SpreadExcelObj.exportSimpleXlsxSheet(setting, data, $('h2')[0].innerHTML + "-部位台账.xlsx");
+    });
 });

+ 17 - 0
app/public/js/stage_compare.js

@@ -84,6 +84,7 @@ $(document).ready(function () {
     };
     // 初始化台账
     const ledgerSpread = SpreadJsObj.createNewSpread($('#ledger-spread')[0]);
+    const ledgerSheet = ledgerSpread.getActiveSheet();
     sjsSettingObj.setFxTreeStyle(ledgerSpreadSetting, sjsSettingObj.FxTreeStyle.jz);
     SpreadJsObj.initSheet(ledgerSpread.getActiveSheet(), ledgerSpreadSetting);
     // 初始化部位
@@ -271,4 +272,20 @@ $(document).ready(function () {
             }
         });
     })('a[name=showLevel]', ledgerSpread.getActiveSheet());
+
+    $('#exportExcel').click(function () {
+        const data = [];
+        if (!ledgerSheet.zh_tree) return;
+        for (const node of ledgerSheet.zh_tree.nodes) {
+            data.push(node);
+            const posRange = scPos.getLedgerPos(node.id);
+            if (posRange && posRange.length > 0) {
+                for (const pr of posRange) {
+                    data.push(pr);
+                }
+            }
+        }
+
+        SpreadExcelObj.exportSimpleXlsxSheet(ledgerSpreadSetting, data, $('h2')[0].innerHTML + "-审核比较.xlsx");
+    });
 });

+ 76 - 1
app/public/js/stage_gather.js

@@ -53,11 +53,12 @@ $(document).ready(function () {
     autoFlashHeight();
     // 初始化工程量清单
     const gclSpread = SpreadJsObj.createNewSpread($('#gcl-spread')[0]);
+    const gclSheet = gclSpread.getActiveSheet();
     gclSpreadSetting.getColor = function (sheet, data, row, col, defaultColor) {
         return data 
             ? (data.overRange ? '#f8d7da' : data.differ ? '#FFE699' : defaultColor)
             : defaultColor;
-    }
+    };
     SpreadJsObj.initSheet(gclSpread.getActiveSheet(), gclSpreadSetting);
     // 初始化所属项目节
     const leafXmjSpread = SpreadJsObj.createNewSpread($('#leaf-xmj-spread')[0]);
@@ -211,4 +212,78 @@ $(document).ready(function () {
             leafXmjSpread.refresh();
         }
     });
+    $('#exportExcel').click(function () {
+        const data = [];
+        const setting = {
+            cols: [
+                {title: '清单编号', colSpan: '1', rowSpan: '2', field: 'b_code', hAlign: 0, width: 70, formatter: '@'},
+                {title: '名称', colSpan: '1', rowSpan: '2', field: 'name', hAlign: 0, width: 300, formatter: '@'},
+                {title: '单位', colSpan: '1', rowSpan: '2', field: 'unit', hAlign: 1, width: 60, formatter: '@'},
+                {title: '单价', colSpan: '1', rowSpan: '2', field: 'unit_price', hAlign: 2, width: 80, type: 'Number'},
+                {title: '项目节编号', colSpan: '1', rowSpan: '2', field: 'code', hAlign: 0, width: 100, formatter: '@'},
+                {title: '签约清单|数量', colSpan: '2|1', rowSpan: '1|1', field: 'deal_bills_qty', hAlign: 2, width: 80, type: 'Number'},
+                {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'deal_bills_tp', hAlign: 2, width: 80, type: 'Number'},
+                {title: '台账|数量', colSpan: '2|1', rowSpan: '1|1', field: 'quantity', hAlign: 2, width: 80, type: 'Number'},
+                {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'total_price', hAlign: 2, width: 80, type: 'Number'},
+                {title: '本期合同计量|数量', colSpan: '2|1', rowSpan: '1|1', field: 'contract_qty', hAlign: 2, width: 60, type: 'Number'},
+                {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'contract_tp', hAlign: 2, width: 60, type: 'Number'},
+                {title: '本期数量变更|数量', colSpan: '2|1', rowSpan: '1|1', field: 'qc_qty', hAlign: 2, width: 60, type: 'Number'},
+                {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'qc_tp', hAlign: 2, width: 60, type: 'Number'},
+                {title: '本期完成计量|数量', colSpan: '2|1', rowSpan: '1|1', field: 'gather_qty', hAlign: 2, width: 60, type: 'Number'},
+                {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'gather_tp', hAlign: 2, width: 60, type: 'Number'},
+                {title: '截止本期合同计量|数量', colSpan: '2|1', rowSpan: '1|1', field: 'end_contract_qty', hAlign: 2, width: 60, type: 'Number'},
+                {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'end_contract_tp', hAlign: 2, width: 60, type: 'Number'},
+                {title: '截止本期数量变更|数量', colSpan: '2|1', rowSpan: '1|1', field: 'end_qc_qty', hAlign: 2, width: 60, type: 'Number'},
+                {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'end_qc_tp', hAlign: 2, width: 60, type: 'Number'},
+                {title: '截止本期完成计量|数量', colSpan: '3|1', rowSpan: '1|1', field: 'end_gather_qty', hAlign: 2, width: 60, type: 'Number'},
+                {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'end_gather_tp', hAlign: 2, width: 60, type: 'Number'},
+                {title: '|完成率(%)', colSpan: '|1', rowSpan: '|1', field: 'end_gather_percent', hAlign: 2, width: 60, type: 'Number'},
+                {title: '台账+变更令|数量', colSpan: '2|1', rowSpan: '1|1', field: 'end_final_qty', hAlign: 2, width: 60, type: 'Number'},
+                {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'end_final_tp', hAlign: 2, width: 60, type: 'Number'},
+                {title: '单位工程', colSpan: '1', rowSpan: '2', field: 'dwgc', hAlign: 0, width: 80, formatter: '@'},
+                {title: '分部工程', colSpan: '1', rowSpan: '2', field: 'fbgc', hAlign: 0, width: 80, formatter: '@'},
+                {title: '分项工程', colSpan: '1', rowSpan: '2', field: 'fxgc', hAlign: 0, width: 80, formatter: '@'},
+                {title: '细目', colSpan: '1', rowSpan: '2', field: 'jldy', hAlign: 0, width: 80, formatter: '@'},
+                {title: '计量单元', colSpan: '1', rowSpan: '2', field: 'bwmx', hAlign: 0, width: 80, formatter: '@'},
+                {title: '图册号', colSpan: '1', rowSpan: '2', field: 'drawing_code', hAlign: 0, width: 80, formatter: '@'},
+            ],
+            headRows: 2,
+            headRowHeight: [25, 25],
+            defaultRowHeight: 21,
+            headerFont: 'bold 10px 微软雅黑',
+            font: '10px 微软雅黑'
+        };
+        if (!gclSheet.zh_data || gclSheet.zh_data.length === 0) return;
+        for (const gcl of gclSheet.zh_data) {
+            data.push({
+                b_code: gcl.b_code, name: gcl.name, unit: gcl.unit,
+                unit_price: gcl.unit_price,
+                deal_bills_qty: gcl.deal_bills_qty, deal_bills_tp: gcl.deal_bills_tp,
+                quantity: gcl.quantity, total_price: gcl.total_price,
+                contract_qty: gcl.contract_qty, contract_tp: gcl.contract_tp,
+                qc_qty: gcl.qc_qty, qc_tp: gcl.qc_tp,
+                gather_qty: gcl.gather_qty, gather_tp: gcl.gather_tp,
+                end_contract_qty: gcl.end_contract_qty, end_contract_tp: gcl.end_contract_tp,
+                end_qc_qty: gcl.end_qc_qty, end_qc_tp: gcl.end_qc_tp,
+                end_gather_qty: gcl.end_gather_qty, end_gather_tp: gcl.end_gather_tp,
+                end_gather_percent: gcl.end_gather_percent,
+                end_final_qty: gcl.end_final_qty, end_final_tp: gcl.end_final_tp,
+            });
+            if (gcl.leafXmjs && gcl.leafXmjs.length > 0) {
+                for (const xmj of gcl.leafXmjs) {
+                    data.push({
+                        code: xmj.code,
+                        quantity: xmj.quantity,
+                        contract_qty: xmj.contract_qty, qc_qty: xmj.qc_qty, gather_qty: xmj.gather_qty,
+                        end_contract_qty: xmj.end_contract_qty, end_qc_qty: xmj.end_qc_qty,
+                        end_gather_qty: xmj.end_gather_qty, end_gather_percent: xmj.end_gather_percent,
+                        dwgc: xmj.dwgc, fbgc: xmj.fbgc, fxgc: xmj.fxgc,
+                        jldy: xmj.jldy, bwmx: xmj.bwmx, drawing_code: xmj.drawing_code,
+                    });
+                }
+            }
+        }
+
+        SpreadExcelObj.exportSimpleXlsxSheet(setting, data, $('h2')[0].innerHTML + "-清单汇总.xlsx");
+    });
 });

+ 10 - 8
app/public/js/tender_list.js

@@ -317,13 +317,13 @@ function recursiveGetTenderNodeHtml (node, arr, pid) {
 function getTenderTreeHtml () {
     if (tenderTree.length > 0) {
         const html = [];
-        html.push('<table class="table table-hover table-bordered" style="margin-top: 25px">');
-        html.push('<thead style="position: fixed;left:56px;top: 34px">', '<tr>');
-        html.push('<th class="text-center">', '标段名称', '</th>');
-        html.push('<th class="text-center">', '创建人', '</th>');
-        html.push('<th class="text-center">', '创建时间', '</th>');
-        html.push('<th class="text-center">', '计量期数', '</th>');
-        html.push('<th class="text-center">', '审批状态', '</th>');
+        html.push('<table class="table table-hover table-bordered">');
+        html.push('<thead>', '<tr>');
+        html.push('<th class="text-center" style="width: 45%">', '标段名称', tenderListOrder.getOrderButton('name'), '</th>');
+        html.push('<th class="text-center" style="width: 10%">', '创建人', '</th>');
+        html.push('<th class="text-center" style="width: 15%">', '创建时间', tenderListOrder.getOrderButton('create_time'), '</th>');
+        html.push('<th class="text-center" style="width: 10%">', '计量期数', '</th>');
+        html.push('<th class="text-center" style="width: 20%">', '审批状态', '</th>');
         html.push('</tr>', '</thead>');
         parentId = 0;
         for (const t of tenderTree) {
@@ -341,6 +341,7 @@ function bindTenderUrl() {
         const tender = _.find(tenders, function (t) {
             return t.id === tenderId;
         });
+        if (!tender) return;
         if (tender.measure_type) {
             window.location.href = '/tender/' + tenderId;
         } else {
@@ -359,6 +360,7 @@ $(document).ready(() => {
     initCategoryLevelNode();
     $('.modal-body', '#add-bd').append(getCategoryHtml());
     // 初始化标段树结构
+    tenderListOrder.reOrderTenders();
     initTenderTree();
     $('.c-body').html(getTenderTreeHtml());
     bindTenderUrl();
@@ -396,7 +398,7 @@ $(document).ready(() => {
     // 新增标段
     $('#add-bd-ok').click(function () {
         const data = {
-            name: $('[name=name]', '#add-bd').val(),
+            name: cleanSymbols($('[name=name]', '#add-bd').val()),
             valuation: $('[name=valuation]:checked').val(),
             category: [],
         };

+ 15 - 15
app/public/js/tender_list_info.js

@@ -367,20 +367,20 @@ function recursiveGetTenderNodeHtml (node, arr, pid) {
 function getTenderTreeHtml () {
     if (tenderTree.length > 0) {
         const html = [];
-        html.push('<table class="table table-hover table-bordered" style="margin-top: 25px">');
-        html.push('<thead style="position: fixed;left:56px;top: 34px">', '<tr>');
-        html.push('<th class="text-center">', '标段名称', '</th>');
-        html.push('<th class="text-center">', '计量模式', '</th>');
-        html.push('<th class="text-center">', '计量期数', '</th>');
-        html.push('<th class="text-center">', '审批状态', '</th>');
-        html.push('<th class="text-center">', '0号台帐', '</th>');
-        html.push('<th class="text-center">', '本期完成', '</th>');
-        html.push('<th class="text-center">', '截止本期合同', '</th>');
-        html.push('<th class="text-center">', '截止本期变更', '</th>');
-        html.push('<th class="text-center">', '截止本期完成', '</th>');
-        html.push('<th class="text-center">', '截止上期完成', '</th>');
-        html.push('<th class="text-center">', '本期应付', '</th>');
-        html.push('<th class="text-center">', '截止本期应付', '</th>');
+        html.push('<table class="table table-hover table-bordered">');
+        html.push('<thead>', '<tr>');
+        html.push('<th class="text-center" style="width: 23%">', '标段名称', '</th>');
+        html.push('<th class="text-center" style="width: 7%">', '计量模式', '</th>');
+        html.push('<th class="text-center" style="width: 7%">', '计量期数', '</th>');
+        html.push('<th class="text-center" style="width: 7%">', '审批状态', '</th>');
+        html.push('<th class="text-center" style="width: 7%">', '0号台帐', '</th>');
+        html.push('<th class="text-center" style="width: 7%">', '本期完成', '</th>');
+        html.push('<th class="text-center" style="width: 7%">', '截止本期合同', '</th>');
+        html.push('<th class="text-center" style="width: 7%">', '截止本期变更', '</th>');
+        html.push('<th class="text-center" style="width: 7%">', '截止本期完成', '</th>');
+        html.push('<th class="text-center" style="width: 7%">', '截止上期完成', '</th>');
+        html.push('<th class="text-center" style="width: 7%">', '本期应付', '</th>');
+        html.push('<th class="text-center" style="width: 7%">', '截止本期应付', '</th>');
         html.push('</tr>', '</thead>');
         parentId = 0;
         for (const t of tenderTree) {
@@ -452,7 +452,7 @@ $(document).ready(() => {
     // 新增标段
     $('#add-bd-ok').click(function () {
         const data = {
-            name: $('[name=name]', '#add-bd').val(),
+            name: cleanSymbols($('[name=name]', '#add-bd').val()),
             valuation: $('[name=valuation]:checked').val(),
             category: [],
         };

+ 10 - 9
app/public/js/tender_list_manage.js

@@ -301,13 +301,13 @@ function recursiveGetTenderNodeHtml (node, arr, pid) {
 }
 function getTenderTreeHeaderHtml() {
     const html = [];
-    html.push('<table class="table table-hover table-bordered" style="margin-top: 25px">');
-    html.push('<thead style="position: fixed;left:56px;top: 34px">', '<tr>');
-    html.push('<th class="text-center">', '标段名称', '</th>');
-    html.push('<th class="text-center">', '创建人', '</th>');
-    html.push('<th class="text-center">', '创建时间', '</th>');
-    html.push('<th class="text-center">', '完成期数', '</th>');
-    html.push('<th class="text-center">', '管理', '</th>');
+    html.push('<table class="table table-hover table-bordered">');
+    html.push('<thead>', '<tr>');
+    html.push('<th class="text-center" style="width: 45%">', '标段名称', tenderListOrder.getOrderButton('name'), '</th>');
+    html.push('<th class="text-center" style="width: 10%">', '创建人', '</th>');
+    html.push('<th class="text-center" style="width: 15%">', '创建时间', tenderListOrder.getOrderButton('create_time'), '</th>');
+    html.push('<th class="text-center" style="width: 10%">', '完成期数', '</th>');
+    html.push('<th class="text-center" style="width: 20%">', '管理', '</th>');
     html.push('</tr>', '</thead>');
     return html.join('');
 }
@@ -371,6 +371,7 @@ $(document).ready(() => {
     $('.modal-body', '#add-bd').append(getCategoryHtml());
     $('.modal-body', '#edit-bd').append(getCategoryHtml());
     // 初始化标段树结构
+    tenderListOrder.reOrderTenders();
     initTenderTree();
     $('.c-body').html(getTenderTreeHtml());
     bindTenderUrl();
@@ -412,7 +413,7 @@ $(document).ready(() => {
     // 新增标段
     $('#add-bd-ok').click(function () {
         const data = {
-            name: $('[name=name]', '#add-bd').val(),
+            name: cleanSymbols($('[name=name]', '#add-bd').val()),
             valuation: $('[name=valuation]:checked').val(),
             category: [],
         };
@@ -448,7 +449,7 @@ $(document).ready(() => {
     $('#edit-bd-ok').click(function () {
         const data = {
             id: parseInt($(this).attr('tid')),
-            name: $('[name=name]', '#edit-bd').val(),
+            name: cleanSymbols($('[name=name]', '#edit-bd').val()),
             category: [],
         };
         if (!data.name || data.name === '') {

+ 7 - 7
app/public/js/tender_list_progress.js

@@ -350,12 +350,12 @@ function recursiveGetTenderNodeHtml (node, arr, pid) {
 function getTenderTreeHtml () {
     if (tenderTree.length > 0) {
         const html = [];
-        html.push('<table class="table table-hover table-bordered" style="margin-top: 25px">');
-        html.push('<thead style="position: fixed;left:56px;top: 34px">', '<tr>');;
-        html.push('<th width="60%" class="text-center">', '标段名称', '</th>');
-        html.push('<th width="120" class="text-center">', '计量期数', '</th>');
-        html.push('<th width="10%" class="text-center">', '总价 <i class="fa fa-question-circle text-primary"  data-placement="bottom" data-toggle="tooltip" data-original-title="0号台账+截止本期数量变更"></i>', '</th>');
-        html.push('<th class="text-center">', '截止上期完成/本期完成/未完成', '</th>');
+        html.push('<table class="table table-hover table-bordered">');
+        html.push('<thead>', '<tr>');
+        html.push('<th style="width: 35%" class="text-center">', '标段名称', '</th>');
+        html.push('<th style="width: 10%" class="text-center">', '计量期数', '</th>');
+        html.push('<th style="width: 10%" class="text-center">', '总价 <i class="fa fa-question-circle text-primary"  data-placement="bottom" data-toggle="tooltip" data-original-title="0号台账+截止本期数量变更"></i>', '</th>');
+        html.push('<th style="width: 45%" class="text-center">', '截止上期完成/本期完成/未完成', '</th>');
         html.push('</tr>', '</thead>');
         parentId = 0;
         for (const t of tenderTree) {
@@ -427,7 +427,7 @@ $(document).ready(() => {
     // 新增标段
     $('#add-bd-ok').click(function () {
         const data = {
-            name: $('[name=name]', '#add-bd').val(),
+            name: cleanSymbols($('[name=name]', '#add-bd').val()),
             valuation: $('[name=valuation]:checked').val(),
             category: [],
         };

+ 5 - 4
app/public/js/tender_showhide.js

@@ -54,10 +54,11 @@ function localHideList() {
 $(window).resize(setTopTr);
 // 设置表头固定并动态调整宽度高度
 function setTopTr() {
-    for(let item = 0; item < $(".c-body table>thead>tr>th").length; item ++) {
-        $(".c-body table>thead>tr>th").eq(item).outerWidth($(".c-body table>tbody>tr:first").children('td').eq(item).outerWidth());
-    }
-    $('.c-body table').css('margin-top', $(".c-body table>thead").height() - 4);
+    // for(let item = 0; item < $(".c-body table>thead>tr>th").length; item ++) {
+    //     $(".c-body table>thead>tr>th").eq(item).outerWidth($(".c-body table>tbody>tr:first").children('td').eq(item).outerWidth());
+    // }
+    //$('.c-body table').css('margin-top', $(".c-body table>thead").height() - 4);
+    $('.c-body table').css('margin-top', -2);
 }
 
 function doTrStatus(node, status, all = '') {

+ 6 - 2
app/service/change.js

@@ -833,7 +833,11 @@ module.exports = app => {
          */
         async getValidChanges(tid, bills, pos) {
             const timesLen = 100;
-            const filter = 'cb.`code` = ' + this.db.escape(bills.b_code) + (pos ? ' And cb.`bwmx` = ' + this.db.escape(pos.name) : '');
+            const filter = 'cb.`code` = ' + this.db.escape(bills.b_code) +
+                ' And cb.`name` = ' + this.db.escape(bills.name) +
+                ' And cb.`unit` = ' + this.db.escape(bills.unit) +
+                ' And cb.`unit_price` = ' + this.db.escape(bills.unit_price) +
+                (pos ? ' And cb.`bwmx` = ' + this.db.escape(pos.name) : '');
             const sql = 'SELECT c.cid, c.code, c.name, c.w_code, c.p_code, c.peg, c.org_name, c.org_code, c.new_name, c.new_code,' +
                         '    c.content, c.basis, c.memo, c.type, c.class, c.quality, c.company, c.charge, ' +
                         '    cb.id As cbid, cb.code As b_code, cb.name As b_name, cb.unit As b_unit, cb.samount As b_amount, cb.detail As b_detail, cb.bwmx As b_bwmx, ' +
@@ -928,7 +932,7 @@ module.exports = app => {
                 // 先删除清单,审批人列表
                 await this.transaction.delete(this.ctx.service.changeAuditList.tableName, { cid });
                 await this.transaction.delete(this.ctx.service.changeAudit.tableName, { cid });
-                // 再删除附件和附件文件
+                // 再删除附件和附件文件ni zuo
                 const attList = await this.ctx.service.changeAtt.getAllDataByCondition({ where: { cid } });
                 if (attList.length !== 0) {
                     for (const att of attList) {

+ 11 - 3
app/service/change_audit.js

@@ -302,13 +302,21 @@ module.exports = app => {
                         '  FROM (SELECT * FROM ?? WHERE `user_id` = ? OR `id` in (SELECT `tid` FROM ?? WHERE `uid` = ? GROUP BY `tid`)) As t' +
                         '  LEFT JOIN ?? As c ON c.`tid` = t.`id`' +
                         '  LEFT JOIN ?? As ca ON ca.`cid` = c.`cid`' +
-                        '  WHERE t.`project_id` = ? and `ca`.`sin_time` > ?' +
+                        '  WHERE t.`project_id` = ? and `ca`.`sin_time` > ? and `ca`.`usite` != 0 and `ca`.`status` != ?' +
                         '  ORDER By ca.`sin_time` DESC LIMIT 1000) as new_t GROUP BY new_t.`id`' +
                         '  ORDER BY new_t.`cu_time`';
-            const sqlParam = [this.ctx.service.tender.tableName, uid, this.tableName, uid, this.ctx.service.change.tableName, this.tableName, pid, time];
-            console.log(sql, sqlParam);
+            const sqlParam = [this.ctx.service.tender.tableName, uid, this.tableName, uid, this.ctx.service.change.tableName, this.tableName, pid, time, audit.flow.status.checking];
             return await this.db.query(sql, sqlParam);
         }
+
+        async getAllAuditors(tenderId) {
+            const sql = 'SELECT ca.uid, ca.tid FROM ' + this.tableName + ' ca' +
+                '  LEFT JOIN ' + this.ctx.service.tender.tableName + ' t On ca.tid = t.id' +
+                '  WHERE t.id = ?' +
+                '  GROUP BY ca.uid';
+            const sqlParam = [tenderId];
+            return this.db.query(sql, sqlParam);
+        }
     }
 
     return ChangeAudit;

+ 9 - 0
app/service/material_audit.js

@@ -591,6 +591,15 @@ module.exports = app => {
             }
             return auditor;
         }
+
+        async getAllAuditors(tenderId) {
+            const sql = 'SELECT ma.aid, ma.tid FROM ' + this.tableName + ' ma' +
+                '  LEFT JOIN ' + this.ctx.service.tender.tableName + ' t On ma.tid = t.id' +
+                '  WHERE t.id = ?' +
+                '  GROUP BY  ma.aid';
+            const sqlParam = [tenderId];
+            return this.db.query(sql, sqlParam);
+        }
     }
 
     return MaterialAudit;

+ 27 - 0
app/service/material_list.js

@@ -103,6 +103,33 @@ module.exports = app => {
         }
 
         /**
+         * 修改工料信息
+         * @param {Object} data 工料内容
+         * @return {void}
+         */
+        async saveDatas(datas) {
+            if (!this.ctx.tender || !this.ctx.material) {
+                throw '数据错误';
+            }
+            // 判断是否可修改
+            // 判断t_type是否为费用
+            const transaction = await this.db.beginTransaction();
+            try {
+                for (const data of datas) {
+                    const mb_id = data.mb_id;
+                    delete data.mb_id;
+                    await transaction.update(this.tableName, data);
+                    await this.calcQuantityByML(transaction, mb_id);
+                }
+                await transaction.commit();
+                return true;
+            } catch (err) {
+                await transaction.rollback();
+                throw err;
+            }
+        }
+
+        /**
          * 应用工料清单到其它清单中
          * @return {void}
          */

+ 9 - 0
app/service/revise_audit.js

@@ -455,6 +455,15 @@ module.exports = app => {
             // const sqlParam = [this.tableName, stageId, times];
             // return await this.db.query(sql, sqlParam);
         }
+
+        async getAllAuditors(tenderId) {
+            const sql = 'SELECT ra.audit_id, ra.tender_id FROM ' + this.tableName + ' ra' +
+                '  LEFT JOIN ' + this.ctx.service.tender.tableName + ' t On ra.tender_id = t.id' +
+                '  WHERE t.id = ?' +
+                '  GROUP BY ra.audit_id';
+            const sqlParam = [tenderId];
+            return this.db.query(sql, sqlParam);
+        }
     }
 
     return ReviseAudit;

+ 60 - 84
app/service/rpt_gather_memory.js

@@ -134,6 +134,34 @@ module.exports = app => {
             this.resultDealBills = [];
         }
 
+        async _getValidStages(tenderId) {
+            const stages = await this.db.select(this.ctx.service.stage.tableName, {
+                where: { tid: tenderId },
+                orders: [['order', 'desc']],
+            });
+            if (stages.length !== 0) {
+                const lastStage = stages[0];
+                if (lastStage.status === auditConst.stage.status.uncheck && lastStage.user_id !== this.ctx.session.sessionUser.accountId) {
+                    stages.splice(0, 1);
+                }
+            }
+            return stages;
+        }
+
+        async _getCheckedStages(tenderId) {
+            const stages = await this.db.select(this.ctx.service.stage.tableName, {
+                where: { tid: tenderId },
+                orders: [['order', 'desc']],
+            });
+            if (stages.length !== 0) {
+                const lastStage = stages[0];
+                if (lastStage.status !== auditConst.stage.status.checked) {
+                    stages.splice(0, 1);
+                }
+            }
+            return stages;
+        }
+
         _checkSpecialTender(tender, special) {
             if (special) {
                 for (const spec of special) {
@@ -203,7 +231,8 @@ module.exports = app => {
 
         async _gatherMonthData(sTender, index, month, hasPre) {
             const tender = await this.ctx.service.tender.getCheckTender(sTender.tid);
-            const stage = await this.ctx.service.stage.getDataByCondition({tid: tender.id, s_time: month});
+            const stages = await this._getValidStages(tender.id);
+            const stage = this.ctx.helper._.find(stages, {s_time: month});
             await this._gatherStageData(index, tender, stage, hasPre);
         }
 
@@ -259,14 +288,13 @@ module.exports = app => {
 
             const times = zone.split(' - ');
             if (times.length !== 2) throw '选择的汇总周期无效';
-            const beginTime = moment(times[0], 'YYYY-MM').date();
-            const endTime = moment(times[1], 'YYYY-MM').date();
-
+            const beginTime = moment(times[0], 'YYYY-MM');
+            const endTime = moment(times[1], 'YYYY-MM');
 
-            const stages = await this.ctx.service.stage.getAllDataByCondition({ where: { tid: tender.id } });
+            const stages = await this._getValidStages(tender.id);
             for (const stage of stages) {
-                const sTime = moment(stage.s_time, 'YYYY-MM').date();
-                if (sTime >= beginTime && sTime <= endTime) {
+                const sTime = moment(stage.s_time, 'YYYY-MM');
+                if (sTime.isBetween(beginTime, endTime, null, '[]')) {
                     await this.ctx.service.stage.doCheckStage(stage);
                     if (stage.readOnly) {
                         const curStage = await this.ctx.service.stageBills.getAuditorStageData(tender.id,
@@ -291,31 +319,13 @@ module.exports = app => {
 
         async _gatherFinalData(sTender, index, hasPre) {
             const tender = await this.ctx.service.tender.getCheckTender(sTender.tid);
-            const stages = await this.db.select(this.ctx.service.stage.tableName, {
-                where: { tid: tender.id },
-                orders: [['order', 'desc']],
-            });
-            if (stages.length !== 0) {
-                const lastStage = stages[0];
-                if (lastStage.status === auditConst.stage.status.uncheck && lastStage.user_id !== this.ctx.session.sessionUser.accountId) {
-                    stages.splice(0, 1);
-                }
-            }
+            const stages = await this._getValidStages(tender.id);
             await this._gatherStageData(index, tender, stages[0], hasPre);
         }
 
         async _gatherCheckedFinalData(sTender, index, hasPre) {
             const tender = await this.ctx.service.tender.getCheckTender(sTender.tid);
-            const stages = await this.db.select(this.ctx.service.stage.tableName, {
-                where: { tid: tender.id },
-                orders: [['order', 'desc']],
-            });
-            if (stages.length !== 0) {
-                const lastStage = stages[0];
-                if (lastStage.status !== auditConst.stage.status.checked) {
-                    stages.splice(0, 1);
-                }
-            }
+            const stages = await this._getCheckedStages(tender.id);
             await this._gatherStageData(index, tender, stages[0], hasPre);
         }
 
@@ -435,17 +445,18 @@ module.exports = app => {
                 info.end_qc_tp = helper.add(info.pre_qc_tp, info.qc_tp);
                 info.end_gather_tp = helper.add(info.pre_gather_tp, info.gather_tp);
 
-                info.yf = stage.yf;
-                info.pre_yf = stage.pre_yf;
-                info.end_yf = helper.add(stage.yf, stage.pre_yf);
+                info.yf_tp = stage.yf_tp;
+                info.pre_yf_tp = stage.pre_yf_tp;
+                info.end_yf_tp = helper.add(stage.yf_tp, stage.pre_yf_tp);
             }
         }
 
         async _gatherMonthTenderInfo(sTender, index, month, hasPre) {
             const tender = await this.ctx.service.tender.getCheckTender(sTender.tid);
             const info = await this._getBaseTenderInfo(tender);
-            const stage = await this.ctx.service.stage.getDataByCondition({tid: tender.id, s_time: month});
-            this._getStageTenderInfo(stage, info);
+            const stages = await this._getValidStages(tender.id);
+            const stage = this.ctx.helper._.find(stages, {s_time: month});
+            await this._getStageTenderInfo(stage, info);
             this.resultTenderInfo.push(info);
         }
 
@@ -456,20 +467,20 @@ module.exports = app => {
 
             const times = zone.split(' - ');
             if (times.length !== 2) throw '选择的汇总周期无效';
-            const beginTime = moment(times[0], 'YYYY-MM').date();
-            const endTime = moment(times[1], 'YYYY-MM').date();
+            const beginTime = moment(times[0], 'YYYY-MM');
+            const endTime = moment(times[1], 'YYYY-MM');
 
-            const stages = await this.ctx.service.stage.getAllDataByCondition({ where: { tid: tender.id } });
+            const stages = await this._getValidStages(tender.id, true);
             for (const stage of stages) {
-                const sTime = moment(stage.s_time, 'YYYY-MM').date();
-                if (sTime >= beginTime && sTime <= endTime) {
+                const sTime = moment(stage.s_time, 'YYYY-MM');
+                if (sTime.isBetween(beginTime, endTime, null, '[]')) {
                     await this.ctx.service.stage.doCheckStage(stage);
                     await this.ctx.service.stage.checkStageGatherData(stage);
 
                     info.contract_tp = helper.add(info.contract_tp, stage.contract_tp);
                     info.qc_tp = helper.add(info.qc_tp, stage.qc_tp);
 
-                    info.yf = helper.add(info.yf, stage.yf);
+                    info.yf_tp = helper.add(info.yf_tp, stage.yf_tp);
                 }
             }
             info.gather_tp = helper.add(info.contract_tp, info.qc_tp);
@@ -480,16 +491,7 @@ module.exports = app => {
         async _gatherFinalTenderInfo(sTender, index, hasPre) {
             const tender = await this.ctx.service.tender.getCheckTender(sTender.tid);
             const info = await this._getBaseTenderInfo(tender);
-            const stages = await this.db.select(this.ctx.service.stage.tableName, {
-                where: { tid: tender.id },
-                orders: [['order', 'desc']],
-            });
-            if (stages.length !== 0) {
-                const lastStage = stages[0];
-                if (lastStage.status === auditConst.stage.status.uncheck && lastStage.user_id !== this.ctx.session.sessionUser.accountId) {
-                    stages.splice(0, 1);
-                }
-            }
+            const stages = await this._getValidStages(tender.id);
             await this._getStageTenderInfo(stages[0], info);
             this.resultTenderInfo.push(info);
         }
@@ -497,16 +499,7 @@ module.exports = app => {
         async _gatherCheckedFinalTenderInfo(sTender, index, hasPre) {
             const tender = await this.ctx.service.tender.getCheckTender(sTender.tid);
             const info = await this._getBaseTenderInfo(tender);
-            const stages = await this.db.select(this.ctx.service.stage.tableName, {
-                where: { tid: tender.id },
-                orders: [['order', 'desc']],
-            });
-            if (stages.length !== 0) {
-                const lastStage = stages[0];
-                if (lastStage.status !== auditConst.stage.status.checked) {
-                    stages.splice(0, 1);
-                }
-            }
+            const stages = await this._getCheckedStages(tender.id);
             await this._getStageTenderInfo(stages[0], info);
             this.resultTenderInfo.push(info);
         }
@@ -622,7 +615,8 @@ module.exports = app => {
 
         async _gatherMonthStagePay(sTender, index, month, hasPre) {
             const tender = await this.ctx.service.tender.getCheckTender(sTender.tid);
-            const stage = await this.ctx.service.stage.getDataByCondition({tid: tender.id, s_time: month});
+            const stages = await this._getValidStages(tender.id);
+            const stage = this.ctx.helper._.find(stages, {s_time: month});
             await this._gatherStagePay(index, tender, stage, hasPre);
         }
 
@@ -630,16 +624,16 @@ module.exports = app => {
             const helper = this.ctx.helper;
 
             const tender = await this.ctx.service.tender.getCheckTender(sTender.tid);
+            const stages = await this._getValidStages(tender.id);
 
             const times = zone.split(' - ');
             if (times.length !== 2) throw '选择的汇总周期无效';
-            const beginTime = moment(times[0], 'YYYY-MM').date();
-            const endTime = moment(times[1], 'YYYY-MM').date();
+            const beginTime = moment(times[0], 'YYYY-MM');
+            const endTime = moment(times[1], 'YYYY-MM');
 
-            const stages = await this.ctx.service.stage.getAllDataByCondition({ where: { tid: tender.id } });
             for (const stage of stages) {
-                const sTime = moment(stage.s_time, 'YYYY-MM').date();
-                if (sTime >= beginTime && sTime <= endTime) {
+                const sTime = moment(stage.s_time, 'YYYY-MM');
+                if (sTime.isBetween(beginTime, endTime, null, '[]')) {
                     await this.ctx.service.stage.doCheckStage(stage);
 
                     const dealPay = await this.ctx.service.stagePay.getStagePays(stage);
@@ -661,31 +655,13 @@ module.exports = app => {
 
         async _gatherFinalStagePay(sTender, index, hasPre) {
             const tender = await this.ctx.service.tender.getCheckTender(sTender.tid);
-            const stages = await this.db.select(this.ctx.service.stage.tableName, {
-                where: { tid: tender.id },
-                orders: [['order', 'desc']],
-            });
-            if (stages.length !== 0) {
-                const lastStage = stages[0];
-                if (lastStage.status === auditConst.stage.status.uncheck && lastStage.user_id !== this.ctx.session.sessionUser.accountId) {
-                    stages.splice(0, 1);
-                }
-            }
+            const stages = await this._getValidStages(tender.id);
             await this._gatherStagePay(index, tender, stages[0], hasPre);
         }
 
         async _gatherCheckedFinalStagePay(sTender, index, hasPre) {
             const tender = await this.ctx.service.tender.getCheckTender(sTender.tid);
-            const stages = await this.db.select(this.ctx.service.stage.tableName, {
-                where: { tid: tender.id },
-                orders: [['order', 'desc']],
-            });
-            if (stages.length !== 0) {
-                const lastStage = stages[0];
-                if (lastStage.status !== auditConst.stage.status.checked) {
-                    stages.splice(0, 1);
-                }
-            }
+            const stages = await this._getCheckedStages(tender.id);
             await this._gatherStagePay(index, tender, stages[0], hasPre);
         }
 

+ 1 - 1
app/service/stage_att.js

@@ -65,7 +65,7 @@ module.exports = app => {
             const sql = 'SELECT att.id, att.lid, att.uid, att.filename, att.fileext, att.filesize, att.remark, att.in_time,' +
                 ' pa.name as `username`, leg.name as `lname`, leg.code as `code`, leg.b_code as `b_code`' +
                 ' FROM ?? AS att,?? AS pa,?? AS leg' +
-                ' WHERE leg.id = att.lid AND pa.id = att.uid AND att.tid = ? AND att.sid = ? ORDER BY att.in_time DESC';
+                ' WHERE leg.id = att.lid AND pa.id = att.uid AND att.tid = ? AND att.sid = ? ORDER BY att.id DESC';
             const sqlParam = [this.tableName, this.ctx.service.projectAccount.tableName, this.ctx.service.ledger.tableName, tid, sid];
             return await this.db.query(sql, sqlParam);
         }

+ 4 - 4
app/service/stage_bills.js

@@ -360,11 +360,11 @@ module.exports = app => {
             const sql = 'SELECT Sum(`contract_tp`) As `contract_tp`, Sum(`qc_tp`) As `qc_tp` FROM ' + this.tableName + ' As Bills ' +
                 '  INNER JOIN ( ' +
                 '    SELECT MAX(`times` * ' + timesLen + ' + `order`) As `flow`, `lid`, `sid` From ' + this.tableName +
-                '      WHERE `times` <= ? AND `order` <= ? AND `sid` = ?' +
+                '      WHERE (`times` < ? OR (`times` = ? AND `order` <= ?)) AND `sid` = ?' +
                 '      GROUP BY `lid`' +
                 '  ) As MaxFilter ' +
                 '  ON (Bills.times * ' + timesLen + ' + `order`) = MaxFilter.flow And Bills.lid = MaxFilter.lid And Bills.sid = MaxFilter.sid';
-            const sqlParam = [stage.curTimes, stage.curOrder, stage.id];
+            const sqlParam = [stage.curTimes, stage.curTimes, stage.curOrder, stage.id];
             const result = await this.db.queryOne(sql, sqlParam);
             return result;
         }
@@ -374,13 +374,13 @@ module.exports = app => {
                 '  FROM ' + this.tableName + ' As Bills ' +
                 '  INNER JOIN ( ' +
                 '    SELECT MAX(`times` * ' + timesLen + ' + `order`) As `flow`, `lid` From ' + this.tableName +
-                '      WHERE `times` <= ? AND `order` <= ?' +
+                '      WHERE (`times` < ? OR (`times` = ? AND `order` <= ?))' +
                 '      GROUP BY `lid`' +
                 '  ) As MaxFilter ' +
                 '  ON (Bills.times * ' + timesLen + ' + `order`) = MaxFilter.flow And Bills.lid = MaxFilter.lid ' +
                 '  INNER JOIN ' + this.ctx.service.ledger.tableName + ' As Ledger ON Bills.lid = Ledger.id' +
                 '  WHERE Bills.sid = ? And Ledger.b_code ' + operate + ' ?';
-            const sqlParam = [stage.times, stage.curAuditor ? stage.curAuditor.order : 0, stage.id, filter];
+            const sqlParam = [stage.times, stage.curTimes, stage.curAuditor ? stage.curAuditor.order : 0, stage.id, filter];
             const result = await this.db.queryOne(sql, sqlParam);
             return result;
         }

+ 11 - 2
app/service/stage_other.js

@@ -37,14 +37,22 @@ module.exports = app => {
         }
 
         async getPreStageData(sorder) {
-            const sql = 'SELECT uuid, Sum(tp) as tp From ' + this.tableName + ' WHERE sorder < ? And tid = ? GROUP By uuid';
+            const sql = 'SELECT o.uuid, Sum(o.tp) as tp ' +
+                '  From ' + this.tableName + ' o ' +
+                '  LEFT JOIN ' + this.ctx.service.stage.tableName + ' s ON s.id = o.sid' +
+                '  WHERE s.order < ? And o.tid = ?' +
+                '  GROUP By uuid';
             const sqlParam = [sorder, this.ctx.tender.id];
             const data = await this.db.query(sql, sqlParam);
             return data;
         }
 
         async getEndStageData(sorder) {
-            const sql = 'SELECT uuid, Sum(tp) as tp From' + this.tableName + ' WHERE sorder <= ? And tid = ? GROUP By uuid';
+            const sql = 'SELECT o.uuid, Sum(o.tp) as tp ' +
+                '  From ' + this.tableName + ' o ' +
+                '  LEFT JOIN ' + this.ctx.service.stage.tableName + ' s ON s.id = o.sid' +
+                '  WHERE s.order <= ? And o.tid = ?' +
+                '  GROUP By uuid';
             const sqlParam = [sorder, this.ctx.tender.id];
             const data = await this.db.query(sql, sqlParam);
             return data;
@@ -179,6 +187,7 @@ module.exports = app => {
                     pd.pre_used = pd.pre_used || !this.ctx.helper.checkZero(pd.tp);
                     delete pd.tp;
                     pd.sid = stage.id;
+                    pd.sorder = stage.order;
                 }
                 const result = await transaction.insert(this.tableName, preDatas);
                 return result.affectedRows === preDatas.length;

+ 4 - 0
app/service/tender.js

@@ -126,6 +126,9 @@ module.exports = app => {
                     // 参与审批 台账修订 的标段
                     '    OR (t.`ledger_status` = ' + auditConst.ledger.status.checked + ' AND ' +
                     '        t.id IN ( SELECT ra.`tender_id` FROM ?? AS ra WHERE ra.`audit_id` = ? GROUP BY ra.`tender_id`))' +
+                    // 参与审批 材料调差 的标段
+                    '    OR (t.`ledger_status` = ' + auditConst.ledger.status.checked + ' AND ' +
+                    '        t.id IN ( SELECT ma.`tid` FROM ?? AS ma WHERE ma.`aid` = ? GROUP BY ma.`tid`))' +
                     // 未参与,但可见的标段
                     ') ORDER BY CONVERT(t.`name` USING GBK) ASC';
                 sqlParam = [this.tableName, this.ctx.service.projectAccount.tableName, session.sessionProject.id, session.sessionUser.accountId,
@@ -133,6 +136,7 @@ module.exports = app => {
                     this.ctx.service.stageAudit.tableName, session.sessionUser.accountId,
                     this.ctx.service.changeAudit.tableName, session.sessionUser.accountId,
                     this.ctx.service.reviseAudit.tableName, session.sessionUser.accountId,
+                    this.ctx.service.materialAudit.tableName, session.sessionUser.accountId,
                 ];
             }
             const list = await this.db.query(sql, sqlParam);

+ 3 - 3
app/view/dashboard/index.ejs

@@ -180,7 +180,7 @@
                                                 <div class="row">
                                                     <div class="col-auto"><span class="badge badge-info">台账修订</span></div>
                                                     <div class="col-6">
-                                                        <a href="/tender/<%- nr.id %>>"><%- nr.name %></a>
+                                                        <a href="/tender/<%- nr.t_id %>"><%- nr.t_name %></a>
                                                         <a href="/tender/<%- nr.t_id %>/revise/info">台账修订(第<%- nr.corder %>次)</a>
                                                         <%- acRevise.statusString[nr.status]%>
                                                     </div>
@@ -219,7 +219,7 @@
                                                         <%- acChange.statusString[nc.cu_status]%>
                                                     </div>
                                                 </div>
-                                                <p class="mt-1 mb-0"><%- nc.cu_name %><small class="ml-1 text-muted"><%- (nc.cu_role ? '- ' + nc.cu_role : '') %></small>
+                                                <p class="mt-1 mb-0"><%- nc.cu_name %><small class="ml-1 text-muted"><%- (nc.cu_jobs ? '- ' + nc.cu_jobs : '') %></small>
                                                     <span class="pull-right text-muted"><%- nc.cu_time.toLocaleString() %></span>
                                                 </p>
                                             </div>
@@ -233,7 +233,7 @@
                                                     <div class="col-6">
                                                         <a href="/tender/<%- nm.tid %>"><%- nm.name %></a>
                                                         <a href="/tender/<%- nm.tid %>/measure/stage/<%- nm.m_order %>">第<%- nm.m_order %>期 </a>
-                                                        <%- acStage.statusString[nm.status]%>
+                                                        <%- acMaterial.statusString[nm.status]%>
                                                     </div>
                                                 </div>
                                                 <p class="mt-1 mb-0"><%- nm.su_name %><small class="ml-1 text-muted"><%- (nm.su_role ? '- ' + nm.su_role : '') %></small>

+ 51 - 14
app/view/ledger/bwtz.ejs

@@ -5,30 +5,67 @@
             <% include ../tender/tender_sub_mini_menu.ejs %>
             <div>
                 <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>
+                        </div>
+                    </div>
                 </div>
+                <% if (ctx.app.config.is_debug) { %>
+                <div class="d-inline-block ml-3">
+                    <a id="exportBwtz" class="btn btn-primary btn-sm" href="javascript: void(0)">导出部位台账Excel</a>
+                </div>
+                <% } %>
             </div>
             <div class="ml-auto">
             </div>
         </div>
     </div>
-    <div class="content-wrap">
+    <div class="content-wrap pr-46">
         <div class="c-header p-0"></div>
-        <div class="c-body">
-            <div class="sjs-height-1" id="xmj-spread">
-            </div>
-            <div class="bcontent-wrap" id="main-bottom">
-                <div id="main-resize" class="resize-y" r-Type="height" div1="#xmj-spread" div2="#main-bottom" store-id="stage-bwtz" store-version="1.0.0" min="100"></div>
-                <div class="bc-bar mb-1">
-                    <ul class="nav nav-tabs">
-                        <li class="nav-item">
-                            <a class="nav-link active" data-toggle="tab" href="#jldyjlqd" role="tab">计量单元/计量清单</a>
-                        </li>
-                    </ul>
+        <div class="row w-100 sub-content">
+            <!--左栏-->
+            <div class="c-body" id="left-view" style="width: 100%">
+                <div class="sjs-height-1" id="xmj-spread">
                 </div>
-                <div class="sp-wrap" id="unit-spread">
+                <div class="bcontent-wrap" id="main-bottom">
+                    <div id="main-resize" class="resize-y" r-Type="height" div1="#xmj-spread" div2="#main-bottom" store-id="stage-bwtz" store-version="1.0.0" min="100"></div>
+                    <div class="bc-bar mb-1">
+                        <ul class="nav nav-tabs">
+                            <li class="nav-item">
+                                <a class="nav-link active" data-toggle="tab" href="#jldyjlqd" role="tab">计量单元/计量清单</a>
+                            </li>
+                        </ul>
+                    </div>
+                    <div class="sp-wrap" id="unit-spread">
+                    </div>
                 </div>
             </div>
+            <!--右栏-->
+            <div class="c-body" id="right-view" style="display: none; width: 33%">
+                <div class="resize-x" id="right-spr" r-Type="width" div1="#left-view" div2="#right-view" title="调整大小" a-type="percent"><!--调整左右高度条--></div>
+                <div class="tab-content">
+                    <div id="search" class="tab-pane">
+                    </div>
+                </div>
+            </div>
+        </div>
+        <!--右侧菜单-->
+        <div class="side-menu">
+            <!--右侧菜单-->
+            <ul class="nav flex-column right-nav">
+                <li class="nav-item">
+                    <a class="nav-link" href="javascript: void(0);" role="tab" content="#search">查找定位</a>
+                </li>
+            </ul>
         </div>
     </div>
 </div>

+ 2 - 2
app/view/ledger/explode.ejs

@@ -40,8 +40,8 @@
                     </div>
                 </div>
                 <div class="d-inline-block ml-3">
-                    <!--<a id="exportLedger" class="btn btn-primary btn-sm" href="/tender/<%- ctx.tender.id %>/ledger/download/台账分解.xlsx">下载台账Excel</a>-->
-                    <a id="exportLedger" class="btn btn-primary btn-sm" href="javascript: void(0)">下载台账Excel</a>
+                    <!--<a id="exportLedger" class="btn btn-primary btn-sm" href="/tender/<%- ctx.tender.id %>/ledger/download/台账分解.xlsx">导出台账Excel</a>-->
+                    <a id="exportLedger" class="btn btn-primary btn-sm" href="javascript: void(0)">导出台账Excel</a>
                 </div>
             </div>
             <div class="ml-auto">

+ 7 - 0
app/view/ledger/explode_modal.ejs

@@ -55,6 +55,12 @@
                 <h5 class="modal-title">批量插入清单-计量单元</h5>
             </div>
             <div class="modal-body">
+                <div class="custom-control custom-checkbox mb-2">
+                    <label class="form-check-label">
+                        <input class="form-check-input" type="checkbox" name="batch-filter">
+                        过滤无计量单元的清单
+                    </label>
+                </div>
                 <div class="row">
                     <div class="col-6">
                         <h6>清单信息</h6>
@@ -405,3 +411,4 @@
 <% } %>
 <% include ../shares/merge_peg_modal.ejs %>
 <% include ../shares/import_excel_modal.ejs %>
+<% include ../shares/delete_hint_modal.ejs %>

+ 1 - 1
app/view/material/list.ejs

@@ -49,7 +49,7 @@
                                     <div class="sp-wrap" id="leaf-xmj-spread">
                                     </div>
                                 </div>
-                                <div class="col-4">
+                                <div class="col-4" id="material-spread-div" style="position: relative">
                                     <div class="sp-wrap" id="material-spread">
                                     </div>
                                 </div>

+ 5 - 0
app/view/measure/compare.ejs

@@ -23,6 +23,11 @@
                 <div class="d-inline-block">
                     <button href="#cate-set" class="btn btn-sm btn-light text-primary" data-toggle="modal" data-target="#select-qi"><i class="fa fa-clone"></i> 选择比较期</button>
                 </div>
+                <% if (ctx.app.config.is_debug) { %>
+                <div class="d-inline-block ml-3">
+                    <a id="exportExcel" class="btn btn-primary btn-sm" href="javascript: void(0)">导出清单汇总Excel</a>
+                </div>
+                <% } %>
             </div>
             <div class="ml-auto"></div>
         </div>

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

@@ -147,7 +147,7 @@
 <script src="/public/js/datepicker/datepicker.min.js"></script>
 <script src="/public/js/datepicker/datepicker.zh.js"></script>
 <script>
-    <% if (stages.length > 0 && stages[0].status === auditConst.status.uncheck && stages[0].user_id === ctx.session.sessionUser.accountId) { %>
+    <% if (stages.length > 0 && stages[0].status !== auditConst.status.checked && stages[0].user_id === ctx.session.sessionUser.accountId) { %>
     const editDate = $('#edit-date').datepicker().data('datepicker');
     if ('<%- stages[0].s_time %>' && '<%- stages[0].s_time %>' !== '') {
         editDate.selectDate(new Date('<%- stages[0].s_time %>'));

+ 7 - 0
app/view/revise/info_modal.ejs

@@ -6,6 +6,12 @@
                 <h5 class="modal-title">批量插入清单-计量单元</h5>
             </div>
             <div class="modal-body">
+                <div class="custom-control custom-checkbox mb-2">
+                    <label class="form-check-label">
+                        <input class="form-check-input" type="checkbox" name="batch-filter">
+                        过滤无计量单元的清单
+                    </label>
+                </div>
                 <div class="row">
                     <div class="col-6">
                         <h6>清单信息</h6>
@@ -480,6 +486,7 @@
 <% } %>
 <% include ../shares/merge_peg_modal.ejs %>
 <% include ../shares/import_excel_modal.ejs %>
+<% include ../shares/delete_hint_modal.ejs %>
 <script>
     <% if (ctx.session.sessionUser.accountId === revise.uid && (revise.status === audit.status.uncheck || revise.status === audit.status.checkNo)) { %>
     const accountList = JSON.parse('<%- JSON.stringify(accountList) %>');

+ 28 - 0
app/view/shares/delete_hint_modal.ejs

@@ -0,0 +1,28 @@
+<!--删除节点确认-->
+<div class="modal fade" id="del-node" 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">
+                <h6 id="del-node-hint">确认删除「当前节点及子项」?</h6>
+                <h6>删除后,数据无法恢复,请谨慎操作。</h6>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-sm btn-danger" id="del-node-ok" data-dismiss="modal">确定删除</button>
+                <button type="button" class="btn btn-sm btn-secondary" data-dismiss="modal">取消</button>
+            </div>
+        </div>
+    </div>
+</div>
+<script>
+    const deleteAfterHint = function (fun, hint = '') {
+        $('#del-node').modal('show');
+        $('#del-node-ok').bind('click', fun);
+        if (hint) $('#del-node-hint').html(hint);
+        $('#del-node').bind('hidden.bs.modal', function () {
+            $('#del-node-ok').unbind('click');
+        });
+    }
+</script>

+ 64 - 15
app/view/stage/bwtz.ejs

@@ -5,30 +5,79 @@
             <% include ./stage_sub_mini_menu.ejs %>
             <div>
                 <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="curMeasure" href="javascirpt: void(0);">只显示本期计量</a>-->
+                        </div>
+                    </div>
                 </div>
+                <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" href="javascirpt: void(0);" name="load-data" tag="cur">本期</a>
+                            <a class="dropdown-item" href="javascirpt: void(0);" name="load-data" tag="end">截止本期</a>
+                            <a class="dropdown-item" href="javascirpt: void(0);" name="load-data" tag="all">显示全部</a>
+                        </div>
+                    </div>
+                </div>
+                <% if (ctx.app.config.is_debug) { %>
+                <div class="d-inline-block ml-3">
+                    <a id="exportBwtz" class="btn btn-primary btn-sm" href="javascript: void(0)">导出部位台账Excel</a>
+                </div>
+                <% } %>
             </div>
             <div class="ml-auto">
             </div>
         </div>
     </div>
-    <div class="content-wrap">
+    <div class="content-wrap pr-46">
         <div class="c-header p-0"></div>
-        <div class="c-body">
-            <div class="sjs-height-1" id="xmj-spread">
-            </div>
-            <div class="bcontent-wrap" id="main-bottom">
-                <div id="main-resize" class="resize-y" r-Type="height" div1="#xmj-spread" div2="#main-bottom" store-id="stage-bwtz" store-version="1.0.0" min="100"></div>
-                <div class="bc-bar mb-1">
-                    <ul class="nav nav-tabs">
-                        <li class="nav-item">
-                            <a class="nav-link active" data-toggle="tab" href="#jldyjlqd" role="tab">计量单元/计量清单</a>
-                        </li>
-                    </ul>
+        <div class="row w-100 sub-content">
+            <div class="c-body" id="left-view" style="width: 100%">
+                <div class="sjs-height-1" id="xmj-spread">
                 </div>
-                <div class="sp-wrap" id="unit-spread">
+                <div class="bcontent-wrap" id="main-bottom">
+                    <div id="main-resize" class="resize-y" r-Type="height" div1="#xmj-spread" div2="#main-bottom" store-id="stage-bwtz" store-version="1.0.0" min="100"></div>
+                    <div class="bc-bar mb-1">
+                        <ul class="nav nav-tabs">
+                            <li class="nav-item">
+                                <a class="nav-link active" data-toggle="tab" href="#jldyjlqd" role="tab">计量单元/计量清单</a>
+                            </li>
+                        </ul>
+                    </div>
+                    <div class="sp-wrap" id="unit-spread">
+                    </div>
                 </div>
             </div>
+            <!--右栏-->
+            <div class="c-body" id="right-view" style="display: none; width: 33%">
+                <div class="resize-x" id="right-spr" r-Type="width" div1="#left-view" div2="#right-view" title="调整大小" a-type="percent"><!--调整左右高度条--></div>
+                <div class="tab-content">
+                    <div id="search" class="tab-pane">
+                    </div>
+                </div>
+            </div>
+        </div>
+        <!--右侧菜单-->
+        <div class="side-menu">
+            <!--右侧菜单-->
+            <ul class="nav flex-column right-nav">
+                <li class="nav-item">
+                    <a class="nav-link" href="javascript: void(0);" role="tab" content="#search">查找定位</a>
+                </li>
+            </ul>
         </div>
     </div>
 </div>
@@ -90,7 +139,7 @@
             {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'end_qc_tp', hAlign: 2, width: 60, type: 'Number'},
             {title: '截止本期完成计量|数量', colSpan: '3|1', rowSpan: '1|1', field: 'end_gather_qty', hAlign: 2, width: 60, type: 'Number'},
             {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'end_gather_tp', hAlign: 2, width: 60, type: 'Number'},
-            {title: '|完成率(%)', colSpan: '1', rowSpan: '|1', field: 'end_gather_percent', hAlign: 2, width: 60, readOnly: true, type: 'Number'},
+            {title: '|完成率(%)', colSpan: '|1', rowSpan: '|1', field: 'end_gather_percent', hAlign: 2, width: 60, readOnly: true, type: 'Number'},
             {title: '图(册)号', colSpan: '1', rowSpan: '2', field: 'drawing_code_merge', hAlign: 0, width: 80, formatter: '@'},
             {title: '本期批注', colSpan: '1', rowSpan: '2', field: 'postil_merge', hAlign: 0, width: 100, formatter: '@', cellType: 'ellipsisAutoTip'},
             {title: '备注', colSpan: '1', rowSpan: '2', field: 'memo_merge', hAlign: 0, width: 100, formatter: '@', cellType: 'ellipsisAutoTip'},

+ 5 - 0
app/view/stage/compare.ejs

@@ -33,6 +33,11 @@
                     <button href="#cate-set" class="btn btn-sm btn-light text-primary" data-toggle="modal" data-target="#select-qi"><i class="fa fa-users"></i> 选择比较人</button>
                     <% } %>
                 </div>
+                <% if (ctx.app.config.is_debug) { %>
+                <div class="d-inline-block ml-3">
+                    <a id="exportExcel" class="btn btn-primary btn-sm" href="javascript: void(0)">导出清单汇总Excel</a>
+                </div>
+                <% } %>
             </div>
         </div>
     </div>

+ 5 - 0
app/view/stage/gather.ejs

@@ -45,6 +45,11 @@
                 <div class="d-inline-block">
                     <button href="#zjhj" class="btn btn-sm btn-light text-primary" data-toggle="modal" data-target="#zjhj">章节合计</button>
                 </div>
+                <% if (ctx.app.config.is_debug) { %>
+                <div class="d-inline-block ml-3">
+                    <a id="exportExcel" class="btn btn-primary btn-sm" href="javascript: void(0)">导出清单汇总Excel</a>
+                </div>
+                <% } %>
             </div>
         </div>
     </div>

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

@@ -57,7 +57,7 @@
                             <li class="nav-item">
                                 <div class="ml-2">
                                     <div class="input-group input-group-sm">
-                                        <div class="input-group-prepend" style="display: none;">
+                                        <div class="input-group-prepend">
                                             <div class="input-group-text">
                                                 <div class="form-group form-check mb-0">
                                                     <input type="checkbox" class="form-check-input group-checkbox" id="pos-over-search">

+ 5 - 0
app/view/stage_extra/bonus.ejs

@@ -6,6 +6,11 @@
             <div>
                 奖罚金
             </div>
+            <% if (ctx.app.config.is_debug) { %>
+            <div class="d-inline-block ml-3">
+                <a id="exportExcel" class="btn btn-primary btn-sm" href="javascript: void(0)">导出奖罚金Excel</a>
+            </div>
+            <% } %>
             <div class="ml-auto"></div>
         </div>
     </div>

+ 5 - 0
app/view/stage_extra/jgcl.ejs

@@ -8,6 +8,11 @@
             </div>
             <!--<div id="sum" class="ml-3">
             </div>-->
+            <% if (ctx.app.config.is_debug) { %>
+            <div class="d-inline-block ml-3">
+                <a id="exportExcel" class="btn btn-primary btn-sm" href="javascript: void(0)">导出甲供材料Excel</a>
+            </div>
+            <% } %>
             <div class="ml-auto"></div>
         </div>
     </div>

+ 5 - 0
app/view/stage_extra/other.ejs

@@ -6,6 +6,11 @@
             <div>
                 其他
             </div>
+            <% if (ctx.app.config.is_debug) { %>
+            <div class="d-inline-block ml-3">
+                <a id="exportExcel" class="btn btn-primary btn-sm" href="javascript: void(0)">导出其他Excel</a>
+            </div>
+            <% } %>
             <div class="ml-auto"></div>
         </div>
     </div>

+ 1 - 1
builder_report_index_define.js

@@ -824,7 +824,7 @@ const gather_deal_bills = {
     remark: '',
     id: 39,
     key: 'mem_gather_deal_bills',
-    prefix: '汇总-期-合同支付',
+    prefix: '汇总-签约清单',
     cols: [
         { name: '编号', field: 'code', type: dataType.str },
         { name: '名称', field: 'name', type: dataType.str },

+ 34 - 0
config/web.js

@@ -62,6 +62,7 @@ const JsFiles = {
                 ],
                 mergeFiles: [
                     "/public/js/zh_calc.js",
+                    "/public/js/shares/tender_list_order.js",
                     "/public/js/tender_showhide.js",
                     "/public/js/tender_list.js"
                 ],
@@ -75,6 +76,7 @@ const JsFiles = {
                 ],
                 mergeFiles: [
                     "/public/js/zh_calc.js",
+                    "/public/js/shares/tender_list_order.js",
                     "/public/js/tender_showhide.js",
                     "/public/js/tender_list_info.js"
                 ],
@@ -88,6 +90,7 @@ const JsFiles = {
                 ],
                 mergeFiles: [
                     "/public/js/zh_calc.js",
+                    "/public/js/shares/tender_list_order.js",
                     "/public/js/tender_showhide.js",
                     "/public/js/tender_list_progress.js"
                 ],
@@ -102,6 +105,7 @@ const JsFiles = {
                 ],
                 mergeFiles: [
                     "/public/js/zh_calc.js",
+                    "/public/js/shares/tender_list_order.js",
                     "/public/js/tender_showhide.js",
                     "/public/js/tender_list_manage.js",
                 ],
@@ -168,13 +172,17 @@ const JsFiles = {
             bwtz: {
                 files: [
                     "/public/js/spreadjs/sheets/v11/gc.spread.sheets.all.11.2.2.min.js",
+                    "/public/js/spreadjs/sheets/v11/interop/gc.spread.excelio.11.2.2.min.js",
                     "/public/js/decimal.min.js",
+                    "/public/js/file-saver/FileSaver.js",
+                    "/public/js/shares/export_excel.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/ledger_search.js",
                     "/public/js/zh_calc.js",
                     "/public/js/path_tree.js",
                     "/public/js/shares/bills_pos_convert.js",
@@ -185,7 +193,10 @@ const JsFiles = {
             gather: {
                 files: [
                     "/public/js/spreadjs/sheets/v11/gc.spread.sheets.all.11.2.2.min.js",
+                    "/public/js/spreadjs/sheets/v11/interop/gc.spread.excelio.11.2.2.min.js",
                     "/public/js/decimal.min.js",
+                    "/public/js/file-saver/FileSaver.js",
+                    "/public/js/shares/export_excel.js",
                 ],
                 mergeFiles: [
                     "/public/js/sub_menu.js",
@@ -271,6 +282,7 @@ const JsFiles = {
                     "/public/js/msg_box.js",
                     "/public/js/spreadjs_rela/spreadjs_zh.js",
                     "/public/js/shares/sjs_setting.js",
+                    "/public/js/ledger_search.js",
                     "/public/js/shares/merge_peg.js",
                     "/public/js/zh_calc.js",
                     "/public/js/path_tree.js",
@@ -314,13 +326,17 @@ const JsFiles = {
             bwtz: {
                 files: [
                     "/public/js/spreadjs/sheets/v11/gc.spread.sheets.all.11.2.2.min.js",
+                    "/public/js/spreadjs/sheets/v11/interop/gc.spread.excelio.11.2.2.min.js",
                     "/public/js/decimal.min.js",
+                    "/public/js/file-saver/FileSaver.js",
+                    "/public/js/shares/export_excel.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/ledger_search.js",
                     "/public/js/zh_calc.js",
                     "/public/js/path_tree.js",
                     "/public/js/shares/bills_pos_convert.js",
@@ -348,7 +364,10 @@ const JsFiles = {
             gather: {
                 files: [
                     "/public/js/spreadjs/sheets/v11/gc.spread.sheets.all.11.2.2.min.js",
+                    "/public/js/spreadjs/sheets/v11/interop/gc.spread.excelio.11.2.2.min.js",
                     "/public/js/decimal.min.js",
+                    "/public/js/file-saver/FileSaver.js",
+                    "/public/js/shares/export_excel.js",
                 ],
                 mergeFiles: [
                     "/public/js/sub_menu.js",
@@ -365,7 +384,10 @@ const JsFiles = {
             compare: {
                 files: [
                     "/public/js/spreadjs/sheets/v11/gc.spread.sheets.all.11.2.2.min.js",
+                    "/public/js/spreadjs/sheets/v11/interop/gc.spread.excelio.11.2.2.min.js",
                     "/public/js/decimal.min.js",
+                    "/public/js/file-saver/FileSaver.js",
+                    "/public/js/shares/export_excel.js",
                 ],
                 mergeFiles: [
                     "/public/js/sub_menu.js",
@@ -384,7 +406,10 @@ const JsFiles = {
             jgcl: {
                 files: [
                     "/public/js/spreadjs/sheets/v11/gc.spread.sheets.all.11.2.2.min.js",
+                    "/public/js/spreadjs/sheets/v11/interop/gc.spread.excelio.11.2.2.min.js",
                     "/public/js/decimal.min.js",
+                    "/public/js/file-saver/FileSaver.js",
+                    "/public/js/shares/export_excel.js",
                 ],
                 mergeFiles: [
                     "/public/js/sub_menu.js",
@@ -397,9 +422,12 @@ const JsFiles = {
             bonus: {
                 files: [
                     "/public/js/spreadjs/sheets/v11/gc.spread.sheets.all.11.2.2.min.js",
+                    "/public/js/spreadjs/sheets/v11/interop/gc.spread.excelio.11.2.2.min.js",
                     "/public/js/decimal.min.js",
                     "/public/js/datepicker/datepicker.min.js",
                     "/public/js/datepicker/datepicker.zh.js",
+                    "/public/js/file-saver/FileSaver.js",
+                    "/public/js/shares/export_excel.js",
                 ],
                 mergeFiles: [
                     "/public/js/sub_menu.js",
@@ -412,9 +440,12 @@ const JsFiles = {
             other: {
                 files: [
                     "/public/js/spreadjs/sheets/v11/gc.spread.sheets.all.11.2.2.min.js",
+                    "/public/js/spreadjs/sheets/v11/interop/gc.spread.excelio.11.2.2.min.js",
                     "/public/js/decimal.min.js",
                     "/public/js/datepicker/datepicker.min.js",
                     "/public/js/datepicker/datepicker.zh.js",
+                    "/public/js/file-saver/FileSaver.js",
+                    "/public/js/shares/export_excel.js",
                 ],
                 mergeFiles: [
                     "/public/js/sub_menu.js",
@@ -431,7 +462,10 @@ const JsFiles = {
             compare: {
                 files: [
                     "/public/js/spreadjs/sheets/v11/gc.spread.sheets.all.11.2.2.min.js",
+                    "/public/js/spreadjs/sheets/v11/interop/gc.spread.excelio.11.2.2.min.js",
                     "/public/js/decimal.min.js",
+                    "/public/js/file-saver/FileSaver.js",
+                    "/public/js/shares/export_excel.js",
                 ],
                 mergeFiles: [
                     "/public/js/sub_menu.js",