Browse Source

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

# Conflicts:
#	sql/update.sql
TonyKang 4 years ago
parent
commit
d50e6ef27d
38 changed files with 1142 additions and 231 deletions
  1. 6 4
      app/controller/change_controller.js
  2. 20 7
      app/controller/schedule_controller.js
  3. 2 2
      app/lib/analysis_excel.js
  4. 2 0
      app/lib/bills_pos_convert.js
  5. 60 0
      app/lib/rpt_data_analysis.js
  6. 14 0
      app/public/js/change_information_approval.js
  7. 56 9
      app/public/js/change_information_set.js
  8. 14 0
      app/public/js/change_information_show.js
  9. 4 0
      app/public/js/ledger.js
  10. 1 1
      app/public/js/path_tree.js
  11. 27 9
      app/public/js/schedule_ledger.js
  12. 358 20
      app/public/js/schedule_plan.js
  13. 4 2
      app/public/js/shares/cs_tools.js
  14. 1 0
      app/public/js/shares/tenders2tree.js
  15. 112 35
      app/public/js/shenpi.js
  16. 13 11
      app/public/js/spreadjs_rela/spreadjs_zh.js
  17. 8 1
      app/public/js/stage.js
  18. 63 13
      app/public/report/js/rpt_custom.js
  19. 3 0
      app/service/change_audit_list.js
  20. 7 0
      app/service/ledger_cooperation.js
  21. 14 0
      app/service/report.js
  22. 18 0
      app/service/report_memory.js
  23. 158 83
      app/service/rpt_gather_memory.js
  24. 11 0
      app/service/schedule_ledger_month.js
  25. 38 0
      app/service/schedule_month.js
  26. 1 1
      app/service/stage_audit.js
  27. 8 11
      app/service/stage_detail.js
  28. 3 3
      app/view/profile/wechat.ejs
  29. 28 2
      app/view/report/rpt_all_popup.ejs
  30. 1 1
      app/view/schedule/index.ejs
  31. 2 1
      app/view/schedule/plan.ejs
  32. 13 9
      app/view/schedule/plan_modal.ejs
  33. 1 1
      app/view/shares/check_modal2.ejs
  34. 1 1
      app/view/stage/index.ejs
  35. 3 2
      app/view/wap/list.ejs
  36. 35 2
      builder_report_index_define.js
  37. 16 0
      sql/update.sql
  38. 16 0
      test/app/lib/rpt_data_analysis.test.js

+ 6 - 4
app/controller/change_controller.js

@@ -637,9 +637,10 @@ module.exports = app => {
                     renderData.auditList3 = auditList3;
 
                     // 获取已选清单
-                    let changeList = await ctx.service.changeAuditList.getAllDataByCondition({ where: { cid: ctx.params.cid } });
+                    // let changeList = await ctx.service.changeAuditList.getAllDataByCondition({ where: { cid: ctx.params.cid } });
+                    const changeList = await ctx.service.changeAuditList.getList(change.cid);
 
-                    changeList = JSON.parse(JSON.stringify(changeList.sort())).sort().sort();
+                    // changeList = JSON.parse(JSON.stringify(changeList.sort())).sort().sort();
                     for (const cl of changeList) {
                         const audit_amount = cl.audit_amount !== null && cl.audit_amount !== '' ? cl.audit_amount.split(',') : '';
                         // 清单表页赋值
@@ -685,9 +686,10 @@ module.exports = app => {
                     renderData.auditList4 = auditList4;
 
                     // 获取已选清单
-                    let changeList = await ctx.service.changeAuditList.getAllDataByCondition({ where: { cid: ctx.params.cid } });
+                    // let changeList = await ctx.service.changeAuditList.getAllDataByCondition({ where: { cid: ctx.params.cid } });
+                    const changeList = await ctx.service.changeAuditList.getList(change.cid);
 
-                    changeList = JSON.parse(JSON.stringify(changeList.sort())).sort().sort();
+                    // changeList = JSON.parse(JSON.stringify(changeList.sort())).sort().sort();
                     for (const cl of changeList) {
                         const audit_amount = cl.audit_amount !== null && cl.audit_amount !== '' ? cl.audit_amount.split(',') : '';
                         // 清单表页赋值

+ 20 - 7
app/controller/schedule_controller.js

@@ -22,11 +22,17 @@ module.exports = app => {
             return _.map(scheduleLedgerList, 'ledger_id');
         }
 
+        async _getLastPlanMonth(ctx) {
+            const lastMonth = await ctx.service.scheduleMonth.getLastPlanMonth();
+            return lastMonth && lastMonth[0] && lastMonth[0].yearmonth ? lastMonth[0].yearmonth : null;
+        }
+
         async index(ctx) {
             try {
                 const renderData = {
                     tender: ctx.tender.data,
                     tenderMenu: this.menu.tenderMenu,
+                    planMonth: await this._getLastPlanMonth(ctx),
                     scheduleLedgerList: await this._getSelectedLedgerList(ctx),
                     preUrl: '/tender/' + ctx.tender.id,
                 };
@@ -52,12 +58,13 @@ module.exports = app => {
         async plan(ctx) {
             const tender = ctx.tender;
             const schedule = await ctx.service.schedule.getDataByCondition({ tid: tender.id });
-            const scheduleMonth = await ctx.service.scheduleMonth.getAllDataByCondition({ tid: tender.id });
+            const scheduleMonth = await ctx.service.scheduleMonth.getAllDataByCondition({ where: { tid: tender.id }, orders: [['yearmonth', 'asc']] });
             const renderData = {
                 tender: tender.data,
                 tenderInfo: tender.info,
                 schedule,
                 scheduleMonth,
+                planMonth: await this._getLastPlanMonth(ctx),
                 measureType,
                 mode: scheduleConst.plan_mode,
                 scheduleLedgerList: await this._getSelectedLedgerList(ctx),
@@ -75,12 +82,12 @@ module.exports = app => {
         async loadLedgerData(ctx) {
             try {
                 const ledgerData = await ctx.service.ledger.getData(ctx.tender.id);
-                const posData = ctx.tender.data.measure_type === measureType.tz.value
-                    ? await ctx.service.pos.getPosData({ tid: ctx.tender.id }) : [];
-                const convert = new billsPosConvert(ctx);
-                convert.loadData(ledgerData, posData, []);
-                const result = await convert.convert();
-                ctx.body = { err: 0, msg: '', data: result };
+                // const posData = ctx.tender.data.measure_type === measureType.tz.value
+                //     ? await ctx.service.pos.getPosData({ tid: ctx.tender.id }) : [];
+                // const convert = new billsPosConvert(ctx);
+                // convert.loadData(ledgerData, posData, []);
+                // const result = await convert.convert();
+                ctx.body = { err: 0, msg: '', data: ledgerData };
             } catch (err) {
                 this.log(err);
                 ctx.body = { err: 1, msg: err.toString(), data: [] };
@@ -122,6 +129,12 @@ module.exports = app => {
                     case 'mode':
                         responseData.data = await ctx.service.schedule.saveMode(data.postData);
                         break;
+                    case 'addmonth':
+                        responseData.data = await ctx.service.scheduleMonth.add(data.postData);
+                        break;
+                    case 'delmonth':
+                        responseData.data = await ctx.service.scheduleMonth.del(data.postData);
+                        break;
                     default: throw '参数有误';
                 }
                 ctx.body = responseData;

+ 2 - 2
app/lib/analysis_excel.js

@@ -279,9 +279,9 @@ class ImportBaseTree {
         const splitChar = this.splitChar;
         const firstPart = this.roots[0];
         firstPart.children.sort(function (a, b) {
-            if (a.code === '') {
+            if (!a.code) {
                 return 1;
-            } else if (b.code === '') {
+            } else if (!b.code) {
                 return -1;
             }
             const codeA = a.code.split(splitChar);

+ 2 - 0
app/lib/bills_pos_convert.js

@@ -241,6 +241,8 @@ class BillsPosConvert {
             node.real_tp = this.ctx.helper.add(node.real_tp, child.real_tp);
             node.estimate_tp = this.ctx.helper.add(node.estimate_tp, child.estimate_tp);
         }
+        if (node.dgn_qty1)
+            node.dgn_price = this.ctx.helper.div(node.total_price, node.dgn_qty1, this.ctx.tender.info.decimal.up);
         node.final_tp = this.ctx.helper.add(node.total_price, node.end_qc_tp);
         node.end_gather_percent = this.ctx.helper.mul(this.ctx.helper.div(node.end_gather_tp, node.final_tp, 4), 100);
     }

+ 60 - 0
app/lib/rpt_data_analysis.js

@@ -1416,6 +1416,65 @@ const stageSelectConverse = {
         }
     }
 };
+const loadCooperationData = {
+    name: '加载协作数据',
+    hint: '须使用台账、计量审批流程、协作数据作为基础',
+    defaultSetting: {
+        table: 'mem_stage_im_zl',
+        co_sign: [0,1,2,3],
+    },
+    _findSign: function (relaId, stageCooperation, auditor) {
+        if (relaId.length > 0) {
+            for (const id of relaId) {
+                const c = stageCooperation.find(x => {return x.ledger_id == id});
+                if (c) return c.sign_path;
+            }
+            return auditor.sign_path;
+        } else {
+            return auditor.sign_path;
+        }
+    },
+    _loadImCooperationData: function (ctx, data, options, csRela) {
+        let coSignOrder = [];
+        if (csRela && csRela.tplDefine && csRela.tplDefine.audit_select && csRela.cDefine && csRela.cDefine.audit_select) {
+            if (csRela.cDefine.audit_select) {
+                for (const asc of csRela.cDefine.audit_select) {
+                    coSignOrder.push(asc.order);
+                }
+            }
+        } else {
+            coSignOrder = options.co_sign || [];
+        }
+
+        const stageCooperation = [];
+        for (const sa of data.stage_audit) {
+            stageCooperation.push(data.ledger_cooperation.filter(x => {return x.user_id === sa.aid}));
+        }
+        for (const d of data[options.table]) {
+            const bills = data.mem_stage_bills.find(x => {return x.id === d.lid});
+            const relaId = bills ? bills.full_path.split('-').reverse() : [];
+
+            d.cooperation = [];
+            for (const [i, sa] of data.stage_audit.entries()) {
+                d.cooperation.push(this._findSign(relaId, stageCooperation[i], sa));
+            }
+
+            for (const [i, cs] of coSignOrder.entries()) {
+                d['co_sign' + (i+1)] = d.cooperation[cs] || '';
+            }
+        }
+    },
+    fun: function (ctx, data, fieldsKey, options, csRela) {
+        if (!options || !options.table) return;
+        if (!data[options.table]) return;
+        if (!data.mem_stage_bills) return;
+        if (!data.stage_audit) return;
+        if (!data.ledger_cooperation) return;
+
+        if (['mem_stage_im_zl', 'mem_stage_im_tz'].indexOf(options.table) >= 0)
+            this._loadImCooperationData(ctx, data, options, csRela);
+    }
+};
 
 const analysisObj = {
     changeSort,
@@ -1435,6 +1494,7 @@ const analysisObj = {
     sortPos,
     unionPos,
     splitXmjCode,
+    loadCooperationData,
 };
 const analysisDefine = (function (obj) {
     const result = [];

+ 14 - 0
app/public/js/change_information_approval.js

@@ -28,6 +28,20 @@ $(document).ready(() => {
         headerFont: '12px 微软雅黑',
         font: '12px 微软雅黑',
         // readOnly: true,
+        rowHeader:[
+            {
+                rowHeaderType: 'circle',
+                setting: {
+                    size: 5,
+                    indent: 16,
+                    getColor: function (index, data) {
+                        if (!data) return;
+                        if(data.lid != 0) return;
+                        return '#007bff';
+                    }
+                },
+            },
+        ],
     };
     for (const aid of aidList) {
         const userinfo = _.find(auditList2, { 'uid': aid });

+ 56 - 9
app/public/js/change_information_set.js

@@ -81,6 +81,20 @@ $(document).ready(() => {
         headerFont: '12px 微软雅黑',
         font: '12px 微软雅黑',
         readOnly: readOnly,
+        rowHeader:[
+            {
+                rowHeaderType: 'circle',
+                setting: {
+                    size: 5,
+                    indent: 16,
+                    getColor: function (index, data) {
+                        if (!data) return;
+                        if(data.lid != 0) return;
+                        return '#007bff';
+                    }
+                },
+            },
+        ],
     };
 
     const changeCol = {
@@ -161,7 +175,7 @@ $(document).ready(() => {
             const sel = info.sheet.getSelections()[0];
             const col = info.sheet.zh_setting.cols[sel.col];
             const data = SpreadJsObj.getSelectObject(info.sheet);
-            if (col.field === 'del_list') {
+            if (col && col.field === 'del_list') {
                 changeSpreadObj.del();
             }
         },
@@ -347,7 +361,7 @@ $(document).ready(() => {
         let gcl_index = 0;
         for (const gcl of changeListData) {
             const unit = gcl.unit !== undefined && gcl.unit !== null ? gcl.unit : '';
-            const quantity = gcl.quantity !== null && gcl.quantity !== undefined ? (unit !== '' ? ZhCalc.round(gcl.quantity, findDecimal(gcl.unit)) : gcl.quantity) : 0;
+            const quantity = gcl.quantity !== 0 && gcl.quantity !== null && gcl.quantity !== undefined ? (unit !== '' ? ZhCalc.round(gcl.quantity, findDecimal(gcl.unit)) : gcl.quantity) : 0;
             const unit_price = gcl.unit_price !== null && gcl.unit_price !== undefined ? gcl.unit_price : 0;
             let gclhtml = gcl.leafXmjs !== undefined && gcl.leafXmjs !== null ? ' data-gcl="' + gcl_index + '"' : '';
             gcl_index = gclhtml !== '' ? ++gcl_index : gcl_index;
@@ -445,7 +459,12 @@ $(document).ready(() => {
                 const quantity = leaf.quantity !== undefined && leaf.quantity !== null ? leaf.quantity : 0;
                 const gcl_id = leaf.gcl_id ? leaf.gcl_id : '';
                 const bwmx = leaf.bwmx !== undefined ? leaf.bwmx : '';
-                const isChecked = data_bwmx.indexOf(leaf.code + '!_!' + (leaf.jldy ? leaf.jldy : '') + '!_!' + (leaf.gcl_id ? leaf.gcl_id : '0') + '!_!' + (bwmx !== '' ? bwmx : leaf.jldy ? leaf.jldy : '') + '*;*' + quantity) !== -1 && isCheck ? 'checked' : '';
+                const isChecked = data_bwmx.indexOf(
+                    leaf.code + '!_!' + (leaf.jldy ? leaf.jldy : '') + '!_!' +
+                    (leaf.dwgc ? leaf.dwgc : '') + '!_!' + (leaf.fbgc ? leaf.fbgc : '') + '!_!' + (leaf.fxgc ? leaf.fxgc : '')
+                    + '!_!' + (leaf.gcl_id ? leaf.gcl_id : '0') + '!_!' +
+                    (bwmx !== '' ? bwmx : leaf.jldy ? leaf.jldy : '') + '*;*' + quantity) !== -1 && isCheck ?
+                    'checked' : '';
                 codeHtml += '<tr quantity="' + quantity + '" gcl_id="' + gcl_id + '"><td>' + leaf.code + '</td>' +
                     '<td>' + (leaf.jldy ? leaf.jldy: '') + '</td>' +
                     '<td>' + (leaf.dwgc ? leaf.dwgc : '') + '</td>' +
@@ -480,7 +499,13 @@ $(document).ready(() => {
                 const tr = $(this).parents('tr');
                 const length = tr.children('td').length;
                 const gcl_id = tr.attr('gcl_id');
-                const bwmx = length === 8 ? tr.children('td').eq(0).text() + '!_!' + tr.children('td').eq(1).text() + '!_!' + gcl_id + '!_!' + (tr.children('td').eq(5).text() !== '' ? tr.children('td').eq(5).text() : tr.children('td').eq(1).text()) : '0';
+                const bwmx = length === 8 ?
+                    tr.children('td').eq(0).text() + '!_!' +
+                    tr.children('td').eq(1).text() + '!_!' +
+                    tr.children('td').eq(2).text() + '!_!' +
+                    tr.children('td').eq(3).text() + '!_!' +
+                    tr.children('td').eq(4).text() + '!_!' + gcl_id + '!_!' +
+                    (tr.children('td').eq(5).text() !== '' ? tr.children('td').eq(5).text() : tr.children('td').eq(1).text()) : '0';
                 const quantity = tr.attr('quantity');
                 const de_qu = bwmx + '*;*' + quantity;
                 data_bwmx.push(de_qu);
@@ -496,7 +521,13 @@ $(document).ready(() => {
                     const tr = $(this).parents('tr');
                     const length = tr.children('td').length;
                     const gcl_id = tr.attr('gcl_id');
-                    const bwmx = length === 8 ? tr.children('td').eq(0).text() + '!_!'+ tr.children('td').eq(1).text() + '!_!' + gcl_id + '!_!' + (tr.children('td').eq(5).text() !== '' ? tr.children('td').eq(5).text() : tr.children('td').eq(1).text()) : '0';
+                    const bwmx = length === 8 ?
+                        tr.children('td').eq(0).text() + '!_!' +
+                        tr.children('td').eq(1).text() + '!_!' +
+                        tr.children('td').eq(2).text() + '!_!' +
+                        tr.children('td').eq(3).text() + '!_!' +
+                        tr.children('td').eq(4).text() + '!_!' + gcl_id + '!_!' +
+                        (tr.children('td').eq(5).text() !== '' ? tr.children('td').eq(5).text() : tr.children('td').eq(1).text()) : '0';
                     const quantity = tr.attr('quantity');
                     const de_qu = bwmx + '*;*' + quantity;
                     data_bwmx.push(de_qu);
@@ -717,7 +748,12 @@ function tableDataRemake(changeListData) {
                         });
                         console.log(leafInfo);
                         if (leafInfo) {
-                            pushbwmx = leafInfo.code + '!_!' + (leafInfo.jldy !== undefined ? leafInfo.jldy : '') + '!_!' + (leafInfo.gcl_id ? leafInfo.gcl_id : '') + '!_!' + (leafInfo.bwmx !== undefined ? leafInfo.bwmx : '') + '*;*' + (leafInfo.quantity !== null ? leafInfo.quantity : 0);
+                            pushbwmx = leafInfo.code + '!_!' + (leafInfo.jldy !== undefined ? leafInfo.jldy : '') + '!_!' +
+                                (leafInfo.dwgc ? leafInfo.dwgc : '') + '!_!' +
+                                (leafInfo.fbgc ? leafInfo.fbgc : '') + '!_!' +
+                                (leafInfo.fxgc ? leafInfo.fxgc : '') + '!_!' +
+                                (leafInfo.gcl_id ? leafInfo.gcl_id : '') + '!_!' +
+                                (leafInfo.bwmx !== undefined ? leafInfo.bwmx : (leafInfo.jldy !== undefined ? leafInfo.jldy : '')) + '*;*' + (leafInfo.quantity !== null ? leafInfo.quantity : 0);
                         } else {
                             toastr.warning('台账清单列表已不存在'+ clinfo.code +',已更新变更清单列表');
                             changeList.splice(index, 1);
@@ -742,7 +778,12 @@ function tableDataRemake(changeListData) {
                             return (item.bwmx === undefined || item.bwmx === clinfo.bwmx || item.jldy === clinfo.bwmx) && (item.quantity !== null ? item.quantity === parseFloat(clinfo.oamount) : 0 === parseFloat(clinfo.oamount));
                         });
                         if (leafInfo) {
-                            pushbwmx = leafInfo.code + '!_!' + (leafInfo.jldy !== undefined ? leafInfo.jldy : '') + '!_!' + (leafInfo.gcl_id ? leafInfo.gcl_id : '') + '!_!' + (leafInfo.bwmx !== undefined ? leafInfo.bwmx : (leafInfo.jldy ? leafInfo.jldy : '')) + '*;*' + (leafInfo.quantity !== null ? leafInfo.quantity : 0);
+                            pushbwmx = leafInfo.code + '!_!' + (leafInfo.jldy !== undefined ? leafInfo.jldy : '') + '!_!' +
+                                (leafInfo.dwgc ? leafInfo.dwgc : '') + '!_!' +
+                                (leafInfo.fbgc ? leafInfo.fbgc : '') + '!_!' +
+                                (leafInfo.fxgc ? leafInfo.fxgc : '') + '!_!' +
+                                (leafInfo.gcl_id ? leafInfo.gcl_id : '') + '!_!' +
+                                (leafInfo.bwmx !== undefined ? leafInfo.bwmx : (leafInfo.jldy !== undefined ? leafInfo.jldy : '')) + '*;*' + (leafInfo.quantity !== null ? leafInfo.quantity : 0);
                         } else {
                             toastr.warning('台账清单列表已不存在'+ clinfo.code +',已更新变更清单列表');
                             changeList.splice(index, 1);
@@ -819,10 +860,13 @@ function remakeChangeSpread() {
 
         for (const b of data_bwmx) {
             const oamount = b.split('*;*')[1] != '' ? b.split('*;*')[1] : 0;
-            let bwmx = b.split('*;*')[0] != 0 ? b.split('*;*')[0].split('!_!')[3] : '';
+            let bwmx = b.split('*;*')[0] != 0 ? b.split('*;*')[0].split('!_!')[6] : '';
             let xmj_code = b.split('*;*')[0] != 0 ? b.split('*;*')[0].split('!_!')[0] : '';
             let xmj_jldy = b.split('*;*')[0] != 0 ? b.split('*;*')[0].split('!_!')[1] : '';
-            let gcl_id = b.split('*;*')[0] != 0 ? b.split('*;*')[0].split('!_!')[2] : '';
+            let xmj_dwgc = b.split('*;*')[0] != 0 ? b.split('*;*')[0].split('!_!')[2] : '';
+            let xmj_fbgc = b.split('*;*')[0] != 0 ? b.split('*;*')[0].split('!_!')[3] : '';
+            let xmj_fxgc = b.split('*;*')[0] != 0 ? b.split('*;*')[0].split('!_!')[4] : '';
+            let gcl_id = b.split('*;*')[0] != 0 ? b.split('*;*')[0].split('!_!')[5] : '';
             let trlist = {
                 code,
                 name,
@@ -835,6 +879,9 @@ function remakeChangeSpread() {
                 lid,
                 xmj_code,
                 xmj_jldy,
+                xmj_dwgc,
+                xmj_fbgc,
+                xmj_fxgc,
                 gcl_id,
             };
             const radionInfo = changeList.find(function (info) {

+ 14 - 0
app/public/js/change_information_show.js

@@ -30,6 +30,20 @@ $(document).ready(() => {
         headerFont: '12px 微软雅黑',
         font: '12px 微软雅黑',
         readOnly: true,
+        rowHeader:[
+            {
+                rowHeaderType: 'circle',
+                setting: {
+                    size: 5,
+                    indent: 16,
+                    getColor: function (index, data) {
+                        if (!data) return;
+                        if(data.lid != 0) return;
+                        return '#007bff';
+                    }
+                },
+            },
+        ],
     };
     for (const aid of aidList) {
         const userinfo = _.find(auditList2, { 'uid': aid });

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

@@ -1013,10 +1013,14 @@ $(document).ready(function() {
     //         },
     //     },
     // ];
+    ledgerSpreadSetting.headColWidth = [50];
     ledgerSpreadSetting.rowHeader = [
         {
             rowHeaderType: 'tag',
             setting: {
+                indent: 14,
+                tagSize: 0.8,
+                tagFont: '8px 微软雅黑',
                 getColor: function (index, data) {
                     if (!data) return;
                     return billsTag.getBillsTagsColor(data.id);

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

@@ -1131,7 +1131,7 @@ const createNewPathTree = function (type, setting) {
                 if (!ledger_id || !pwd) continue;
 
                 const p = this.pwd.find(x => {return x.ledger_id == ledger_id});
-                p.check = p.pwd === pwd;
+                if (p) p.check = p.pwd === pwd;
             }
         }
         loadPwd(data, cacheKey) {

+ 27 - 9
app/public/js/schedule_ledger.js

@@ -20,15 +20,11 @@ $(function () {
         level: 'level',
         rootId: -1,
         fullPath: 'full_path',
-        calcFields: ['total_price']
         //treeCacheKey: 'ledger_bills_fold' + '_' + getTenderId(),
         // markFoldKey: 'bills-fold',
         // markFoldSubKey: window.location.pathname.split('/')[2],
     };
-    treeSetting.calcFun = function (node) {
-        node.dgn_price = ZhCalc.round(ZhCalc.div(node.total_price, node.dgn_qty1), 2);
-    };
-    const ledgerTree = createNewPathTree('base', treeSetting);
+    const ledgerTree = createNewPathTree('filter', treeSetting);
 
     const ledgerSpreadSetting = {
         cols: [
@@ -59,10 +55,28 @@ $(function () {
     SpreadJsObj.selChangedRefreshBackColor(ledgerSpread.getActiveSheet());
 
     postData(window.location.pathname + '/load', {}, function (data) {
+        const baseLedgerTree = createNewPathTree('base', {
+            id: 'ledger_id',
+            pid: 'ledger_pid',
+            order: 'order',
+            level: 'level',
+            rootId: -1,
+            fullPath: 'full_path',
+            calcFields: ['total_price'],
+            calcFun: function (node) {
+                node.dgn_price = ZhCalc.round(ZhCalc.div(node.total_price, node.dgn_qty1), 2);
+            }
+        });
         data = addIsSelect(data);
-        ledgerTree.loadDatas(data);
-        treeCalc.calculateAll(ledgerTree);
-        // console.log(ledgerTree);
+        baseLedgerTree.loadDatas(data);
+        treeCalc.calculateAll(baseLedgerTree);
+        for (const d of baseLedgerTree.nodes) {
+            if (!d.b_code)
+                ledgerTree.addData(d, ['is_select', 'ledger_id', 'ledger_pid', 'order', 'level', 'tender_id', 'full_path',
+                    'code', 'name', 'unit', 'dgn_qty1', 'dgn_qty2', 'dgn_price', 'quantity', 'total_price']);
+        }
+        ledgerTree.sortTreeNode(true);
+
         SpreadJsObj.loadSheetData(ledgerSpread.getActiveSheet(), SpreadJsObj.DataType.Tree, ledgerTree);
     }, null, true);
 
@@ -225,8 +239,12 @@ $(function () {
     // })('a[name=showLevel]', ledgerSpread.getActiveSheet());
 });
 function addIsSelect(datas) {
+    // const newDatas = [];
     for (const d of datas) {
-        d.is_select = selectedLedgerList.length === 0 ? 1: selectedLedgerList.indexOf(d.ledger_id) !== -1 ? 1 : 0;
+        if (!d.b_code) {
+            d.is_select = selectedLedgerList.length === 0 ? 1: selectedLedgerList.indexOf(d.ledger_id) !== -1 ? 1 : 0;
+            // newDatas.push(d);
+        }
     }
     return datas;
 }

+ 358 - 20
app/public/js/schedule_plan.js

@@ -22,25 +22,22 @@ $(function () {
         level: 'level',
         rootId: -1,
         fullPath: 'full_path',
-        calcFields: ['total_price']
         //treeCacheKey: 'ledger_bills_fold' + '_' + getTenderId(),
         // markFoldKey: 'bills-fold',
         // markFoldSubKey: window.location.pathname.split('/')[2],
     };
-    treeSetting.calcFun = function (node) {
-        node.dgn_price = ZhCalc.round(ZhCalc.div(node.total_price, node.dgn_qty1), 2);
-    };
-    const ledgerTree = createNewPathTree('base', treeSetting);
+    const ledgerTree = createNewPathTree('filter', treeSetting);
+
+    const static_cols = [
+        {title: '编号', colSpan: '1', rowSpan: '2', field: 'code', hAlign: 0, width: 145, formatter: '@', readOnly: true, cellType: 'tree'},
+        {title: '名称', colSpan: '1', rowSpan: '2', field: 'name', hAlign: 0, width: 185, formatter: '@', readOnly: true},
+        {title: '单位', colSpan: '1', rowSpan: '2', field: 'unit', hAlign: 1, width: 50, formatter: '@', readOnly: true},
+        {title: '经济指标', colSpan: '1', rowSpan: '2', field: 'dgn_price', hAlign: 2, width: 60, type: 'Number', readOnly: true},
+        {title: '总设计|工程量', colSpan: '2|1', rowSpan: '1|1', field: 'dgn_qty1', hAlign: 2, width: 70, type: 'Number', readOnly: true},
+        {title: '|金额(万元)', colSpan: '|1', rowSpan: '|1', field: 'total_price', hAlign: 2, width: 70, type: 'Number', readOnly: true},
+    ];
 
     const ledgerSpreadSetting = {
-        cols: [
-            {title: '编号', colSpan: '1', rowSpan: '2', field: 'code', hAlign: 0, width: 145, formatter: '@', readOnly: true, cellType: 'tree'},
-            {title: '名称', colSpan: '1', rowSpan: '2', field: 'name', hAlign: 0, width: 185, formatter: '@', readOnly: true},
-            {title: '单位', colSpan: '1', rowSpan: '2', field: 'unit', hAlign: 1, width: 50, formatter: '@', readOnly: true},
-            {title: '经济指标', colSpan: '1', rowSpan: '2', field: 'dgn_price', hAlign: 2, width: 60, type: 'Number', readOnly: true},
-            {title: '总设计|工程量', colSpan: '2|1', rowSpan: '1|1', field: 'dgn_qty1', hAlign: 2, width: 70, type: 'Number', readOnly: true},
-            {title: '|金额(万元)', colSpan: '|1', rowSpan: '|1', field: 'total_price', hAlign: 2, width: 70, type: 'Number', readOnly: true},
-        ],
         emptyRows: 0,
         headRows: 2,
         headRowHeight: [25, 25],
@@ -53,21 +50,80 @@ $(function () {
             colWidth: true,
         }
     };
+    const monthsCols = [];
+    if(scheduleMonth.length > 0) {
+        for (const sm of scheduleMonth) {
+            const readOnly = sm.sj_gcl !== null || sm.sj_tp !== null;
+            const yearmonth = sm.yearmonth.split('-')[0] + '年' + parseInt(sm.yearmonth.split('-')[1]) + '月';
+            const cols = {title: yearmonth + '|计划工程量', colSpan: '2|1', rowSpan: '1|1', field: sm.yearmonth+'_gcl', hAlign: 2, width: 90, type: 'Number', readOnly: readOnly ? readOnly : 'readOnly.gcl'};
+            const cols2 = {title: '|计划金额(万元)', colSpan: '|1', rowSpan: '|1', field: sm.yearmonth+'_tp', hAlign: 2, width: 90, type: 'Number', readOnly: readOnly ? readOnly : 'readOnly.tp'};
+            monthsCols.push(cols);
+            monthsCols.push(cols2);
+        }
+    }
+    const spreadHeaderCols = static_cols.concat(monthsCols);
+    ledgerSpreadSetting.cols = spreadHeaderCols;
+
+    const ledgerCol = {
+        readOnly: {
+            tp: function (data) {
+                let flag = data.is_leaf;
+                if (data.is_leaf) {
+                    flag = schedule && schedule.mode === mode.tp;
+                }
+                return !flag;
+            },
+            gcl: function (data) {
+                let flag = data.is_leaf;
+                if (data.is_leaf) {
+                    flag = schedule && schedule.mode === mode.gcl;
+                }
+                return !flag;
+            },
+        },
+    };
 
     sjsSettingObj.setFxTreeStyle(ledgerSpreadSetting, sjsSettingObj.FxTreeStyle.jz);
     if (thousandth) sjsSettingObj.setTpThousandthFormat(ledgerSpreadSetting);
+    SpreadJsObj.initSpreadSettingEvents(ledgerSpreadSetting, ledgerCol);
     SpreadJsObj.initSheet(ledgerSpread.getActiveSheet(), ledgerSpreadSetting);
     SpreadJsObj.selChangedRefreshBackColor(ledgerSpread.getActiveSheet());
 
     postData('/tender/' + getTenderId() + '/schedule/ledger/load', {}, function (data) {
-        console.log(data, selectedLedgerList);
-        const treeData = [];
-        for(const sl of selectedLedgerList) {
-            const one = _.find(data, { 'ledger_id' : sl });
-            treeData.push(one);
+        // let treeData = [];
+        // for(const sl of selectedLedgerList) {
+        //     const one = _.find(data, { 'ledger_id' : sl });
+        //     treeData.push(one);
+        // }
+        // treeData = setLeafData(treeData);
+        // console.log(treeData);
+        // let treeData = data;
+        const baseLedgerTree = createNewPathTree('base', {
+            id: 'ledger_id',
+            pid: 'ledger_pid',
+            order: 'order',
+            level: 'level',
+            rootId: -1,
+            fullPath: 'full_path',
+            calcFields: ['total_price'],
+            calcFun: function (node) {
+                node.dgn_price = ZhCalc.round(ZhCalc.div(node.total_price, node.dgn_qty1), 2);
+            }
+        });
+        baseLedgerTree.loadDatas(data);
+        treeCalc.calculateAll(baseLedgerTree);
+        for (const d of baseLedgerTree.nodes) {
+            if (!d.b_code) {
+                const one = _.find(selectedLedgerList, function (item) {
+                    return item === d.ledger_id;
+                });
+                if(one) {
+                    ledgerTree.addData(d, ['ledger_id', 'ledger_pid', 'order', 'level', 'tender_id', 'full_path',
+                        'code', 'name', 'unit', 'dgn_qty1', 'dgn_qty2', 'dgn_price', 'quantity', 'total_price']);
+                }
+            }
         }
-        ledgerTree.loadDatas(treeData);
-        treeCalc.calculateAll(ledgerTree);
+        ledgerTree.sortTreeNode(true);
         // console.log(ledgerTree);
         SpreadJsObj.loadSheetData(ledgerSpread.getActiveSheet(), SpreadJsObj.DataType.Tree, ledgerTree);
     }, null, true);
@@ -122,7 +178,148 @@ $(function () {
                 }
             });
         },
+        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 = is_numeric(info.editingText) ? parseFloat(info.editingText) : (info.editingText ? trimInvalidChar(info.editingText) : null);
+                const orgValue = select[col.field];
+                if (orgValue == validText || ((!orgValue || orgValue === '') && (validText === ''))) {
+                    SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                    return;
+                }
+                if (isNaN(validText)) {
+                    toastr.error('不能输入其它非数字类型字符');
+                    SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                    return;
+                }
+                const yearmonth = col.field.split('_')[0];
+                const mode = col.field.split('_')[1];
+                let plan_gcl = 0;
+                let plan_tp = 0;
+                // 判断输入位数,提示
+                if (mode === 'tp') {
+                    const reg = new RegExp('^([-]?)\\d+(\\.\\d{0,'+ parseInt(tenderInfo.decimal.tp) +'})?$');
+                    console.log(reg);
+                    console.log(reg.test(validText));
+                    if (validText !== null && (!reg.test(validText))) {
+                        toastr.error('输入金额小数位数不能大于' + tenderInfo.decimal.tp + '位');
+                        SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                        return;
+                    }
+                    plan_gcl = select.dgn_price && select.dgn_price !== 0 ? ZhCalc.round(ZhCalc.div(validText, select.dgn_price), tenderInfo.decimal.up) : 0;
+                    plan_tp = validText;
+                    select[yearmonth + '_gcl'] = plan_gcl;
+                } else {
+                    const reg = new RegExp('^([-]?)\\d+(\\.\\d{0,'+ parseInt(tenderInfo.decimal.up) +'})?$');
+                    if (validText !== null && (!reg.test(validText))) {
+                        toastr.error('输入工程量小数位数不能大于' + tenderInfo.decimal.up + '位');
+                        SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                        return;
+                    }
+                    plan_gcl = validText;
+                    plan_tp = select.dgn_price && select.dgn_price !== 0 ? ZhCalc.round(ZhCalc.mul(validText, select.dgn_price), tenderInfo.decimal.tp) : 0;
+                    select[yearmonth + '_tp'] = plan_tp;
+                }
+                select[col.field] = validText;
+                const updateData = {
+                    lid: select.ledger_id,
+                    yearmonth,
+                    plan_gcl,
+                    plan_tp,
+                };
+                console.log(updateData);
+                // postData(window.location.pathname + '/save', {type: 'mode', postData: updateData}, function (result) {
+                    SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                // })
+            }
+        },
+        deletePress: function (sheet) {
+            return;
+        },
+        clipboardPasted(e, info) {
+            const hint = {
+                cellError: {type: 'error', msg: '粘贴内容超出了表格范围'},
+                numberExpr: {type: 'error', msg: '不能粘贴其它非数字类型字符'},
+                numberCan: {type: 'error', msg: '请粘贴大于0并且小于3位小数的浮点数'},
+            };
+            const range = info.cellRange;
+            const sortData = info.sheet.zh_data || [];
+            if (info.cellRange.row + info.cellRange.rowCount > sortData.length) {
+                toastMessageUniq(hint.cellError);
+                SpreadJsObj.reLoadSheetHeader(materialMonthSpread.getActiveSheet());
+                SpreadJsObj.reLoadSheetData(materialMonthSpread.getActiveSheet());
+                return;
+            }
+            if (sortData.length > 0 && range.col + range.colCount > 4 + months.length) {
+                toastMessageUniq(hint.cellError);
+                SpreadJsObj.reLoadSheetHeader(materialMonthSpread.getActiveSheet());
+                SpreadJsObj.reLoadSheetData(materialMonthSpread.getActiveSheet());
+                return;
+            }
+            const data = [];
+            for (let iRow = 0; iRow < range.rowCount; iRow++) {
+                let bPaste = true;
+                const curRow = range.row + iRow;
+                const materialMonthData = sortData[curRow];
+                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;
+                    }
+                    const num = parseFloat(validText);
+                    if (isNaN(validText)) {
+                        toastMessageUniq(getPasteHint(hint.numberExpr, hintRow));
+                        bPaste = false;
+                        continue;
+                    }
+                    if (validText !== null && (num < 0 || !/^\d+(\.\d{1,3})?$/.test(num))) {
+                        toastMessageUniq(getPasteHint(hint.numberCan, hintRow));
+                        bPaste = false;
+                        continue;
+                    }
+                    materialMonthData[colSetting.field] = validText;
+                    sortData[curRow][colSetting.field] = validText;
+                }
+                if (bPaste) {
+                    data.push(materialMonthData);
+                } 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 + '/month/save', { type:'paste', updateData: data }, function (result) {
+            //     SpreadJsObj.reLoadSheetData(materialMonthSpread.getActiveSheet());
+            //     materialBillsData = result.materialBillsData;
+            //     SpreadJsObj.loadSheetData(materialSpread.getActiveSheet(), SpreadJsObj.DataType.Data, materialBillsData);
+            //     m_tp = result.m_tp;
+            //     resetTpTable();
+            // }, function () {
+            //     SpreadJsObj.reLoadRowData(info.sheet, info.cellRange.row, info.cellRange.rowCount);
+            //     return;
+            // });
+        },
     };
+
+    ledgerSpread.bind(spreadNS.Events.EditEnded, ledgerSpreadObj.editEnded);
+    SpreadJsObj.addDeleteBind(ledgerSpread, ledgerSpreadObj.deletePress);
+
     // 进度计算方式选择
     $('.mode-select').on('click', function () {
         const _self = $(this);
@@ -132,6 +329,113 @@ $(function () {
             $('#mode-tips').show();
             $('#mode-cancel').show();
             $('#mode').modal('hide');
+            schedule.mode = _self.data('mode');
+            SpreadJsObj.reLoadSheetData(ledgerSpread.getActiveSheet());
+        })
+    });
+
+    // 月份添加
+    $('#add-month').click(function () {
+        const range = $('#month-range').val();
+        if(range === '') {
+            toastr.error('请选择计划周期时间');
+            return;
+        }
+        const addMonthList = [];
+        const cycle = range.split(' ~ ');
+        if(cycle.length === 1) {
+            addMonthList.push(cycle[0]);
+        } else {
+            // 多个月份
+            const back_year = parseInt(cycle[1].split('-')[0]);
+            const back_month = parseInt(cycle[1].split('-')[1]);
+            const front_year = parseInt(cycle[0].split('-')[0]);
+            const front_month = parseInt(cycle[0].split('-')[1]);
+            if(back_year > front_year) {
+                const num = getDistanceMonth(cycle[0], cycle[1]);
+                let j = 1;
+                for (let i = 0; i <= num; i++) {
+                    if(front_month + i > 12*j) {
+                        j = j + 1;
+                    }
+                    const m = (front_month + i)%12 === 0 ? 12 : (front_month + i)%12;
+                    addMonthList.push((front_year + (j-1)) + '-' + (m < 10 ? '0' + m : m));
+                }
+            } else if (back_year === front_year) {
+                // 小于1年并没有跨年
+                for (let i = front_month; i <= back_month; i++) {
+                    addMonthList.push(back_year + '-' + (i < 10 ? '0' + i : i));
+                }
+            }
+        }
+        // 判断是否已添加本月份
+        if (addMonthList.length > 0) {
+            const hadmonth = [];
+            for (const m of addMonthList) {
+                const one = _.find(scheduleMonth, { yearmonth: m });
+                console.log(one, m);
+                if (one) {
+                    hadmonth.push(m);
+                }
+            }
+            if (hadmonth.length > 0) {
+                let html = '';
+                for (const hm of hadmonth) {
+                    html += `<div class="alert alert-danger">${hm} 已创建</div>`;
+                }
+                $('#add-month-error-list').html(html);
+                $('#add-month-error-list').show();
+                return;
+            }
+        } else {
+            toastr.error('请选择计划周期时间');
+            return;
+        }
+        $('#add-month-error-list').html('');
+        $('#add-month-error-list').hide();
+        const _self = $(this);
+        postData(window.location.pathname + '/save', {type: 'addmonth', postData: addMonthList}, function (result) {
+            _self.addClass('disabled').attr('disabled', true);
+            toastr.success('新增成功');
+            setTimeout(function () {
+                window.location.reload();
+            }, 500)
+
+        })
+    });
+
+    $('#month-table input[type="checkbox"]').click(function () {
+        const selectedMonth = [];
+        $('#month-table input:checkbox:checked').each(function () {
+            selectedMonth.push('「' + $(this).parents('td').siblings('td').text() + '」');
+        });
+        if(selectedMonth.length > 0) {
+            $('#del-month-list').text(selectedMonth.join(''));
+            $('#del-month-list').parent().show();
+            $('#del-month').removeAttr('disabled');
+        } else {
+            $('#del-month-list').parent().hide();
+            $('#del-month').attr('disabled', true);
+        }
+    });
+
+    $('#del-month').click(function () {
+        const selectedMonth = [];
+        $('#month-table input:checkbox:checked').each(function () {
+            selectedMonth.push($(this).parents('td').siblings().text());
+        });
+        if (selectedMonth.length === 0) {
+            toastr.error('请选择删除的计划周期');
+            return;
+        }
+        const _self = $(this);
+        postData(window.location.pathname + '/save', {type: 'delmonth', postData: selectedMonth}, function (result) {
+            _self.addClass('disabled').attr('disabled', true);
+            toastr.success('删除成功');
+            setTimeout(function () {
+                window.location.reload();
+            }, 500)
+
         })
     });
 
@@ -153,3 +457,37 @@ $(function () {
         }
     });
 });
+// 月份间隔
+function getDistanceMonth(startTime,endTime){
+    startTime = new Date(startTime);
+    endTime = new Date(endTime);
+    var dateToMonth = 0;
+    var startDate=startTime.getDate() + startTime.getHours()/24 + startTime.getMinutes()/24/60;
+    var endDate=endTime.getDate()  +endTime.getHours()/24 + endTime.getMinutes()/24/60;
+    if(endDate >= startDate){
+        dateToMonth = 0;
+    }else{
+        dateToMonth = -1;
+    }
+    let yearToMonth = (endTime.getYear() - startTime.getYear()) * 12;
+    let monthToMonth = endTime.getMonth() - startTime.getMonth();
+    return yearToMonth + monthToMonth + dateToMonth;
+}
+function setLeafData(tree) {
+    const newtree = [];
+    for (const t of tree) {
+        const child = _.find(tree, { 'ledger_pid': t.ledger_id });
+        if (!child && !t.is_leaf) {
+            t.is_leaf = true;
+        }
+        newtree.push(t);
+    }
+    return newtree;
+}
+const is_numeric = (value) => {
+    if (typeof(value) === 'object') {
+        return false;
+    } else {
+        return !Number.isNaN(Number(value)) && value.toString().trim() !== '';
+    }
+};

+ 4 - 2
app/public/js/shares/cs_tools.js

@@ -788,8 +788,10 @@ const showSelectTab = function(select, spread, afterShow) {
             if (data.del) {
                 const delTag = billsTags.find(x => {return x.id === data.del});
                 billsTags.splice(billsTags.indexOf(delTag), 1);
-                const bi = billsIndexes[delTag.node.id];
-                bi.splice(bi.indexOf(delTag), 1);
+                if (delTag.node) {
+                    const bi = billsIndexes[delTag.node.id];
+                    bi.splice(bi.indexOf(delTag), 1);
+                }
                 refresh.del = delTag;
             }
             if (data.update) {

+ 1 - 0
app/public/js/shares/tenders2tree.js

@@ -85,6 +85,7 @@ const Tender2Tree = (function () {
                 tid: t.id,
                 name: t.name,
                 phase: t.lastStage ? '第' + t.lastStage.order + '期' : '台账',
+                stageCount: t.lastStage ? t.lastStage.order : 0,
             };
             if (ledgerAuditConst && stageAuditConst) {
                 node.status = t.lastStage ? stageAuditConst.statusString[t.lastStage.status] : ledgerAuditConst.statusString[t.ledger_status];

+ 112 - 35
app/public/js/shenpi.js

@@ -9,6 +9,7 @@
  */
 const tenderTree = [];
 let parentId = 0;
+let selects;
 // 查询方法
 function findNode (key, value, arr) {
     for (const a of arr) {
@@ -603,19 +604,20 @@ $(document).ready(function () {
         getValue: {
             pwd: function (data) {
                 let txt = '';
-                if (data.is_leaf) {
-                    if (data.pwd && data.pwd !== '' && data.pwd !== null) {
-                        txt = data.pwd;
-                    } else {
-                        txt = '设置密码';
-                    }
+                // console.log(data);
+                // if (data.is_leaf) {
+                if (data.pwd && data.pwd !== '' && data.pwd !== null) {
+                    txt = data.pwd;
+                } else if(data.can_edit) {
+                    txt = '请输入密码';
                 }
+                // }
                 return txt;
             }
         },
         readOnly: {
             pwd: function (data) {
-                return !data.is_leaf;
+                return !data.can_edit;
             },
         },
     };
@@ -624,7 +626,7 @@ $(document).ready(function () {
         setFontColor: function(row = null) {
             if(row) {
                 const value = ledgerSpread.getActiveSheet().getValue(row, 2);
-                if (value === '设置密码') {
+                if (value === '请输入密码') {
                     ledgerSpread.getActiveSheet().getCell(row, 2).foreColor('#007bff');
                 } else {
                     ledgerSpread.getActiveSheet().getCell(row, 2).foreColor('#000');
@@ -633,7 +635,7 @@ $(document).ready(function () {
                 const rowCount = ledgerSpread.getActiveSheet().getRowCount();
                 for(let i = 0; i < rowCount; i++){
                     const value = ledgerSpread.getActiveSheet().getValue(i, 2);
-                    if (value === '设置密码') {
+                    if (value === '请输入密码') {
                         ledgerSpread.getActiveSheet().getCell(i, 2).foreColor('#007bff');
                     } else {
                         ledgerSpread.getActiveSheet().getCell(i, 2).foreColor('#000');
@@ -642,18 +644,24 @@ $(document).ready(function () {
             }
         },
         setAllRightPwd: function(uid) {
-            const selects = [];
+            selects = [];
             for (const l of ledgerTree.datas) {
                 const coo = _.find(ledger_cooperation_list, { 'ledger_id': l.ledger_id, 'user_id': parseInt(uid) });
                 if (l.pwd && !coo) {
                     delete l.pwd;
+                    l.can_edit = true;
                     selects.push(l);
                 } else if(coo) {
                     l.pwd = coo.pwd;
+                    l.can_edit = true;
+                    selects.push(l);
+                } else if (l.can_edit === false) {
+                    l.can_edit = true;
                     selects.push(l);
                 }
             }
             if(selects.length > 0) {
+                updateByCanEdit(ledgerTree.nodes, _.filter(ledger_cooperation_list, { user_id: parseInt(uid) }), false);
                 const refreshNode = ledgerTree.loadPostData({update: selects});
                 ledgerSpreadObj.refreshTree(ledgerSpread.getActiveSheet(), refreshNode);
                 ledgerSpreadObj.setFontColor();
@@ -708,12 +716,21 @@ $(document).ready(function () {
                 }
             });
         },
+        enterCell: function(e, info) {
+            if (info.sheet.zh_setting && info.col === 2) {
+                if (ledgerSpread.getActiveSheet().getValue(info.row, info.col) !== '') {
+                    const col = info.sheet.zh_setting.cols[info.col];
+                    info.sheet.setActiveCell(info.row, 2);
+                    info.sheet.startEdit(true);
+                }
+            }
+        },
         editStarting: function (e, info) {
             if (info.sheet.zh_setting) {
                 const select = SpreadJsObj.getSelectObject(info.sheet);
                 const col = info.sheet.zh_setting.cols[info.col];
                 ledgerSpread.getActiveSheet().getCell(info.row, 2).foreColor('#000');
-                if(col.getValue(select) === '设置密码') {
+                if(col.getValue(select) === '请输入密码') {
                     ledgerSpread.getActiveSheet().setValue(info.row, 2, '');
                 }
             }
@@ -737,13 +754,14 @@ $(document).ready(function () {
                     ledgerSpreadObj.setFontColor(info.row);
                     return;
                 }
-                const num = _.filter(ledger_cooperation_list, { pwd: validText, status: 1, user_id });
-                if(num.length > 0) {
-                    toastr.error('同一个审批人密码不能相同');
-                    SpreadJsObj.reLoadRowData(info.sheet, info.row);
-                    ledgerSpreadObj.setFontColor(info.row);
-                    return;
-                }
+                // const num = _.filter(ledger_cooperation_list, { pwd: validText, status: 1, user_id });
+                // console.log(num, ledger_cooperation_list);
+                // if(num.length > 0) {
+                //     toastr.error('同一个审批人密码不能相同');
+                //     SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                //     ledgerSpreadObj.setFontColor(info.row);
+                //     return;
+                // }
                 select.pwd = validText;
                 const data = {
                     type: 'pwd',
@@ -751,16 +769,25 @@ $(document).ready(function () {
                     user_id,
                     pwd: validText,
                 };
+                info.sheet.setSelection(info.row, 0, 1, 1);
                 postData('/tender/' + cur_tenderid + '/shenpi/audit/save', data, function (result) {
                     const lcindex = _.findIndex(ledger_cooperation_list, { ledger_id: select.ledger_id, user_id });
+                    let flag = false;
                     if(lcindex !== -1) {
                         validText === '' ? ledger_cooperation_list.splice(lcindex, 1) : ledger_cooperation_list.splice(lcindex, 1, result);
+                        flag = validText === '';
                     } else {
                         ledger_cooperation_list.push(result);
+                        flag = false;
                     }
                     $('#cooperation-num').text(ledger_cooperation_list.length);
                     setLeftTable(ledgerTree.datas, ledger_cooperation_list, user_id, $('#stage_audits option:selected').text());
-                    const refreshNode = ledgerTree.loadPostData({update: select});
+                    selects = [select];
+                    if(select) {
+                        setAllChildrenCanEdit(select, flag);
+                        // setParentCanEdit(ledgerTree.nodes, select.ledger_pid, flag);
+                    }
+                    const refreshNode = ledgerTree.loadPostData({update: selects});
                     ledgerSpreadObj.refreshTree(info.sheet, refreshNode);
                     ledgerSpreadObj.setFontColor();
                 });
@@ -786,6 +813,7 @@ $(document).ready(function () {
     ledgerSpread.bind(spreadNS.Events.EditEnded, ledgerSpreadObj.editEnded);
     SpreadJsObj.addDeleteBind(ledgerSpread, ledgerSpreadObj.deletePress);
     ledgerSpread.bind(spreadNS.Events.ClipboardPasted, ledgerSpreadObj.clipboardPasted);
+    ledgerSpread.bind(spreadNS.Events.EnterCell, ledgerSpreadObj.enterCell);
     let ledger_data, ledger_cooperation_list = [];
 
     // 多人协同
@@ -826,6 +854,9 @@ $(document).ready(function () {
                 const yb = _.find(accountList, { 'id': cur_uid });
                 setLeftTable(ledgerList, ledger_cooperation_list, cur_uid, yb.name + '(原报)');
                 // treeCalc.calculateAll(ledgerTree);
+                selects = [];
+                updateByCanEdit(ledgerTree.nodes, _.filter(ledger_cooperation_list, { user_id: cur_uid }), false);
+                ledgerTree.loadPostData({update: selects});
                 console.log(ledgerTree);
                 SpreadJsObj.loadSheetData(ledgerSpread.getActiveSheet(), SpreadJsObj.DataType.Tree, ledgerTree);
                 ledgerSpreadObj.setFontColor();
@@ -856,7 +887,13 @@ $(document).ready(function () {
             setLeftTable(ledgerTree.datas, ledger_cooperation_list, user_id, $('#stage_audits option:selected').text());
             const select = _.find(ledgerTree.datas, { ledger_id });
             delete select.pwd;
-            const refreshNode = ledgerTree.loadPostData({update: select});
+            // const refreshNode = ledgerTree.loadPostData({update: select});
+            selects = [select];
+            if(select) {
+                setAllChildrenCanEdit(select, true);
+                // setParentCanEdit(ledgerTree.nodes, select.ledger_pid, true);
+            }
+            const refreshNode = ledgerTree.loadPostData({update: selects});
             ledgerSpreadObj.refreshTree(ledgerSpread.getActiveSheet(), refreshNode);
             ledgerSpreadObj.setFontColor();
             $('#cooperation-num').text(ledger_cooperation_list.length);
@@ -895,11 +932,11 @@ $(document).ready(function () {
             $(this).parents('td').html(html);
             return;
         }
-        const num = _.filter(ledger_cooperation_list, { pwd: validText, status: 1, user_id });
-        if(num.length > 0) {
-            toastr.error('同一个审批人密码不能相同');
-            return;
-        }
+        // const num = _.filter(ledger_cooperation_list, { pwd: validText, status: 1, user_id });
+        // if(num.length > 0) {
+        //     toastr.error('同一个审批人密码不能相同');
+        //     return;
+        // }
         const data = {
             type: 'pwd',
             ledger_id,
@@ -969,23 +1006,63 @@ $(document).ready(function () {
 
 function setRightData(datas, coolist) {
     const newdatas = [];
-    const reg = /(^GD([a-zA-Z0-9\-]+))|(^([0-9\-]+)$)/;
+    const reg = /(^GD([0-9\-]+))|(^([0-9\-]+)$)/;
     for (const l of datas) {
-        if (reg.test(l.code) && l.level <= 4) {
-            if(l.level === 4 && l.is_leaf === false) {
-                l.is_leaf = true;
-            }
-            if(l.is_leaf) {
-                const coo = _.find(coolist, { 'ledger_id': l.ledger_id, 'user_id': cur_uid });
-                if(coo) {
-                    l.pwd = coo.pwd;
-                }
+        // if (reg.test(l.code) && l.level <= 4) {
+        if (reg.test(l.code)) {
+            // if(l.level === 4 && l.is_leaf === false) {
+            //     l.is_leaf = true;
+            // }
+            const coo = _.find(coolist, { 'ledger_id': l.ledger_id, 'user_id': cur_uid });
+            if(coo) {
+                l.pwd = coo.pwd;
             }
+            l.can_edit = true;
             newdatas.push(l);
         }
     }
+    // for (const nd of newdatas) {
+    //     const child = _.find(newdatas, { 'ledger_pid': nd.ledger_id });
+    //     if (!child && !nd.is_leaf) {
+    //         nd.is_leaf = true;
+    //     }
+    // }
     return newdatas;
 }
+function updateByCanEdit(datas, coolist, flag) {
+    for (const coo of coolist) {
+        const ledgerInfo = _.find(datas, { 'ledger_id': coo.ledger_id });
+        if(ledgerInfo) {
+            setAllChildrenCanEdit(ledgerInfo, flag);
+            // setParentCanEdit(datas, ledgerInfo.ledger_pid, flag);
+        }
+    }
+}
+function setParentCanEdit(datas, id, flag) {
+    if(id !== -1) {
+        const pl = _.find(datas, { 'ledger_id': id });
+        // 判断父节点下所有子节点有无pwd,有则为false
+        if (pl) {
+            const existfalse = _.find(pl.children, function (item) {
+                return item.can_edit === false || (item.pwd && item.pwd !== '' && item.pwd !== null);
+            });
+            pl.can_edit = existfalse ? false : flag;
+            selects.push(pl);
+            setParentCanEdit(datas, pl.ledger_pid, flag);
+        }
+    }
+}
+function setAllChildrenCanEdit(ledgerInfo, flag) {
+    if (ledgerInfo.children && ledgerInfo.children.length > 0) {
+        for(const li of ledgerInfo.children) {
+            if (li.pwd === undefined || li.pwd === null || li.pwd === '') {
+                li.can_edit = flag;
+                selects.push(li);
+                setAllChildrenCanEdit(li, flag);
+            }
+        }
+    }
+}
 
 function setLeftTable(ledgerList, coolist, uid, title) {
     $('#stage_audit').text(title);

+ 13 - 11
app/public/js/spreadjs_rela/spreadjs_zh.js

@@ -2251,17 +2251,19 @@ const SpreadJsObj = {
 
     RowHeader: {
         getTagRowHeader: function (setting) {
-            const indent = 16, maxHintWidth = 200, borderIndent = 10;
+            const indent = setting.indent || 18, maxHintWidth = 200, borderIndent = 10;
+            const height = setting.tagSize ? 11 * setting.tagSize : 11, width = setting.tagSize ? 10 * setting.tagSize : 10;
+            const tagFont = setting.tagFont || '9px 微软雅黑';
             const drawTag = function (canvas, x, y, fillColor) {
                 canvas.save();
                 // 设置偏移量
                 canvas.translate(0.5, 0.5);
                 canvas.beginPath();
                 canvas.moveTo(x, y);
-                canvas.lineTo(x, y+5);
-                canvas.lineTo(x+6, y+11);
-                canvas.lineTo(x+11, y+6);
-                canvas.lineTo(x+5, y);
+                canvas.lineTo(x, y+height/11*5);
+                canvas.lineTo(x+width/10*5.5, y+height);
+                canvas.lineTo(x+width, y+height-width/10*4.5);
+                canvas.lineTo(x+width/11*5, y);
                 canvas.lineTo(x, y);
                 canvas.stroke();
                 canvas.fillStyle = fillColor instanceof Array ? fillColor[0] : fillColor;
@@ -2275,8 +2277,8 @@ const SpreadJsObj = {
 
                 if (fillColor instanceof Array && fillColor.length > 1) {
                     canvas.fillStyle = '#444444';
-                    canvas.font="9px 微软雅黑";
-                    canvas.fillText(fillColor.length, x+13, y+14);
+                    canvas.font = tagFont;
+                    canvas.fillText(fillColor.length, x+width/11*13, y+height/11*14);
                 }
 
                 canvas.restore();
@@ -2293,9 +2295,8 @@ const SpreadJsObj = {
                 spreadNS.CellTypes.Text.prototype.paint.apply(this, [canvas, value, x, y, w - indent, h, style, options]);
                 const node = SpreadJsObj.getRowObject(options.sheet, options.row);
                 const color = this.getTagColor(options.row, node);
-                const centerX = x + w - indent + 4, centerY = y + h/2;
-                color && drawTag(canvas, centerX - 6, centerY - 6, color);
-
+                const centerX = x + w - indent + height/2, centerY = y + h/2;
+                color && drawTag(canvas, centerX - height/2, centerY - width/2, color);
             };
             /**
              * 获取点击信息
@@ -2392,6 +2393,7 @@ const SpreadJsObj = {
             CircleTagCellType.prototype = new spreadNS.CellTypes.RowHeader();
             const proto = CircleTagCellType.prototype;
             proto.indent = setting.indent || 16;
+            proto.size = setting.size || 6;
             proto.getTagColor = setting.getColor;
             proto.basePaint = proto.paint;
             proto.paint = function (canvas, value, x, y, w, h, style, options) {
@@ -2411,7 +2413,7 @@ const SpreadJsObj = {
                 let color = this.getTagColor(options.row, node);
                 color = color instanceof Array ? color : [color];
                 for (let i = color.length - 1; i >= 0; i--) {
-                    drawCircle2(canvas, x + w - this.indent + 5 + i*5 , y + h/2, 6, backColor, color[i]);
+                    drawCircle2(canvas, x + w - this.indent + 5 + i*5 , y + h/2, this.size, backColor, color[i]);
                 }
             };
             /**

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

@@ -605,10 +605,14 @@ $(document).ready(() => {
     sjsSettingObj.setPropValue(ledgerSpreadSetting, ['gxby'], 'getValue', getGxbyText);
     sjsSettingObj.setPropValue(ledgerSpreadSetting, ['dagl'], 'getValue', getDaglText);
     if (thousandth) sjsSettingObj.setTpThousandthFormat(ledgerSpreadSetting);
+    ledgerSpreadSetting.headColWidth = [50];
     ledgerSpreadSetting.rowHeader = [
         {
             rowHeaderType: 'tag',
             setting: {
+                indent: 14,
+                tagSize: 0.8,
+                tagFont: '8px 微软雅黑',
                 getColor: function (index, data) {
                     if (!data) return;
                     return billsTag.getBillsTagsColor(data.id);
@@ -654,6 +658,7 @@ $(document).ready(() => {
     };
     posSpreadSetting.imageClick = function (data) {
         const node = SpreadJsObj.getSelectObject(slSpread.getActiveSheet());
+        if (node.lock) return;
         changesObj.loadChanges({bills: node, pos: data});
     };
     posSpreadSetting.getColor = function (sheet, data, row, col, defaultColor) {
@@ -1427,7 +1432,7 @@ $(document).ready(() => {
         loadCurPosData: function () {
             const sheet = slSpread.getActiveSheet();
             const node = SpreadJsObj.getSelectObject(sheet);
-            if (node.lock) spSpread.getActiveSheet().zh_setting.readOnly = node.lock;
+            spSpread.getActiveSheet().zh_setting.readOnly = node.lock;
             if (node) {
                 const posData = stagePos.ledgerPos[itemsPre + node.id] || [];
                 SpreadJsObj.loadSheetData(spSpread.getActiveSheet(), 'data', posData);
@@ -1825,6 +1830,7 @@ $(document).ready(() => {
         if (result.cooperation) {
             stageTree.loadPwd(result.cooperation, 'bills-p-' + userID + '-' + window.location.pathname.split('/')[2]);
             $('#cooperationCount').html(stageTree.pwd.length || '');
+            if (stageTree.pwd.length > 0) $('#cooperationCount').parent().show();
             reloadCooperationHtml();
         }
         // 加载部位明细
@@ -3967,6 +3973,7 @@ $(document).ready(() => {
         if (p.pwd === $('#unlock-pwd').val()) {
             const refresh = stageTree.lockNode(p, false);
             SpreadJsObj.reloadRowsReadonly(slSpread.getActiveSheet(), refresh);
+            stagePosSpreadObj.loadCurPosData();
             $('#unlock').modal('hide');
             reloadCooperationHtml();
         } else {

+ 63 - 13
app/public/report/js/rpt_custom.js

@@ -43,11 +43,28 @@ const rptCustomObj = (function () {
         headColWidth: []
     };
     const gatherSelectSpreadObj = {
+        _getStageSelectHtml: function (valid) {
+            const html = [];
+            for (let i = 1; i <= valid; i++) {
+                html.push(`<option value="${i}">第${i}期</option>`);
+            }
+            return html.join('');
+        },
+        _rebuildStageSelect: function () {
+            if (gsObj.setting.type === 'stage') {
+                const validStage = _.min(_.map(gsObj.grArray, 'stageCount'));
+                $('#gather-stage').html(this._getStageSelectHtml(validStage));
+            } else if (gsObj.setting.type === 'stage-zone') {
+                const validStage = _.max(_.map(gsObj.grArray, 'stageCount'));
+                $('#gather-stage-begin').html(this._getStageSelectHtml(validStage));
+                $('#gather-stage-end').html(this._getStageSelectHtml(validStage));
+            }
+        },
         _addTender: function (tender) {
             const gr = gsObj.grArray.find(function (x) {
                 return x.tid === tender.tid;
             });
-            const t = {tid: tender.tid, name: tender.name}
+            const t = {tid: tender.tid, name: tender.name, stageCount: tender.stageCount};
             if (!gr) gsObj.grArray.push(t);
             return t;
         },
@@ -59,6 +76,7 @@ const rptCustomObj = (function () {
         },
         reloadResultData: function () {
             SpreadJsObj.reLoadSheetData(gsObj.grSheet);
+            this._rebuildStageSelect();
         },
         gsButtonClicked: function (e, info) {
             if (!info.sheet.zh_setting) return;
@@ -217,26 +235,30 @@ const rptCustomObj = (function () {
         $('#gather-select-title').html(gsObj.setting.title + (rptName ? '-' + rptName : ''));
         initGrSpreadSetting(gsObj.setting);
         SpreadJsObj.initSheet(gsObj.grSheet, grSpreadSetting);
-        if (gsObj.setting.type === 'month') {
-            $('#gather-by-month').show();
-            $('#gather-by-zone').hide();
-        } else if (gsObj.setting.type === 'zone') {
-            $('#gather-by-month').hide();
-            $('#gather-by-zone').show();
-        } else {
-            $('#gather-by-month').hide();
-            $('#gather-by-zone').hide();
-        }
-        SpreadJsObj.loadSheetData(gsObj.grSheet, SpreadJsObj.DataType.Data, gsObj.grArray);
+
         // 初始化选择结果
+        SpreadJsObj.loadSheetData(gsObj.grSheet, SpreadJsObj.DataType.Data, gsObj.grArray);
+        gatherSelectSpreadObj.initSelectTenders(gsSelect ? gsSelect.tenders : []);
+
+        $('[name=gather-type]').hide();
+        if (gsObj.setting.type === 'month') $('#gather-by-month').show();
+        if (gsObj.setting.type === 'zone') $('#gather-by-zone').show();
+        if (gsObj.setting.type === 'stage') $('#gather-by-stage').show();
+        if (gsObj.setting.type === 'stage-zone') $('#gather-by-stage-zone').show();
+
         if (gsSelect) {
             if (gsSelect.zone) {
                 $('#gather-zone').val(gsSelect.zone ? gsSelect.zone : '');
             } else if (gsSelect.month) {
                 $('#gather-month').val(gsSelect.month ? gsSelect.month: '');
+            } else if (gsSelect.stage) {
+                $('#gather-stage').val(gsSelect.stage || '');
+            } else if (gsSelect.stage_zone) {
+                const [stageBegin, stageEnd] = gsSelect.stage_zone ? gsSelect.stage_zone.split(':') : ['', ''];
+                $('#gather-stage-begin').val(stageBegin);
+                $('#gather-stage-end').val(stageEnd);
             }
         }
-        gatherSelectSpreadObj.initSelectTenders(gsSelect ? gsSelect.tenders : []);
 
         // 初始化
         $("#gather-select").modal('show');
@@ -423,6 +445,34 @@ const rptCustomObj = (function () {
                 hintObj.html('请选择 完整汇总周期').show();
                 return;
             }
+        } else if (gsObj.setting.type === 'stage') {
+            data[sGatherSelect].stage = _.toInteger($('#gather-stage').val()) || 0;
+            const validStage = _.min(_.map(gsObj.grArray, 'stageCount'));
+            if (!data[sGatherSelect].stage) {
+                hintObj.html('请选择 汇总期').show();
+                return;
+            }
+            if (data[sGatherSelect].stage > validStage) {
+                hintObj.html('选择的期无效,请重新选择').show();
+                return;
+            }
+        } else if (gsObj.setting.type === 'stage-zone') {
+            const stageBegin = _.toInteger($('#gather-stage-begin').val()) || 0;
+            const stageEnd = _.toInteger($('#gather-stage-end').val()) || 0;
+            const validStage = _.max(_.map(gsObj.grArray, 'stageCount'));
+            if (!stageBegin || !stageEnd) {
+                hintObj.html('请选择 汇总开始期与结束期').show();
+                return;
+            }
+            if (stageEnd <= stageBegin) {
+                hintObj.html('结束期应大于开始期').show();
+                return;
+            }
+            if (stageEnd > validStage) {
+                hintObj.html('选择的期无效,请重新选择').show();
+                return;
+            }
+            data[sGatherSelect].stage_zone = stageBegin + ':' + stageEnd;
         }
         hintObj.hide();
         if (resolve) {

+ 3 - 0
app/service/change_audit_list.js

@@ -57,6 +57,9 @@ module.exports = app => {
                 spamount: 0,
                 xmj_code: null,
                 xmj_jldy: null,
+                xmj_dwgc: null,
+                xmj_fbgc: null,
+                xmj_fxgc: null,
                 gcl_id: '',
             };
             // 新增工料

+ 7 - 0
app/service/ledger_cooperation.js

@@ -28,11 +28,18 @@ module.exports = app => {
             if (data.pwd === '' && info) {
                 await this.deleteById(info.id);
             } else if (data.pwd !== '' && !info) {
+                // const ledgerInfo = await this.ctx.service.ledger.getDataByCondition({ ledger_id: data.ledger_id });
+                // if (ledgerInfo) {
+                //     data.lid = ledgerInfo.id;
                 const result = await this.db.insert(this.tableName, data);
                 data.id = result.insertId;
+                data.sign_path = null;
+                data.status = 1;
+                // }
             } else if (data.pwd !== '' && info) {
                 data.id = info.id;
                 await this.db.update(this.tableName, data);
+                data.sign_path = info.sign_path;
             }
             return data;
         }

+ 14 - 0
app/service/report.js

@@ -84,6 +84,10 @@ module.exports = app => {
                             runnableRst.push(service.reportMemory.getMonthProgress(params.tender_id, memFieldKeys[filter]));
                             runnableKey.push(filter);
                             break;
+                        case 'stage_audit':
+                            runnableRst.push(service.reportMemory.getStageAuditors(params.tender_id, params.stage_id), memFieldKeys[filter]);
+                            runnableKey.push(filter);
+                            break;
                         case 'mem_stage_bills':
                             runnableRst.push(service.reportMemory.getStageBillsData(params.tender_id, params.stage_id, memFieldKeys[filter]));
                             runnableKey.push(filter);
@@ -177,6 +181,16 @@ module.exports = app => {
             }
             for (const filter of filters) {
                 switch (filter) {
+                    // test模式下,上方Promise.all的方式,取不到以下3个数据
+                    // case 'ledger_cooperation':
+                    //     rst[filter] = await service.ledgerCooperation.getValidData(params.tender_id);
+                    //     break;
+                    // case 'stage_audit':
+                    //     rst[filter] = await service.reportMemory.getStageAuditors(params.tender_id, params.stage_id), memFieldKeys[filter];
+                    //     break;
+                    // case 'mem_stage_im_zl':
+                    //     rst[filter] = await service.reportMemory.getStageImZlData(params.tender_id, params.stage_id, memFieldKeys[filter]);
+                    //     break;
                     case 'mem_stage_im_tz':
                         rst[filter] = await service.reportMemory.getStageImTzData(params.tender_id, params.stage_id, memFieldKeys[filter]);
                         break;

+ 18 - 0
app/service/report_memory.js

@@ -975,6 +975,24 @@ module.exports = app => {
                 return [];
             }
         }
+
+        async getStageAuditors(tid, sid) {
+            await this.ctx.service.tender.checkTender(tid);
+            await this.ctx.service.stage.checkStage(sid);
+            const user = await this.ctx.service.projectAccount.getDataById(this.ctx.stage.user_id);
+            const auditors = this.ctx.stage.auditors;
+            return [{
+                aid: user.id,
+                name: user.name,
+                company: user.company,
+                role: user.role,
+                mobile: user.mobile,
+                telephone: user.telephone,
+                sign_path: user.sign_path,
+                end_time: auditors && auditors.length > 0 ? auditors[0].begin_time : null,
+                sort: 0,
+            }, ...this.ctx.stage.auditors];
+        }
     }
 
     return ReportMemory;

+ 158 - 83
app/service/rpt_gather_memory.js

@@ -210,6 +210,35 @@ module.exports = app => {
             return '';
         }
 
+        async _getTimeZoneStages(tender, zone) {
+            const times = zone.split(' - ');
+            if (times.length !== 2) throw '选择的汇总周期无效';
+            const beginTime = moment(times[0], 'YYYY-MM');
+            const endTime = moment(times[1], 'YYYY-MM');
+
+            const stages = await this._getValidStages(tender.id), validStages = [];
+            for (const stage of stages) {
+                const sTime = moment(stage.s_time, 'YYYY-MM');
+                if (sTime.isBetween(beginTime, endTime, null, '[]')) {
+                    validStages.push(stage);
+                }
+            }
+            return validStages;
+        }
+
+        async _getOrderZoneStages (tender, zone) {
+            let [iBegin, iEnd] = zone.split(':');
+            iBegin = this.ctx.helper._.toInteger(iBegin) || 0;
+            iEnd = this.ctx.helper._.toInteger(iEnd) || 0;
+            const stages = await this._getValidStages(tender.id), validStages = [];
+            for (const stage of stages) {
+                if (stage.order < iBegin || stage.order > iEnd) continue;
+
+                validStages.push(stage);
+            }
+            return validStages;
+        }
+
         /**
          * 台账数据
          */
@@ -278,15 +307,10 @@ module.exports = app => {
 
         }
 
-        async _gatherMonthData(sTender, completeData, month, hasPre) {
-            const tender = await this.ctx.service.tender.getCheckTender(sTender.tid);
-            const stages = await this._getValidStages(tender.id);
-            const stage = this.ctx.helper._.find(stages, {s_time: month});
-            await this._gatherStageData(completeData, tender, stage, hasPre);
-        }
-
-        async _gatherZoneData(sTender, completeData, zone) {
+        async _gatherStagesData(completeData, tender, stages) {
             const helper = this.ctx.helper;
+            completeData.id = tender.id;
+            completeData.name = tender.name;
             /**
              * 汇总并合并 相关数据
              * @param {Array} index - 主数据
@@ -310,7 +334,6 @@ module.exports = app => {
                     loadFields(r.data, r.fields, r.prefix, r.relaId);
                 }
             };
-
             const billsTree = new Ledger.billsTree(this.ctx, {
                 id: 'ledger_id',
                 pid: 'ledger_pid',
@@ -327,9 +350,6 @@ module.exports = app => {
                     node.gather_tp = helper.add(node.contract_tp, node.qc_tp);
                 }
             });
-            const tender = await this.ctx.service.tender.getCheckTender(sTender.tid);
-            completeData.id = tender.id;
-            completeData.name = tender.name;
             const billsData = await this.ctx.service.ledger.getData(tender.id);
 
             const dgnData = await this.ctx.service.stageBillsDgn.getDgnData(tender.id);
@@ -343,37 +363,55 @@ module.exports = app => {
                 billsIndexData[indexPre + bd.id] = bd;
             }
 
-            const times = zone.split(' - ');
-            if (times.length !== 2) throw '选择的汇总周期无效';
-            const beginTime = moment(times[0], 'YYYY-MM');
-            const endTime = moment(times[1], 'YYYY-MM');
-
-            const stages = await this._getValidStages(tender.id);
             for (const stage of stages) {
-                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,
-                            stage.id, stage.curTimes, stage.curOrder);
-                        sumAssignRelaData(billsIndexData, [
-                            {data: curStage, fields: ['contract_qty', 'contract_tp', 'qc_qty', 'qc_tp'], prefix: '', relaId: 'lid'}
-                        ]);
-                    } else {
-                        const curStage = await this.ctx.service.stageBills.getLastestStageData(tender.id, stage.id);
-                        sumAssignRelaData(billsIndexData, [
-                            {data: curStage, fields: ['contract_qty', 'contract_tp', 'qc_qty', 'qc_tp'], prefix: '', relaId: 'lid'}
-                        ]);
-                    }
+                await this.ctx.service.stage.doCheckStage(stage);
+                if (stage.readOnly) {
+                    const curStage = await this.ctx.service.stageBills.getAuditorStageData(tender.id,
+                        stage.id, stage.curTimes, stage.curOrder);
+                    sumAssignRelaData(billsIndexData, [
+                        {data: curStage, fields: ['contract_qty', 'contract_tp', 'qc_qty', 'qc_tp'], prefix: '', relaId: 'lid'}
+                    ]);
+                } else {
+                    const curStage = await this.ctx.service.stageBills.getLastestStageData(tender.id, stage.id);
+                    sumAssignRelaData(billsIndexData, [
+                        {data: curStage, fields: ['contract_qty', 'contract_tp', 'qc_qty', 'qc_tp'], prefix: '', relaId: 'lid'}
+                    ]);
                 }
             }
+
             billsTree.loadDatas(billsData);
             billsTree.calculateAll();
             this.resultTree.loadGatherTree(billsTree, function (gatherNode, sourceNode) {
-                gatherUtils.gatherZone(gatherNode, sourceNode, completeData.prefix, helper);
+                gatherUtils.gatherZone(tender, gatherNode, sourceNode, completeData.prefix, helper);
             });
         }
 
+        async _gatherMonthData(sTender, completeData, month, hasPre) {
+            const tender = await this.ctx.service.tender.getCheckTender(sTender.tid);
+            const stages = await this._getValidStages(tender.id);
+            const stage = this.ctx.helper._.find(stages, {s_time: month});
+            await this._gatherStageData(completeData, tender, stage, hasPre);
+        }
+
+        async _gatherIndexData(sTender, completeData, index, hasPre) {
+            const tender = await this.ctx.service.tender.getCheckTender(sTender.tid);
+            const stages = await this._getValidStages(tender.id);
+            const stage = this.ctx.helper._.find(stages, {order: index});
+            await this._gatherStageData(completeData, tender, stage, hasPre);
+        }
+
+        async _gatherZoneData(sTender, completeData, zone) {
+            const tender = await this.ctx.service.tender.getCheckTender(sTender.tid);
+            const stages = await this._getTimeZoneStages(tender, zone);
+            await this._gatherStagesData(completeData, tender, stages);
+        }
+
+        async _gatherIndexZoneData(sTender, completeData, stageZone) {
+            const tender = await this.ctx.service.tender.getCheckTender(sTender.tid);
+            const stages = await this._getOrderZoneStages(tender, stageZone);
+            await this._gatherStagesData(completeData, tender, stages);
+        }
+
         async _gatherFinalData(sTender, completeData, hasPre) {
             const tender = await this.ctx.service.tender.getCheckTender(sTender.tid);
             const stages = await this._getValidStages(tender.id);
@@ -458,6 +496,12 @@ module.exports = app => {
                         case 'ledger':
                             await this._gatherLedgerData(tender, completeData);
                             break;
+                        case 'stage':
+                            await this._gatherIndexData(tender, completeData, gsCustom.stage, gsSetting.hasPre);
+                            break;
+                        case 'stage-zone':
+                            await this._gatherIndexZoneData(tender, completeData, gsCustom.stage_zone);
+                            break;
                     }
                     commonIndex++;
                 } else {
@@ -522,6 +566,20 @@ module.exports = app => {
             }
         }
 
+        async _getStagesTenderInfo(stages, info) {
+            const helper = this.ctx.helper;
+            for (const stage of stages) {
+                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_tp = helper.add(info.yf_tp, stage.yf_tp);
+            }
+            info.gather_tp = helper.add(info.contract_tp, info.qc_tp);
+        }
+
         async _gatherMonthTenderInfo(sTender, index, month, hasPre) {
             const tender = await this.ctx.service.tender.getCheckTender(sTender.tid);
             const info = await this._getBaseTenderInfo(tender);
@@ -531,31 +589,28 @@ module.exports = app => {
             this.resultTenderInfo.push(info);
         }
 
-        async _gatherZoneTenderInfo(sTender, index, zone) {
-            const helper = this.ctx.helper;
+        async _gatherOrderTenderInfo(sTender, index, order, hasPre) {
             const tender = await this.ctx.service.tender.getCheckTender(sTender.tid);
             const info = await this._getBaseTenderInfo(tender);
+            const stages = await this._getValidStages(tender.id);
+            const stage = this.ctx.helper._.find(stages, {order: order});
+            await this._getStageTenderInfo(stage, info);
+            this.resultTenderInfo.push(info);
+        }
 
-            const times = zone.split(' - ');
-            if (times.length !== 2) throw '选择的汇总周期无效';
-            const beginTime = moment(times[0], 'YYYY-MM');
-            const endTime = moment(times[1], 'YYYY-MM');
-
-            const stages = await this._getValidStages(tender.id, true);
-            for (const stage of stages) {
-                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_tp = helper.add(info.yf_tp, stage.yf_tp);
-                }
-            }
-            info.gather_tp = helper.add(info.contract_tp, info.qc_tp);
+        async _gatherZoneTenderInfo(sTender, index, zone) {
+            const tender = await this.ctx.service.tender.getCheckTender(sTender.tid);
+            const info = await this._getBaseTenderInfo(tender);
+            const stages = await this._getTimeZoneStages(tender, zone);
+            await this._getStagesTenderInfo(stages, info);
+            this.resultTenderInfo.push(info);
+        }
 
+        async _gatherOrderZoneTenderInfo(sTender, index, stageZone) {
+            const tender = await this.ctx.service.tender.getCheckTender(sTender.tid);
+            const info = await this._getBaseTenderInfo(tender);
+            const stages = await this._getOrderZoneStages(tender, stageZone);
+            await this._getStagesTenderInfo(stages, info);
             this.resultTenderInfo.push(info);
         }
 
@@ -615,6 +670,12 @@ module.exports = app => {
                         case 'ledger':
                             await this._gatherLedgerTenderInfo(tender, commonIndex);
                             break;
+                        case 'stage':
+                            await this._gatherOrderTenderInfo(tender, commonIndex, gsCustom.stage, gsSetting.hasPre);
+                            break;
+                        case 'stage-zone':
+                            await this._gatherOrderZoneTenderInfo(tender, commonIndex, gsCustom.stage_zone);
+                            break;
                     }
                     commonIndex++;
                 } else {
@@ -685,6 +746,29 @@ module.exports = app => {
             }
         }
 
+        async _gatherStagesPay(completeData, tender, stages) {
+            const helper = this.ctx.helper;
+            completeData.id = tender.id;
+            completeData.name = tender.name;
+
+            for (const stage of stages) {
+                await this.ctx.service.stage.doCheckStage(stage);
+
+                const dealPay = await this.ctx.service.stagePay.getStagePays(stage);
+                await this._checkStagePayCalc(tender, stage, dealPay);
+                for (const dp of dealPay) {
+                    dp.end_tp = helper.add(dp.pre_tp, dp.tp);
+                    this._gatherPayRecord(dp, function (gatherData, sourceData) {
+                        gatherData[completeData.prefix + 'id'] = tender.id;
+                        gatherData[completeData.prefix + 'name'] = tender.name;
+
+                        gatherData[completeData.prefix + 'tp'] = helper.add(gatherData[completeData.prefix + 'tp'], sourceData.tp);
+                        gatherData['s_' + 'tp'] = helper.add(gatherData['s_' + 'tp'], sourceData.tp);
+                    });
+                }
+            }
+        }
+
         async _gatherMonthStagePay(sTender, completeData, month, hasPre) {
             const tender = await this.ctx.service.tender.getCheckTender(sTender.tid);
             const stages = await this._getValidStages(tender.id);
@@ -692,38 +776,23 @@ module.exports = app => {
             await this._gatherStagePay(completeData, tender, stage, hasPre);
         }
 
-        async _gatherZoneStagePay(sTender, completeData, zone) {
-            const helper = this.ctx.helper;
-
+        async _gatherOrderStagePay(sTender, completeData, order, hasPre) {
             const tender = await this.ctx.service.tender.getCheckTender(sTender.tid);
-            completeData.id = tender.id;
-            completeData.name = tender.name;
             const stages = await this._getValidStages(tender.id);
+            const stage = this.ctx.helper._.find(stages, {order: order});
+            await this._gatherStagePay(completeData, tender, stage, hasPre);
+        }
 
-            const times = zone.split(' - ');
-            if (times.length !== 2) throw '选择的汇总周期无效';
-            const beginTime = moment(times[0], 'YYYY-MM');
-            const endTime = moment(times[1], 'YYYY-MM');
+        async _gatherZoneStagePay(sTender, completeData, zone) {
+            const tender = await this.ctx.service.tender.getCheckTender(sTender.tid);
+            const stages = await this._getTimeZoneStages(tender, zone);
+            await this._gatherStagesPay(completeData, tender, stages);
+        }
 
-            for (const stage of stages) {
-                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);
-                    await this._checkStagePayCalc(tender, stage, dealPay);
-                    for (const dp of dealPay) {
-                        dp.end_tp = helper.add(dp.pre_tp, dp.tp);
-                        this._gatherPayRecord(dp, function (gatherData, sourceData) {
-                            gatherData[completeData.prefix + 'id'] = tender.id;
-                            gatherData[completeData.prefix + 'name'] = tender.name;
-
-                            gatherData[completeData.prefix + 'tp'] = helper.add(gatherData[completeData.prefix + 'tp'], sourceData.tp);
-                            gatherData['s_' + 'tp'] = helper.add(gatherData['s_' + 'tp'], sourceData.tp);
-                        });
-                    }
-                }
-            }
+        async _gatherOrderZoneStagePay(sTender, completeData, stageZone) {
+            const tender = await this.ctx.service.tender.getCheckTender(sTender.tid);
+            const stages = await this._getOrderZoneStages(tender, stageZone)
+            await this._gatherStagesPay(completeData, tender, stages);
         }
 
         async _gatherFinalStagePay(sTender, completeData, hasPre) {
@@ -766,6 +835,12 @@ module.exports = app => {
                         case 'checked-final':
                             await this._gatherCheckedFinalStagePay(tender, completeData, gsSetting.hasPre);
                             break;
+                        case 'stage':
+                            await this._gatherOrderStagePay(tender, completeData, gsCustom.stage, gsSetting.hasPre);
+                            break;
+                        case 'stage-zone':
+                            await this._gatherOrderZoneStagePay(tender, completeData, gsCustom.stage_zone);
+                            break;
                     }
                     commonIndex++;
                 }

+ 11 - 0
app/service/schedule_ledger_month.js

@@ -0,0 +1,11 @@
+'use strict';
+
+module.exports = app => {
+    class ScheduleLedgerMonth extends app.BaseService {
+        constructor(ctx) {
+            super(ctx);
+            this.tableName = 'schedule_ledger_month';
+        }
+    }
+    return ScheduleLedgerMonth;
+};

+ 38 - 0
app/service/schedule_month.js

@@ -6,6 +6,44 @@ module.exports = app => {
             super(ctx);
             this.tableName = 'schedule_month';
         }
+
+        async getLastPlanMonth() {
+            const sql = 'SELECT `yearmonth` FROM ?? WHERE `tid` = ? ORDER BY `yearmonth` DESC Limit 0,1';
+            const sqlParam = [this.tableName, this.ctx.tender.id];
+            return await this.db.query(sql, sqlParam);
+        }
+
+        async add(data) {
+            const transaction = await this.db.beginTransaction();
+            try {
+                const insertData = [];
+                for (const m of data) {
+                    insertData.push({ tid: this.ctx.tender.id, yearmonth: m });
+                }
+                if (insertData.length > 0) await transaction.insert(this.tableName, insertData);
+                await transaction.commit();
+                return true;
+            } catch (err) {
+                await transaction.rollback();
+                throw err;
+            }
+        }
+
+        async del(data) {
+            const transaction = await this.db.beginTransaction();
+            try {
+                for (const m of data) {
+                    const delData = { tid: this.ctx.tender.id, yearmonth: m };
+                    await transaction.delete(this.tableName, delData);
+                    await transaction.delete(this.ctx.service.scheduleLedgerMonth.tableName, delData);
+                }
+                await transaction.commit();
+                return true;
+            } catch (err) {
+                await transaction.rollback();
+                throw err;
+            }
+        }
     }
     return ScheduleMonth;
 };

+ 1 - 1
app/service/stage_audit.js

@@ -82,7 +82,7 @@ module.exports = app => {
          */
         async getAuditors(stageId, times = 1, order_sort = 'asc') {
             const sql =
-                'SELECT la.`aid`, pa.`name`, pa.`company`, pa.`role`, pa.`mobile`, pa.`telephone`, la.`times`, la.`order`, la.`status`, la.`opinion`, la.`begin_time`, la.`end_time`, g.`sort` ' +
+                'SELECT la.`aid`, pa.`name`, pa.`company`, pa.`role`, pa.`mobile`, pa.`telephone`, pa.sign_path, la.`times`, la.`order`, la.`status`, la.`opinion`, la.`begin_time`, la.`end_time`, g.`sort` ' +
                 'FROM ?? AS la, ?? AS pa, (SELECT `aid`,(@i:=@i+1) as `sort` FROM ??, (select @i:=0) as it WHERE `sid` = ? AND `times` = ? GROUP BY `aid`) as g ' +
                 'WHERE la.`sid` = ? and la.`times` = ? and la.`aid` = pa.`id` and g.`aid` = la.`aid` order by la.`order` ' +
                 order_sort;

+ 8 - 11
app/service/stage_detail.js

@@ -8,6 +8,7 @@
  * @version
  */
 const timesLen = require('../const/audit').stage.timesLen;
+const editField = ['doc_code', 'bw', 'start_peg', 'end_peg', 'jldy', 'drawing_code', 'calc_memo', 'calc_img', 'calc_img_org', 'peg', 'xm', 'position', '']
 
 module.exports = app => {
     class StageDetail extends app.BaseService {
@@ -135,17 +136,13 @@ module.exports = app => {
                     await this.db.update(this.tableName, newData);
                     return newData;
                 } else {
-                    data.code = org.code;
-                    data.name = org.name;
-                    data.unit = org.unit;
-                    data.unit_price = org.unit_price;
-                    data.uuid = org.uuid;
-                    data.tid = this.ctx.tender.id;
-                    data.sid = this.ctx.stage.id;
-                    data.times = this.ctx.stage.times;
-                    data.order = order;
-                    await this.db.insert(this.tableName, data);
-                    return data;
+                    const nd = this._.assign(org, data);
+                    delete nd.id;
+                    nd.times = this.ctx.stage.times;
+                    nd.order = order;
+
+                    await this.db.insert(this.tableName, nd);
+                    return nd;
                 }
             } else {
                 data.uuid = this.uuid.v4();

+ 3 - 3
app/view/profile/wechat.ejs

@@ -14,19 +14,19 @@
                     <!--已绑定手机-->
                     <div class="form-group">
                         <label>微信账号</label>
-                        <input class="form-control-plaintext" readonly="" value="<%= accountData.wx_name %>">
+                        <input class="form-control-plaintext" disabled="" value="<%= accountData.wx_name %>">
                         <a href="#remove-wechat" class="btn btn-sm btn-outline-primary" data-toggle="modal" data-target="#remove-wechat">解绑</a>
                     </div>
                     <% } else { %>
                     <div class="form-group">
                         <label>微信账号</label>
-                        <input class="form-control-plaintext" readonly="" value="未绑定">
+                        <input class="form-control-plaintext" disabled="" value="未绑定">
                     </div>
                     <% } %>
                     <!--二维码-->
                     <div class="form-group">
                         <label>扫码或搜索 关注服务号</label>
-                        <div><img class="w-50" src="/public/images/wechat.png"></div>
+                        <div><img class="w-100" src="/public/images/wechat.png"></div>
                     </div>
                     <% if (accountData.wx_openid !== null && accountData.wx_openid !== '') { %>
                     <!--短信通知开关(已有认证手机后显示)-->

+ 28 - 2
app/view/report/rpt_all_popup.ejs

@@ -357,7 +357,7 @@
                         <h5>已选标段 </h5>
                         <div class="modal-height-300" id="gather-result-spread">
                         </div>
-                        <div class="mt-1" id="gather-by-month" style="width: 60%">
+                        <div class="mt-1" id="gather-by-month" style="width: 60%" name="gather-type">
                             <div class="input-group input-group-sm">
                                 <div class="input-group-prepend">
                                     <span class="input-group-text">汇总年月</span>
@@ -365,7 +365,7 @@
                                 <input id="gather-month" class="datepicker-here form-control form-control-sm" auto-close="true" autocomplete="off" placeholder="点击选择年月" data-view="months" data-min-view="months" data-date-format="yyyy-MM" data-language="zh" type="text" autocomplete="off">
                             </div>
                         </div>
-                        <div id="gather-by-zone">
+                        <div id="gather-by-zone" name="gather-type">
                             <div class="input-group input-group-sm">
                                 <div class="input-group-prepend">
                                     <span class="input-group-text">汇总周期</span>
@@ -373,6 +373,32 @@
                                 <input id="gather-zone" class="datepicker-here form-control mt-0" placeholder="点击选择周期" data-range="true" data-multiple-dates-separator=" - "  data-min-view="months" data-view="months" data-date-format="yyyy-MM" data-language="zh" type="text" autocomplete="off">
                             </div>
                         </div>
+                        <div id="gather-by-stage" name="gather-type">
+                            <div class="input-group input-group-sm">
+                                <div class="input-group-prepend">
+                                    <span class="input-group-text">选择期</span>
+                                </div>
+                                <select class="form-control" id="gather-stage">
+                                    <option>第1期</option>
+                                </select>
+                            </div>
+                        </div>
+                        <div id="gather-by-stage-zone" name="gather-type">
+                            <div class="input-group input-group-sm">
+                                <div class="input-group-prepend">
+                                    <span class="input-group-text">汇总</span>
+                                </div>
+                                <select class="form-control" id="gather-stage-begin">
+                                    <option>第1期</option>
+                                </select>
+                                <div class="input-group-prepend">
+                                    <span class="input-group-text">至</span>
+                                </div>
+                                <select class="form-control" id="gather-stage-end">
+                                    <option>第5期</option>
+                                </select>
+                            </div>
+                        </div>
                     </div>
                     <div class="text-danger text-center ml-3" id="gather-hint">我是提示呀</div>
                 </div>

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

@@ -4,7 +4,7 @@
         <div class="title-main d-flex">
             <% include ../tender/tender_sub_mini_menu.ejs %>
             <h2>
-                计划至至 2020年12月
+                <% if (planMonth) { %>计划至 <%- planMonth.split('-')[0] %>年<%- parseInt(planMonth.split('-')[1]) %>月 <% } %>
             </h2>
             <div class="ml-auto">
                 <a href="/tender/<%- ctx.tender.id %>/schedule/ledger" class="btn btn-sm btn-outline-primary">进度台帐</a>

+ 2 - 1
app/view/schedule/plan.ejs

@@ -4,7 +4,7 @@
         <div class="title-main d-flex">
             <% include ../tender/tender_sub_mini_menu.ejs %>
             <h2>
-                计划至至 2020年12月
+                <% if (planMonth) { %>计划至 <%- planMonth.split('-')[0] %>年<%- parseInt(planMonth.split('-')[1]) %>月 <% } %>
             </h2>
             <div class="ml-auto">
                 <a href="#mode" data-toggle="modal" data-target="#mode" class="btn btn-sm btn-outline-primary">计算方式</a>
@@ -27,5 +27,6 @@
     const measureType = JSON.parse('<%- JSON.stringify(measureType) %>');
     const schedule = JSON.parse('<%- JSON.stringify(schedule) %>');
     const scheduleMonth = JSON.parse('<%- JSON.stringify(scheduleMonth) %>');
+    const monthList = _.map(scheduleMonth, 'yearmonth');
     const mode = JSON.parse('<%- JSON.stringify(mode) %>');
 </script>

+ 13 - 9
app/view/schedule/plan_modal.ejs

@@ -8,13 +8,14 @@
             <div class="modal-body">
                 <div class="form-group">
                     <label>计划周期</label>
-                    <input class="datepicker-here form-control" placeholder="点击选择时间" data-view="months" data-min-view="months" data-date-format="yyyy-MM" data-range="true" data-multiple-dates-separator=" ~ " data-language="zh" type="text">
+                    <input id="month-range" readonly class="datepicker-here form-control" placeholder="点击选择时间" data-view="months" data-min-view="months" data-date-format="yyyy-MM" data-range="true" data-multiple-dates-separator=" ~ " data-language="zh" type="text">
+                </div>
+                <div id="add-month-error-list" style="display: none">
                 </div>
-                <div class="alert alert-danger">2020-05 已创建</div>
             </div>
             <div class="modal-footer">
                 <button type="button" class="btn btn-sm btn-secondary" data-dismiss="modal">取消</button>
-                <button type="button" class="btn btn-sm btn-primary" >确认新增</button>
+                <button type="button" class="btn btn-sm btn-primary" id="add-month">确认新增</button>
             </div>
         </div>
     </div>
@@ -27,17 +28,20 @@
                 <h5 class="modal-title">管理计划</h5>
             </div>
             <div class="modal-body">
-                <table class="table table-bordered table-hover">
+                <table class="table table-bordered table-hover" id="month-table">
                     <tr><th>计划月</th><th width="100">删除</th></tr>
-                    <tr><td>2020-1</td><td></td></tr>
-                    <tr><td>2020-2</td><td><label><input type="checkbox"></label></td></tr>
-                    <tr><td>2020-3</td><td><label><input type="checkbox"></label></td></tr>
+                    <% for (const m of scheduleMonth) { %>
+                    <tr>
+                        <td><%- m.yearmonth %></td>
+                        <td><% if (m.sj_gcl === null && m.sj_tp === null) { %><label></label><input type="checkbox" value="<%- m.id %>"><% } %></td>
+                    </tr>
+                    <% } %>
                 </table>
-                <div class="alert alert-danger">确认删除「2020-2」「2020-3」计划进度?删除后,数据无法恢复,请谨慎操作。</div>
+                <div class="alert alert-danger" style="display: none">确认删除<span id="del-month-list"></span>计划进度?删除后,数据无法恢复,请谨慎操作。</div>
             </div>
             <div class="modal-footer">
                 <button type="button" class="btn btn-sm btn-secondary" data-dismiss="modal">取消</button>
-                <button type="button" class="btn btn-sm btn-danger" >确认删除</button>
+                <button type="button" class="btn btn-sm btn-danger" disabled id="del-month" >确认删除</button>
             </div>
         </div>
     </div>

+ 1 - 1
app/view/shares/check_modal2.ejs

@@ -109,7 +109,7 @@
         if (!setting.prefix) setting.prefix = 'check2-';
         if (setting.randomWait) {
             for (const c of setting.checks) {
-                c.wait = _.random(1, 5) + setting.extra;
+                c.wait = _.random(2, 4) + setting.extra;
             }
         }
 

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

@@ -37,7 +37,7 @@
                     <a class="btn btn-sm btn-primary" href="javascript: void(0);" id="ledger-check2">数据检查</a>
                 </div>
                 <div class="d-inline-block">
-                    <a class="btn btn-sm btn-danger" href="#cooperation" data-toggle="modal" data-target="#cooperation">多人协同 <i class="fa fa-lock"></i> <span id="cooperationCount"></span></a>
+                    <a class="btn btn-sm btn-danger" href="#cooperation" data-toggle="modal" data-target="#cooperation" style="display: none;">多人协同 <i class="fa fa-lock"></i> <span id="cooperationCount"></span></a>
                 </div>
             </div>
             <div class="ml-auto">

+ 3 - 2
app/view/wap/list.ejs

@@ -57,8 +57,8 @@
     </nav>
 </div>
 <script>
-    const tenders = JSON.parse('<%- JSON.stringify(tenderList) %>');
-    const category = JSON.parse('<%- JSON.stringify(categoryData) %>');
+    const tenders = JSON.parse(unescape('<%- escape(JSON.stringify(tenderList)) %>'));
+    const category = JSON.parse(unescape('<%- escape(JSON.stringify(categoryData)) %>'));
     const uid = '<%- uid %>';
     const pid = '<%- pid %>';
     const uphlname = 'user_' + uid + '_pro_' + pid + '_category_wap_hide_list';
@@ -74,6 +74,7 @@
 <script src="/public/js/wap/global.js"></script>
 <script src="/public/js/decimal.min.js"></script>
 <script src="/public/js/zh_calc.js"></script>
+<script src="/public/js/PinYinOrder.bundle.js"></script>
 <script src="/public/js/shares/tender_list_order.js"></script>
 <script src="/public/js/tender_showhide.js"></script>
 <script src="/public/js/wap/list.js"></script>

+ 35 - 2
builder_report_index_define.js

@@ -17,6 +17,7 @@ const dataType = {
     double: 'double',
     currency: 'currency',
     time: 'string',
+    arr: 'array',
 };
 const tag = {
     tp: { type: 'tp' },
@@ -60,7 +61,7 @@ const ledger_cooperation = {
         { name: '密码', field: 'pwd', type: dataType.str },
         { name: '电子签名地址', field: 'sign_path', type: dataType.str },
     ]
-}
+};
 // 其他台账
 const stage_jgcl = {
     name: '期-甲供材料(mem_stage_jgcl)',
@@ -229,6 +230,9 @@ const change_bills = {
         { name: '金额_9', field: 'tp_9', type: dataType.currency, tag: { type: 'tp' } },
         { name: '项目节编号', field: 'xmj_code', type: dataType.str },
         { name: '细目', field: 'xmj_jldy', type: dataType.str },
+        { name: '单位工程', field: 'xmj_dwgc', type: dataType.str },
+        { name: '分部工程', field: 'xmj_fbgc', type: dataType.str },
+        { name: '分项工程', field: 'xmj_fxgc', type: dataType.str },
     ],
 };
 // 期 - 清单
@@ -678,6 +682,11 @@ const stage_im_zl = {
         { name: '位置', field: 'position', type: dataType.str },
         { name: '计量单元', field: 'jldy', type: dataType.str },
         { name: '草图备注', field: 'calc_memo_remark', type: dataType.str },
+        { name: '签名列表', field: 'cooperation', type: dataType.arr },
+        { name: '签名1', field: 'co_sign1', type: dataType.str, isPic: true },
+        { name: '签名2', field: 'co_sign2', type: dataType.str, isPic: true },
+        { name: '签名3', field: 'co_sign3', type: dataType.str, isPic: true },
+        { name: '签名4', field: 'co_sign4', type: dataType.str, isPic: true },
     ],
 };
 const stage_im_tz = {
@@ -714,6 +723,12 @@ const stage_im_tz = {
         { name: '位置', field: 'position', type: dataType.str },
         { name: '计量单元', field: 'jldy', type: dataType.str },
         { name: '草图备注', field: 'calc_img_remark', type: dataType.str },
+        { name: '签名列表', field: 'cooperation', type: dataType.arr },
+        { name: '签名1', field: 'co_sign1', type: dataType.str, isPic: true },
+        { name: '签名2', field: 'co_sign2', type: dataType.str, isPic: true },
+        { name: '签名3', field: 'co_sign3', type: dataType.str, isPic: true },
+        { name: '签名4', field: 'co_sign4', type: dataType.str, isPic: true },
+
     ],
 };
 const stage_im_tz_bills = {
@@ -1157,6 +1172,24 @@ const stage_sum_pay = {
     ],
 };
 
+const stage_audit = {
+    name: '期-审批人 列表(stage_audit)',
+    remark: '',
+    id: 46,
+    key: 'stage_audit',
+    prefix: '期-审批人',
+    cols: [
+        { name: '审批人id', field: 'aid', type: dataType.int },
+        { name: '姓名', field: 'name', type: dataType.str },
+        { name: '公司', field: 'company', type: dataType.str },
+        { name: '角色', field: 'role', type: dataType.str },
+        { name: '手机', field: 'mobile', type: dataType.str },
+        { name: '电话', field: 'telephone', type: dataType.str },
+        { name: '审批意见', field: 'opinion', type: dataType.str },
+        { name: '审批时间', field: 'end_time', type: dataType.str },
+        { name: '审批顺序', field: 'sort', type: dataType.str },
+    ],
+};
 
 const recursiveMkdirSync = async function(pathName) {
     if (!fs.existsSync(pathName)) {
@@ -1261,7 +1294,7 @@ const defines = [
     stage_im_zl, stage_im_tz, stage_im_tz_bills,
     gather_stage_bills, gather_tender_info, gather_stage_pay, gather_deal_bills,
     material, materialGl,
-    stage_sum_bills, stage_sum_pay
+    stage_sum_bills, stage_sum_pay, stage_audit,
 ];
 for (const d of defines) {
     exportTableDefine(d);

+ 16 - 0
sql/update.sql

@@ -3,4 +3,20 @@ ALTER TABLE `zh_change` ADD `tp_decimal` TINYINT(3) NULL DEFAULT NULL COMMENT '
 ALTER TABLE `zh_tender_info`
 ADD COLUMN `bid_info`  varchar(200) NULL AFTER `tech_param`;
 
+--
+-- 表的结构 `zh_ledger_cooperation`
+--
+
+CREATE TABLE `zh_ledger_cooperation` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `tid` int(11) NOT NULL COMMENT '标段id',
+  `ledger_id` int(11) NOT NULL COMMENT '台账id',
+  `user_id` int(11) NOT NULL COMMENT '审批人id',
+  `pwd` varchar(255) COLLATE utf8_unicode_ci NOT NULL COMMENT '密码',
+  `sign_path` varchar(500) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '电子签名地址',
+  `status` tinyint(1) NOT NULL DEFAULT '1' COMMENT '是否调用',
+  PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='台账多人协同表';
+
+
 CREATE UNIQUE INDEX `idx_tid_sid_rid`  ON `zh_rpt_custom_define` (tid, sid, rid) COMMENT '电子签名数据索引' ALGORITHM DEFAULT LOCK DEFAULT;

+ 16 - 0
test/app/lib/rpt_data_analysis.test.js

@@ -188,6 +188,22 @@ describe('test/app/service/report_memory.test.js', () => {
         const chapter400 = ctx.helper._.find(data.mem_stage_bills, {code: '400'});
         assert(chapter400.total_price.toFixed(0) == 1231018);
     });
+    it('test loadCooperationData', function* () {
+        const ctx = app.mockContext(mockData);
+        const stage = yield ctx.service.stage.getDataByCondition({tid: 3301, order: 1});
+        const params = {
+            tender_id: stage.tid,
+            stage_id: stage.id,
+        };
+        const data = yield ctx.service.report.getReportData(params, ['mem_stage_bills', 'stage', 'stage_audit', 'ledger_cooperation', 'mem_stage_im_zl'], {
+            mem_stage_bills: [
+                'id', 'tender_id', 'ledger_id', 'ledger_pid', 'level', 'order', 'full_path', 'is_leaf'
+            ],
+            mem_stage_im_zl: ['lid', 'code'],
+        });
+        reportDataAnalysis.analysisObj.loadCooperationData.fun(ctx, data, [], {table: 'mem_stage_im_zl', co_sign: [0, 1, 2, 3]}, null);
+        console.log(data.mem_stage_im_zl);
+    });
     it('test join', function* () {
         const ctx = app.mockContext(mockData);