浏览代码

Merge remote-tracking branch 'remotes/origin/uat'

Conflicts:
	app/public/js/ledger.js
	app/public/js/stage.js
	builder_report_index_define.js
MaiXinRong 4 年之前
父节点
当前提交
026ea6a5d3
共有 100 个文件被更改,包括 3592 次插入911 次删除
  1. 33 10
      app/const/audit.js
  2. 34 0
      app/const/material.js
  3. 6 0
      app/const/setting.js
  4. 2 0
      app/const/tender_info.js
  5. 42 13
      app/controller/advance_controller.js
  6. 55 15
      app/controller/change_controller.js
  7. 43 2
      app/controller/deal_bills_controller.js
  8. 2 5
      app/controller/ledger_audit_controller.js
  9. 150 0
      app/controller/material_controller.js
  10. 5 0
      app/controller/profile_controller.js
  11. 22 2
      app/controller/report_controller.js
  12. 2 5
      app/controller/revise_controller.js
  13. 11 7
      app/controller/stage_controller.js
  14. 95 51
      app/controller/tender_controller.js
  15. 24 24
      app/extend/helper.js
  16. 1 1
      app/lib/stage_im.js
  17. 0 3
      app/middleware/session_auth.js
  18. 35 41
      app/middleware/tender_check.js
  19. 65 0
      app/middleware/uncheck_tender_check.js
  20. 12 5
      app/public/css/main.css
  21. 18 18
      app/public/js/advance.js
  22. 8 7
      app/public/js/advance_audit.js
  23. 10 2
      app/public/js/change_detail.js
  24. 39 29
      app/public/js/change_set.js
  25. 304 39
      app/public/js/ledger.js
  26. 2 0
      app/public/js/ledger_audit.js
  27. 7 0
      app/public/js/local-forage/localforage.min.js
  28. 116 46
      app/public/js/material.js
  29. 454 0
      app/public/js/material_exponent.js
  30. 19 2
      app/public/js/material_file.js
  31. 15 34
      app/public/js/material_list.js
  32. 2 0
      app/public/js/measure_compare.js
  33. 20 0
      app/public/js/measure_stage.js
  34. 69 24
      app/public/js/path_tree.js
  35. 29 0
      app/public/js/revise.js
  36. 1 1
      app/public/js/se_jgcl.js
  37. 1 1
      app/public/js/se_other.js
  38. 13 1
      app/public/js/spreadjs_rela/spreadjs_zh.js
  39. 126 36
      app/public/js/stage.js
  40. 4 0
      app/public/js/stage_bwtz.js
  41. 11 15
      app/public/js/stage_im.js
  42. 1 3
      app/public/js/stage_pay.js
  43. 1 0
      app/public/js/tender_list.js
  44. 53 37
      app/public/js/tender_list_info.js
  45. 2 0
      app/public/js/tender_list_manage.js
  46. 24 16
      app/public/js/tender_list_progress.js
  47. 4 2
      app/public/report/js/rpt_jspdf.js
  48. 5 0
      app/public/report/js/rpt_main.js
  49. 17 6
      app/public/report/js/rpt_preview_common.js
  50. 7 4
      app/public/report/js/rpt_print.js
  51. 2 1
      app/reports/rpt_component/jpc_flow_tab.js
  52. 108 9
      app/reports/util/rpt_excel_util.js
  53. 139 133
      app/router.js
  54. 39 8
      app/service/advance.js
  55. 67 29
      app/service/change.js
  56. 1 1
      app/service/change_att.js
  57. 87 2
      app/service/deal_bills.js
  58. 22 0
      app/service/ledger_audit.js
  59. 137 0
      app/service/login_logging.js
  60. 58 3
      app/service/material.js
  61. 36 10
      app/service/material_audit.js
  62. 5 3
      app/service/material_bills.js
  63. 253 0
      app/service/material_exponent.js
  64. 37 0
      app/service/material_exponent_history.js
  65. 50 20
      app/service/material_list.js
  66. 1 0
      app/service/material_month.js
  67. 5 1
      app/service/project_account.js
  68. 4 4
      app/service/report_memory.js
  69. 16 0
      app/service/stage.js
  70. 3 11
      app/service/stage_att.js
  71. 26 7
      app/service/stage_audit.js
  72. 13 31
      app/service/stage_bills.js
  73. 13 3
      app/service/stage_bonus.js
  74. 9 9
      app/service/stage_change.js
  75. 23 10
      app/service/stage_jgcl.js
  76. 17 6
      app/service/stage_other.js
  77. 21 13
      app/service/stage_pos.js
  78. 79 9
      app/service/tender_info.js
  79. 9 9
      app/view/advance/detail.ejs
  80. 8 8
      app/view/advance/index.ejs
  81. 1 1
      app/view/advance/modal.ejs
  82. 15 15
      app/view/change/info.ejs
  83. 3 0
      app/view/layout/menu.ejs
  84. 5 2
      app/view/ledger/bwtz.ejs
  85. 2 11
      app/view/ledger/explode_modal.ejs
  86. 2 1
      app/view/material/audit_modal.ejs
  87. 123 0
      app/view/material/exponent.ejs
  88. 1 0
      app/view/material/exponent_modal.ejs
  89. 16 5
      app/view/material/file.ejs
  90. 19 6
      app/view/material/index.ejs
  91. 19 10
      app/view/material/info.ejs
  92. 1 1
      app/view/material/info_modal.ejs
  93. 0 5
      app/view/material/list.ejs
  94. 8 1
      app/view/material/material_sub_menu.ejs
  95. 15 1
      app/view/material/material_sub_mini_menu.ejs
  96. 5 1
      app/view/measure/stage.ejs
  97. 17 0
      app/view/measure/stage_modal.ejs
  98. 26 0
      app/view/profile/safe.ejs
  99. 5 4
      app/view/profile/sms.ejs
  100. 0 0
      app/view/report/index.ejs

+ 33 - 10
app/const/audit.js

@@ -41,7 +41,20 @@ const ledger = (function() {
     auditStringClass[status.checking] = 'text-warning';
     auditStringClass[status.checked] = 'text-success';
     auditStringClass[status.checkNo] = 'text-warning';
-    return { status, statusString, statusClass, auditString, auditStringClass };
+
+    // 金额概况
+
+    const tiStatusString = [];
+    tiStatusString[status.uncheck] = '未上报';
+    tiStatusString[status.checking] = '审批中';
+    tiStatusString[status.checked] = '审批通过';
+    tiStatusString[status.checkNo] = '审批退回';
+    const tiStatusStringClass = [];
+    tiStatusStringClass[status.uncheck] = '';
+    tiStatusStringClass[status.checking] = 'text-warning';
+    tiStatusStringClass[status.checked] = 'text-success';
+    tiStatusStringClass[status.checkNo] = 'text-warning';
+    return { status, statusString, statusClass, auditString, auditStringClass, tiStatusString, tiStatusStringClass };
 })();
 
 // 台账修订 审批流程
@@ -180,22 +193,32 @@ const stage = (function() {
     auditProgressClass[status.checkAgain] = 'text-warning';
     /* ------------------------------------------------------- */
 
+    const tiStatusString = [];
+    tiStatusString[status.uncheck] = '待上报';
+    tiStatusString[status.checking] = '审批中';
+    tiStatusString[status.checked] = '审批通过';
+    tiStatusString[status.checkNo] = '审批退回';
+    tiStatusString[status.checkNoPre] = '审批中';
+    tiStatusString[status.checkAgain] = '审批中';
+    const tiStatusStringClass = [];
+    tiStatusStringClass[status.uncheck] = '';
+    tiStatusStringClass[status.checking] = 'text-warning';
+    tiStatusStringClass[status.checked] = 'text-success';
+    tiStatusStringClass[status.checkNo] = 'text-warning';
+    tiStatusStringClass[status.checkNoPre] = 'text-warning';
+    tiStatusStringClass[status.checkAgain] = 'text-warning';
     const backType = {
         org: 1,
         pre: 2,
     };
     return {
-        status,
-        statusString,
-        statusClass,
-        statusButton,
-        statusButtonClass,
-        auditString,
-        auditStringClass,
-        auditProgress,
-        auditProgressClass,
+        status, statusString, statusClass,
+        statusButton, statusButtonClass,
+        auditString, auditStringClass,
+        auditProgress, auditProgressClass,
         backType,
         timesLen: 100,
+        tiStatusString, tiStatusStringClass
     };
 })();
 

+ 34 - 0
app/const/material.js

@@ -23,8 +23,42 @@ const m_type = [
     { text: '半成品', value: 6 },
     { text: '其他', value: 7 },
 ];
+// 指数调差类型
+const ex_type = [
+    { text: '定值', value: 1 },
+    { text: '变值', value: 2 },
+];
+
+const ex_calc = [
+    {
+        code: 'bqht',
+        text: '本期合同计量金额',
+        value: '',
+        select: false,
+    },
+    {
+        code: 'bqbg',
+        text: '本期变更计量金额',
+        value: '',
+        select: false,
+    },
+    {
+        code: 'bqwc',
+        text: '本期完成计量金额',
+        value: '',
+        select: true,
+    },
+];
+
+const is_summary = {
+    yes: 1,
+    no: 2,
+};
 
 module.exports = {
     t_type,
     m_type,
+    ex_type,
+    is_summary,
+    ex_calc,
 };

+ 6 - 0
app/const/setting.js

@@ -23,6 +23,11 @@ const listPath = [
     { label_name: '金额概况', path: '/list/info', is_default: false }, // 金额概况
 ];
 
+// 登录方式
+const loginWay = {
+    normalPsw: 0, // 正常登录
+    extraPsw: 1, // 副密码登录
+};
 
 module.exports = {
     cType: {
@@ -30,4 +35,5 @@ module.exports = {
         text: cTypeStr,
     },
     listPath,
+    loginWay,
 };

+ 2 - 0
app/const/tender_info.js

@@ -63,6 +63,8 @@ const defaultInfo = {
         tp: 0,
         pay: false,
         payTp: 0,
+        extra: false,
+        extraTp: 0,
     },
     precision: {
         t: { unit: 't', value: 3 },

+ 42 - 13
app/controller/advance_controller.js

@@ -1,7 +1,6 @@
 'use strict';
 const accountGroup = require('../const/account_group').group;
 const auditConst = require('../const/audit').advance;
-const ledgerAuditConst = require('../const/audit').ledger.status;
 const sendToWormhole = require('stream-wormhole');
 const path = require('path');
 const fs = require('fs');
@@ -19,11 +18,10 @@ module.exports = app => {
          */
         async index(ctx) {
             const type = auditConst.type.start;
-            const advanceList = await ctx.service.advance.getAdvanceList(ctx.tender.id, type);
-            const latestOrder = await ctx.service.advance.getLastestAdvance(ctx.tender.id, type, true);
             const advancePayTotal = ctx.tender.info.deal_param.startAdvance;
+            const advanceList = await ctx.service.advance.getAdvanceList(ctx.tender.id, type, this.decimal, advancePayTotal);
+            const latestOrder = await ctx.service.advance.getLastestAdvance(ctx.tender.id, type, true);
             const progress = await ctx.service.advance.calcProgress(latestOrder, advancePayTotal);
-            // const showAddBtn = ctx.tender.data.ledger_status !== ledgerAuditConst.uncheck && ctx.tender.data.user_id === ctx.session.sessionUser.accountId ? !latestOrder || (latestOrder.status === auditConst.status.checked && latestOrder.prev_total_amount < advancePayTotal) : false;
             const showAddBtn = ctx.tender.data.user_id === ctx.session.sessionUser.accountId ? (!latestOrder || (latestOrder.status === auditConst.status.checked && latestOrder.prev_total_amount < advancePayTotal)) : false;
             const renderData = {
                 type,
@@ -46,9 +44,9 @@ module.exports = app => {
          */
         async materialList(ctx) {
             const type = auditConst.type.material;
-            const advanceList = await ctx.service.advance.getAdvanceList(ctx.tender.id, type);
-            const latestOrder = await ctx.service.advance.getLastestAdvance(ctx.tender.id, type, true);
             const advancePayTotal = ctx.tender.info.deal_param.materialAdvance;
+            const advanceList = await ctx.service.advance.getAdvanceList(ctx.tender.id, type, this.decimal, advancePayTotal);
+            const latestOrder = await ctx.service.advance.getLastestAdvance(ctx.tender.id, type, true);
             const progress = await ctx.service.advance.calcProgress(latestOrder, advancePayTotal);
             const showAddBtn = ctx.tender.data.user_id === ctx.session.sessionUser.accountId ? !latestOrder || (latestOrder.status === auditConst.status.checked && latestOrder.prev_total_amount < advancePayTotal) : false;
             const renderData = {
@@ -109,9 +107,21 @@ module.exports = app => {
             return data;
         }
 
-        _checkCanEntry(ctx) {
-            if (ctx.session.sessionUser.accountId !== ctx.advance.uid) {
-                if (ctx.advance.status === auditConst.status.uncheck) {
+        /**
+         * 权限判断-进入详情页
+         * @param {Object} ctx - 全局上下文
+         * @return {void}
+         * @private
+         */
+        async _checkCanEntry(ctx) {
+            if (ctx.advance.status === auditConst.status.uncheck) {
+                if (ctx.session.sessionUser.accountId !== ctx.advance.uid) {
+                    throw '无权访问';
+                }
+            } else {
+                const auditors = await ctx.service.advanceAudit.getAuditorsWithOwner(ctx.advance.id, ctx.advance.times);
+                const cur_uid = ctx.session.sessionUser.accountId;
+                if (auditors.findIndex(item => item.audit_id === cur_uid) === -1) {
                     throw '无权访问';
                 }
             }
@@ -124,18 +134,37 @@ module.exports = app => {
         async detail(ctx) {
             const advancePayTotal = ctx.advance.type === 0 ? ctx.tender.info.deal_param.startAdvance : ctx.tender.info.deal_param.materialAdvance;
             try {
-                this._checkCanEntry(ctx);
-                const renderData = await this._getDefaultRenderData(ctx);
+                await this._checkCanEntry(ctx);
                 const { uncheck, checkNo } = auditConst.status;
                 const { status } = ctx.advance;
+                const isEdited = ctx.session.sessionUser.accountId === ctx.advance.uid && (status === uncheck || status === checkNo);
+                const renderData = await this._getDefaultRenderData(ctx);
                 // 获取上一期预付款记录
                 const prevAdvance = await ctx.service.advance.getPreviousRecord(ctx.tender.id, ctx.advance.type);
                 // 最大支付比例
                 const max_pr = ctx.helper.mul(ctx.helper.div(ctx.helper.sub(advancePayTotal, (prevAdvance && prevAdvance.prev_total_amount || 0)), advancePayTotal, 10), 100);
-                renderData.isEdited = status === uncheck || status === checkNo;
+                // 特殊处理金额的显示(formatMoney)
+                let cur_amount,
+                    prev_total_amount;
+                const s1 = parseFloat(ctx.advance.prev_amount).toString().split('.')[1];
+                const prev_amount = ctx.helper.formatMoney(ctx.advance.prev_amount, ',', s1 && s1.length || 0);
+                if (status === uncheck || status === checkNo) {
+                    cur_amount = parseFloat(ctx.helper.round(ctx.advance.cur_amount, this.decimal)) || 0;
+                    const s2 = parseFloat(ctx.helper.add(cur_amount, ctx.advance.prev_amount)).toString().split('.')[1];
+                    prev_total_amount = ctx.helper.formatMoney(ctx.helper.add(cur_amount, ctx.advance.prev_amount), ',', s2 && s2.length || 0);
+                } else {
+                    cur_amount = ctx.advance.cur_amount;
+                    const s2 = parseFloat(ctx.advance.prev_total_amount).toString().split('.')[1];
+                    prev_total_amount = ctx.helper.formatMoney(ctx.advance.prev_total_amount, ',', s2 && s2.length || 0);
+                }
+
+                renderData.isEdited = isEdited;
                 renderData.advance = ctx.advance;
-                renderData.decimal = this.decimal;
+                renderData.cur_amount = cur_amount;
+                renderData.prev_amount = prev_amount;
+                renderData.prev_total_amount = prev_total_amount;
                 renderData.max_pr = max_pr;
+                renderData.decimal = this.decimal;
                 renderData.advancePayTotal = advancePayTotal;
                 renderData.prevAdvance = prevAdvance;
                 await this.layout('advance/detail.ejs', renderData, 'advance/modal_audit.ejs');

+ 55 - 15
app/controller/change_controller.js

@@ -266,7 +266,7 @@ module.exports = app => {
                 const auditStatus = await ctx.service.changeAudit.getStatusByChange(change);
 
                 // 获取附件列表
-                const attList = await ctx.service.changeAtt.getAllChangeFiles(ctx.params.cid);
+                const attList = await ctx.service.changeAtt.getChangeAttachment(ctx.params.cid);
 
                 // 根据auditStatus获取审批人列表
                 const auditList = await ctx.service.changeAudit.getListByStatus(change, auditStatus);
@@ -343,13 +343,15 @@ module.exports = app => {
                             cl.camount,
                             cl.detail,
                             cl.lid,
+                            cl.xmj_code,
+                            cl.xmj_jldy,
                         ];
                         ototalCost += cl.unit_price === null ? 0 : ctx.helper.mul(cl.unit_price, cl.oamount, ctx.tender.info.decimal.tp);
                         ctotalCost += cl.unit_price === null ? 0 : ctx.helper.mul(cl.unit_price, cl.camount, ctx.tender.info.decimal.tp);
                         if (cl.lid !== '0') {
-                            changeListData.push(cLArray.join(';'));
+                            changeListData.push(cLArray.join('*;*'));
                         } else {
-                            changeWhiteListData.push(cLArray.join(';'));
+                            changeWhiteListData.push(cLArray.join('*;*'));
                         }
                     }
                     renderData.changeListData = changeListData.join('^_^');
@@ -391,9 +393,12 @@ module.exports = app => {
                     let stotalCost = 0;
                     const auditTotalCost = [];
                     for (const cl of changeList) {
-                        ototalCost += cl.unit_price === null ? 0 : parseFloat(ctx.helper.roundNum(ctx.helper.accMul(cl.unit_price, cl.oamount), renderData.tpUnit));
-                        ctotalCost += cl.unit_price === null ? 0 : parseFloat(ctx.helper.roundNum(ctx.helper.accMul(cl.unit_price, cl.camount), renderData.tpUnit));
-                        stotalCost += cl.samount !== '' && cl.unit_price !== null ? parseFloat(ctx.helper.roundNum(ctx.helper.accMul(cl.unit_price, cl.samount), renderData.tpUnit)) : 0;
+                        // ototalCost += cl.unit_price === null ? 0 : parseFloat(ctx.helper.roundNum(ctx.helper.accMul(cl.unit_price, cl.oamount), renderData.tpUnit));
+                        ototalCost += cl.unit_price === null ? 0 : ctx.helper.mul(cl.unit_price, cl.oamount, renderData.tpUnit);
+                        // ctotalCost += cl.unit_price === null ? 0 : parseFloat(ctx.helper.roundNum(ctx.helper.accMul(cl.unit_price, cl.camount), renderData.tpUnit));
+                        ctotalCost += cl.unit_price === null ? 0 : ctx.helper.mul(cl.unit_price, cl.camount, renderData.tpUnit);
+                        // stotalCost += cl.samount !== '' && cl.unit_price !== null ? parseFloat(ctx.helper.roundNum(ctx.helper.accMul(cl.unit_price, cl.samount), renderData.tpUnit)) : 0;
+                        stotalCost += cl.samount !== '' && cl.unit_price !== null ? ctx.helper.mul(cl.unit_price, cl.samount, renderData.tpUnit) : 0;
                         const audit_amount = cl.audit_amount !== null && cl.audit_amount !== '' ? cl.audit_amount.split(',') : '';
                         auditTotalCost.push(audit_amount);
                     }
@@ -408,7 +413,8 @@ module.exports = app => {
                             au.totalCost = 0;
                             for (const [auindex, at] of auditTotalCost.entries()) {
                                 au.list_amount.push(at[index - 1]);
-                                au.totalCost += at[index - 1] !== undefined && changeList[auindex].unit_price !== null ? parseFloat(ctx.helper.roundNum(ctx.helper.accMul(changeList[auindex].unit_price, at[index - 1]), renderData.tpUnit)) : 0;
+                                // au.totalCost += at[index - 1] !== undefined && changeList[auindex].unit_price !== null ? parseFloat(ctx.helper.roundNum(ctx.helper.accMul(changeList[auindex].unit_price, at[index - 1]), renderData.tpUnit)) : 0;
+                                au.totalCost += at[index - 1] !== undefined && changeList[auindex].unit_price !== null ? ctx.helper.mul(changeList[auindex].unit_price, at[index - 1], renderData.tpUnit) : 0;
                             }
                         }
                     }
@@ -453,8 +459,10 @@ module.exports = app => {
                     const auditTotalCost = [];
                     const auditUnit = [];
                     for (const cl of changeList) {
-                        ototalCost += cl.unit_price === null ? 0 : parseFloat(ctx.helper.roundNum(ctx.helper.accMul(cl.unit_price, cl.oamount), renderData.tpUnit));
-                        ctotalCost += cl.unit_price === null ? 0 : parseFloat(ctx.helper.roundNum(ctx.helper.accMul(cl.unit_price, cl.camount), renderData.tpUnit));
+                        // ototalCost += cl.unit_price === null ? 0 : parseFloat(ctx.helper.roundNum(ctx.helper.accMul(cl.unit_price, cl.oamount), renderData.tpUnit));
+                        ototalCost += cl.unit_price === null ? 0 : ctx.helper.mul(cl.unit_price, cl.oamount, renderData.tpUnit);
+                        // ctotalCost += cl.unit_price === null ? 0 : parseFloat(ctx.helper.roundNum(ctx.helper.accMul(cl.unit_price, cl.camount), renderData.tpUnit));
+                        ctotalCost += cl.unit_price === null ? 0 : ctx.helper.mul(cl.unit_price, cl.camount, renderData.tpUnit);
                         const audit_amount = cl.audit_amount !== null && cl.audit_amount !== '' ? cl.audit_amount.split(',') : '';
                         auditTotalCost.push(audit_amount);
                     }
@@ -476,12 +484,14 @@ module.exports = app => {
                                     //     au.totalCost += parseFloat(ctx.helper.roundNum(ctx.helper.accMul(changeList[auindex].unit_price, changeList[auindex].camount), renderData.tpUnit));
                                     // }
                                     au.list_amount.push(changeList[auindex].spamount);
-                                    au.totalCost += changeList[auindex].unit_price === null ? 0 : parseFloat(ctx.helper.roundNum(ctx.helper.accMul(changeList[auindex].unit_price, changeList[auindex].spamount), renderData.tpUnit));
+                                    // au.totalCost += changeList[auindex].unit_price === null ? 0 : parseFloat(ctx.helper.roundNum(ctx.helper.accMul(changeList[auindex].unit_price, changeList[auindex].spamount), renderData.tpUnit));
+                                    au.totalCost += changeList[auindex].unit_price === null ? 0 : ctx.helper.mul(changeList[auindex].unit_price, changeList[auindex].spamount, renderData.tpUnit);
                                 }
                             } else {
                                 for (const [auindex, at] of auditTotalCost.entries()) {
                                     au.list_amount.push(at[index - 1]);
-                                    au.totalCost += at[index - 1] !== undefined && changeList[auindex].unit_price !== null ? parseFloat(ctx.helper.roundNum(ctx.helper.accMul(changeList[auindex].unit_price, at[index - 1]), renderData.tpUnit)) : 0;
+                                    // au.totalCost += at[index - 1] !== undefined && changeList[auindex].unit_price !== null ? parseFloat(ctx.helper.roundNum(ctx.helper.accMul(changeList[auindex].unit_price, at[index - 1]), renderData.tpUnit)) : 0;
+                                    au.totalCost += at[index - 1] !== undefined && changeList[auindex].unit_price !== null ? ctx.helper.mul(changeList[auindex].unit_price, at[index - 1], renderData.tpUnit) : 0;
                                 }
                             }
                         }
@@ -638,8 +648,8 @@ module.exports = app => {
                     // await ctx.helper.saveStreamFile(stream, path.join(this.app.baseDir, dirName, fileName));
                     const fileInfo = path.parse(stream.filename);
                     const create_time = Date.parse(new Date()) / 1000;
-                    const filepath = `public/upload/change/fujian_${create_time + index.toString() + fileInfo.ext}`;
-                    await ctx.helper.saveStreamFile(stream, path.resolve(this.app.baseDir, 'app', filepath));
+                    const filepath = `app/public/upload/change/fujian_${create_time + index.toString() + fileInfo.ext}`;
+                    await ctx.helper.saveStreamFile(stream, path.resolve(this.app.baseDir, filepath));
                     await sendToWormhole(stream);
                     // 保存数据到att表
                     const fileData = {
@@ -683,7 +693,7 @@ module.exports = app => {
                 try {
                     const fileInfo = await ctx.service.changeAtt.getDataById(id);
                     if (fileInfo !== undefined && fileInfo !== '') {
-                        const fileName = path.join(this.app.baseDir, 'app', fileInfo.filepath);
+                        const fileName = path.join(this.app.baseDir, fileInfo.filepath);
                         // 解决中文无法下载问题
                         const userAgent = (ctx.request.header['user-agent'] || '').toLowerCase();
                         let disposition = '';
@@ -727,7 +737,7 @@ module.exports = app => {
                 const fileInfo = await ctx.service.changeAtt.getDataById(data.id);
                 if (fileInfo !== undefined && fileInfo !== '') {
                     // 先删除文件
-                    await fs.unlinkSync(path.join(this.app.baseDir, './app', fileInfo.filepath));
+                    await fs.unlinkSync(path.join(this.app.baseDir, fileInfo.filepath));
                     // 再删除数据库
                     await ctx.service.changeAtt.deleteById(data.id);
                     responseData.data = '';
@@ -749,6 +759,36 @@ module.exports = app => {
         }
 
         /**
+         * 查看附件
+         * @param {Object} ctx - egg全局变量
+         * @return {void}
+         */
+        async checkFile(ctx) {
+            const responseData = { err: 0, msg: '' };
+            const id = parseInt(ctx.params.id);
+            if (id) {
+                try {
+                    const fileInfo = await ctx.service.changeAtt.getDataById(id);
+                    if (fileInfo && Object.keys(fileInfo).length) {
+                        let filepath = fileInfo.filepath;
+                        if (!ctx.helper.canPreview(fileInfo.fileext)) {
+                            filepath = `/change/download/file/${fileInfo.id}`;
+                        } else {
+                            filepath = filepath.replace(/^app|\/app/, '');
+                        }
+                        fileInfo.filepath && (responseData.data = { filepath });
+                    }
+                } catch (error) {
+                    this.log(error);
+                    this.setMessage(error.toString(), this.messageType.ERROR);
+                    responseData.err = 1;
+                    responseData.msg = error.toString();
+                }
+            }
+            ctx.body = responseData;
+        }
+
+        /**
          * 删除变更令
          * @param {Object} ctx - egg全局变量
          * @return {void}

+ 43 - 2
app/controller/deal_bills_controller.js

@@ -18,6 +18,7 @@ const loadExcelType = {
     actual: 2,
 };
 const loadType = loadExcelType.display;
+const auditConst = require('../const/audit').ledger;
 
 module.exports = app => {
     class DealBillsController extends app.BaseController {
@@ -34,7 +35,10 @@ module.exports = app => {
                 data: [],
             };
             try {
-                responseData.data = await ctx.service.dealBills.getAllDataByCondition({ where: { tender_id: ctx.tender.id } });
+                responseData.data = await ctx.service.dealBills.getAllDataByCondition({
+                    where: { tender_id: ctx.tender.id },
+                    orders: [['order', 'asc']],
+                });
             } catch (error) {
                 this.log(error);
                 responseData.err = 1;
@@ -81,7 +85,10 @@ module.exports = app => {
                     }
                 }
 
-                const dealBills = await this.ctx.service.dealBills.getAllDataByCondition({ where: { tender_id: ctx.tender.id } });
+                const dealBills = await this.ctx.service.dealBills.getAllDataByCondition({
+                    where: { tender_id: ctx.tender.id },
+                    orders: [['order', 'asc']],
+                });
                 ctx.body = {err: 0, msg: '', data: dealBills};
             } catch (err) {
                 this.log(err);
@@ -137,6 +144,40 @@ module.exports = app => {
                 }
             }
         }
+
+        async _base(ctx, type, data) {
+            if (isNaN(data.id) || data.id <= 0) throw '数据错误';
+            if (type !== 'add') {
+                if (isNaN(data.count) || data.count <= 0) data.count = 1;
+            }
+            switch (type) {
+                case 'add':
+                    return await ctx.service.dealBills.addNodeBatch(ctx.tender.id, data.id, {}, data.count);
+                case 'delete':
+                    return await ctx.service.dealBills.delete(ctx.tender.id, data.id, data.count);
+            }
+        }
+
+        /**
+         * 更新清单相关 (Ajax)
+         * @param ctx
+         * @return {Promise<void>}
+         */
+        async update(ctx) {
+            try {
+                if (!ctx.tender.data) throw '标段数据错误';
+                if (ctx.tender.data.user_id !== ctx.session.sessionUser.accountId ||
+                    (ctx.tender.ledger_status === auditConst.status.checking || ctx.tender.ledger_status === auditConst.status.checked))
+                    throw '您无权进行该操作';
+                const data = JSON.parse(ctx.request.body.data);
+                const result = await ctx.service.dealBills.updateDatas(data);
+                ctx.body = { err: 0, msg: '', data: result };
+            } catch (err) {
+                this.log(err);
+                ctx.body = this.ajaxErrorBody(err);
+            }
+
+        }
     }
 
     return DealBillsController;

+ 2 - 5
app/controller/ledger_audit_controller.js

@@ -203,13 +203,10 @@ module.exports = app => {
 
                 await ctx.service.ledgerAudit.start(ctx.tender.id, ctx.tender.data.ledger_times);
 
-                // ctx.body = { err: 0, msg: '', data: { url: '/tender/' + ctx.tender.id + '/ledger' } };
-                ctx.redirect('/tender/' + ctx.tender.id + '/ledger');
+                ctx.body = { err: 0, msg: '', data: { url: '/tender/' + ctx.tender.id + '/ledger' } };
             } catch (err) {
                 this.log(err);
-                // ctx.body = this.ajaxErrorBody(err, '上报失败,请刷新页面重试');
-                ctx.session.postError = err.toString();
-                ctx.redirect(ctx.request.header.referer);
+                ctx.body = this.ajaxErrorBody(err, '上报失败,请刷新页面重试');
             }
         }
 

+ 150 - 0
app/controller/material_controller.js

@@ -248,6 +248,7 @@ module.exports = app => {
             const monthsList = [];
             if (ctx.material.months) {
                 const material_month = ctx.material.months.split(',');
+                material_month.sort();
                 const materialMonthList = await ctx.service.materialMonth.getListByMid(ctx.material.id);
                 for (const mbd of materialBillsData) {
                     const one_mb = {
@@ -268,6 +269,40 @@ module.exports = app => {
         }
 
         /**
+         * 检查旧数据是否存在指数法调差数据,没有自动添加定值
+         * @param ctx
+         * @return {Promise<void>}
+         * @private
+         */
+        async _checkExponentExist(ctx) {
+            const material_exponent_constant = await ctx.service.materialExponent.getDataByCondition({
+                tid: ctx.tender.id,
+                type: materialConst.ex_type[0].value,
+            });
+            // 不存在则生成指数调差清单表定值和历史期定值(防止变更定值发生变化)
+            if (!material_exponent_constant) {
+                await ctx.service.materialExponent.addOldData();
+            }
+        }
+
+        /**
+         * 获取当前期指数列表信息
+         * @param ctx
+         * @return {Promise<void>}
+         * @private
+         */
+        async _getMaterialExponentData(ctx) {
+            // 根据期判断需要获取的工料信息值
+            const searchsql = { tid: ctx.tender.id };
+            if (ctx.material.highOrder !== ctx.material.order) {
+                const midList = await ctx.service.material.getPreMidList(ctx.tender.id, ctx.material.order);
+                searchsql.mid = midList;
+            }
+            // 取所有工料表
+            return await ctx.service.materialExponent.getAllDataByCondition({ where: searchsql });
+        }
+
+        /**
          * 调差工料页面 (Get)
          * @param {Object} ctx - egg全局变量
          * @return {Promise<void>}
@@ -296,6 +331,7 @@ module.exports = app => {
 
                 // 取当前期截止上期含税金额
                 renderData.pre_tp_hs = await ctx.service.material.getPreTpHs(ctx.tender.id, ctx.material.order);
+                renderData.ex_pre_tp_hs = await ctx.service.material.getExPreTpHs(ctx.tender.id, ctx.material.order);
 
                 renderData.months = ctx.material.months ? ctx.material.months.split(',') : [];
                 renderData.monthsList = await this._getMaterialMonthsData(ctx, renderData.materialBillsData);
@@ -346,6 +382,58 @@ module.exports = app => {
         }
 
         /**
+         * 指数调差页面 (Get)
+         * @param {Object} ctx - egg全局变量
+         * @return {Promise<void>}
+         */
+        async exponent(ctx) {
+            try {
+                await this._checkExponentExist(ctx);
+                await this._getMaterialAuditViewData(ctx);
+                const renderData = await this._getDefaultRenderData(ctx);
+                const stage_list = await ctx.service.stage.getStageMsgByStageId(ctx.material.stage_id);
+                renderData.ex_calc = materialConst.ex_calc;
+                if (!ctx.material.ex_calc) {
+                    const calcBase = await ctx.service.stage.getMaterialCalcBase(stage_list, ctx.tender.info);
+                    for (const bq of renderData.ex_calc) {
+                        const calc = _.find(calcBase, function(item) {
+                            return bq.code === item.code;
+                        });
+                        bq.value = calc.value;
+                        if (calc.code === 'bqwc') {
+                            ctx.material.ex_expr = calc.value.toString() + '*[0-1]';
+                        }
+                    }
+                    // 并更新至调差表中
+                    await ctx.service.material.update({ ex_calc: JSON.stringify(renderData.ex_calc), ex_expr: ctx.material.ex_expr }, { id: ctx.material.id });
+                } else {
+                    renderData.ex_calc = JSON.parse(ctx.material.ex_calc);
+                }
+
+                renderData.materialExponentData = await this._getMaterialExponentData(ctx);
+                // 取对应期的截取上期的调差金额和应耗数量
+                if (ctx.material.highOrder !== ctx.material.order) {
+                    for (const [mindex, me] of renderData.materialExponentData.entries()) {
+                        const result = await ctx.service.materialExponentHistory.getByMeId(ctx.material.id, ctx.material.order, me.id);
+                        _.forEach(result, function(value, key) {
+                            renderData.materialExponentData[mindex][key] = result ? result[key] : null;
+                        });
+                    }
+                }
+
+                // 取当前期截止上期含税金额
+                renderData.pre_tp_hs = await ctx.service.material.getPreTpHs(ctx.tender.id, ctx.material.order);
+                renderData.ex_pre_tp_hs = await ctx.service.material.getExPreTpHs(ctx.tender.id, ctx.material.order);
+                renderData.materialType = materialConst;
+                renderData.jsFiles = this.app.jsFiles.common.concat(this.app.jsFiles.material.exponent);
+                await this.layout('material/exponent.ejs', renderData, 'material/exponent_modal.ejs');
+            } catch (err) {
+                this.log(err);
+                ctx.redirect('/tender/' + ctx.tender.id + '/measure/material');
+            }
+        }
+
+        /**
          * 附件页面 (Get)
          * @param {Object} ctx - egg全局变量
          * @return {Promise<void>}
@@ -561,6 +649,68 @@ module.exports = app => {
             }
         }
 
+        /**
+         * 指数调差 - 编辑指数清单项 (Ajax)
+         * @param ctx
+         * @return {Promise<void>}
+         */
+        async saveExponentData(ctx) {
+            try {
+                const data = JSON.parse(ctx.request.body.data);
+                const responseData = {
+                    err: 0,
+                    msg: '',
+                    data: {},
+                };
+                let ex_tp = ctx.material.ex_tp;
+                let ex_expr = ctx.material.ex_expr;
+                switch (data.type) {
+                    case 'add':
+                        responseData.data = await ctx.service.materialExponent.add();
+                        break;
+                    case 'del':
+                        [ex_tp, ex_expr] = await ctx.service.materialExponent.del(data.id);
+                        responseData.data.ex_tp = ex_tp;
+                        responseData.data.ex_expr = ex_expr;
+                        break;
+                    case 'update':
+                        [ex_tp, ex_expr] = await ctx.service.materialExponent.save(data.updateData);
+                        responseData.data.ex_tp = ex_tp;
+                        responseData.data.ex_expr = ex_expr;
+                        break;
+                    case 'rate':
+                        // 判断数量是否为数字
+                        if (isNaN(data.rate)) {
+                            throw '不能输入其它非数字类型字符';
+                        }
+                        if (ctx.material.readOnly) {
+                            throw '无权操作';
+                        }
+                        await ctx.service.material.changeRate(data.rate);
+                        break;
+                    case 'paste':
+                        [ex_tp, ex_expr] = await ctx.service.materialExponent.saveDatas(data.updateData);
+                        responseData.data.ex_tp = ex_tp;
+                        responseData.data.ex_expr = ex_expr;
+                        // 取所有指数清单
+                        responseData.data.info = await this._getMaterialExponentData(ctx);
+                        break;
+                    case 'ex_calc':
+                        // 判断数量是否为数字
+                        [ex_tp, ex_expr] = await ctx.service.material.changeExCalc(data.updateData);
+                        responseData.data.ex_tp = ex_tp;
+                        responseData.data.ex_expr = ex_expr;
+                        break;
+                    default: throw '参数有误';
+                }
+
+                ctx.body = responseData;
+            } catch (err) {
+                this.log(err);
+                ctx.body = { err: 1, msg: err.toString(), data: null };
+            }
+        }
+
         // 审批相关
         /**
          * 添加审批人

+ 5 - 0
app/controller/profile_controller.js

@@ -14,6 +14,7 @@ const smsTypeConst = require('../const/sms_type');
 const qr = require('qr-image');
 const path = require('path');
 const sendToWormhole = require('stream-wormhole');
+const loginWay = require('../const/setting').loginWay;
 
 module.exports = app => {
 
@@ -359,9 +360,13 @@ module.exports = app => {
             const passwordRule = ctx.service.projectAccount.rule('modifyPassword');
             const passwordJsValidator = await this.jsValidator.convert(passwordRule).setSelector('#password-form').build();
 
+            // 获取登录日志
+            const loginLogging = await ctx.service.loginLogging.getLoginLogs(ctx.session.sessionProject.id, ctx.session.sessionUser.accountId);
             const renderData = {
                 accountData,
                 passwordJsValidator,
+                loginLogging,
+                loginWay,
             };
             await this.layout('profile/safe.ejs', renderData);
         }

+ 22 - 2
app/controller/report_controller.js

@@ -280,7 +280,7 @@ module.exports = app => {
             for (const page of pageRst.items) {
                 page[JV.PROP_WATERMARK_CELLS] = [];
             }
-            if (params.stage_status !== 3) {
+            if (params.stage_status !== 3 && params.closeWatermark === 0) {
                 // 加水印
                 fillWaterMark([pageRst]);
             }
@@ -380,7 +380,7 @@ module.exports = app => {
                     page[JV.PROP_WATERMARK_CELLS] = [];
                 }
             }
-            if (params.stage_status !== 3) {
+            if (params.stage_status !== 3 && params.closeWatermark === 0) {
                 // 加水印
                 fillWaterMark(pageRstArr);
             }
@@ -435,6 +435,17 @@ module.exports = app => {
             const roleRelArr = (params.stage_status === 3) ? (await ctx.service.roleRptRel.getRoleRptRelByDetailIds(params.tender_id, params.rpt_ids, params.stage_id)) : [];
             // const roleRel = (params.stage_status === 3) ? (await ctx.service.roleRptRel.getRoleRptRelByDetailIds(params.tender_id, params.rpt_tpl_id)) : [];
             const pageRstArr = await getMultiRptsCommon(ctx, params, JV.OUTPUT_TYPE_NORMAL, this.app.baseDir);
+            // console.log('params.stage_status: ' + params.stage_status);
+            for (const pageRst of pageRstArr) {
+                for (const page of pageRst.items) {
+                    page[JV.PROP_WATERMARK_CELLS] = [];
+                }
+            }
+            if (params.stage_status !== 3 && params.closeWatermark === 0) {
+                // 加水印(注意:还得看用户设置是否需要加水印)
+                fillWaterMark(pageRstArr);
+            }
+
             await this.ctx.helper.recursiveMkdirSync(this.app.baseDir + '/app/public/download');
 
             const runnableRst = [];
@@ -472,6 +483,15 @@ module.exports = app => {
             }
             const roleRelArr = (params.stage_status === 3) ? (await ctx.service.roleRptRel.getRoleRptRelByDetailIds(params.tender_id, params.rpt_ids, params.stage_id)) : [];
             const pageRstArr = await getMultiRptsCommon(ctx, params, JV.OUTPUT_TYPE_NORMAL, this.app.baseDir);
+            for (const pageRst of pageRstArr) {
+                for (const page of pageRst.items) {
+                    page[JV.PROP_WATERMARK_CELLS] = [];
+                }
+            }
+            if (params.stage_status !== 3 && params.closeWatermark === 0) {
+                // 加水印
+                fillWaterMark(pageRstArr);
+            }
             await this.ctx.helper.recursiveMkdirSync(this.app.baseDir + '/app/public/download');
             const runnableRst = [];
             let rptRoleRelArr = [];

+ 2 - 5
app/controller/revise_controller.js

@@ -843,13 +843,10 @@ module.exports = app => {
                 if (revise.uid !== ctx.session.sessionUser.accountId) throw '上报失败';
 
                 await ctx.service.reviseAudit.start(revise, revise.times);
-                ctx.redirect('/tender/' + ctx.tender.id + '/revise/info');
-                // ctx.body = { err: 0, msg: '', data: {} };
+                ctx.body = { err: 0, msg: '', data: {} };
             } catch (err) {
                 this.log(err);
-                // ctx.body = this.ajaxErrorBody(err, '上报失败');
-                this.postError(err, '审批失败');
-                ctx.redirect('/tender/' + ctx.tender.id + '/revise/info');
+                ctx.body = this.ajaxErrorBody(err, '上报失败');
             }
         }
         /**

+ 11 - 7
app/controller/stage_controller.js

@@ -439,8 +439,12 @@ module.exports = app => {
                 }
                 const bills = data.bills ? data.bills : await ctx.service.ledger.getDataById(data.pos.lid);
                 const pos = data.pos;
-                const changes = await ctx.service.change.getValidChanges(ctx.tender.id, bills);
-                const useChanges = await ctx.service.stageChange.getLastestStageData(ctx.tender.id, ctx.stage.id, bills.id, pos ? pos.id : -1);
+                const changes = ctx.stage.readOnly
+                    ? await ctx.service.change.getAuditValidChanges(ctx.tender.id, bills, null, ctx.stage.curTimes, ctx.stage.curOrder)
+                    : await ctx.service.change.getValidChanges(ctx.tender.id, bills);
+                const useChanges = ctx.stage.readOnly
+                    ? await ctx.service.stageChange.getAuditorStageData(ctx.tender.id, ctx.stage.id, ctx.stage.curTimes, ctx.stage.curOrder, bills.id, pos ? pos.id : -1)
+                    : await ctx.service.stageChange.getLastestStageData(ctx.tender.id, ctx.stage.id, bills.id, pos ? pos.id : -1);
                 ctx.body = { err: 0, msg: '', data: { changes, useChanges } };
             } catch (err) {
                 this.log(err);
@@ -1304,8 +1308,8 @@ module.exports = app => {
                     // }
                     const fileInfo = path.parse(stream.filename);
                     const create_time = Date.parse(new Date()) / 1000;
-                    const filepath = `public/upload/${this.ctx.tender.id}/stage/fujian_${create_time + index.toString() + fileInfo.ext}`;
-                    await ctx.helper.saveStreamFile(stream, path.resolve(this.app.baseDir, 'app', filepath));
+                    const filepath = `app/public/upload/${this.ctx.tender.id}/stage/fujian_${create_time + index.toString() + fileInfo.ext}`;
+                    await ctx.helper.saveStreamFile(stream, path.resolve(this.app.baseDir, filepath));
                     // console.log(await fs.existsSync(path.resolve(this.app.baseDir, 'app', filepath)));
                     // const fileInfo = path.parse(stream.filename);
                     // const fileName = 'stage' + create_time + '_' + index + fileInfo.ext;
@@ -1363,7 +1367,7 @@ module.exports = app => {
                 try {
                     const fileInfo = await ctx.service.stageAtt.getDataById(id);
                     if (fileInfo !== undefined && fileInfo !== '') {
-                        const fileName = path.join(this.app.baseDir, 'app', fileInfo.filepath);
+                        const fileName = path.join(this.app.baseDir, fileInfo.filepath);
                         // 解决中文无法下载问题
                         const userAgent = (ctx.request.header['user-agent'] || '').toLowerCase();
                         let disposition = '';
@@ -1407,7 +1411,7 @@ module.exports = app => {
                         if (!ctx.helper.canPreview(fileInfo.fileext)) {
                             filepath = `/tender/${ctx.tender.id}/measure/stage/${ctx.params.order}/download/file/${fileInfo.id}`;
                         } else {
-                            filepath = '/' + filepath;
+                            filepath = filepath.replace(/^app|\/app/, '');
                         }
                         fileInfo.filepath && (responseData.data = { filepath });
                     }
@@ -1439,7 +1443,7 @@ module.exports = app => {
                 const fileInfo = await ctx.service.stageAtt.getDataById(data.id);
                 if (fileInfo !== undefined && fileInfo !== '') {
                     // 先删除文件
-                    await fs.unlinkSync(path.join(this.app.baseDir, './app', fileInfo.filepath));
+                    await fs.unlinkSync(path.join(this.app.baseDir, fileInfo.filepath));
                     // 再删除数据库
                     await ctx.service.stageAtt.deleteById(data.id);
                     responseData.data = '';

+ 95 - 51
app/controller/tender_controller.js

@@ -31,6 +31,82 @@ module.exports = app => {
             ctx.showTitle = true;
         }
 
+        async _getLedgerAuditInfo (tender) {
+            tender.cur_flow = {
+                title: '台账',
+                status: auditConst.ledger.tiStatusString[tender.ledger_status],
+                status_class: auditConst.ledger.tiStatusStringClass[tender.ledger_status],
+            };
+            if (tender.ledger_status === auditConst.ledger.status.uncheck) {
+                tender.cur_flow.name = tender.user_name;
+            } else {
+                const cur = tender.ledger_status === auditConst.ledger.status.checkNo
+                    ? await this.ctx.service.ledgerAudit.getLastestAuditor(tender.id, tender.ledger_times - 1, auditConst.ledger.status.checkNo)
+                    : await this.ctx.service.ledgerAudit.getLastestAuditor(tender.id, tender.ledger_times, tender.ledger_status);
+                if (cur) {
+                    tender.cur_flow.name = cur.name;
+                    if (cur.audit_order === 1) {
+                        tender.pre_flow = { name: tender.user_name, time: cur.begin_time };
+                    } else {
+                        const pre = await this.ctx.service.ledgerAudit.getAuditorByOrder(tender.id, cur.audit_order - 1, cur.times);
+                        if (pre) tender.pre_flow = { name: pre.name, time: pre.end_time };
+                    }
+                } else {
+                    tender.cur_flow.name = '';
+                }
+            }
+        }
+
+        async _getStageAuditInfo (tender, stage) {
+            tender.cur_flow = {
+                title: '第' + stage.order + '期',
+                status: auditConst.stage.tiStatusString[stage.status],
+                status_class: auditConst.stage.tiStatusStringClass[stage.status],
+            };
+            if (stage.status === auditConst.stage.status.uncheck) {
+                if (tender.user_id === stage.user_id) {
+                    tender.cur_flow.name = tender.user_name;
+                } else {
+                    const user = await this.ctx.service.projectAccount.getDataById(stage.user_id);
+                    tender.cur_flow.name = user.name;
+                }
+                if (stage.order > 1) {
+                    const preStage = await this.ctx.service.stage.getDataByCondition({ tid: tender.id, order: stage.order - 1});
+                    if (!preStage) return;
+
+                    const pre = await this.ctx.service.stageAudit.getLastestAuditor(preStage.id, preStage.times, auditConst.stage.status.checked);
+                    if (pre) tender.pre_flow = { name: pre.name, time: pre.end_time};
+                }
+            } else {
+                let cur;
+                if (stage.status === auditConst.stage.status.checkNo) {
+                    cur = await this.ctx.service.stageAudit.getLastestAuditor(stage.id, stage.times - 1, auditConst.stage.status.checkNo);
+                } else if (stage.status === auditConst.stage.status.checked) {
+                    cur = await this.ctx.service.stageAudit.getLastestAuditor(stage.id, stage.times, auditConst.stage.status.checked);
+                } else {
+                    cur = await this.ctx.service.stageAudit.getCurAuditor(stage.id, stage.times);
+                }
+                if (cur) {
+                    tender.cur_flow.name = cur.name;
+                    if (cur.order === 1) {
+                        tender.pre_flow = {};
+                        if (tender.user_id === stage.user_id) {
+                            tender.pre_flow.name = tender.user_name;
+                        } else {
+                            const user = await this.ctx.service.projectAccount.getDataById(stage.user_id);
+                            tender.pre_flow.name = user.name;
+                        }
+                        tender.pre_flow.time = cur.begin_time;
+                    } else {
+                        const pre = await this.ctx.service.stageAudit.getAuditorByOrder(stage.id, cur.order - 1, cur.times);
+                        if (pre) tender.pre_flow = { name: pre.name, time: pre.end_time };
+                    }
+                } else {
+                    tender.cur_flow.name = '';
+                }
+            }
+        }
+
         async _listDetail(view, modal = '') {
             try {
                 // 获取用户新建标段权利
@@ -40,62 +116,31 @@ module.exports = app => {
 
                 const tenderList = await this.ctx.service.tender.getList('', userPermission);
 
-                // for (const t of tenderList) {
-                //     if (t.user_id === this.ctx.session.sessionUser.accountId && (
-                //         t.ledger_status === auditConst.ledger.status.checkNo || t.ledger_status === auditConst.ledger.status.uncheck)) {
-                //         const sum = await this.ctx.service.ledger.addUp({ tender_id: t.id/* , is_leaf: true*/ });
-                //         t.total_price = sum.total_price;
-                //         t.deal_tp = sum.deal_tp;
-                //     }
-                //     if (t.ledger_status === auditConst.ledger.status.checked) {
-                //         t.lastStage = await this.ctx.service.stage.getLastestStage(t.id, true);
-
-                //         if (!t.lastStage) continue;
-                //         if (t.lastStage.status === auditConst.stage.status.uncheck &&
-                //             t.lastStage.user_id !== this.ctx.session.sessionUser.accountId) {
-                //             t.lastStage = await this.ctx.service.stage.getLastestStage(t.id);
-                //         }
-
-                //         if (!t.lastStage) continue;
-                //         await this.ctx.service.stage.checkStageGatherData(t.lastStage);
-                //     }
-                //     if (t.lastStage) {
-                //         if (t.lastStage.status === auditConst.stage.status.uncheck) {
-                //             const status_name = await this.ctx.service.projectAccount.getAccountInfoById(t.lastStage.user_id);
-                //             t.status_users = status_name ? status_name.name : '';
-                //         } else {
-                //             const status_name = await this.ctx.service.stageAudit.getAuditorByStatus(t.lastStage.id, t.lastStage.status, t.lastStage.times);
-                //             t.status_users = status_name ? status_name.name : '';
-                //         }
-                //     } else {
-                //         if (t.ledger_status !== auditConst.ledger.status.uncheck) {
-                //             const status_name = await this.ctx.service.ledgerAudit.getStatusName(t.id, t.ledger_times);
-                //             t.status_users = status_name ? status_name.name : '';
-                //         }
-                //     }
-                // }
-                tenderList.forEach(async t => {
+                for (const t of tenderList) {
+                    if (t.user_id === this.ctx.session.sessionUser.accountId && (
+                            t.ledger_status === auditConst.ledger.status.checkNo || t.ledger_status === auditConst.ledger.status.uncheck)) {
+                        const sum = await this.ctx.service.ledger.addUp({ tender_id: t.id/* , is_leaf: true*/ });
+                        t.total_price = sum.total_price;
+                        t.deal_tp = sum.deal_tp;
+                    }
+                    t.advance_tp = await this.ctx.service.advance.getSumAdvance(t.id);
+
                     if (t.ledger_status === auditConst.ledger.status.checked) {
                         t.lastStage = await this.ctx.service.stage.getLastestStage(t.id, true);
+                        if (t.lastStage && t.lastStage.status === auditConst.stage.status.uncheck &&
+                            t.lastStage.user_id !== this.ctx.session.sessionUser.accountId) {
+                            t.lastStage = await this.ctx.service.stage.getLastestStage(t.id);
+                        }
+                        if (t.lastStage) await this.ctx.service.stage.checkStageGatherData(t.lastStage);
                         t.completeStage = await this.ctx.service.stage.getLastestCompleteStage(t.id);
                     }
+
                     if (t.lastStage) {
-                        if (t.lastStage.status === auditConst.stage.status.uncheck) {
-                            const status_name = await this.ctx.service.projectAccount.getAccountInfoById(t.lastStage.user_id);
-                            t.status_users = status_name ? status_name.name : '';
-                            // const status_name = await this.ctx.service.stageAudit.getStatusName(t.lastStage.id, t.lastStage.times - 1);
-                            // t.status_users = status_name ? status_name.name : '';
-                        } else {
-                            const status_name = await this.ctx.service.stageAudit.getAuditorByStatus(t.lastStage.id, t.lastStage.status, t.lastStage.times);
-                            t.status_users = status_name ? status_name.name : '';
-                        }
+                        await this._getStageAuditInfo(t, t.lastStage);
                     } else {
-                        if (t.ledger_status !== auditConst.ledger.status.uncheck) {
-                            const status_name = await this.ctx.service.ledgerAudit.getStatusName(t.id, t.ledger_times);
-                            t.status_users = status_name ? status_name.name : '';
-                        }
+                        await this._getLedgerAuditInfo(t);
                     }
-                });
+                }
                 const categoryData = await this.ctx.service.category.getAllCategory(this.ctx.session.sessionProject.id);
                 const valuations = await this.ctx.service.valuation.getProjectValidValuation(this.ctx.session.sessionProject.id);
                 const renderData = {
@@ -155,7 +200,6 @@ module.exports = app => {
                 renderData.pid = this.ctx.session.sessionProject.id;
                 await this.layout(view, renderData, modal);
             } catch (err) {
-                console.log('error', err);
                 this.log(err);
                 this.ctx.redirect('/dashboard');
             }
@@ -445,7 +489,7 @@ module.exports = app => {
                 ctx.body = { err: 0, msg: '', data: JSON.parse(ctx.request.body.data) };
             } catch (err) {
                 this.log(err);
-                ctx.body = { err: 1, msg: err.toString(), data: null };
+                ctx.body = this.ajaxErrorBody(err, '保存标段设置失败');
             }
         }
 

+ 24 - 24
app/extend/helper.js

@@ -987,30 +987,30 @@ module.exports = {
     },
 
     async sendAliSms(userId, type, judge, code, data = {}) {
-        const mobiles = [];
-        if (!userId || (userId instanceof Array && userId.length === 0)) return;
-        const smsUser = await this.ctx.service.projectAccount.getAllDataByCondition({ where: { id: userId } });
-        for (const su of smsUser) {
-            if (!su.auth_mobile || su.auth_mobile === '') continue;
-            if (!su.sms_type || su.sms_type === '') continue;
-
-            const smsType = JSON.parse(su.sms_type);
-            if (smsType[type] && smsType[type].indexOf(judge) !== -1) {
-                mobiles.push(su.auth_mobile);
-            }
-        }
-
-        if (mobiles.length > 0) {
-            const sms = new SMS(this.ctx);
-            const tenderName = await sms.contentChange(this.ctx.tender.data.name);
-            const projectName = await sms.contentChange(this.ctx.tender.info.deal_info.buildName);
-            const param = {
-                project: projectName,
-                number: tenderName,
-            };
-            const postParam = Object.assign(param, data);
-            sms.aliSend(mobiles, postParam, code);
-        }
+        // const mobiles = [];
+        // if (!userId || (userId instanceof Array && userId.length === 0)) return;
+        // const smsUser = await this.ctx.service.projectAccount.getAllDataByCondition({ where: { id: userId } });
+        // for (const su of smsUser) {
+        //     if (!su.auth_mobile || su.auth_mobile === '') continue;
+        //     if (!su.sms_type || su.sms_type === '') continue;
+        //
+        //     const smsType = JSON.parse(su.sms_type);
+        //     if (smsType[type] && smsType[type].indexOf(judge) !== -1) {
+        //         mobiles.push(su.auth_mobile);
+        //     }
+        // }
+        //
+        // if (mobiles.length > 0) {
+        //     const sms = new SMS(this.ctx);
+        //     const tenderName = await sms.contentChange(this.ctx.tender.data.name);
+        //     const projectName = await sms.contentChange(this.ctx.tender.info.deal_info.buildName);
+        //     const param = {
+        //         project: projectName,
+        //         number: tenderName,
+        //     };
+        //     const postParam = Object.assign(param, data);
+        //     sms.aliSend(mobiles, postParam, code);
+        // }
     },
 
 

+ 1 - 1
app/lib/stage_im.js

@@ -859,7 +859,7 @@ class StageIm {
                     end_tp: p.end_gather_tp, end_contract_tp: p.end_contract_tp, end_qc_tp: p.end_qc_tp,
                     bw,
                     peg: peg ? this._getPegStr(peg.name) : '',
-                    xm: node.name, jldy: node.name,
+                    xm: node.name,
                     drawing_code: this._getDrawingCode(p),
                     changes: [],
                     position: '',

+ 0 - 3
app/middleware/session_auth.js

@@ -47,7 +47,6 @@ module.exports = options => {
             path && (this.curListUrl = path);
 
         } catch (error) {
-            console.log(error);
             if (this.helper.isAjax(this.request)) {
                 return this.body = {
                     err: 2,
@@ -71,8 +70,6 @@ module.exports = options => {
                 message: '登录信息异常,请重新登录',
             };
             return this.redirect('/login?referer=' + this.url);
-
-
         }
         yield next;
     };

+ 35 - 41
app/middleware/tender_check.js

@@ -43,51 +43,38 @@ module.exports = options => {
                 tender.data.ledger_times = 1;
             }
             if (tender.data.project_id !== this.session.sessionProject.id) {
-
                 throw '您无权查看该项目';
-            } else {
-                const accountId = this.session.sessionUser.accountId;
-                const advanceAuditors = yield this.service.advanceAudit.getAllAuditors(tender.id);
-                const advanceAuditorsId = this.helper._.map(advanceAuditors, 'audit_id');
-                if (tender.data.ledger_status === auditConst.status.uncheck) {
-                    if (tender.data.user_id !== accountId && advanceAuditorsId.indexOf(accountId) === -1) {
-                        throw '您无权查看该项目';
-                    } else if (advanceAuditorsId.indexOf(accountId) !== -1) {
-                        if (!(this.url === '/tender/' + this.params.id || this.url.indexOf('/advance') !== -1)) {
-                            throw '您无权查看该内容';
-                        }
-                    }
-                } else {
-                    const times = tender.data.ledger_status === auditConst.status.checkNo ? tender.data.ledger_times - 1 : tender.data.ledger_times;
-                    const auditors = yield this.service.ledgerAudit.getAuditors(tender.id, times);
-                    const auditorsId = this.helper._.map(auditors, 'audit_id');
-                    const stageAuditors = yield this.service.stageAudit.getAllAuditors(tender.id);
-                    const stageAuditorsId = this.helper._.map(stageAuditors, 'aid');
-                    const changeAuditors = yield this.service.changeAudit.getAllAuditors(tender.id);
-                    const changeAuditorsId = this.helper._.map(changeAuditors, 'uid');
-                    const reviseAuditors = yield this.service.reviseAudit.getAllAuditors(tender.id);
-                    const reviseAuditorsId = this.helper._.map(reviseAuditors, 'audit_id');
-                    const materialAuditors = yield this.service.materialAudit.getAllAuditors(tender.id);
-                    const materialAuditorsId = this.helper._.map(materialAuditors, 'aid');
-                    // const advanceAuditors = yield this.service.advanceAudit.getAllAuditors(tender.id);
-                    // const advanceAuditorsId = this.helper._.map(advanceAuditors, 'audit_id');
-                    const tenderPermission = this.session.sessionUser.permission ? this.session.sessionUser.permission.tender : null;
-                    if (auditorsId.indexOf(accountId) === -1 && tender.data.user_id !== accountId &&
-                        (tenderPermission === null || tenderPermission === undefined || tenderPermission.indexOf('2') === -1) &&
-                        stageAuditorsId.indexOf(accountId) === -1 && changeAuditorsId.indexOf(accountId) === -1 &&
-                        reviseAuditorsId.indexOf(accountId) === -1 && materialAuditorsId.indexOf(accountId) === -1 &&
-                        advanceAuditorsId.indexOf(accountId) === -1) {
-                        throw '您无权查看该项目';
-                    }
-                }
             }
+            const accountId = this.session.sessionUser.accountId;
+            const advanceAuditors = yield this.service.advanceAudit.getAllAuditors(tender.id);
+            const advanceAuditorsId = this.helper._.map(advanceAuditors, 'audit_id');
+            const times = tender.data.ledger_status === auditConst.status.checkNo ? tender.data.ledger_times - 1 : tender.data.ledger_times;
+            const auditors = yield this.service.ledgerAudit.getAuditors(tender.id, times);
+            const auditorsId = this.helper._.map(auditors, 'audit_id');
+            const stageAuditors = yield this.service.stageAudit.getAllAuditors(tender.id);
+            const stageAuditorsId = this.helper._.map(stageAuditors, 'aid');
+            const changeAuditors = yield this.service.changeAudit.getAllAuditors(tender.id);
+            const changeAuditorsId = this.helper._.map(changeAuditors, 'uid');
+            const reviseAuditors = yield this.service.reviseAudit.getAllAuditors(tender.id);
+            const reviseAuditorsId = this.helper._.map(reviseAuditors, 'audit_id');
+            const materialAuditors = yield this.service.materialAudit.getAllAuditors(tender.id);
+            const materialAuditorsId = this.helper._.map(materialAuditors, 'aid');
+            const tenderPermission = this.session.sessionUser.permission ? this.session.sessionUser.permission.tender : null;
+            if (auditorsId.indexOf(accountId) === -1 && tender.data.user_id !== accountId &&
+                (tenderPermission === null || tenderPermission === undefined || tenderPermission.indexOf('2') === -1) &&
+                stageAuditorsId.indexOf(accountId) === -1 && changeAuditorsId.indexOf(accountId) === -1 &&
+                reviseAuditorsId.indexOf(accountId) === -1 && materialAuditorsId.indexOf(accountId) === -1 &&
+                advanceAuditorsId.indexOf(accountId) === -1) {
+                throw '您无权查看该项目';
+            }
+
             tender.ledgerReadOnly = this.session.sessionUser.accountId !== tender.data.user_id ||
                 tender.data.ledger_status === auditConst.status.checking || tender.data.ledger_status === auditConst.status.checked;
+            tender.advanceAuditorsId = advanceAuditorsId;
             this.tender = tender;
             this.session.sessionProject.page_show = yield this.service.project.getPageshow(this.session.sessionProject.id);
             yield next;
         } catch (err) {
-            console.log(err);
             // 输出错误到日志
             if (err.stack) {
                 this.logger.error(err);
@@ -104,11 +91,18 @@ module.exports = options => {
                     body: this.session.body,
                 }));
             }
-            // 重定向值标段管理
-            if (this.helper.isWap(this.request)) {
-                this.redirect('/wap/list');
+            if (this.helper.isAjax(this.request)) {
+                if (err.stack) {
+                    this.body = {err: 2, msg: '标段数据未知错误', data: null};
+                } else {
+                    this.body = {err: 1, msg: err.toString(), data: null};
+                }
             } else {
-                err === '您无权查看该内容' ? this.redirect(this.request.headers.referer) : this.redirect('/list');
+                if (this.helper.isWap(this.request)) {
+                    this.redirect('/wap/list');
+                } else {
+                    err === '您无权查看该内容' ? this.redirect(this.request.headers.referer) : this.redirect('/list');
+                }
             }
         }
     };

+ 65 - 0
app/middleware/uncheck_tender_check.js

@@ -0,0 +1,65 @@
+'use strict';
+
+/**
+ *
+ *
+ * @author Mai
+ * @date
+ * @version
+ */
+
+const auditConst = require('../const/audit').ledger;
+const messageType = require('../const/message_type');
+
+module.exports = options => {
+    /**
+     * 标段校验 中间件
+     * 1. 读取标段数据(包括属性)
+     * 2. 检验用户是否可见标段(不校验具体权限)
+     *
+     * @param {function} next - 中间件继续执行的方法
+     * @return {void}
+     */
+    return function* uncheckTenderCheck(next) {
+        try {
+            if (this.tender.data.ledger_status === auditConst.status.uncheck) {
+                if (this.tender.data.user_id !== this.session.sessionUser.accountId && this.tender.advanceAuditorsId.indexOf(this.session.sessionUser.accountId) === -1) {
+                    throw '您无权查看该项目';
+                } else if (this.tender.advanceAuditorsId.indexOf(this.session.sessionUser.accountId) !== -1) {
+                    throw '您无权查看该内容';
+                }
+            }
+            yield next;
+        } catch (err) {
+            // 输出错误到日志
+            if (err.stack) {
+                this.logger.error(err);
+            } else {
+                this.session.message = {
+                    type: messageType.ERROR,
+                    icon: 'exclamation-circle',
+                    message: err,
+                };
+                this.getLogger('fail').info(JSON.stringify({
+                    error: err,
+                    project: this.session.sessionProject,
+                    user: this.session.sessionUser,
+                    body: this.session.body,
+                }));
+            }
+            if (this.helper.isAjax(this.request)) {
+                if (err.stack) {
+                    this.body = {err: 2, msg: '标段数据未知错误', data: null};
+                } else {
+                    this.body = {err: 1, msg: err.toString(), data: null};
+                }
+            } else {
+                if (this.helper.isWap(this.request)) {
+                    this.redirect('/wap/list');
+                } else {
+                    err === '您无权查看该内容' ? this.redirect(this.request.headers.referer) : this.redirect('/list');
+                }
+            }
+        }
+    };
+};

+ 12 - 5
app/public/css/main.css

@@ -3,7 +3,11 @@
 body {
     font-size: 0.9rem;
     overflow: hidden;
-    background: #e4e7ea
+    background: #e4e7ea;
+
+}
+.text-warning {
+  color: #da9500 !important;
 }
 .dropdown-menu {
     font-size: 12px
@@ -1119,17 +1123,20 @@ a.maintain-icon:hover .fa{
   margin: 0 0 0 70px;
   word-break: break-word;
 }
-
 .book-list{
   padding: 0;
-  margin: 0;
-  height: 285px;
-  overflow-y: auto;
+margin: 0;
+height: 285px;
+overflow-y: auto;
 }
 .book-list dt{
   padding:5px 0 5px 5px;
   background-color: #f2f2f2;
 }
+.book-list dd{
+  padding-left:15px;
+  cursor: pointer;
+}
 .book-list dd:hover{
   background-color: #f2f2f2
 }

+ 18 - 18
app/public/js/advance.js

@@ -14,24 +14,24 @@ $(document).ready(function () {
         $('#erro').modal('show');
     }
 
-    $('#advance_add').click(function() {
-        postData(`${window.location.pathname}/${type}/create`, {}, res => {
-            const html = `<tr>
-                <td><a href="/tender/${res.tid}/advance/${res.id}/detail" data-id="${res.id}">第${res.order}期</a></td>
-                <td>${res.pay_ratio || 0}%</td>
-                <td class="text-right">${formatMoney((res.cur_amount || 0), ',', decimal)}</td>
-                <td class="text-right">${formatMoney((res.prev_amount || 0), ',', decimal)}</td>
-                <td class="text-right">${formatMoney((res.prev_total_amount || 0),',', decimal)}</td>
-                <td><a class="btn btn-sm" href="#file" data-toggle="modal" data-target="#file"><i class="fa fa-paperclip "></i> 3</a></td>
-                <td>${auditConst.statusString[res.status]}</td>
-                <td><a href="/tender/${res.tid}/advance/${res.id}/detail" class="btn btn-primary btn-sm">编辑</a></td>
-                </tr>`
-            $('#advanceList').prepend(html)
-            $('#advance_add').remove()
-            window.location.href = `${window.location.pathname}/${res.id}/detail`
-        })
-        return false
-    })
+    // $('#advance_add').click(function() {
+    //     postData(`${window.location.pathname}/${type}/create`, {}, res => {
+    //         const html = `<tr>
+    //             <td><a href="/tender/${res.tid}/advance/${res.id}/detail" data-id="${res.id}">第${res.order}期</a></td>
+    //             <td>${res.pay_ratio || 0}%</td>
+    //             <td class="text-right">${formatMoney((res.cur_amount || 0), ',', decimal)}</td>
+    //             <td class="text-right">${formatMoney((res.prev_amount || 0), ',', decimal)}</td>
+    //             <td class="text-right">${formatMoney((res.prev_total_amount || 0),',', decimal)}</td>
+    //             <td><a class="btn btn-sm" href="#file" data-toggle="modal" data-target="#file"><i class="fa fa-paperclip "></i> 3</a></td>
+    //             <td>${auditConst.statusString[res.status]}</td>
+    //             <td><a href="/tender/${res.tid}/advance/${res.id}/detail" class="btn btn-primary btn-sm">编辑</a></td>
+    //             </tr>`
+    //         $('#advanceList').prepend(html)
+    //         $('#advance_add').remove()
+    //         window.location.href = `${window.location.pathname}/${res.id}/detail`
+    //     })
+    //     return false
+    // })
 
     $('#advanceList').on('click', `a[href="#file"]`, function() {
         const { fileList = [] } = advanceList.find(item => item.id === parseInt($(this).data('id')))

+ 8 - 7
app/public/js/advance_audit.js

@@ -18,7 +18,6 @@ $(document).ready(function () {
 
     // 控制上报弹窗的文案
     function checkModal(isHide) {
-        console.log($('#start-modal'))
         $('#start-modal').empty()
         if (isHide) {
             // 隐藏上传按钮
@@ -156,7 +155,7 @@ $(document).ready(function () {
     // 上报审批
     $('#tm-submit').click(function() {
         const pay_ratio = parseFloat($(`.pay-input[data-type=0]`).val())
-        const cur_amount = parseFloat($(`.pay-input[data-type=1]`).val())
+        const cur_amount = parseFloat(parseFloat($(`.pay-input[data-type=1]`).val()).toFixed(decimal))
         if (!pay_ratio || !cur_amount) {
             return toastr.error('请填写本期金额!')
         }
@@ -208,10 +207,11 @@ $(document).ready(function () {
             $(this).val(fixedToSub(val, decimal)) // 重新赋值限制只有两位小数
             const pay_a_input = $(`.pay-input[data-type=${reverse(type)}]`)
             pay_ratio = parseFloat(ZhCalc.mul(ZhCalc.div(val, advancePayTotal), 100).toFixed(2))
-            cur_amount = val
+            cur_amount = ZhCalc.round(val, decimal)
             pay_a_input.val(pay_ratio)
+            const total = parseFloat(ZhCalc.add(cur_amount, p_amount)).toString().split('.')[1]
             // 截止本期金额文案更新
-            $('#p_total2').text(formatMoney(ZhCalc.add(val, p_amount), ',', decimal) + '元')
+            $('#p_total2').text(formatMoney(ZhCalc.add(cur_amount, p_amount), ',', total && total.length || 0) + '元')
         } else {
             if (val.toFixed(1) === max.toFixed(1)) {
                 val = max
@@ -219,11 +219,12 @@ $(document).ready(function () {
             // 支付比例转化
             $(this).val(fixedToSub(val)) // 重新赋值限制只有两位小数
             const cur_m_input = $(`.pay-input[data-type=${reverse(type)}]`)
-            cur_amount = ZhCalc.mul(advancePayTotal, ZhCalc.div(val, 100))
+            cur_amount = ZhCalc.round(ZhCalc.mul(advancePayTotal, ZhCalc.div(val, 100)), decimal)
             pay_ratio = val
-            cur_m_input.val(cur_amount.toFixed(decimal))
+            cur_m_input.val(parseFloat(cur_amount.toFixed(decimal)))
+            const total = parseFloat(ZhCalc.add(cur_amount, p_amount)).toString().split('.')[1]
             // 截止本期金额文案更新
-            $('#p_total2').text(formatMoney(ZhCalc.add(cur_amount, p_amount), ',', decimal) + '元')
+            $('#p_total2').text(formatMoney(ZhCalc.add(cur_amount, p_amount), ',', total && total.length || 0) + '元')
         }
         const data = {
             pay_ratio,

+ 10 - 2
app/public/js/change_detail.js

@@ -81,7 +81,7 @@ $(document).ready(() => {
             for (const fileInfo of data) {
                 html += '<tr> ' +
                     '<td>' + index + '</td> ' +
-                    '<td><a href="/change/download/file/' + fileInfo.id + '">' + fileInfo.filename + fileInfo.fileext + '</a></td> ' +
+                    `<td><a href="javascript: void(0);" class="file-atn" f-id="${fileInfo.id}">${fileInfo.filename}${fileInfo.fileext}</a></td>`+
                     '<td>' + fileInfo.filesize + '</td> ' +
                     '<td>' + fileInfo.in_time + '</td> ' +
                     '<td> <a class="btn btn-light btn-sm delete-file" data-attid="' + fileInfo.id + '"  title="删除附件"><span class="fa fa-trash text-danger"></span></a> </td> ' +
@@ -112,7 +112,15 @@ $(document).ready(() => {
             });
         });
     });
-
+    // /change/download/file/
+    $('#attList').on('click', '.file-atn', function() {
+        const id = $(this).attr('f-id')
+        postData(`/change/download/file/${id}`, {}, (data) => {
+            const { filepath } = data
+            $('#file-upload').attr('href', filepath)
+            $('#file-upload')[0].click()
+        })
+    })
     //
     const cca = getLocalCache('change-checkbox-account-' + accountId);
     if (cca !== null && cca !== undefined) {

+ 39 - 29
app/public/js/change_set.js

@@ -167,7 +167,7 @@ $(document).ready(() => {
                 }
                 changeList.push.apply(changeList, changeWhiteList);
                 for (const [index, cl] of changeList.entries()) {
-                    const clInfo = cl.split(';');
+                    const clInfo = cl.split('*;*');
                     // if (clInfo[0] === '' || clInfo[1] === '' || clInfo[3] === '' || clInfo[4] === '' || clInfo[5] === '') {
                     if (clInfo[0] === '' || clInfo[1] === '' || clInfo[5] === '') {
                         toastr.error('变更清单第' + (index+1) + '行未完整填写数据(变更部位、变更详情、单位、单价可空)');
@@ -363,7 +363,7 @@ $(document).ready(() => {
             for (const leaf of gcl.leafXmjs) {
                 const quantity = leaf.quantity !== undefined && leaf.quantity !== null ? leaf.quantity : 0;
                 const bwmx = leaf.bwmx !== undefined ? leaf.bwmx : '';
-                const isChecked = data_bwmx.indexOf(leaf.code + '_' + (bwmx !== '' ? bwmx : leaf.jldy ? leaf.jldy : '') + ';' + quantity) !== -1 && isCheck ? 'checked' : '';
+                const isChecked = data_bwmx.indexOf(leaf.code + '!_!' + (leaf.jldy ? leaf.jldy : '') + '!_!' + (bwmx !== '' ? bwmx : leaf.jldy ? leaf.jldy : '') + '*;*' + quantity) !== -1 && isCheck ? 'checked' : '';
                 codeHtml += '<tr quantity="' + quantity + '"><td>' + leaf.code + '</td>' +
                     '<td>' + (leaf.jldy ? leaf.jldy: '') + '</td>' +
                     '<td>' + (leaf.dwgc ? leaf.dwgc : '') + '</td>' +
@@ -397,9 +397,9 @@ $(document).ready(() => {
             $('#code-list input:checked').each(function () {
                 const tr = $(this).parents('tr');
                 const length = tr.children('td').length;
-                const bwmx = length === 8 ? tr.children('td').eq(0).text() + '_' + (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(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;
+                const de_qu = bwmx + '*;*' + quantity;
                 data_bwmx.push(de_qu);
             });
             data_bwmx = data_bwmx.join('$#$');
@@ -412,9 +412,9 @@ $(document).ready(() => {
                 $('#code-list input:checked').each(function () {
                     const tr = $(this).parents('tr');
                     const length = tr.children('td').length;
-                    const bwmx = length === 8 ? tr.children('td').eq(0).text() + '_' + (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(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;
+                    const de_qu = bwmx + '*;*' + quantity;
                     data_bwmx.push(de_qu);
                 });
                 data_bwmx = data_bwmx.join('$#$');
@@ -480,6 +480,8 @@ $(document).ready(() => {
     $('body').on('valuechange', '.clist input', function (e, previous) {
         const index = $(this).parents('tr').data('index');
         const lid = $(this).parents('tr').data('lid');
+        const xmj_code = $(this).parents('tr').data('xmjcode') || '';
+        const xmj_jldy = $(this).parents('tr').data('xmjjldy') || '';
         const isWhite = !isNaN(lid) ? true : false;
         const tr = $('#list tr[data-lid="' + lid + '"]').eq(0);
         const site = parseInt($(this).parents('td').data('site'));
@@ -508,12 +510,16 @@ $(document).ready(() => {
         if (isWhite) {
             let changelist = $('#change-whitelist').val().split('^_^');
             trlist.push(0);
-            changelist.splice(index, 1, trlist.join(';'));
+            trlist.push(xmj_code);
+            trlist.push(xmj_jldy);
+            changelist.splice(index, 1, trlist.join('*;*'));
             $('#change-whitelist').val(changelist.join('^_^'));
         } else {
             let changelist = $('#change-list').val().split('^_^');
             trlist.push(lid.split('_')[0]);
-            changelist.splice(index, 1, trlist.join(';'));
+            trlist.push(xmj_code);
+            trlist.push(xmj_jldy);
+            changelist.splice(index, 1, trlist.join('*;*'));
             $('#change-list').val(changelist.join('^_^'));
         }
         tr.children('td[data-site="7"]').text(price != '' && oamount != '' ? roundnum(parseFloat(price).mul(parseFloat(oamount)),totalPriceUnit) : '');
@@ -543,9 +549,9 @@ $(document).ready(() => {
         tr.children('td[data-site="6"]').children('input').attr('onkeyup','RegNum(this,event,'+ numdecimal +')');
         tr.children('td[data-site="8"]').children('input').val(scnum);
         tr.children('td[data-site="8"]').children('input').attr('onkeyup','RegNum(this,event,'+ numdecimal +')');
-        const trlist = [code,name,bwmx,unit,price,oamount,scnum,detail,0];
+        const trlist = [code,name,bwmx,unit,price,oamount,scnum,detail,0,'',''];
         let changelist = $('#change-whitelist').val().split('^_^');
-        changelist.splice(index, 1, trlist.join(';'));
+        changelist.splice(index, 1, trlist.join('*;*'));
         $('#change-whitelist').val(changelist.join('^_^'));
         tr.children('td[data-site="7"]').text(price != '' && oamount != '' ? roundnum(parseFloat(price).mul(parseFloat(oamount)),totalPriceUnit) : '');
         tr.children('td[data-site="9"]').text(price != '' && scnum != '' ? roundnum(parseFloat(price).mul(parseFloat(scnum)),totalPriceUnit) : '');
@@ -635,7 +641,7 @@ function tableDataRemake(changeListData) {
     const changeList = $('#change-list').val().split('^_^');
     if (changeList.length > 0 && changeList[0]) {
         for (const [index,cl] of changeList.entries()) {
-            const clinfo = cl.split(';');
+            const clinfo = cl.split('*;*');
             // const listinfo = changeListData[clinfo[8] - 1];
             let listinfo = changeListData.find(function (item) {
                 return (item.id !== undefined && item.id == clinfo[8]) || (item.id === undefined && item.leafXmjs !== undefined && item.leafXmjs.length !== 0 && item.leafXmjs[0].gcl_id == clinfo[8]);
@@ -653,21 +659,21 @@ function tableDataRemake(changeListData) {
                     continue;
                 }
                 $('#table-list-select tr[data-index="'+ clinfo[8] +'"]').addClass('table-success');
-                let pushbwmx = '0;0';
+                let pushbwmx = '0*;*0';
                 if (listinfo.leafXmjs !== undefined) {
                     const leafInfo = listinfo.leafXmjs.find(function (item) {
                         return (item.bwmx === undefined || item.bwmx === clinfo[2]) && (item.quantity !== null ? item.quantity === parseFloat(clinfo[5]) : 0 === parseFloat(clinfo[5]));
                     });
                     console.log(leafInfo);
                     if (leafInfo) {
-                        pushbwmx = leafInfo.code + '_' + (leafInfo.bwmx !== undefined ? leafInfo.bwmx : '') + ';' + (leafInfo.quantity !== null ? leafInfo.quantity : 0);
+                        pushbwmx = leafInfo.code + '!_!' + (leafInfo.jldy !== undefined ? leafInfo.jldy : '') + '!_!' + (leafInfo.bwmx !== undefined ? leafInfo.bwmx : '') + '*;*' + (leafInfo.quantity !== null ? leafInfo.quantity : 0);
                     } else {
                         toastr.warning('台账清单列表已不存在'+ clinfo[0] +',已更新变更清单列表');
                         changeList.splice(index, 1);
                         continue;
                     }
                 } else {
-                    pushbwmx = '0;' + (listinfo.quantity !== null ? listinfo.quantity : 0);
+                    pushbwmx = '0*;*' + (listinfo.quantity !== null ? listinfo.quantity : 0);
                 }
                 const bwmx = $('#table-list-select tr[data-index="'+ clinfo[8] +'"]').attr('data-bwmx');
                 if (bwmx) {
@@ -679,20 +685,20 @@ function tableDataRemake(changeListData) {
                 }
             } else {
                 $('#table-list-select tr[data-lid="'+ clinfo[8] +'"]').addClass('table-success');
-                let pushbwmx = '0;0';
+                let pushbwmx = '0*;*0';
                 if (listinfo.leafXmjs !== undefined) {
                     const leafInfo = listinfo.leafXmjs.find(function (item) {
                         return (item.bwmx === undefined || item.bwmx === clinfo[2] || item.jldy === clinfo[2]) && (item.quantity !== null ? item.quantity === parseFloat(clinfo[5]) : 0 === parseFloat(clinfo[5]));
                     });
                     if (leafInfo) {
-                        pushbwmx = leafInfo.code + '_' + (leafInfo.bwmx !== undefined ? leafInfo.bwmx : (leafInfo.jldy ? leafInfo.jldy : '')) + ';' + (leafInfo.quantity !== null ? leafInfo.quantity : 0);
+                        pushbwmx = leafInfo.code + '!_!' + (leafInfo.jldy !== undefined ? leafInfo.jldy : '') + '!_!' + (leafInfo.bwmx !== undefined ? leafInfo.bwmx : (leafInfo.jldy ? leafInfo.jldy : '')) + '*;*' + (leafInfo.quantity !== null ? leafInfo.quantity : 0);
                     } else {
                         toastr.warning('台账清单列表已不存在'+ clinfo[0] +',已更新变更清单列表');
                         changeList.splice(index, 1);
                         continue;
                     }
                 } else {
-                    pushbwmx = '0;' + (listinfo.quantity !== null ? listinfo.quantity : 0);
+                    pushbwmx = '0*;*' + (listinfo.quantity !== null ? listinfo.quantity : 0);
                 }
                 const bwmx = $('#table-list-select tr[data-lid="'+ clinfo[8] +'"]').attr('data-bwmx');
                 if (bwmx) {
@@ -834,18 +840,20 @@ function maketablelist(status){
         // 原清单和数量改变
         let data_bwmx = $(this).attr('data-bwmx').split('$#$');
         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('_')[1] : '';
-            let trlist = [code, name, bwmx, unit, price, oamount, scnum, '', lid];
+            const oamount = b.split('*;*')[1] != '' ? b.split('*;*')[1] : 0;
+            let bwmx = b.split('*;*')[0] != 0 ? b.split('*;*')[0].split('!_!')[2] : '';
+            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 trlist = [code, name, bwmx, unit, price, oamount, scnum, '', lid, xmj_code, xmj_jldy];
             const radionInfo = radionList.find(function (item) {
-                const info = item.split(';');
+                const info = item.split('*;*');
                 return info[0] === code && (info[8] == lid || parseInt(info[8]) === parseInt(lindex)) && info[2] === bwmx;
             });
             if (radionInfo) {
-                trlist[6] = radionInfo.split(';')[6];
-                trlist[7] = radionInfo.split(';')[7];
+                trlist[6] = radionInfo.split('*;*')[6];
+                trlist[7] = radionInfo.split('*;*')[7];
             }
-            newTableList.push(trlist.join(';'));
+            newTableList.push(trlist.join('*;*'));
         }
     });
     // 排序
@@ -854,7 +862,7 @@ function maketablelist(status){
     let whiteIndex = 0;
     let deteletr = '<td><a class="text-danger">移除</a></td>';
     for (const radion of newTableList) {
-        const radionArray = radion.split(';');
+        const radionArray = radion.split('*;*');
         let code = radionArray[0];
         let name = radionArray[1];
         let bwmx = radionArray[2];
@@ -864,11 +872,13 @@ function maketablelist(status){
         let scnum = radionArray[6];
         let detail = radionArray[7];
         let lid = radionArray[8];
+        let xmj_code = radionArray[9];
+        let xmj_jldy = radionArray[10];
         let sctotal = scnum !== '' && scnum !== '-' ? roundnum(parseFloat(price).mul(parseFloat(scnum)),decimal) : '';
 
         // 根据单位获取数量的位数,并得出
         let numdecimal = findDecimal(unit);
-        html += '<tr class="clist clid" data-lid="' + lid + '_' + index + '" data-index="' + index + '">' +
+        html += '<tr class="clist clid" data-lid="' + lid + '_' + index + '" data-index="' + index + '" data-xmjcode="'+ xmj_code +'" data-xmjjldy="'+ xmj_jldy +'">' +
             '<td data-site="0">'+ code +'</td>' +
             '<td data-site="1">'+ name +'</td>' +
             '<td data-site="2">'+ bwmx +'</td>' +
@@ -888,12 +898,12 @@ function maketablelist(status){
     let radionWhiteList = $('#change-whitelist').val() !== '' ? $('#change-whitelist').val().split('^_^') : [];
     //判断是否添加空白清单
     if(status == 'addwhite'){
-        let trlist = ['','','','','',makedecimalzero(findDecimal(3)),makedecimalzero(findDecimal(3)),'',0];
-        radionWhiteList.push(trlist.join(';'));
+        let trlist = ['','','','','',makedecimalzero(findDecimal(3)),makedecimalzero(findDecimal(3)),'',0, '', ''];
+        radionWhiteList.push(trlist.join('*;*'));
     }
 
     for (const rw of radionWhiteList) {
-        const radionArray = rw.split(';');
+        const radionArray = rw.split('*;*');
         let code = radionArray[0];
         let name = radionArray[1];
         let bwmx = radionArray[2];

+ 304 - 39
app/public/js/ledger.js

@@ -48,6 +48,9 @@ $(document).ready(function() {
         rootId: -1,
         keys: ['id', 'tender_id', 'ledger_id'],
         preUrl: '/tender/' + getTenderId() + '/ledger',
+        //treeCacheKey: 'ledger_bills_fold' + '_' + getTenderId(),
+        markFoldKey: 'bills-fold',
+        markFoldSubKey: window.location.pathname.split('/')[2],
     };
     if (checkTzMeasureType()) {
         treeSetting.calcFields = ['sgfh_tp', 'sjcl_tp', 'qtcl_tp', 'total_price'];
@@ -2046,10 +2049,10 @@ $(document).ready(function() {
             if (searchLedger) {
                 searchLedger.spread.refresh();
             }
-            if (errorList && errorList.spread) {
+            if (errorList) {
                 errorList.spread.refresh();
             }
-            if (checkList && checkList.spread) {
+            if (checkList) {
                 checkList.spread.refresh();
             }
         }
@@ -2190,14 +2193,14 @@ $(document).ready(function() {
                 if (!dealBills) {
                     dealBills = new DealBills('#deal-bills-spread', {
                         cols: [
-                            {title: '清单编号', field: 'code', hAlign: 0, width: 85, formatter: '@', readOnly: true},
-                            {title: '名称', field: 'name', hAlign: 0, width: 150, formatter: '@', readOnly: true},
-                            {title: '单位', field: 'unit', hAlign: 1, width: 50, formatter: '@', readOnly: true},
-                            {title: '单价', field: 'unit_price', hAlign: 2, width: 50, readOnly: true},
-                            {title: '数量', field: 'quantity', hAlign: 2, width: 50, readOnly: true},
+                            {title: '清单编号', field: 'code', hAlign: 0, width: 85, formatter: '@'},
+                            {title: '名称', field: 'name', hAlign: 0, width: 150, formatter: '@'},
+                            {title: '单位', field: 'unit', hAlign: 1, width: 50, formatter: '@'},
+                            {title: '单价', field: 'unit_price', hAlign: 2, width: 50},
+                            {title: '数量', field: 'quantity', hAlign: 2, width: 50},
                             {title: '金额', field: 'total_price', hAlign: 2, width: 50, readOnly: true},
                         ],
-                        emptyRows: 0,
+                        emptyRows: 3,
                         headRows: 1,
                         headRowHeight: [32],
                         defaultRowHeight: 21,
@@ -2205,6 +2208,7 @@ $(document).ready(function() {
                         font: '12px 微软雅黑',
                         headColWidth: [0],
                         selectedBackColor: '#fffacd',
+                        readOnly: true,
                     });
                     dealBills.loadData();
                 }
@@ -2260,44 +2264,224 @@ $(document).ready(function() {
             this.url = '/tender/' + getTenderId() + '/deal';
             this.spreadSetting = spreadSetting;
             this.spread = SpreadJsObj.createNewSpread(this.obj);
+            this.sheet = this.spread.getActiveSheet();
             SpreadJsObj.initSheet(this.spread.getActiveSheet(), this.spreadSetting);
+
+
+            this.OprObj = {
+                /**
+                 * 删除按钮响应事件
+                 * @param sheet
+                 */
+                deletePress: function (sheet) {
+                    if (!sheet.zh_setting || readOnly || sheet.zh_setting.readOnly) return;
+
+                    const sortData = sheet.zh_data;
+                    const datas = [];
+                    const sels = sheet.getSelections();
+                    if (!sels || !sels[0]) return;
+
+                    for (let iRow = sels[0].row; iRow < sels[0].row + sels[0].rowCount; iRow++) {
+                        let bDel = false;
+                        const node = sortData[iRow];
+                        if (node) {
+                            const data = {id: node.id};
+                            for (let iCol = sels[0].col; iCol < sels[0].col + sels[0].colCount; iCol++) {
+                                const colSetting = sheet.zh_setting.cols[iCol];
+                                if (colSetting.field === 'code') {
+                                    toastr.error('清单编号不能为空,如需删除签约清单请使用右键删除');
+                                    return;
+                                }
+                                const style = sheet.getStyle(iRow, iCol);
+                                if (!style.locked) {
+                                    const colSetting = sheet.zh_setting.cols[iCol];
+                                    data[colSetting.field] = null;
+                                    bDel = true;
+                                }
+                            }
+                            if (bDel) {
+                                datas.push(data);
+                            }
+                        }
+                    }
+                    if (datas.length > 0) {
+                        postData(self.url + '/update', {update: datas}, function (result) {
+                            self.loadUpdateData(result);
+                            SpreadJsObj.reLoadSheetData(sheet);
+                        }, function () {
+                            SpreadJsObj.reLoadSheetData(sheet);
+                        });
+                    }
+                },
+                delete: function (sheet) {
+                    if (!sheet.zh_setting || readOnly || sheet.zh_setting.readOnly) return;
+
+                    const sortData = sheet.zh_data;
+                    const datas = [];
+                    const sels = sheet.getSelections();
+                    if (!sels || !sels[0]) return;
+
+                    for (let iRow = sels[0].row, iLen = sels[0].row + sels[0].rowCount; iRow < iLen; iRow++) {
+                        const node = sortData[iRow];
+                        datas.push(node.id);
+                    }
+                    if (datas.length > 0) {
+                        postData(self.url + '/update', {del: datas}, function (result) {
+                            self.loadUpdateData(result);
+                            SpreadJsObj.reLoadSheetData(sheet);
+                        }, function () {
+                            SpreadJsObj.reLoadSheetData(sheet);
+                        });
+                    }
+                },
+                editEnded: function (e, info) {
+                    if (!info.sheet.zh_setting || !info.sheet.zh_data || info.sheet.zh_setting.readOnly) return;
+
+                    const node = info.sheet.zh_data[info.row];
+                    const col = info.sheet.zh_setting.cols[info.col];
+                    const data = {};
+
+                    if (node) {
+                        data.update = {};
+                        data.update.id = node.id;
+
+                        const oldValue = node ? node[col.field] : null;
+                        const newValue = trimInvalidChar(info.editingText);
+                        if (oldValue == info.editingText || ((!oldValue || oldValue === '') && (newValue === ''))) {
+                            SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                            return;
+                        }
+                        data.update[col.field] = newValue;
+                    } else {
+                        if (col.field !== 'code') {
+                            toastr.warning('新增签约清单,请先输入清单编号');
+                            SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                            return;
+                        }
+                        data.add = {};
+                        data.add.order = info.row + 1;
+                        data.add.code = trimInvalidChar(info.editingText);
+                    }
+
+                    postData(self.url +  '/update', data, function (result) {
+                        self.loadUpdateData(result);
+                        SpreadJsObj.reLoadSheetData(info.sheet);
+                    }, function () {
+                        SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                    });
+                },
+                clipboardPasting(e, info) {
+                    const setting = info.sheet.zh_setting, sortData = info.sheet.zh_data;
+                    info.cancel = true;
+
+                    if (!setting || !sortData || setting.readOnly) return;
+                    const pasteData = info.pasteData.html
+                        ? SpreadJsObj.analysisPasteHtml(info.pasteData.html)
+                        : (info.pasteData.text === ''
+                            ? SpreadJsObj.Clipboard.getAnalysisPasteText()
+                            : SpreadJsObj.analysisPasteText(info.pasteData.text));
+                    const hint = {
+                        code: {type: 'warning', msg: '签约清单编号不可为空,已过滤'},
+                        unit_price: {type: 'warning', msg: '输入的 单价 非法,已过滤'},
+                        quantity: {type: 'warning', msg: '输入的 数量 非法,已过滤'},
+                    };
+
+                    const uDatas = [], iDatas = [];
+                    for (let iRow = 0; iRow < info.cellRange.rowCount; iRow++) {
+                        const curRow = info.cellRange.row + iRow;
+                        const node = sortData[curRow];
+
+                        let bPaste = false;
+                        const data = {};
+                        for (let iCol = 0; iCol < info.cellRange.colCount; iCol++) {
+                            const curCol = info.cellRange.col + iCol;
+                            const colSetting = setting.cols[curCol];
+                            const value = trimInvalidChar(pasteData[iRow][iCol]);
+
+                            if (colSetting.field === 'code' && (!value || value === '')) {
+                                toastMessageUniq(hint.code);
+                                break;
+                            }
+                            if (colSetting.type === 'Number') {
+                                const num = _.toNumber(value);
+                                if (num) {
+                                    data[colSetting.field] = num;
+                                    bPaste = true;
+                                } else if (colSetting.field === 'unit_price') {
+                                    toastMessageUniq(hint.unit_price);
+                                } else if (colSetting.field === 'quantity') {
+                                    toastMessageUniq(hint.quantity);
+                                }
+                            } else {
+                                data[colSetting.field] = value;
+                                bPaste = true;
+                            }
+                        }
+                        if (bPaste) {
+                            if (node) {
+                                data.id = node.id;
+                                uDatas.push(data);
+                            } else {
+                                data.order = curRow + 1;
+                                iDatas.push(data);
+                            }
+                        }
+                    }
+                    const updateData = {};
+                    if (uDatas.length > 0) updateData.update = uDatas;
+                    if (iDatas.length > 0) updateData.add = iDatas;
+                    if (uDatas.length > 0 || iDatas.length > 0) {
+                        postData(self.url + '/update', updateData, function (result) {
+                            self.loadUpdateData(result);
+                            SpreadJsObj.reLoadSheetData(info.sheet);
+                        });
+                    } else {
+                        SpreadJsObj.reLoadSheetData(info.sheet);
+                    }
+                },
+            };
             if (!readOnly) {
                 this.spread.bind(spreadNS.Events.CellDoubleClick, function (e, info) {
-                const dealSheet = info.sheet;
-                const mainSheet = ledgerSpread.getActiveSheet();
+                    const dealSheet = info.sheet;
+                    if (!dealSheet.zh_setting.readOnly) return;
 
-                const dealBills = SpreadJsObj.getSelectObject(dealSheet);
-                if (!dealBills) { return; }
-                const mainTree = mainSheet.zh_tree;
-                const mainNode = SpreadJsObj.getSelectObject(mainSheet);
-                if (!mainNode || !mainTree) { return; }
+                    const mainSheet = ledgerSpread.getActiveSheet();
 
-                if (mainNode.code && mainNode.code !== '' && !mainTree.isLeafXmj(mainNode)) {
-                    toastr.error('非最底层项目下,不应添加清单');
-                    return;
-                }
+                    const dealBills = SpreadJsObj.getSelectObject(dealSheet);
+                    if (!dealBills) { return; }
+                    const mainTree = mainSheet.zh_tree;
+                    const mainNode = SpreadJsObj.getSelectObject(mainSheet);
+                    if (!mainNode || !mainTree) { return; }
 
-                postData(window.location.pathname + '/update', {
-                    postType: 'add-deal',
-                    postData: {
-                        id: mainNode.ledger_id,
-                        type: mainNode.code ? 'child' : 'next',
-                        dealBills: {
-                            b_code: dealBills.code, name: dealBills.name, unit: dealBills.unit,
-                            unit_price: dealBills.unit_price,
-                        }
-                    },
-                }, function (result) {
-                    const refreshData = mainTree.loadPostData(result);
-                    treeOperationObj.refreshTree(mainSheet, refreshData);
-                    const sel = mainSheet.getSelections()[0];
-                    mainSheet.setSelection(refreshData.create[0].index, sel.col, sel.rowCount, sel.colCount);
-                    SpreadJsObj.reloadRowsBackColor(mainSheet, [sel.row, refreshData.create[0].index]);
-                    treeOperationObj.refreshOperationValid(mainSheet);
-                    ledgerSpread.focus();
-                    posOperationObj.loadCurPosData();
+                    if (mainNode.code && mainNode.code !== '' && !mainTree.isLeafXmj(mainNode)) {
+                        toastr.error('非最底层项目下,不应添加清单');
+                        return;
+                    }
+
+                    postData(window.location.pathname + '/update', {
+                        postType: 'add-deal',
+                        postData: {
+                            id: mainNode.ledger_id,
+                            type: mainNode.code ? 'child' : 'next',
+                            dealBills: {
+                                b_code: dealBills.code, name: dealBills.name, unit: dealBills.unit,
+                                unit_price: dealBills.unit_price,
+                            }
+                        },
+                    }, function (result) {
+                        const refreshData = mainTree.loadPostData(result);
+                        treeOperationObj.refreshTree(mainSheet, refreshData);
+                        const sel = mainSheet.getSelections()[0];
+                        mainSheet.setSelection(refreshData.create[0].index, sel.col, sel.rowCount, sel.colCount);
+                        SpreadJsObj.reloadRowsBackColor(mainSheet, [sel.row, refreshData.create[0].index]);
+                        treeOperationObj.refreshOperationValid(mainSheet);
+                        ledgerSpread.focus();
+                        posOperationObj.loadCurPosData();
+                    });
                 });
-            });
+                this.spread.bind(spreadNS.Events.EditEnded, this.OprObj.editEnded);
+                this.spread.bind(spreadNS.Events.ClipboardPasting, this.OprObj.clipboardPasting);
+                SpreadJsObj.addDeleteBind(this.spread, this.OprObj.deletePress);
             }
             $('#upload-deal-bills').click(function () {
                     const file = $('#deal-bills-file')[0];
@@ -2319,6 +2503,43 @@ $(document).ready(function() {
                         return target.hitTestType === spreadNS.SheetArea.viewport || target.hitTestType === spreadNS.SheetArea.rowHeader;
                     },
                     items: {
+                        edit: {
+                            name: '进入编辑模式',
+                            icon: 'fa-edit',
+                            visible: function (key, opt) {
+                                return self.sheet.zh_setting.readOnly;
+                            },
+                            callback: function (key, opt) {
+                                self.sheet.zh_setting.readOnly = false;
+                                SpreadJsObj.refreshSheetReadOnly(self.sheet);
+                            },
+                        },
+                        read: {
+                            name: '退出编辑模式',
+                            icon: 'fa-sign-out',
+                            visible: function (key, opt) {
+                                return !self.sheet.zh_setting.readOnly;
+                            },
+                            callback: function (key, opt) {
+                                self.sheet.zh_setting.readOnly = true;
+                                SpreadJsObj.refreshSheetReadOnly(self.sheet);
+                            },
+                        },
+                        del: {
+                            name: '删除',
+                            icon: 'fa-times',
+                            visible: function (key, opt) {
+                                return !self.sheet.zh_setting.readOnly;
+                            },
+                            disabled: function (key, opt) {
+                                const select = SpreadJsObj.getSelectObject(self.sheet);
+                                return !select;
+                            },
+                            callback: function (key, opt) {
+                                self.OprObj.delete(self.sheet);
+                            },
+                        },
+                        sprEdit: '---------',
                         apply: {
                             name: '应用全部清单单价至台账',
                             icon: 'fa-magic',
@@ -2368,6 +2589,30 @@ $(document).ready(function() {
                 }
             }
         }
+        loadUpdateData(updateData) {
+            if (updateData.add) {
+                for (const a of updateData.add) {
+                    this.data.push(a);
+                }
+            }
+            if (updateData.update) {
+                for (const u of updateData.update) {
+                    const d = this.data.find(function (x) {
+                        return u.id === x.id;
+                    });
+                    if (d) {
+                        _.assign(d, u);
+                    } else {
+                        this.data.push(d);
+                    }
+                }
+            }
+            if (updateData.del) {
+                _.remove(this.data, function (d) {
+                    return updateData.del.indexOf(d.id) >= 0;
+                });
+            }
+        }
     }
     class BatchInsertBillsPosObj {
         constructor (obj) {
@@ -2948,6 +3193,26 @@ $(document).ready(function() {
         ledgerTree: ledgerTree,
         ledgerPos: pos,
         checkList: checkList,
+        decimal: tenderInfo.decimal,
+        checkOption: {
+            sibling: { enable: 1 },
+            empty_code: { enable: 1 },
+            calc: {
+                enable: 1,
+                fields: ['sgfh_qty', 'qtcl_qty', 'sjcl_qty', 'quantity'],
+            },
+            zero: { enable: 1 },
+            tp: {
+                enable: 1,
+                fields: [
+                    {qty: 'sgfh_qty', tp: 'sgfh_tp'},
+                    {qty: 'sjcl_qty', tp: 'sjcl_tp'},
+                    {qty: 'qtcl_qty', tp: 'qtcl_tp'},
+                    {qty: 'quantity', tp: 'total_price'},
+                    {qty: 'deal_qty', tp: 'deal_tp'},
+                ],
+            },
+        }
     });
 });
 

+ 2 - 0
app/public/js/ledger_audit.js

@@ -28,6 +28,8 @@ $(document).ready(() => {
         rootId: -1,
         keys: ['id', 'tender_id', 'ledger_id'],
         preUrl: '/ledger',
+        markFoldKey: 'bills-fold',
+        markFoldSubKey: window.location.pathname.split('/')[2],
     };
     if (checkTzMeasureType()) {
         treeSetting.calcFields = ['sgfh_tp', 'sjcl_tp', 'qtcl_tp', 'total_price'];

文件差异内容过多而无法显示
+ 7 - 0
app/public/js/local-forage/localforage.min.js


+ 116 - 46
app/public/js/material.js

@@ -95,23 +95,6 @@ const is_numeric = (value) => {
     }
 };
 
-function setMonthHtml() {
-    let html = '';
-    let qihtml = '';
-    for (const m of months) {
-        html += '<div class="custom-control custom-checkbox mb-2">\n' +
-            '                            <input type="checkbox" name="del_month" value="' + m + '" class="custom-control-input" id="month_' + m + '">\n' +
-            '                            <label class="custom-control-label" for="month_' + m + '">' + m + '月</label>\n' +
-            '                        </div>';
-        qihtml += parseInt(m.split('-')[1]) + '月,';
-    }
-    if (months.length > 0) {
-        qihtml = '<span class="mx-2 text-muted">/</span>本期月信息价:' + qihtml;
-        qihtml = qihtml.substring(0, qihtml.length-1);
-    }
-    $('#show_month').html(html);
-    $('#qi-month').html(qihtml);
-}
 $(document).ready(() => {
     autoFlashHeight();
     const materialSpread = SpreadJsObj.createNewSpread($('#material-spread')[0]);
@@ -128,13 +111,14 @@ $(document).ready(() => {
             {title: '基准时间', colSpan: '1', rowSpan: '2', field: 'basic_times', hAlign: 0, width: 80, formatter: '@', readOnly: 'readOnly.isEdit'},
             {title: '本期信息价|单价', colSpan: '3|1', rowSpan: '1|1', field: 'msg_tp', hAlign: 2, width: 60, type: 'Number', readOnly: 'readOnly.msg_tp'},
             {title: '|时间', colSpan: '|1', rowSpan: '|1', field: 'msg_times', hAlign: 0, width: 80, formatter: '@', readOnly: 'readOnly.remark'},
-            {title: '|价差', colSpan: '1', rowSpan: '1|1', field: 'msg_spread', hAlign: 2, width: 60, type: 'Number', readOnly: true, getValue: 'getValue.msg_spread'},
+            {title: '|价差', colSpan: '|1', rowSpan: '|1', field: 'msg_spread', hAlign: 2, width: 60, type: 'Number', readOnly: true, getValue: 'getValue.msg_spread'},
             {title: '本期材料调差|上涨幅度(%)', colSpan: '4|1', rowSpan: '1|1', field: 'm_up_risk', hAlign: 2, width: 100, type: 'Number', readOnly: 'readOnly.isEdit'},
             {title: '|下跌幅度(%)', colSpan: '|1', rowSpan: '|1', field: 'm_down_risk', hAlign: 2, width: 100, type: 'Number', readOnly: 'readOnly.isEdit'},
             {title: '|有效价差', colSpan: '|1', rowSpan: '|1', field: 'm_spread', hAlign: 2, width: 80, type: 'Number', readOnly: true, getValue: 'getValue.m_spread'},
-            {title: '|调差金额', colSpan: '|1', rowSpan: '1|1', field: 'm_tp', hAlign: 2, width: 80, type: 'Number', readOnly: true, getValue: 'getValue.m_tp'},
+            {title: '|调差金额', colSpan: '|1', rowSpan: '|1', field: 'm_tp', hAlign: 2, width: 80, type: 'Number', readOnly: true, getValue: 'getValue.m_tp'},
             {title: '截止上期调差金额', colSpan: '1', rowSpan: '2', field: 'pre_tp', hAlign: 2, width: 120, type: 'Number', readOnly: true},
             {title: '备注', colSpan: '1', rowSpan: '2', field: 'remark', hAlign: 0, width: 60, formatter: '@', readOnly: 'readOnly.remark'},
+            {title: '是否汇总', colSpan: '1', rowSpan: '2', field: 'is_summary', hAlign: 1, width: 60, cellType: 'checkbox', readOnly: 'readOnly.isEdit'},
         ],
         emptyRows: 0,
         headRows: 2,
@@ -297,6 +281,9 @@ $(document).ready(() => {
             if (info.sheet.zh_setting) {
                 const select = SpreadJsObj.getSelectObject(info.sheet);
                 const col = info.sheet.zh_setting.cols[info.col];
+                if (col.field === 'is_summary') {
+                    return;
+                }
                 // 未改变值则不提交
                 const validText = is_numeric(info.editingText) ? parseFloat(info.editingText) : (info.editingText ? trimInvalidChar(info.editingText) : null);
                 const orgValue = select[col.field];
@@ -389,6 +376,30 @@ $(document).ready(() => {
                 });
             }
         },
+        buttonClicked: function (e, info) {
+            if (info.sheet.zh_setting) {
+                const select = SpreadJsObj.getSelectObject(info.sheet);
+                const col = info.sheet.zh_setting.cols[info.col];
+                if (materialCol.readOnly.isEdit(select)) {
+                    return;
+                }
+                if (col.field === 'is_summary') {
+                    if (info.sheet.isEditing()) {
+                        info.sheet.endEdit(true);
+                    }
+                    select.is_summary = info.sheet.getValue(info.row, info.col) ? 1 : 0;
+                    // 更新至服务器
+                    postData(window.location.pathname + '/save', { type:'update', updateData: select }, function (result) {
+                        m_tp = result.m_tp;
+                        resetTpTable();
+                        SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                    }, function () {
+                        select.is_summary = info.sheet.getValue(info.row, info.col) ? 0 : 1;
+                        SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                    });
+                }
+            }
+        },
         deletePress: function (sheet) {
             return;
         },
@@ -544,15 +555,80 @@ $(document).ready(() => {
     // msg_range.cellType(new DatePickerCellType());
     // msg_range.formatter("yyyy-MM-dd");
     sheet.resumePaint();
+    const static_cols = [
+        {title: '编号', colSpan: '1', rowSpan: '2', field: 'code', hAlign: 0, width: 60, formatter: '@', readOnly: true},
+        {title: '名称', colSpan: '1', rowSpan: '2', field: 'name', hAlign: 0, width: 180, formatter: '@', readOnly: true},
+        {title: '单位', colSpan: '1', rowSpan: '2', field: 'unit', hAlign: 1, width: 60, formatter: '@', readOnly: true},
+        {title: '平均单价', colSpan: '1', rowSpan: '2', field: 'average_msg_tp', hAlign: 2, width: 60, type: 'Number', readOnly: true, getValue:'getValue.average_msg_tp'},
+    ];
+    // 月信息价方法集合
+    const monthFunGather = {
+        _setMonthHtml: function() {
+            let html = '';
+            let qihtml = '';
+            for (const m of months) {
+                html += '<div class="custom-control custom-checkbox mb-2">\n' +
+                    '                            <input type="checkbox" name="del_month" value="' + m + '" class="custom-control-input" id="month_' + m + '">\n' +
+                    '                            <label class="custom-control-label" for="month_' + m + '">' + m + '月</label>\n' +
+                    '                        </div>';
+                qihtml += parseInt(m.split('-')[1]) + '月,';
+            }
+            if (months.length > 0) {
+                qihtml = '<span class="mx-2 text-muted">/</span>本期月信息价:' + qihtml;
+                qihtml = qihtml.substring(0, qihtml.length-1);
+            }
+            $('#show_month').html(html);
+            $('#qi-month').html(qihtml);
+        },
+        _monthHeaderSet: function() {
+            const newMonths = [];
+            for (const m of months) {
+                const year = m.split('-')[0];
+                const month = parseInt(m.split('-')[1]);
+                let one = _.find(newMonths, { 'year': year });
+                let oneIndex = _.findIndex(newMonths, { 'year': year });
+                if (one) {
+                    one.month.push(month);
+                    newMonths.splice(oneIndex, 1, one);
+                } else {
+                    one = {
+                        year: year,
+                        month: [ month ],
+                    };
+                    newMonths.push(one);
+                }
+            }
+            const pushMonth = [];
+            for(const mo of newMonths) {
+                for (let i in mo.month) {
+                    i = parseInt(i);
+                    const newCols = {
+                        title: (i === 0 ? mo.year: '') + '|' + mo.month[i] + '月',
+                        colSpan: (i === 0 ? (mo.month.length === 1 ? '0' : mo.month.length) : '') + '|1',
+                        rowSpan: i === 0 ? '1|1' : '|1',
+                        field: mo.year + '-' + (mo.month[i] < 10 ? '0' + mo.month[i] : mo.month[i]),
+                        hAlign: 2, width: 60, type: '@', readOnly: 'readOnly.isEdit'};
+                    pushMonth.push(newCols);
+                }
+            }
+            return pushMonth;
+        },
+        monthSheetReset: function () {
+            const monthCols = monthFunGather._monthHeaderSet();
+            const newMonthSpreadHeaderCols = static_cols.concat(monthCols);
+            materialMonthSpreadSetting.cols = newMonthSpreadHeaderCols;
+            // 表头变化需要重新绘制,不然报错;
+            materialMonthSpread.getActiveSheet().reset();
+            SpreadJsObj.initSpreadSettingEvents(materialMonthSpreadSetting, materialMonthCol);
+            SpreadJsObj.initSheet(materialMonthSpread.getActiveSheet(), materialMonthSpreadSetting);
+            SpreadJsObj.loadSheetData(materialMonthSpread.getActiveSheet(), SpreadJsObj.DataType.Data, monthsList);
+            monthFunGather._setMonthHtml();
+        }
+    }
 
     const materialMonthSpread = SpreadJsObj.createNewSpread($('#material-month-spread')[0]);
     const materialMonthSpreadSetting = {
-        cols: [
-            {title: '编号', colSpan: '1', rowSpan: '2', field: 'code', hAlign: 0, width: 60, formatter: '@', readOnly: true},
-            {title: '名称', colSpan: '1', rowSpan: '2', field: 'name', hAlign: 0, width: 180, formatter: '@', readOnly: true},
-            {title: '单位', colSpan: '1', rowSpan: '2', field: 'unit', hAlign: 1, width: 60, formatter: '@', readOnly: true},
-            {title: '平均单价', colSpan: '1', rowSpan: '2', field: 'average_msg_tp', hAlign: 2, width: 60, type: 'Number', readOnly: true, getValue:'getValue.average_msg_tp'},
-        ],
+        cols: static_cols,
         emptyRows: 0,
         headRows: 2,
         headRowHeight: [25, 25],
@@ -562,11 +638,9 @@ $(document).ready(() => {
         readOnly: readOnly,
     };
     if (months.length > 0) {
-        for (const m of months) {
-            const month = parseInt(m.split('-')[1]);
-            const newCols = {title: month + '月|单价', colSpan: '1|1', rowSpan: '1|1', field: m, hAlign: 2, width: 60, type: '@', readOnly: 'readOnly.isEdit'};
-            materialMonthSpreadSetting.cols.push(newCols);
-        }
+        const monthCols = monthFunGather._monthHeaderSet();
+        const monthSpreadHeaderCols = static_cols.concat(monthCols);
+        materialMonthSpreadSetting.cols = monthSpreadHeaderCols;
     }
 
     const materialMonthCol = {
@@ -725,7 +799,9 @@ $(document).ready(() => {
         $('#add').click(materialSpreadObj.add);
         $('#del').click(materialSpreadObj.del);
         materialSpread.bind(spreadNS.Events.EditEnded, materialSpreadObj.editEnded);
+        materialSpread.bind(spreadNS.Events.ButtonClicked, materialSpreadObj.buttonClicked);
         materialMonthSpread.bind(spreadNS.Events.EditEnded, materialMonthSpreadObj.editEnded);
+
         // 右键菜单
         $.contextMenu({
             selector: '#material-spread',
@@ -765,9 +841,13 @@ $(document).ready(() => {
             const rate = parseInt($(this).val());
             postData(window.location.pathname + '/save', { type:'rate', rate: rate }, function (result) {
                 const bqhs = ZhCalc.round(ZhCalc.mul(m_tp, 1+rate/100), 2);
+                const exbqhs = ZhCalc.round(ZhCalc.mul(ex_tp, 1+rate/100), 2);
                 const jzbqhs = ZhCalc.round(ZhCalc.add(pre_tp_hs, bqhs), 2);
+                const exjzbqhs = ZhCalc.round(ZhCalc.add(ex_pre_tp_hs, exbqhs), 2);
                 $('#rate_set').find('td').eq(1).text(bqhs !== 0 ? bqhs : '');
                 $('#rate_set').find('td').eq(2).text(jzbqhs !== 0 ? jzbqhs : '');
+                $('#rate_set').find('td').eq(3).text(exbqhs !== 0 ? exbqhs : '');
+                $('#rate_set').find('td').eq(4).text(exjzbqhs !== 0 ? exjzbqhs : '');
             });
         });
 
@@ -941,17 +1021,10 @@ $(document).ready(() => {
                 return false;
             }
             postData(window.location.pathname + '/month/save', { type: 'add', updateData: { yearmonth: yearmonth } }, function (data) {
-                const month = parseInt(yearmonth.split('-')[1]);
-                const newCols = {title: month + '月|单价', colSpan: '1|1', rowSpan: '1|1', field: yearmonth, hAlign: 2, width: 60, type: '@', readOnly: 'readOnly.isEdit'};
-                materialMonthSpreadSetting.cols.push(newCols);
                 months.push(yearmonth);
+                months.sort();
                 monthsList = data.monthsList;
-                SpreadJsObj.reinitSheetHeader(materialMonthSpread.getActiveSheet(), materialMonthSpreadSetting);
-                SpreadJsObj.initSpreadSettingEvents(materialMonthSpreadSetting, materialMonthCol);
-                // SpreadJsObj.reLoadSheetData(materialMonthSpread.getActiveSheet());
-                SpreadJsObj.loadSheetData(materialMonthSpread.getActiveSheet(), SpreadJsObj.DataType.Data, monthsList);
-                setMonthHtml();
-
+                monthFunGather.monthSheetReset();
                 // 工料表单价显示也要更新
                 materialBillsData = data.materialBillsData;
                 SpreadJsObj.loadSheetData(materialSpread.getActiveSheet(), SpreadJsObj.DataType.Data, materialBillsData);
@@ -972,18 +1045,15 @@ $(document).ready(() => {
             }
             postData(window.location.pathname + '/month/save', { type: 'del', updateData: { del_yearmonth: del_month_array } }, function (data) {
                 for (const dm of del_month_array) {
-                    _.remove(materialMonthSpreadSetting.cols, function (n) {
-                        return n.field === dm;
-                    });
+                    // _.remove(materialMonthSpreadSetting.cols, function (n) {
+                    //     return n.field === dm;
+                    // });
                     _.remove(months, function (n) {
                         return n === dm;
                     });
                 }
                 monthsList = data.monthsList;
-                SpreadJsObj.reinitSheetHeader(materialMonthSpread.getActiveSheet(), materialMonthSpreadSetting);
-                SpreadJsObj.initSpreadSettingEvents(materialMonthSpreadSetting, materialMonthCol);
-                SpreadJsObj.loadSheetData(materialMonthSpread.getActiveSheet(), SpreadJsObj.DataType.Data, monthsList);
-                setMonthHtml();
+                monthFunGather.monthSheetReset();
 
                 // 工料表单价显示也要更新
                 materialBillsData = data.materialBillsData;

+ 454 - 0
app/public/js/material_exponent.js

@@ -0,0 +1,454 @@
+const is_numeric = (value) => {
+    if (typeof(value) === 'object') {
+        return false;
+    } else {
+        return !Number.isNaN(Number(value)) && value.toString().trim() !== '';
+    }
+};
+function getPasteHint (str, row = '') {
+    let returnObj = str;
+    if (row) {
+        returnObj.msg = '指数清单第' + (row+1) + '行' + str.msg;
+    }
+    return returnObj;
+}
+function resetExTpTable() {
+    const rate = $('#changeRate').val();
+    const bqhs = ZhCalc.round(ZhCalc.mul(ex_tp, 1+rate/100), 2);
+    const jzbqhs = ZhCalc.round(ZhCalc.add(ex_pre_tp_hs, bqhs), 2);
+    $('#tp_set').find('td').eq(3).text(ZhCalc.round(ex_tp, 2));
+    $('#tp_set').find('td').eq(4).text(ZhCalc.round(ZhCalc.add(ex_pre_tp, ex_tp), 2));
+    $('#rate_set').find('td').eq(3).text(bqhs !== 0 ? bqhs : '');
+    $('#rate_set').find('td').eq(4).text(jzbqhs !== 0 ? jzbqhs : '');
+    $('#ex_expr').html(ex_expr);
+}
+$(document).ready(() => {
+    autoFlashHeight();
+    const materialExponentSpread = SpreadJsObj.createNewSpread($('#material-exponent-spread')[0]);
+    const materialExponentSpreadSetting = {
+        cols: [
+            {title: '类型', colSpan: '1', rowSpan: '2', field: 'type', hAlign: 1, width: 60, formatter: '@', readOnly: true,cellType: 'customizeCombo', comboItems: materialType.ex_type, cellTypeKey: 1},
+            {title: '符号', colSpan: '1', rowSpan: '2', field: 'symbol', hAlign: 1, width: 60, formatter: '@', readOnly: 'readOnly.isEdit'},
+            {title: '符号说明', colSpan: '1', rowSpan: '2', field: 'symbol_desc', hAlign: 0, width: 180, formatter: '@', readOnly: 'readOnly.isEdit'},
+            {title: '编号', colSpan: '1', rowSpan: '2', field: 'code', hAlign: 1, width: 60, formatter: '@', readOnly: 'readOnly.isEdit'},
+            {title: '加权系数', colSpan: '1', rowSpan: '2', field: 'weight_num', hAlign: 2, width: 120, formatter: '@', readOnly: 'readOnly.isConstant'},
+            {title: '基本价格指数', colSpan: '1', rowSpan: '2', field: 'basic_price', hAlign: 2, width: 120, readOnly: 'readOnly.isEdit'},
+            {title: '基准时间', colSpan: '1', rowSpan: '2', field: 'basic_times', hAlign: 0, width: 80, formatter: '@', readOnly: 'readOnly.isEdit'},
+            {title: '现行价格指数', colSpan: '1', rowSpan: '2', field: 'm_price', hAlign: 2, width: 120, type: 'Number', readOnly: 'readOnly.remark'},
+            {title: '计算值', colSpan: '1', rowSpan: '2', field: 'calc_num', hAlign: 2, width: 80, formatter: '@', readOnly: true},
+            {title: '备注', colSpan: '1', rowSpan: '2', field: 'remark', hAlign: 0, width: 60, formatter: '@', readOnly: 'readOnly.remark'},
+            {title: '是否汇总', colSpan: '1', rowSpan: '2', field: 'is_summary', hAlign: 1, width: 60, cellType: 'checkbox', readOnly: 'readOnly.isEdit'},
+        ],
+        emptyRows: 0,
+        headRows: 2,
+        headRowHeight: [25, 25],
+        defaultRowHeight: 21,
+        headerFont: '12px 微软雅黑',
+        font: '12px 微软雅黑',
+        readOnly: readOnly,
+    };
+    const materialExponentBase = {
+        isUsed: function (data) {
+            if (data.type === 2) {
+                return data.mid === materialID || data.weight_num === null;
+            } else {
+                return false;
+            }
+        },
+        isEdit: function (data) {
+            return data.mid === materialID && data.type === 2;
+        },
+        isConstant: function (data) {
+            return (materialOrder === 1 && data.type === 1) || (data.mid === materialID && data.type === 2);
+        }
+    };
+    const materialExponentCol = {
+        getValue: {
+            calc_num : function (data) {
+                const calc_num = data.basic_price > 0 ? ZhCalc.mul(data.weight_num, ZhCalc.div(data.m_price, data.basic_price)) : 0;
+                return calc_num > 0 ? ZhCalc.round(calc_num, 3) : 0;
+            },
+        },
+        readOnly: {
+            isEdit: function (data) {
+                return !(!readOnly && materialExponentBase.isEdit(data));
+            },
+            isUsed: function (data) {
+                return !(!readOnly && materialExponentBase.isUsed(data));
+            },
+            remark: function (data) {
+                return !(!readOnly && data.type === 2);
+            },
+            isConstant: function (data) {
+                return !(!readOnly && materialExponentBase.isConstant(data));
+            }
+        },
+    };
+    SpreadJsObj.initSpreadSettingEvents(materialExponentSpreadSetting, materialExponentCol);
+    SpreadJsObj.initSheet(materialExponentSpread.getActiveSheet(), materialExponentSpreadSetting);
+    SpreadJsObj.loadSheetData(materialExponentSpread.getActiveSheet(), SpreadJsObj.DataType.Data, materialExponentData);
+
+    const materialExponentSpreadObj = {
+        refreshActn: function (rowCount = 1) {
+            const setObjEnable = function (obj, enable) {
+                if (enable) {
+                    obj.removeClass('disabled');
+                } else {
+                    obj.addClass('disabled');
+                }
+            };
+            const sheet = materialExponentSpread.getActiveSheet();
+            const select = SpreadJsObj.getSelectObject(sheet);
+            // 还需判断是否已被调差清单调用
+            setObjEnable($('#del'), !readOnly && select && materialExponentBase.isUsed(select) && rowCount === 1);
+        },
+        add: function () {
+            const sheet = materialExponentSpread.getActiveSheet();
+            postData(window.location.pathname + '/save', {type: 'add'}, function (result) {
+                if (result) {
+                    materialExponentData.push(result);
+                    sheet.addRows(materialExponentData.length - 1, 1);
+                    SpreadJsObj.reLoadRowData(sheet, materialExponentData.length - 1);
+                    sheet.setSelection(materialExponentData.length - 1, 0, 1, 1);
+                    materialExponentSpreadObj.refreshActn();
+                }
+            });
+        },
+        del: function () {
+            const sheet = materialExponentSpread.getActiveSheet();
+            const select = SpreadJsObj.getSelectObject(sheet);
+            postData(window.location.pathname + '/save', {type: 'del', id: select.id}, function (result) {
+                ex_tp = result.ex_tp;
+                ex_expr = result.ex_expr;
+                resetExTpTable();
+                const index = materialExponentData.indexOf(select);
+                materialExponentData.splice(index, 1);
+                sheet.deleteRows(index, 1);
+                const sel = sheet.getSelections();
+                sheet.setSelection(index > 0 ? index - 1 : 0, sel.length > 0 ? sel[0].col : 0, 1, 1);
+                materialExponentSpreadObj.refreshActn();
+            });
+        },
+        selectionChanged: function (e, info) {
+            const sel = info.sheet.getSelections()[0];
+            const col = info.sheet.zh_setting.cols[sel.col];
+            materialExponentSpreadObj.refreshActn(sel.rowCount);
+            const data = SpreadJsObj.getSelectObject(info.sheet);
+            materialExponentSpreadObj.setReadOnly(true);
+        },
+        editEnded: function (e, info) {
+            if (info.sheet.zh_setting) {
+                const select = SpreadJsObj.getSelectObject(info.sheet);
+                const col = info.sheet.zh_setting.cols[info.col];
+                if (col.field === 'is_summary') {
+                    return;
+                }
+                // 未改变值则不提交
+                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 (col.field === 'basic_price' || col.field === 'm_price') {
+                    if (isNaN(validText)) {
+                        toastr.error('不能输入其它非数字类型字符');
+                        SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                        return;
+                    }
+                    const num = parseFloat(validText);
+                    if (validText !== null && (num < 0 || !/^(\d{1,10}|\d{1,7}\.\d{1,3})?$/.test(num))) {
+                        toastr.error('请输入10位以内有效数字并且小于3位小数的浮点数');
+                        SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                        return;
+                    }
+                }
+                if (col.field === 'weight_num') {
+                    if (isNaN(validText)) {
+                        toastr.error('不能输入其它非数字类型字符');
+                        SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                        return;
+                    }
+                    const num = parseFloat(validText);
+                    if (validText !== null && (num < 0 || num >= 1 || !/^\d+(\.\d{1,3})?$/.test(num))) {
+                        toastr.error('请输入0~1范围内并且小于3位小数的浮点数');
+                        SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                        return;
+                    }
+                    const total_weight = ZhCalc.add(ZhCalc.sub(_.sumBy(materialExponentData, 'weight_num'), parseFloat(orgValue)), num);
+                    if (total_weight > 1) {
+                        toastr.error('加权系数总和不能大于1');
+                        SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                        return;
+                    }
+                }
+                select[col.field] = validText;
+                select.calc_num = materialExponentCol.getValue.calc_num(select);
+                // console.log(select);
+
+                // 更新至服务器
+                postData(window.location.pathname + '/save', { type:'update', updateData: select }, function (result) {
+                    ex_tp = result.ex_tp;
+                    ex_expr = result.ex_expr;
+                    resetExTpTable();
+                    SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                    materialExponentData.splice(info.row, 1, select);
+                }, function () {
+                    select[col.field] = orgValue;
+                    SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                });
+            }
+        },
+        buttonClicked: function (e, info) {
+            if (info.sheet.zh_setting) {
+                const select = SpreadJsObj.getSelectObject(info.sheet);
+                const col = info.sheet.zh_setting.cols[info.col];
+                if (materialExponentCol.readOnly.isEdit(select)) {
+                    return;
+                }
+                if (col.field === 'is_summary') {
+                    if (info.sheet.isEditing()) {
+                        info.sheet.endEdit(true);
+                    }
+                    select.is_summary = info.sheet.getValue(info.row, info.col) ? 1 : 0;
+                    // 更新至服务器
+                    postData(window.location.pathname + '/save', { type:'update', updateData: select }, function (result) {
+                        ex_tp = result.ex_tp;
+                        ex_expr = result.ex_expr;
+                        resetExTpTable();
+                        SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                    }, function () {
+                        select.is_summary = info.sheet.getValue(info.row, info.col) ? 0 : 1;
+                        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: '请粘贴10位以内有效数字并且小于3位小数的浮点数'},
+                numberCan2: {type: 'error', msg: '请粘贴0~1范围内并且小于3位小数的浮点数'},
+                weightNumberCan: {type: 'error', msg: '粘贴的加权系数总和不能大于1'},
+            };
+            const range = info.cellRange;
+            const sortData = info.sheet.zh_data || [];
+            if (info.cellRange.row + info.cellRange.rowCount > sortData.length) {
+                toastMessageUniq(hint.cellError);
+                // SpreadJsObj.loadSheetData(materialSpread.getActiveSheet(), SpreadJsObj.DataType.Data, materialBillsData);
+                SpreadJsObj.reLoadSheetHeader(materialExponentSpread.getActiveSheet());
+                SpreadJsObj.reLoadSheetData(materialExponentSpread.getActiveSheet());
+                return;
+            }
+            if (sortData.length > 0 && range.col + range.colCount > 10) {
+                toastMessageUniq(hint.cellError);
+                SpreadJsObj.reLoadSheetHeader(materialExponentSpread.getActiveSheet());
+                SpreadJsObj.reLoadSheetData(materialExponentSpread.getActiveSheet());
+                return;
+            }
+            const data = [];
+            // const rowData = [];
+            for (let iRow = 0; iRow < range.rowCount; iRow++) {
+                let bPaste = true;
+                const curRow = range.row + iRow;
+                // const materialData = JSON.parse(JSON.stringify(sortData[curRow]));
+                const materialExData = { id: sortData[curRow].id };
+                const hintRow = range.rowCount > 1 ? curRow : '';
+                let sameCol = 0;
+                for (let iCol = 0; iCol < range.colCount; iCol++) {
+                    const curCol = range.col + iCol;
+                    const colSetting = info.sheet.zh_setting.cols[curCol];
+                    if (!colSetting) continue;
+
+                    let validText = info.sheet.getText(curRow, curCol);
+                    validText = is_numeric(validText) ? parseFloat(validText) : (validText ? trimInvalidChar(validText) : null);
+                    const orgValue = sortData[curRow][colSetting.field];
+                    if (orgValue == validText || ((!orgValue || orgValue === '') && (validText === ''))) {
+                        sameCol++;
+                        if (range.colCount === sameCol)  {
+                            bPaste = false;
+                        }
+                        continue;
+                    }
+                    if (colSetting.field === 'basic_price' || colSetting.field === 'm_price') {
+                        if (isNaN(validText)) {
+                            toastMessageUniq(getPasteHint(hint.numberExpr, hintRow));
+                            bPaste = false;
+                            continue;
+                        }
+                        const num = parseFloat(validText);
+                        if (validText !== null && (num < 0 || !/^(\d{1,10}|\d{1,7}\.\d{1,3})?$/.test(num))) {
+                            toastMessageUniq(getPasteHint(hint.numberCan, hintRow));
+                            bPaste = false;
+                            continue;
+                        }
+                    }
+                    if (colSetting.field === 'weight_num') {
+                        if (isNaN(validText)) {
+                            toastMessageUniq(getPasteHint(hint.numberExpr, hintRow));
+                            bPaste = false;
+                            continue;
+                        }
+                        const num = parseFloat(validText);
+                        if (validText !== null && (num < 0 || num >= 1 || !/^\d+(\.\d{1,3})?$/.test(num))) {
+                            toastMessageUniq(getPasteHint(hint.numberCan2, hintRow));
+                            bPaste = false;
+                            continue;
+                        }
+                        const total_weight = ZhCalc.add(ZhCalc.sub(_.sumBy(materialExponentData, 'weight_num'), parseFloat(orgValue)), num);
+                        console.log(total_weight, _.sumBy(materialExponentData, 'weight_num'), orgValue, num);
+                        if (total_weight > 1) {
+                            toastMessageUniq(getPasteHint(hint.weightNumberCan, hintRow));
+                            bPaste = false;
+                            continue;
+                        }
+                    }
+                    materialExData[colSetting.field] = validText;
+                    sortData[curRow][colSetting.field] = validText;
+                }
+                if (bPaste) {
+                    materialExData.calc_num = materialExponentCol.getValue.calc_num(sortData[curRow]);
+                    data.push(materialExData);
+                    // rowData.push(curRow);
+                } else {
+                    SpreadJsObj.reLoadRowData(info.sheet, curRow);
+                }
+            }
+            if (data.length === 0) {
+                SpreadJsObj.reLoadRowData(info.sheet, info.cellRange.row, info.cellRange.rowCount);
+                return;
+            }
+            // console.log(data);
+            // 更新至服务器
+            postData(window.location.pathname + '/save', { type:'paste', updateData: data }, function (result) {
+                materialExponentData = result.info;
+                SpreadJsObj.loadSheetData(materialExponentSpread.getActiveSheet(), SpreadJsObj.DataType.Data, materialExponentData);
+                ex_tp = result.ex_tp;
+                ex_expr = result.ex_expr;
+                resetExTpTable();
+            }, function () {
+                SpreadJsObj.reLoadRowData(info.sheet, info.cellRange.row, info.cellRange.rowCount);
+                return;
+            });
+        },
+        setReadOnly: function(readOnly) {
+            // SpreadJsObj.resetFieldReadOnly(materialSpread.getActiveSheet(), 'msg_spread', 'm_spread', 'm_tp', 'pre_tp', readOnly);
+        }
+    };
+    materialExponentSpreadObj.refreshActn();
+    materialExponentSpread.bind(spreadNS.Events.SelectionChanged, materialExponentSpreadObj.selectionChanged);
+    materialExponentSpread.bind(spreadNS.Events.ClipboardPasted, materialExponentSpreadObj.clipboardPasted);
+    SpreadJsObj.addDeleteBind(materialExponentSpread, materialExponentSpreadObj.deletePress);
+
+    if (!readOnly) {
+        $('#add').click(materialExponentSpreadObj.add);
+        $('#del').click(materialExponentSpreadObj.del);
+        materialExponentSpread.bind(spreadNS.Events.EditEnded, materialExponentSpreadObj.editEnded);
+        materialExponentSpread.bind(spreadNS.Events.ButtonClicked, materialExponentSpreadObj.buttonClicked);
+        // 右键菜单
+        $.contextMenu({
+            selector: '#material-exponent-spread',
+            build: function ($trigger, e) {
+                const target = SpreadJsObj.safeRightClickSelection($trigger, e, materialExponentSpread);
+                return target.hitTestType === GC.Spread.Sheets.SheetArea.viewport || target.hitTestType === GC.Spread.Sheets.SheetArea.rowHeader;
+            },
+            items: {
+                'create': {
+                    name: '新增',
+                    icon: 'fa-sign-in',
+                    callback: function (key, opt) {
+                        materialExponentSpreadObj.add(materialExponentSpread.getActiveSheet());
+                    },
+                },
+                'delete': {
+                    name: '删除',
+                    icon: 'fa-remove',
+                    callback: function (key, opt) {
+                        materialExponentSpreadObj.del(materialExponentSpread.getActiveSheet());
+                    },
+                    disabled: function (key, opt) {
+                        const sheet = materialExponentSpread.getActiveSheet();
+                        const select = SpreadJsObj.getSelectObject(sheet);
+                        const sel = sheet.getSelections()[0];
+                        materialExponentSpreadObj.refreshActn(sel.rowCount);
+                        if (!readOnly && select && materialExponentBase.isUsed(select) && sel.rowCount === 1) {
+                            return false;
+                        } else {
+                            return true;
+                        }
+                    }
+                },
+            }
+        });
+
+        // 调差基数选中
+        $('.calc_select').on('click', function () {
+            // 如果是选中则清除其余2个的选中
+            const code = $(this).val();
+            for (const calc of ex_calc) {
+                calc.select = $(this).is(':checked') && code === calc.code ? true : false;
+                if (!calc.select) {
+                    $('.calc_select[value="'+ calc.code +'"]').prop('checked', false);
+                }
+            }
+            postData(window.location.pathname + '/save', { type:'ex_calc', updateData: ex_calc }, function (result) {
+                ex_tp = result.ex_tp;
+                ex_expr = result.ex_expr;
+                resetExTpTable();
+            });
+        });
+
+        $('#changeRate').change(function () {
+            const rate = parseInt($(this).val());
+            postData(window.location.pathname + '/save', { type:'rate', rate: rate }, function (result) {
+                const bqhs = ZhCalc.round(ZhCalc.mul(m_tp, 1+rate/100), 2);
+                const exbqhs = ZhCalc.round(ZhCalc.mul(ex_tp, 1+rate/100), 2);
+                const jzbqhs = ZhCalc.round(ZhCalc.add(pre_tp_hs, bqhs), 2);
+                const exjzbqhs = ZhCalc.round(ZhCalc.add(ex_pre_tp_hs, exbqhs), 2);
+                $('#rate_set').find('td').eq(1).text(bqhs !== 0 ? bqhs : '');
+                $('#rate_set').find('td').eq(2).text(jzbqhs !== 0 ? jzbqhs : '');
+                $('#rate_set').find('td').eq(3).text(exbqhs !== 0 ? exbqhs : '');
+                $('#rate_set').find('td').eq(4).text(exjzbqhs !== 0 ? exjzbqhs : '');
+            });
+        });
+    }
+
+    $.divResizer({
+        select: '#right-spr',
+        callback: function () {
+            materialExponentSpread.refresh();
+            const width = (($('#right-view').width()/$('#right-view').parent('div').width())*100).toFixed();
+            // setLocalCache('material_month_' + materialID, width);
+        }
+    });
+
+    // 展开收起月信息价并浏览器记住本期展开收起
+    $('a', '.right-nav').bind('click', function () {
+        //const main = $('#main-view'), tool = $('#tools-view');
+        const tab = $(this), tabPanel = $(tab.attr('content'));
+        if (!tab.hasClass('active')) {
+            $('a', '.side-menu').removeClass('active');
+            $('.tab-content .tab-select-show').removeClass('active');
+            tab.addClass('active');
+            tabPanel.addClass('active');
+            showSideTools(tab.hasClass('active'));
+            if (tab.attr('content') === '#base-tab') {
+                const width = (($('#right-view').width()/$('#right-view').parent('div').width())*100).toFixed();
+                // setLocalCache('material_month_' + materialID, width);
+                // materialMonthSpread.refresh();
+            }
+        } else {
+            // removeLocalCache('material_month_' + materialID);
+            tab.removeClass('active');
+            tabPanel.removeClass('active');
+            showSideTools(tab.hasClass('active'));
+        }
+        // materialSpread.refresh();
+        materialExponentSpread.refresh();
+    });
+});

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

@@ -216,8 +216,8 @@ $(document).ready(function () {
             // 只判断当前期,因为以往期都是只读的
             if (file.mid === parseInt(mid) && file.tid === parseInt(tid) && file.user_id === parseInt(cur_uid)) {
                 if (!curAuditor) {
-                    material.status === auditConst.status.uncheck && parseInt(cur_uid) === material.user_id && (showDel = true)
-                    material.status === auditConst.status.checkNo && parseInt(cur_uid) === material.user_id && (showDel = true)
+                    materialStatus === auditConst.status.uncheck && parseInt(cur_uid) === materialUid && (showDel = true)
+                    materialStatus === auditConst.status.checkNo && parseInt(cur_uid) === materialUid && (showDel = true)
                 } else {
                     curAuditor.aid === parseInt(cur_uid) && (showDel = true)
                 }
@@ -234,6 +234,23 @@ $(document).ready(function () {
         }
         return total
     }
+
+    $.subMenu({
+        menu: '#sub-menu', miniMenu: '#sub-mini-menu', miniMenuList: '#mini-menu-list',
+        toMenu: '#to-menu', toMiniMenu: '#to-mini-menu',
+        key: 'menu.1.0.0',
+        miniHint: '#sub-mini-hint', hintKey: 'menu.hint.1.0.1',
+        callback: function (info) {
+            if (info.mini) {
+                $('.panel-title').addClass('fluid');
+                $('#sub-menu').removeClass('panel-sidebar');
+            } else {
+                $('.panel-title').removeClass('fluid');
+                $('#sub-menu').addClass('panel-sidebar');
+            }
+            autoFlashHeight();
+        }
+    });
 });
 
 /**

+ 15 - 34
app/public/js/material_list.js

@@ -138,6 +138,7 @@ $(document).ready(() => {
             {title: '本期计量数量|合同', colSpan: '3|1', rowSpan: '1|1', field: 'contract_qty', hAlign: 2, width: 120, type: 'Number'},
             {title: '|数量变更', colSpan: '|1', rowSpan: '|1', field: 'qc_qty', hAlign: 2, width: 120, type: 'Number'},
             {title: '|小计', colSpan: '|1', rowSpan: '|1', field: 'gather_qty', hAlign: 2, width: 120, type: 'Number'},
+            {title: '本期完成金额', colSpan: '1', rowSpan: '2', field: 'gather_tp', hAlign: 2, width: 150, type: 'Number'},
             {title: '本期价差', colSpan: '1', rowSpan: '2', field: 'total_jiacha', hAlign:3, width: 150, type: 'Number'}
         ],
         emptyRows: 0,
@@ -153,10 +154,8 @@ $(document).ready(() => {
     gclGatherModel.loadPosData(pos, curPosData);
     let gclGatherData = gclGatherModel.gatherGclData().filter(item => {
         return item.qc_qty || item.contract_qty
-    })
-
-
-    calculateJiaCha(gclGatherData)
+    });
+    calculateJiaCha(gclGatherData);
     // let gclGatherData = gclGatherModel.gatherGclData()
     // 获取项目节数据
     function loadLeafXmjData(iGclRow) {
@@ -406,8 +405,9 @@ $(document).ready(() => {
                     visible: function (key, opt) {
                         const sheet = leafXmjSpread.getActiveSheet();
                         const select = SpreadJsObj.getSelectObject(sheet);
+                        const sel = sheet.getSelections()[0];
                         const notx = findNotJoinLeafXmj(select);
-                        if (!select) {
+                        if (!select || sel.rowCount !== 1) {
                             return false;
                         }
                         if (!readOnly && select && notx === undefined) {
@@ -426,8 +426,9 @@ $(document).ready(() => {
                     visible: function (key, opt) {
                         const sheet = leafXmjSpread.getActiveSheet();
                         const select = SpreadJsObj.getSelectObject(sheet);
+                        const sel = sheet.getSelections()[0];
                         const notx = findNotJoinLeafXmj(select);
-                        if (!select) {
+                        if (!select || sel.rowCount !== 1) {
                             return false;
                         }
                         if (!readOnly && select && notx === undefined) {
@@ -593,40 +594,20 @@ $(document).ready(() => {
         SpreadJsObj.addDeleteBind(materialSpread, materialSpreadObj.deletePress);
         // 应用调差工料至其他清单明细
         $('#user_all_material').click(function () {
-            const sheet = materialSpread.getActiveSheet();
-            const select = SpreadJsObj.getSelectObject(sheet);
-            if (select === undefined) {
-                toastr.warning('请选中需要应用到其他清单明细的调差工料');
-                return false;
-            }
-            const leafXmjSheet = leafXmjSpread.getActiveSheet();
-            const leafXmjSelect = SpreadJsObj.getSelectObject(leafXmjSheet);
-            const notl = findNotJoinLeafXmj(leafXmjSelect);
-            if (notl !== undefined) {
-                toastr.error('该清单不参与调差,调差工料无法应用到其它清单中');
-                return false;
-            }
             const ledgerSheet = ledgerSpread.getActiveSheet();
             const ledgerSelect = SpreadJsObj.getSelectObject(ledgerSheet);
             if (ledgerSelect.leafXmjs.length < 2) {
                 toastr.warning('没有需要应用调差工料的其它清单明细');
                 return false;
             }
+            const xmjSheet = leafXmjSpread.getActiveSheet();
+            const xmjSelect = SpreadJsObj.getSelectObject(xmjSheet);
             // 判断需要应用调差工料的清单明细
             const needAddList = [];
-            // const gather_qty = [];
             for (const xmj of ledgerSelect.leafXmjs) {
-                if (xmj.mx_id !== undefined) {
-                    const notx = findNotJoinLeafXmj(xmj);
-                    if (notx === undefined) {
-                        const ml = materialListData.find(function (item) {
-                            return xmj.mx_id === item.mx_id && select.mb_id === item.mb_id;
-                        });
-                        if (ml === undefined) {
-                            needAddList.push(xmj);
-                            // gather_qty.push(xmj.gather_qty);
-                        }
-                    }
+                const notx = findNotJoinLeafXmj(xmj);
+                if (notx === undefined && xmjSelect !== xmj) {
+                    needAddList.push(xmj);
                 }
             }
             if (needAddList.length === 0) {
@@ -634,10 +615,10 @@ $(document).ready(() => {
                 return false;
             }
             // 更新至服务器
-            postData(window.location.pathname + '/save', { type:'useOther', postData: { addXmj: needAddList, select: select } }, function (result) {
+            postData(window.location.pathname + '/save', { type:'useOther', postData: { addXmj: needAddList, materialBills: materialList } }, function (result) {
                 materialListData = result;
-                toastr.success('成功添加了' + needAddList.length + '条调差工料到其他清单明细中');
-                calculateJiaCha(gclGatherData)
+                toastr.success('已成功应用');
+                calculateJiaCha(gclGatherData);
                 const index = gclGatherData.indexOf(ledgerSelect);
                 loadLeafXmjData(index);
                 const xmjSheet = leafXmjSpread.getActiveSheet();

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

@@ -144,6 +144,8 @@ $(document).ready(() => {
         masterId: 'id',
         minorId: 'lid',
         calcFields: [],
+        markFoldKey: 'bills-fold',
+        markFoldSubKey: window.location.pathname.split('/')[2],
     });
     const cPos = new MasterPosData({
         id: 'id', ledgerId: 'lid', masterId: 'id', minorId: 'pid',

+ 20 - 0
app/public/js/measure_stage.js

@@ -212,3 +212,23 @@ function checkValidForm() {
     $('#hide-all').show();
     return true;
 }
+$(window).resize(checkTableWidth);
+function checkTableWidth() {
+    if($('table th').eq(2).outerWidth() < 107) {
+        $('table th').eq(2).html('本期<br>合同计量');
+        $('table th').eq(3).html('本期数量<br>变更计量');
+        $('table th').eq(4).html('本期<br>完成计量');
+        $('table th').eq(5).html('截止上期<br>完成计量');
+        $('table th').eq(6).html('截止本期<br>完成计量');
+    } else {
+        $('table th').eq(2).html('本期合同计量');
+        $('table th').eq(3).html('本期数量变更计量');
+        $('table th').eq(4).html('本期完成计量');
+        $('table th').eq(5).html('截止上期完成计量');
+        $('table th').eq(6).html('截止本期完成计量');
+    }
+}
+
+$(function () {
+    checkTableWidth();
+});

+ 69 - 24
app/public/js/path_tree.js

@@ -263,9 +263,10 @@ const createNewPathTree = function (type, setting) {
          * 构造函数
          */
         constructor(setting) {
+            const self = this;
             // 无索引
             this.datas = [];
-            // 以key为索引
+            // 以key为索引indexedDB
             this.items = {};
             // 以排序为索引
             this.nodes = [];
@@ -273,6 +274,18 @@ const createNewPathTree = function (type, setting) {
             this.children = [];
             // 树设置
             this.setting = setting;
+
+            if (this.setting.markFoldKey) {
+                 const markStr = getLocalCache(this.setting.markFoldKey);
+                 const markData = markStr ? markStr.split('|') : ['', ''];
+                 this.markFold = markData[0] === this.setting.markFoldSubKey && markData[1]
+                     ? _.map(markData[1].split(','), _.toInteger) : [];
+            }
+            // if (this.setting.treeCacheKey) {
+            //     localforage.getItem(this.setting.treeCacheKey).then(function (v) {
+            //         self.markFold = v && v.markFold ? v.markFold : [];
+            //     });
+            // }
         }
         /**
          * 树结构根据显示排序
@@ -309,6 +322,7 @@ const createNewPathTree = function (type, setting) {
          * @param datas
          */
         loadDatas(datas) {
+            self = this;
             // 清空旧数据
             this.items = {};
             this.nodes = [];
@@ -342,6 +356,9 @@ const createNewPathTree = function (type, setting) {
             });
             this.sortTreeNode(true);
             if (this.setting.autoExpand >= 0) this.expandByLevel(this.setting.autoExpand);
+            if (this.setting.markFoldKey) this.expandByCustom(function (node) {
+                return self.markFold.indexOf(node[self.setting.id]) === -1;
+            });
         }
 
         getItemsByIndex(index) {
@@ -487,6 +504,49 @@ const createNewPathTree = function (type, setting) {
             const siblings = this.getChildren(this.getParent(node));
             return (siblings && siblings.length > 0) ? node.order === siblings[siblings.length - 1].order : false;
         };
+
+        /**
+         * 提取节点key和索引数据
+         * @param {Object} node - 节点
+         * @returns {key}
+         */
+        getNodeKeyData(node) {
+            const data = {};
+            for (const key of this.setting.keys) {
+                data[key] = node[key];
+            }
+            return data;
+        };
+        /**
+         * 得到树结构构成id
+         * @param node
+         * @returns {*}
+         */
+        getNodeKey(node) {
+            return node[this.setting.id];
+        };
+
+        _markFold(node) {
+            if (!this.setting.markFoldKey || !this.setting.markFoldSubKey) return;
+            // if (!this.setting.treeCacheKey) return;
+
+            if (!node.expanded) {
+                if (this.markFold.indexOf(node[this.setting.id]) === -1) this.markFold.push(node[this.setting.id]);
+            } else {
+                if (this.markFold.indexOf(node[this.setting.id]) >= 0) this.markFold.splice(this.markFold.indexOf(node[this.setting.id]), 1);
+            }
+        }
+        _saveMarkFold() {
+            if (this.setting.markFoldKey && this.setting.markFoldSubKey) {
+                setLocalCache(this.setting.markFoldKey, this.setting.markFoldSubKey + '|' + this.markFold.join(','));
+            }
+            // if (this.setting.treeCacheKey) {
+            //     localforage.setItem(this.setting.treeCacheKey, {
+            //         markFold: this.markFold,
+            //         time: new Date(),
+            //     });
+            // }
+        }
         /**
          * 刷新子节点是否可见
          * @param {Object} node
@@ -510,30 +570,10 @@ const createNewPathTree = function (type, setting) {
          */
         setExpanded(node, expanded) {
             node.expanded = expanded;
+            this._markFold(node);
             this._refreshChildrenVisible(node);
+            this._saveMarkFold();
         };
-
-        /**
-         * 提取节点key和索引数据
-         * @param {Object} node - 节点
-         * @returns {key}
-         */
-        getNodeKeyData(node) {
-            const data = {};
-            for (const key of this.setting.keys) {
-                data[key] = node[key];
-            }
-            return data;
-        };
-        /**
-         * 得到树结构构成id
-         * @param node
-         * @returns {*}
-         */
-        getNodeKey(node) {
-            return node[this.setting.id];
-        };
-
         /**
          * 递归 设置节点展开状态
          * @param {Array} nodes - 需要设置状态的节点
@@ -543,7 +583,11 @@ const createNewPathTree = function (type, setting) {
          */
         _recursiveExpand(nodes, parent, checkFun) {
             for (const node of nodes) {
-                node.expanded = checkFun(node);
+                const expanded = checkFun(node);
+                if (node.expanded !== expanded) {
+                    node.expanded = expanded;
+                    this._markFold(node);
+                }
                 node.visible = parent ? (parent.expanded && parent.visible) : true;
                 this._recursiveExpand(node.children, node, checkFun);
             }
@@ -554,6 +598,7 @@ const createNewPathTree = function (type, setting) {
          */
         expandByCustom(checkFun) {
             this._recursiveExpand(this.children, null, checkFun);
+            this._saveMarkFold();
         }
         /**
          * 展开到第几层

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

@@ -2231,6 +2231,12 @@ $(document).ready(() => {
             if (searchLedger) {
                 searchLedger.spread.refresh();
             }
+            if (errorList) {
+                errorList.spread.refresh();
+            }
+            if (checkList) {
+                checkList.spread.refresh();
+            }
         }
     });
     $.subMenu({
@@ -2266,6 +2272,9 @@ $(document).ready(() => {
             if (searchLedger) {
                 searchLedger.spread.refresh();
             }
+            if (errorList) {
+                errorList.spread.refresh();
+            }
             if (checkList) {
                 checkList.spread.refresh();
             }
@@ -2519,6 +2528,26 @@ $(document).ready(() => {
         ledgerTree: billsTree,
         ledgerPos: pos,
         checkList: checkList,
+        decimal: decimal,
+        checkOption: {
+            sibling: { enable: 1 },
+            empty_code: { enable: 1 },
+            calc: {
+                enable: 1,
+                fields: ['sgfh_qty', 'qtcl_qty', 'sjcl_qty', 'quantity'],
+            },
+            zero: { enable: 1 },
+            tp: {
+                enable: 1,
+                fields: [
+                    {qty: 'sgfh_qty', tp: 'sgfh_tp'},
+                    {qty: 'sjcl_qty', tp: 'sjcl_tp'},
+                    {qty: 'qtcl_qty', tp: 'qtcl_tp'},
+                    {qty: 'quantity', tp: 'total_price'},
+                    {qty: 'deal_qty', tp: 'deal_tp'},
+                ],
+            },
+        }
     });
 
     $('[name=revise-start]').submit(function (e) {

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

@@ -224,7 +224,7 @@ $(document).ready(() => {
 
                 for (let iRow = sels[0].row, iLen = sels[0].row + sels[0].rowCount; iRow < iLen; iRow++) {
                     const node = sortData[iRow];
-                    if (node.pre_used) {
+                    if (node.pre_used || !checkZero(node.end_arrive_qty) || !checkZero(node.end_deduct_qty)) {
                         toastMessageUniq(hint.isOld);
                         continue;
                     } else {

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

@@ -230,7 +230,7 @@ $(document).ready(() => {
 
                 for (let iRow = sels[0].row, iLen = sels[0].row + sels[0].rowCount; iRow < iLen; iRow++) {
                     const node = sortData[iRow];
-                    if (node.pre_used) {
+                    if (node.pre_used || !checkZero(node.end_tp)) {
                         toastMessageUniq(hint.isOld);
                         continue;
                     } else {

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

@@ -637,7 +637,7 @@ const SpreadJsObj = {
             sheet.setRowCount(totalRow, spreadNS.SheetArea.viewport);
             // 控制空白行
             const emptyRows = sheet.getRange(sortData.length, -1, sheet.zh_setting.emptyRows, -1);
-            emptyRows.locked(sheet.zh_dataType === 'tree');
+            emptyRows.locked(sheet.zh_dataType === 'tree' || sheet.zh_setting.readOnly);
             if (sortData) {
                 // 单元格写入数据
                 sortData.forEach(function (data, i) {
@@ -981,6 +981,18 @@ const SpreadJsObj = {
             }
         }
     },
+    refreshSheetReadOnly: function (sheet) {
+        this.beginMassOperation(sheet);
+        if (sheet.zh_setting) {
+            sheet.zh_setting.cols.forEach(function (col, i) {
+                for (let iRow = 0; iRow < sheet.getRowCount(); iRow++) {
+                    sheet.getCell(iRow, i).locked(col.readOnly || sheet.zh_setting.readOnly || false);
+                }
+                //sheet.getRange(-1, i, -1, 1, GC.Spread.Sheets.SheetArea.viewport).locked(col.readOnly || sheet.zh_setting.readOnly || false);
+            });
+        }
+        this.endMassOperation(sheet);
+    },
     /**
      * 刷新列是否只读
      * @param sheet

+ 126 - 36
app/public/js/stage.js

@@ -163,6 +163,8 @@ $(document).ready(() => {
         rootId: -1,
         keys: ['id', 'tender_id', 'ledger_id'],
         stageId: 'id',
+        markFoldKey: 'bills-fold',
+        markFoldSubKey: window.location.pathname.split('/')[2],
     };
     // 台账树结构计算相关设置
     stageTreeSetting.updateFields = ['contract_qty', 'contract_tp', 'qc_qty', 'qc_tp', 'postil', 'used', 'contract_expr'];
@@ -517,16 +519,24 @@ $(document).ready(() => {
                 if (def && def.color) return def.color;
             }
 
-            const posRange = stagePos.ledgerPos[itemsPre + data.id] || [];
-            if (posRange.length > 0) {
-                for (const p of posRange) {
-                    if (p.end_contract_qty > p.quantity) return '#f8d7da';
+            if (checkTzMeasureType()) {
+                const posRange = stagePos.ledgerPos[itemsPre + data.id] || [];
+                if (posRange.length > 0) {
+                    for (const p of posRange) {
+                        if (p.end_contract_qty > p.quantity) return '#f8d7da';
+                    }
+                }
+                if (data.is_tp) {
+                    return data.end_contract_tp > data.total_price ? '#f8d7da' : defaultColor;
+                } else {
+                    return data.end_contract_qty > data.quantity ? '#f8d7da' : defaultColor;
                 }
-            }
-            if (data.is_tp) {
-                return data.end_contract_tp > data.total_price ? '#f8d7da' : defaultColor;
             } else {
-                return data.end_contract_qty > data.quantity ? '#f8d7da' : defaultColor;
+                if (data.is_tp) {
+                    return data.end_contract_tp > data.deal_tp ? '#f8d7da' : defaultColor;
+                } else {
+                    return data.end_contract_qty > data.deal_qty ? '#f8d7da' : defaultColor;
+                }
             }
         } else {
             return defaultColor;
@@ -593,6 +603,22 @@ $(document).ready(() => {
         },
     });
 
+    const checkList = $.ledger_checkList({
+        id: 'check-list',
+        tabSelector: '#check-list-tab',
+        selector: '#check-list',
+        relaSpread: slSpread,
+        storeKey: 'stage-check-' + window.location.pathname.split('/')[2] + '-' + window.location.pathname.split('/')[4],
+        checkType: ledgerCheckType,
+        afterLocated:  function () {
+            stagePosSpreadObj.loadCurPosData();
+        },
+        afterShow: function () {
+            slSpread.refresh();
+            if (spSpread) spSpread.refresh();
+        },
+    });
+
     const stageTreeSpreadObj = {
         loadExprToInput(sheet) {
             const sel = sheet.getSelections()[0];
@@ -1577,6 +1603,7 @@ $(document).ready(() => {
         stageIm.init(stage, imType, tenderInfo.decimal);
         stageIm.loadData(result.ledgerData, result.posData, result.detailData, result.changeData);
         errorList.loadHisErrorData();
+        checkList.loadHisCheckData();
     }, null, true);
     spSpread.bind(spreadNS.Events.EditEnded, stagePosSpreadObj.editEnded);
     spSpread.bind(spreadNS.Events.ClipboardPasting, stagePosSpreadObj.clipboardPasting);
@@ -1710,9 +1737,13 @@ $(document).ready(() => {
                 detail.spread.refresh();
             }
             if (checkedChanges) checkedChanges.refresh();
+
             if (errorList && errorList.spread) {
                 errorList.spread.refresh();
             }
+            if (checkList) {
+                checkList.spread.refresh();
+            }
         }
     });
 
@@ -1891,10 +1922,13 @@ $(document).ready(() => {
             if (detail) {
                 detail.spread.refresh();
             }
+            if (checkedChanges) checkedChanges.refresh();
             if (errorList && errorList.spread) {
                 errorList.spread.refresh();
             }
-            if (checkedChanges) checkedChanges.refresh();
+            if (checkList) {
+                checkList.spread.refresh();
+            }
             window.getSelection ? window.getSelection().removeAllRanges() : document.selection.empty();
         }
     });
@@ -2687,7 +2721,7 @@ $(document).ready(() => {
                     $('#calc-img').attr('src', '');
                     $('#view-calc-img').attr('src', '');
                     $('#view-calc-remark').val('');
-                    $('#text-edit').val('')
+                    $('#text-edit').val('');
                     $('#edit-img').modal('hide');
                     // postData(window.location.pathname + '/detail/merge-img', {updateType: 'clear', lid: data.lid, pid: data.pid, uuid: data.uuid}, function (result) {
                     //     stageIm.loadUpdateDetailData(result);
@@ -2705,7 +2739,7 @@ $(document).ready(() => {
                     $('#calc-img').attr('src', '');
                     $('#view-calc-img').attr('src', '');
                     $('#view-calc-remark').val('');
-                    $('#text-edit').val('')
+                    $('#text-edit').val('');
                     $('#edit-img').modal('hide');
                 }
             });
@@ -2971,16 +3005,24 @@ $(document).ready(() => {
                         }, {
                             key: 'over', title: '超计', valid: true,
                             check: function (node) {
-                                const posRange = stagePos.ledgerPos[itemsPre + node.id] || [];
-                                if (posRange.length > 0) {
-                                    for (const p of posRange) {
-                                        if (p.end_contract_qty > p.quantity) return true;
+                                if (checkTzMeasureType()) {
+                                    const posRange = stagePos.ledgerPos[itemsPre + node.id] || [];
+                                    if (posRange.length > 0) {
+                                        for (const p of posRange) {
+                                            if (p.end_contract_qty > p.quantity) return true;
+                                        }
+                                        return false;
+                                    } else if (node.end_gather_qty) {
+                                        return !node.quantity || Math.abs(node.end_gather_qty) > Math.abs(ZhCalc.add(node.quantity, node.end_qc_qty));
+                                    } else if (node.end_gather_tp) {
+                                        return !node.total_price || Math.abs(node.end_gather_tp) > Math.abs(ZhCalc.add(node.total_price, node.end_qc_tp));
+                                    }
+                                } else {
+                                    if (node.end_gather_qty) {
+                                        return !node.deal_qty || Math.abs(node.end_gather_qty) > Math.abs(ZhCalc.add(node.deal_qty, node.end_qc_qty));
+                                    } else if (node.end_gather_tp) {
+                                        return !node.deal_tp || Math.abs(node.end_gather_tp) > Math.abs(ZhCalc.add(node.deal_tp, node.end_qc_tp));
                                     }
-                                    return false;
-                                } else if (node.end_gather_qty) {
-                                    return !node.quantity || Math.abs(node.end_gather_qty) > Math.abs(ZhCalc.add(node.quantity, node.end_qc_qty));
-                                } else if (node.end_gather_tp) {
-                                    return !node.total_price || Math.abs(node.end_gather_tp) > Math.abs(ZhCalc.add(node.total_price, node.end_qc_tp));
                                 }
                             }
                         }, {
@@ -3042,6 +3084,9 @@ $(document).ready(() => {
             if (tab.attr('content') === '#error-list') {
                 errorList.spread.refresh();
             }
+            if (tab.attr('content') === '#check-list') {
+                checkList.spread.refresh();
+            }
         } else {
             tab.removeClass('active');
             tabPanel.removeClass('active');
@@ -3063,8 +3108,8 @@ $(document).ready(() => {
     });
     // 上传附件
     $('#upload-file-btn').click(function () {
-        if (curAuditor && curAuditor.aid !== parseInt(cur_uid)) {
-            return toastr.error('无法上传图片!');
+        if (curAuditor && curAuditor.aid !== cur_uid) {
+            return toastr.error('当前操作没有权限!');
         }
         const files = $('#upload-file')[0].files;
         const node = SpreadJsObj.getSelectObject(slSpread.getActiveSheet());
@@ -3118,8 +3163,8 @@ $(document).ready(() => {
             $('#show-att tr').eq(0).children('td').text(att.filename + att.fileext);
             const name = att.code !== null && att.code !== '' ? att.code : (att.b_code !== null ? att.b_code : '');
             $('#show-att tr').eq(1).children('td').text($.trim(name + ' ' + att.lname));
-            // $('#show-att tr').eq(2).find('a').attr('href', '/tender/' + tender.id + '/measure/stage/' + stage.order + '/download/file/' + att.id);
-            $('#show-att tr').eq(2).find('a').attr('href', att.filepath);
+            $('#show-att tr').eq(2).find('a').attr('href', '/tender/' + tender.id + '/measure/stage/' + stage.order + '/download/file/' + att.id);
+            // $('#show-att tr').eq(2).find('a').attr('href', att.filepath);
             $('#show-att tr').eq(3).children('td').eq(0).text(att.username);
             $('#show-att tr').eq(3).children('td').eq(1).text(att.in_time);
             $('#show-att tr').eq(4).children('td').text(att.remark);
@@ -3243,9 +3288,11 @@ $(document).ready(() => {
         } else if (content === 'view') {
             const data = {id: fid};
             postData('/tender/' + tender.id + '/measure/stage/' + stage.order + '/check/file', data, function (result) {
-              const { filepath } = result
-              $('#show-att tr').eq(2).find('a').attr('href', filepath)
-              $('#show-att tr').eq(2).find('a').children('span').eq(0).trigger('click')
+                const { filepath } = result
+                $('#load-file').attr('href', filepath);
+                $('#load-file')[0].click();
+                // $('#show-att tr').eq(2).find('a').attr('href', filepath)
+              // $('#show-att tr').eq(2).find('a').children('span').eq(0).trigger('click')
             });
         } else if (content === 'location') {
             const att = attData.find(item => item.id === parseInt(fid));
@@ -3328,6 +3375,42 @@ $(document).ready(() => {
         });
     })('a[name=showLevel]', slSpread.getActiveSheet());
 
+    const stageCheckerSetting = {
+        ledgerTree: stageTree,
+        ledgerPos: stagePos,
+        checkList: checkList,
+        decimal: tenderInfo.decimal,
+        checkOption: {
+            sibling: { enable: 0 },
+            empty_code: { enable: 0 },
+            calc: {
+                enable: 1,
+                fields: ['contract_qty', 'qc_qty'],
+            },
+            zero: { enable: 0 },
+            tp: {
+                enable: 1,
+                fields: [
+                    {qty: 'contract_qty', tp: 'contract_tp'},
+                    {qty: 'qc_qty', tp: 'qc_tp'},
+                ],
+                filter: function (node) {
+                    return node.is_tp;
+                }
+            },
+        }
+    };
+    if (!checkTzMeasureType()) {
+        stageCheckerSetting.checkOption.calc.fields.push('sgfh_qty', 'sjcl_qty', 'qtcl_qty', 'quantity');
+        stageCheckerSetting.checkOption.tp.fields.push(
+            {qty: 'sgfh_qty', tp: 'sgfh_tp'},
+            {qty: 'sjcl_qty', tp: 'sjcl_tp'},
+            {qty: 'qtcl_qty', tp: 'qtcl_tp'},
+            {qty: 'quantity', tp: 'total_price'},
+        );
+    }
+    LedgerChecker(stageCheckerSetting);
+
     const dataChecker = DataChecker({
         checkUrl: window.location.pathname + '/check',
         completeData: function (data) {
@@ -3350,22 +3433,29 @@ $(document).ready(() => {
         return false;
     });
     $('#audit-check0').submit(function (e) {
-        $(this).parent().parent().modal('hide');
+        const checkType = parseInt($('[name=checkType]').val());
         const data = {
-            opinion: $('[name=opinion]', this).val().replace(/\r\n/g, '<br/>').replace(/\n/g, '<br/>').replace(/\s/g, ' '),
-            checkType: parseInt($('[name=checkType]', this).val()),
+            opinion: $(`${'#sp-done'}`).find('[name=opinion]').val().replace(/\r\n/g, '<br/>').replace(/\n/g, '<br/>').replace(/\s/g, ' '),
+            checkType,
         };
-        dataChecker.checkAndPost(this.action, data);
+        $('#sp-done').modal('hide');
+        checkType && dataChecker.checkAndPost(this.action, data);
         $('#hide-all').hide();
         return false;
     });
-    $('#audit-check2').submit(function (e) {
-        $(this).parent().parent().modal('hide');
+    $('#audit-check1').submit(function (e) {
+        const checkType = parseInt($('[name=checkType]:checked').val());
         const data = {
-            opinion: $('[name=opinion]', this).val().replace(/\r\n/g, '<br/>').replace(/\n/g, '<br/>').replace(/\s/g, ' '),
-            checkType: parseInt($('[name=checkType]', this).val()),
+            opinion: $(`${'#sp-back'}`).find('[name=opinion]').val().replace(/\r\n/g, '<br/>').replace(/\n/g, '<br/>').replace(/\s/g, ' '),
+            checkType,
         };
-        dataChecker.checkAndPost(this.action, data);
+        if ($('#warning-text').length) $('#warning-text').remove()
+        if (!checkType && !$('#warning-text').length) {
+            $('#reject-process').prepend('<p id="warning-text" style="color: red; margin: 0;">请选择退回流程</p>');
+            return false
+        }
+        $('#sp-back').modal('hide');
+        checkType && dataChecker.checkAndPost(this.action, data);
         $('#hide-all').hide();
         return false;
     });

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

@@ -19,11 +19,15 @@ $(document).ready(() => {
     sjsSettingObj.setFxTreeStyle(xmjSpreadSetting, sjsSettingObj.FxTreeStyle.jz);
     if (thousandth) sjsSettingObj.setTpThousandthFormat(xmjSpreadSetting);
     SpreadJsObj.initSheet(xmjSheet, xmjSpreadSetting);
+    xmjSheet.frozenColumnCount(3);
+    xmjSheet.options.frozenlineColor = '#93b5e4';
 
     const unitSpread = SpreadJsObj.createNewSpread($('#unit-spread')[0]);
     const unitSheet = unitSpread.getActiveSheet();
     if (thousandth) sjsSettingObj.setTpThousandthFormat(xmjSpreadSetting);
     SpreadJsObj.initSheet(unitSheet, unitSpreadSetting);
+    unitSheet.frozenColumnCount(5);
+    unitSheet.options.frozenlineColor = '#93b5e4';
 
     const unitTreeObj = {
         loadCurUnitData: function () {

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

@@ -253,6 +253,15 @@ const stageIm = (function () {
         }
     }
 
+    function loadCustomDetail(im, detail) {
+        im.custom_define = detail.custom_define ? detail.custom_define.split(',') : imFields;
+        _.assignInWith(im, detail, function (oV, sV, key) {
+            return (im.custom_define.indexOf(key) > -1 && sV !== undefined && sV !== null) ? sV : oV;
+        });
+        im.calc_img_org = detail.calc_img_org;
+        im.calc_img_remark = detail.calc_img_remark;
+    }
+
     function checkCustomDetail(im) {
         const cd = _.find(details, function (d) {
             return im.lid === d.lid &&
@@ -263,12 +272,7 @@ const stageIm = (function () {
                 (!im.pid || im.pid === d.pid) &&
                 (!im.pos_name || im.pos_name === d.pos_name);
         });
-        if (cd) {
-            im.custom_define = cd.custom_define ? cd.custom_define.split(',') : imFields;
-            _.assignInWith(im, cd, function (oV, sV, key) {
-                return (im.custom_define.indexOf(key) > -1 && sV !== undefined && sV !== null) ? sV : oV;
-            });
-        }
+        if (cd) loadCustomDetail(im, cd);
     }
 
     function getCalcMemo(im) {
@@ -881,15 +885,7 @@ const stageIm = (function () {
                         (!im.pid || im.pid === d.pid);
                 });
             }
-            if (imData) {
-                _.assignInWith(imData, d, function (oV, sV, key) {
-                    return imFields.indexOf(key) > -1 && !_.isUndefined(sV) && !_.isNull(sV) ? sV : oV;
-
-                });
-                imData.calc_img = d.calc_img;
-                imData.calc_img_org = d.calc_img_org;
-                imData.calc_img_remark = d.calc_img_remark;
-            }
+            if (imData) loadCustomDetail(imData, d);
         }
     }
 

+ 1 - 3
app/public/js/stage_pay.js

@@ -470,14 +470,12 @@ $(document).ready(() => {
             if (text) {
                 const num = _.toNumber(text);
                 if (num) {
-                    data.tp = num;
-                    data.expr = null;
+                    data.expr = num;
                 } else {
                     const expr = $.trim(text).replace('\t', '').replace('=', '').toLowerCase();
                     const [valid, msg] = this._checkExprValid(expr);
                     if (!valid) return [valid, msg];
                     data.expr = expr;
-                    data.tp = null;
                 }
             } else {
                 data.tp = null;

+ 1 - 0
app/public/js/tender_list.js

@@ -342,6 +342,7 @@ function bindTenderUrl() {
             return t.id === tenderId;
         });
         if (!tender) return;
+        if (!tender.measure_type && tender.user_id !== userID) return;
         if (tender.measure_type) {
             // window.location.href = '/tender/' + tenderId;
             window.open('/tender/' + tenderId, '_blank');

+ 53 - 37
app/public/js/tender_list_info.js

@@ -296,7 +296,7 @@ function recursiveGetTenderNodeHtml (node, arr, pid) {
     const html = [];
     html.push('<tr pid="' + pid + '">');
     // 名称
-    html.push('<td style="width: 23%" class="in-' + node.level + '">');
+    html.push('<td style="width: 20%" class="in-' + node.level + '">');
     if (node.cid) {
         html.push('<span onselectstart="return false" style="{-moz-user-select:none}" class="fold-switch mr-1" title="收起" cid="'+ node.sort_id +'"><i class="fa fa-minus-square-o"></i></span> <i class="fa fa-folder-o"></i> ', node.name);
     } else {
@@ -308,52 +308,64 @@ function recursiveGetTenderNodeHtml (node, arr, pid) {
     }
     html.push('</td>');
     // 计量模式
-    html.push('<td style="width: 7%" class="text-center">');
+    html.push('<td style="width: 6%" class="text-center">');
     if (node.measure_type) {
         html.push(node.measure_type === measureType.tz.value ? '0号台账' : '工程量清单');
     }
     html.push('</td>');
-    // 计量期数
-    html.push('<td style="width: 7%" class="text-center">');
-    if (!node.cid) {
-        html.push(node.lastStage ? '第' + node.lastStage.order + '期' : '台账');
+    // 计量进度
+    html.push('<td style="width: 6%">');
+    if (!node.cid && node.cur_flow) {
+        html.push(node.cur_flow.title + ' (' + '<span class="' + node.cur_flow.status_class +'">' + node.cur_flow.status + '</span>' + ')');
     }
     html.push('</td>');
-    // 审批状态
-    html.push('<td style="width: 7%">');
-    html.push(node.lastStage ? auditConst.stage.statusString[node.lastStage.status] : auditConst.ledger.statusString[node.ledger_status]);
+    // 当前流程
+    html.push('<td style="width: 6%">');
+    if (!node.cid && node.cur_flow) {
+        html.push(node.cur_flow.name + ' ' + '<span class="' + node.cur_flow.status_class +'">' + node.cur_flow.status + '</span>');
+    }
+    html.push('</td>');
+    // 上一流程审批时间
+    html.push('<td style="width: 8%">');
+    if (!node.cid && node.pre_flow) {
+        html.push(node.pre_flow.name + ' ' + moment(node.pre_flow.time).format('YYYY-MM-DD'));
+    }
     html.push('</td>');
     // 0号台账合同
-    html.push('<td style="width: 7%" class="text-right">');
-    html.push(node.total_price);
+    html.push('<td style="width: 6%" class="text-right">');
+    html.push(node.total_price || '');
     html.push('</td>');
     // 本期完成
-    html.push('<td style="width: 7%" class="text-right">');
-    html.push(node.gather_tp);
+    html.push('<td style="width: 6%" class="text-right">');
+    html.push(node.gather_tp || '');
     html.push('</td>');
     // 截止本期合同
-    html.push('<td style="width: 7%" class="text-right">');
-    html.push(node.end_contract_tp);
+    html.push('<td style="width: 6%" class="text-right">');
+    html.push(node.end_contract_tp || '');
     html.push('</td>');
     // 截止本期变更
-    html.push('<td style="width: 7%" class="text-right">');
-    html.push(node.end_qc_tp);
+    html.push('<td style="width: 6%" class="text-right">');
+    html.push(node.end_qc_tp || '');
     html.push('</td>');
     // 截止本期完成
-    html.push('<td style="width: 7%" class="text-right">');
-    html.push(node.end_gather_tp);
+    html.push('<td style="width: 6%" class="text-right">');
+    html.push(node.end_gather_tp || '');
     html.push('</td>');
     // 截止上期完成
-    html.push('<td style="width: 7%" class="text-right">');
-    html.push(node.pre_gather_tp);
+    html.push('<td style="width: 6%" class="text-right">');
+    html.push(node.pre_gather_tp || '');
+    html.push('</td>');
+    // 预付款
+    html.push('<td style="width: 6%" class="text-right">');
+    html.push(node.advance_tp || '');
     html.push('</td>');
     // 本期应付
-    html.push('<td style="width: 7%" class="text-right">');
-    html.push(node.yf_tp);
+    html.push('<td style="width: 6%" class="text-right">');
+    html.push(node.yf_tp || '');
     html.push('</td>');
     // 截止本期应付
-    html.push('<td style="width: 7%" class="text-right">');
-    html.push(node.end_yf_tp);
+    html.push('<td style="width: 6%" class="text-right">');
+    html.push(node.end_yf_tp || '');
     html.push('</td>');
     html.push('</tr>');
     if (node.children) {
@@ -369,18 +381,20 @@ function getTenderTreeHtml () {
         const html = [];
         html.push('<table class="table table-hover table-bordered">');
         html.push('<thead style="position: fixed;left:56px;top: 34px;">', '<tr>');
-        html.push('<th class="text-center" style="width: 23%">', '标段名称', '</th>');
-        html.push('<th class="text-center" style="width: 7%">', '计量模式', '</th>');
-        html.push('<th class="text-center" style="width: 7%">', '计量期数', '</th>');
-        html.push('<th class="text-center" style="width: 7%">', '审批状态', '</th>');
-        html.push('<th class="text-center" style="width: 7%">', '0号台帐', '</th>');
-        html.push('<th class="text-center" style="width: 7%">', '本期完成', '</th>');
-        html.push('<th class="text-center" style="width: 7%">', '截止本期合同', '</th>');
-        html.push('<th class="text-center" style="width: 7%">', '截止本期变更', '</th>');
-        html.push('<th class="text-center" style="width: 7%">', '截止本期完成', '</th>');
-        html.push('<th class="text-center" style="width: 7%">', '截止上期完成', '</th>');
-        html.push('<th class="text-center" style="width: 7%">', '本期应付', '</th>');
-        html.push('<th class="text-center" style="width: 7%">', '截止本期应付', '</th>');
+        html.push('<th class="text-center" style="width: 20%">', '标段名称', '</th>');
+        html.push('<th class="text-center" style="width: 6%">', '计量模式', '</th>');
+        html.push('<th class="text-center" style="width: 6%">', '计量进度', '</th>');
+        html.push('<th class="text-center" style="width: 6%">', '当前流程', '</th>');
+        html.push('<th class="text-center" style="width: 8%">', '上一流程审批时间', '</th>');
+        html.push('<th class="text-center" style="width: 6%">', '0号台帐', '</th>');
+        html.push('<th class="text-center" style="width: 6%">', '本期完成', '</th>');
+        html.push('<th class="text-center" style="width: 6%">', '截止本期合同', '</th>');
+        html.push('<th class="text-center" style="width: 6%">', '截止本期变更', '</th>');
+        html.push('<th class="text-center" style="width: 6%">', '截止本期完成', '</th>');
+        html.push('<th class="text-center" style="width: 6%">', '截止上期完成', '</th>');
+        html.push('<th class="text-center" style="width: 6%">', '预付款', '<i class="fa fa-question-circle text-primary" data-placement="bottom" data-toggle="tooltip" data-original-title="预付款流程中截止本期金额"></i>', '</th>');
+        html.push('<th class="text-center" style="width: 6%">', '本期应付', '</th>');
+        html.push('<th class="text-center" style="width: 6%">', '截止本期应付', '</th>');
         html.push('</tr>', '</thead>');
         parentId = 0;
         for (const t of tenderTree) {
@@ -398,6 +412,8 @@ function bindTenderUrl() {
         const tender = _.find(tenders, function (t) {
             return t.id === tenderId;
         });
+        if (!tender) return;
+        if (!tender.measure_type && tender.user_id !== userID) return;
         if (tender.measure_type) {
             // window.location.href = '/tender/' + tenderId;
             window.open('/tender/' + tenderId, '_blank');

+ 2 - 0
app/public/js/tender_list_manage.js

@@ -333,6 +333,8 @@ function bindTenderUrl() {
         const tender = _.find(tenders, function (t) {
             return t.id === tenderId;
         });
+        if (!tender) return;
+        if (!tender.measure_type && tender.user_id !== userID) return;
         if (tender.measure_type) {
             // window.location.href = '/tender/' + tenderId;
             window.open('/tender/' + tenderId, '_blank');

+ 24 - 16
app/public/js/tender_list_progress.js

@@ -308,7 +308,7 @@ function recursiveGetTenderNodeHtml (node, arr, pid) {
     const html = [];
     html.push('<tr pid="' + pid + '">');
     // 名称
-    html.push('<td width="30%" class="in-' + node.level + '">');
+    html.push('<td width="25%" class="in-' + node.level + '">');
     if (node.cid) {
         html.push('<span onselectstart="return false" style="{-moz-user-select:none}" class="fold-switch mr-1" title="收起" cid="'+ node.sort_id +'"><i class="fa fa-minus-square-o"></i></span> <i class="fa fa-folder-o"></i> ', node.name);
     } else {
@@ -319,21 +319,26 @@ function recursiveGetTenderNodeHtml (node, arr, pid) {
         html.push('<a href="javascript: void(0)" id="' + node.id + '">', node.name, '</a>');
     }
     html.push('</td>');
-    // 计量期数
-    html.push('<td width="120" class="text-center">');
-    if (!node.cid) {
-        html.push(node.lastStage ? '第' + node.lastStage.order + '期' : '台账');
+    // 计量进度
+    html.push('<td style="width: 10%">');
+    if (!node.cid && node.cur_flow) {
+        html.push(node.cur_flow.title + ' (' + '<span class="' + node.cur_flow.status_class +'">' + node.cur_flow.status + '</span>' + ')');
     }
     html.push('</td>');
-
-    // 审批状态
+    // 当前流程
     html.push('<td style="width: 10%">');
-    html.push(node.lastStage ? auditConst.stage.statusString[node.lastStage.status] : auditConst.ledger.statusString[node.ledger_status]);
-    html.push(node.status_users ? '(' + node.status_users + ')' : '');
+    if (!node.cid && node.cur_flow) {
+        html.push(node.cur_flow.name + ' ' + '<span class="' + node.cur_flow.status_class +'">' + node.cur_flow.status + '</span>');
+    }
     html.push('</td>');
-
-    // 累计合同计量
-    html.push('<td width="15%" class="text-right">');
+    // 上一流程审批时间
+    html.push('<td style="width: 10%">');
+    if (!node.cid && node.pre_flow) {
+        html.push(node.pre_flow.name + ' ' + moment(node.pre_flow.time).format('YYYY-MM-DD'));
+    }
+    html.push('</td>');
+    // 总价
+    html.push('<td width="10%" class="text-right">');
     const sum = node.lastStage ? ZhCalc.add(node.total_price, node.lastStage.end_qc_tp) : node.total_price;
     html.push(node.sum_tp ? node.sum_tp : '');
     html.push('</td>');
@@ -359,11 +364,12 @@ function getTenderTreeHtml () {
         const html = [];
         html.push('<table class="table table-hover table-bordered">');
         html.push('<thead style="position: fixed;left:56px;top: 34px;">', '<tr>');
-        html.push('<th style="width: 30%" class="text-center">', '标段名称', '</th>');
-        html.push('<th style="width: 10%" class="text-center">', '计量期数', '</th>');
-        html.push('<th style="width: 10%" class="text-center">','审批状态','</th>');
+        html.push('<th style="width: 25%" class="text-center">', '标段名称', '</th>');
+        html.push('<th class="text-center" style="width: 10%">', '计量进度', '</th>');
+        html.push('<th class="text-center" style="width: 10%">', '当前流程', '</th>');
+        html.push('<th class="text-center" style="width: 10%">', '上一流程审批时间', '</th>');
         html.push('<th style="width: 10%" class="text-center">', '总价 <i class="fa fa-question-circle text-primary"  data-placement="bottom" data-toggle="tooltip" data-original-title="0号台账+截止本期数量变更"></i>', '</th>');
-        html.push('<th style="width: 40%" class="text-center">', '截止上期完成/本期完成/未完成', '</th>');
+        html.push('<th style="width: 35%" class="text-center">', '截止上期完成/本期完成/未完成', '</th>');
         html.push('</tr>', '</thead>');
         parentId = 0;
         for (const t of tenderTree) {
@@ -381,6 +387,8 @@ function bindTenderUrl() {
         const tender = _.find(tenders, function (t) {
             return t.id === tenderId;
         });
+        if (!tender) return;
+        if (!tender.measure_type && tender.user_id !== userID) return;
         if (tender.measure_type) {
             // window.location.href = '/tender/' + tenderId;
             window.open('/tender/' + tenderId, '_blank');

+ 4 - 2
app/public/report/js/rpt_jspdf.js

@@ -101,8 +101,10 @@ let JpcJsPDFHelper = {
                         private_drawSignature(doc, ctx, cell, styles, controls, newPageMergeBand, true, signatureRelArr);
                     }
                 }
-                for (let cell of page.watermark_cells) {
-                    _drawWatermark(doc, ctx, cell, controls);
+                if (PAGE_SHOW['closeWatermark'] === 0) {
+                    for (let cell of page.watermark_cells) {
+                        _drawWatermark(doc, ctx, cell, controls);
+                    }
                 }
             }
         }

+ 5 - 0
app/public/report/js/rpt_main.js

@@ -242,6 +242,7 @@ let zTreeOprObj = {
             params.stage_order = getStageOrder();
             params.stage_times = getStageTimes();
             params.material_order = getMaterialOrder();
+            params.closeWatermark = getCloseWatermark();
             params.custCfg = CUST_CFG;
 
             const gather_select = customSelects.gather_select.find(function (x) {
@@ -534,6 +535,7 @@ let rptControlObj = {
         rst.stage_times = getStageTimes();
         rst.material_order = getMaterialOrder();
         rst.custCfg = CUST_CFG;
+        rst.closeWatermark = getCloseWatermark();
 
         return rst;
     },
@@ -907,3 +909,6 @@ function getMaterialOrder() {
 function getStageTimes() {
     return current_stage_times;
 }
+function getCloseWatermark() {
+    return PAGE_SHOW['closeWatermark'];
+}

+ 17 - 6
app/public/report/js/rpt_preview_common.js

@@ -7,6 +7,7 @@ function printPageLoading() {
     if (sessionStorage.multiRptsData) {
         let multiRptData = JSON.parse(sessionStorage.multiRptsData);
         let scaleFactor = parseInt(sessionStorage.scaleFactor);
+        let closeWaterMark = parseInt(sessionStorage.closeWaterMark);
         for (let idx = 0; idx < multiRptData.length; idx++) {
             let pageData = multiRptData[idx];
             if (idx === 0) {
@@ -14,7 +15,7 @@ function printPageLoading() {
             }
             let orgPixelSize = getPixelSize(pageData);
             let actArea = getActualArea(pageData);
-            let svgArr = rptPrintHelper.buildSvgArr(pageData, actArea, G_OFFSET_X, G_OFFSET_Y);
+            let svgArr = rptPrintHelper.buildSvgArr(pageData, actArea, G_OFFSET_X, G_OFFSET_Y, closeWaterMark);
             //let orientation = (pageData[JV.NODE_PAGE_INFO][JV.NODE_PAGE_SIZE][0] < pageData[JV.NODE_PAGE_INFO][JV.NODE_PAGE_SIZE][1])?"纵向":"横向";
             let orientation = "纵向";
             // showPreviewData(svgArr, actArea, scaleFactor, sessionStorage.pageSize, orientation, orgPixelSize);
@@ -81,11 +82,9 @@ function showPreviewData(svgArr, actAreaArr, scaleFactor, pageSize, orientation,
     }
 }
 
-function getActualArea(pageData) {
-    let rst = [];
-    for (let item of pageData.items) {
-        let area = {Left: 10000, Right: 0, Top: 10000, Bottom: 0};
-        for (let cell of item.cells) {
+function _chkActArea(area, cells) {
+    if (cells && cells.length > 0) {
+        for (let cell of cells) {
             if (cell.area.Left < area.Left) {
                 area.Left = cell.area.Left;
             }
@@ -99,6 +98,18 @@ function getActualArea(pageData) {
                 area.Bottom = cell.area.Bottom;
             }
         }
+    }
+}
+
+function getActualArea(pageData) {
+    let rst = [];
+
+    for (let item of pageData.items) {
+        let area = {Left: 10000, Right: 0, Top: 10000, Bottom: 0};
+        _chkActArea(area, item.cells);
+        _chkActArea(area, item.signature_cells);
+        _chkActArea(area, item.signature_date_cells);
+        _chkActArea(area, item.signature_audit_cells);
         rst.push(area);
     }
     return rst;

+ 7 - 4
app/public/report/js/rpt_print.js

@@ -57,6 +57,7 @@ let rptPrintHelper = {
                     sessionStorage.pageSize = rptControlObj.getCurrentPageSize();
                     sessionStorage.orientation = rptControlObj.getCurrentOrientation();
                     sessionStorage.scaleFactor = 1;
+                    sessionStorage.closeWaterMark = getCloseWatermark();
                     if (sessionStorage.pageSize === 'A3') {
                         window.open('/printReport/A3');
                     } else {
@@ -79,7 +80,7 @@ let rptPrintHelper = {
     previewSvgData: function() {
         //
     },
-    buildSvgArr: function (pagesData, actAreaArr, offsetX, offsetY) {
+    buildSvgArr: function (pagesData, actAreaArr, offsetX, offsetY, closeWaterMark) {
         let styles = pagesData[JV.NODE_STYLE_COLLECTION],
             fonts = pagesData[JV.NODE_FONT_COLLECTION],
             controls = pagesData[JV.NODE_CONTROL_COLLECTION]
@@ -115,9 +116,11 @@ let rptPrintHelper = {
                 svgPageArr.push(buildSignatureCellSvg(cell, styles, controls, page[JV.PROP_PAGE_MERGE_BORDER], pagesData[JV.BAND_PROP_MERGE_BAND],
                     offsetX - actAreaOffsetX, offsetY - actAreaOffsetY, adjustY, canvas, isHtoV, pixelSize, actAreaArr[idx]));
             }
-            for (let cell of page.watermark_cells) {
-                svgPageArr.push(buildSignatureCellSvg(cell, styles, controls, page[JV.PROP_PAGE_MERGE_BORDER], pagesData[JV.BAND_PROP_MERGE_BAND],
-                    offsetX - actAreaOffsetX, offsetY - actAreaOffsetY, adjustY, canvas, isHtoV, pixelSize, actAreaArr[idx]));
+            if (closeWaterMark === 0) {
+                for (let cell of page.watermark_cells) {
+                    svgPageArr.push(buildSignatureCellSvg(cell, styles, controls, page[JV.PROP_PAGE_MERGE_BORDER], pagesData[JV.BAND_PROP_MERGE_BAND],
+                        offsetX - actAreaOffsetX, offsetY - actAreaOffsetY, adjustY, canvas, isHtoV, pixelSize, actAreaArr[idx]));
+                }
             }
             // 计量有电子签名日期,在处理上与cells一样
             for (let cell of page.signature_date_cells) {

+ 2 - 1
app/reports/rpt_component/jpc_flow_tab.js

@@ -716,7 +716,8 @@ JpcFlowTabSrv.prototype.createNew = function() {
                             // 2018-08-04 其实之前的判断逻辑完全是自找麻烦,而且还不够正确。其实只需要判断已经处理了多少行纪录(所有的都算,包括空白行),
                             // 与总的seg纪录数想比较,就很容易得到结果,而且能处理极端边界的情况。
                             pageStatus[JV.STATUS_SEGMENT_END] = true;
-                            pageStatus[JV.STATUS_REPORT_END] = true;
+                            // pageStatus[JV.STATUS_REPORT_END] = true;
+                            pageStatus[JV.STATUS_REPORT_END] = (segIdx === me.segments.length - 1);
                             private_resetBandArea();
                             const hasAdHocRow = ((adHocAutoHeightAmt > maxRowRec) || !JpcFlowTabHelper.chkSegEnd(bands, rptTpl, ttlSegRecAmt, handledRowAmt, maxRowRec, me.isEx));
 

+ 108 - 9
app/reports/util/rpt_excel_util.js

@@ -331,6 +331,31 @@ function writeSharedString(sharedStrList) {
     }
     return rst;
 }
+function _setupPgBrks(pageData) {
+    pageData.pageBreaks = [];
+    let pgBrkIdx = 0;
+    const pgBrk = [];
+    function _insertMaxBottom(pCells, targetBottomArr) {
+        if (pCells) {
+            for (const cell of pCells) {
+                if (targetBottomArr.indexOf(cell[JV.PROP_AREA][JV.PROP_BOTTOM]) < 0) {
+                    targetBottomArr.push(cell[JV.PROP_AREA][JV.PROP_BOTTOM]);
+                }
+            }
+        }
+    }
+    for (const page of pageData.items) {
+        const maxBottomArr = [];
+        _insertMaxBottom(page.cells, maxBottomArr);
+        _insertMaxBottom(page.signature_cells, maxBottomArr);
+        _insertMaxBottom(page.signature_date_cells, maxBottomArr);
+        _insertMaxBottom(page.signature_audit_cells, maxBottomArr);
+        pgBrkIdx += maxBottomArr.length;
+        pgBrk.push(pgBrkIdx);
+    }
+    pageData.pageBreaks.push(pgBrk);
+}
+
 function writeSheets(pageData, paperSize, sharedStrList, stylesObj, isSinglePage, custSheetMergeBands, hasSignature, signSheetIdxArr) {
     const rst = [];
     const private_pushDftFont = function() {
@@ -374,14 +399,14 @@ function writeSheets(pageData, paperSize, sharedStrList, stylesObj, isSinglePage
     private_buildFirstDftStyle();
     let sheetIdx = 0;
     if (isSinglePage) {
-        rst.push(writeSheet(pageData, null, paperSize, sharedStrList, stylesObj, null, hasSignature, sheetIdx, signSheetIdxArr, true));
+        rst.push(writeSheet(pageData, null, paperSize, sharedStrList, stylesObj, null, hasSignature, sheetIdx, signSheetIdxArr, true, -1));
     } else {
         for (let i = 0; i < pageData.items.length; i++) {
             let appointedMergeBand = null;
             if (custSheetMergeBands && custSheetMergeBands.length > i) {
                 appointedMergeBand = custSheetMergeBands[i];
             }
-            rst.push(writeSheet(pageData, pageData.items[i], paperSize, sharedStrList, stylesObj, appointedMergeBand, hasSignature, sheetIdx, signSheetIdxArr, i === 0));
+            rst.push(writeSheet(pageData, pageData.items[i], paperSize, sharedStrList, stylesObj, appointedMergeBand, hasSignature, sheetIdx, signSheetIdxArr, i === 0, i));
             sheetIdx++;
         }
     }
@@ -477,6 +502,18 @@ function preAnalyzePos(pageData, sheetData, xPos, yPos, yMultiPos) {
             cell = theShtData[JV.PROP_SIGNATURE_AUDIT_CELLS][i];
             private_set_cell_pos(cell, theXPos, theYPos);
         }
+        for (let i = 0; i < theShtData[JV.PROP_WATERMARK_CELLS].length; i++) {
+            cell = theShtData[JV.PROP_WATERMARK_CELLS][i];
+            // private_set_cell_pos(cell, theXPos, theYPos);
+            let cellControl;
+            if (typeof cell[JV.PROP_CONTROL] === 'string') {
+                cellControl = pageData[JV.NODE_CONTROL_COLLECTION][cell[JV.PROP_CONTROL]];
+            } else {
+                cellControl = cell[JV.PROP_CONTROL];
+            }
+            const area = getProperSignatureArea(cell, cellControl);
+            private_set_cell_pos2(area, theXPos, theYPos);
+        }
     };
     xPos.push(0);
     if (sheetData) {
@@ -509,7 +546,7 @@ function preAnalyzePos(pageData, sheetData, xPos, yPos, yMultiPos) {
         }
     }
 }
-function writeSheet(pageData, sheetData, paperSize, sharedStrList, stylesObj, appointedMergeBand, hasSignature, sheetIdx, signSheetIdxArr, isFirstSheet) {
+function writeSheet(pageData, sheetData, paperSize, sharedStrList, stylesObj, appointedMergeBand, hasSignature, sheetIdx, signSheetIdxArr, isFirstSheet, pgBrkIdx) {
     const rst = [];
     const xPos = [];
     const yPos = [];
@@ -943,6 +980,23 @@ function writeSheet(pageData, sheetData, paperSize, sharedStrList, stylesObj, ap
         // }
         rst.push('<drawing r:id="rId1"/>');
     }
+    // 插入分页符---------------------------
+    if (pageData.pageBreaks) {
+        let pgBrks = [];
+        if (sheetData !== null) {
+            pgBrks = pageData.pageBreaks[pgBrkIdx];
+        } else {
+            pgBrks = pageData.pageBreaks[0];
+        }
+        if (pgBrks.length > 1) {
+            rst.push('<rowBreaks count="' + pgBrks.length + '" manualBreakCount="' + pgBrks.length + '" >');
+            for (let pbi = 0; pbi < pgBrks.length; pbi++) {
+                rst.push('<brk id="' + pgBrks[pbi] + '" max="16383" man="1" />');
+            }
+            rst.push('</rowBreaks>');
+        }
+    }
+    // 插入分页符结束---------------------------
     rst.push('</worksheet>');
     return rst;
 }
@@ -1087,6 +1141,12 @@ function writeDrawing(pageData, sheetData, subSignKeyArr) {
                 startPicIdx++;
             }
         }
+        for (const sCell of sheetData[JV.PROP_WATERMARK_CELLS]) {
+            if (subSignKeyArr.indexOf(sCell.signature_name) >= 0) {
+                private_setSheetDrawingCellData(sCell, yPos, startPicIdx, 0);
+                startPicIdx++;
+            }
+        }
     } else {
         // total data in one sheet
         let rowOffset = 0;
@@ -1100,6 +1160,12 @@ function writeDrawing(pageData, sheetData, subSignKeyArr) {
                     startPicIdx++;
                 }
             }
+            for (const sCell of shtItemData[JV.PROP_WATERMARK_CELLS]) {
+                if (subSignKeyArr.indexOf(sCell.signature_name) >= 0) {
+                    private_setSheetDrawingCellData(sCell, tmpPos, startPicIdx, rowOffset);
+                    startPicIdx++;
+                }
+            }
             rowOffset += tmpPos.length - 2;
         }
     }
@@ -1159,7 +1225,7 @@ function _createDummyCell(dummyOrgCells, parentPage) {
             control: 'Default',
             style: 'Default_None',
             Value: '',
-            area: { Left: 0, Right: 0, Top: 0, Bottom: 0 }
+            area: { Left: 0, Right: 0, Top: 0, Bottom: 0 },
         };
         if (signature.style) dummyCell.style = signature.style;
         dummyCell[JV.PROP_AREA][JV.PROP_LEFT] = signature[JV.PROP_AREA][JV.PROP_LEFT];
@@ -1200,9 +1266,9 @@ function createDummySignatureCell(pageData) {
     }
 }
 
-function checkAndSetSignatureCache(pageData, signKeyArr, signPathArr, roleRel, signSheetIdxArr) {
+function _checkAndSetSignatureCache(pageData, signKeyArr, signPathArr, roleRel, signSheetIdxArr) {
     // 备注:电子签名是以图形的方式处理,一页可以有多个签名,多页的签名基本是引用同样的图片,在这里先处理一下,后期统一引用。
-    //      另:以后的图片(在电子签名(signature_cells)以外的图片)会单独处理(如计算草图等)
+    //      另:以后的图片(在电子签名(signature_cells)以外的图片)会单独处理(如计算草图、水印等)
     let rst = false;
     let chkRoles = [];
     if (roleRel instanceof Array) {
@@ -1264,6 +1330,19 @@ function checkAndSetSignatureCache(pageData, signKeyArr, signPathArr, roleRel, s
                 }
             }
         }
+        if (page[JV.PROP_WATERMARK_CELLS] && page[JV.PROP_WATERMARK_CELLS].length > 0) {
+            // 水印,单独处理
+            const waterMarkCell = page[JV.PROP_WATERMARK_CELLS][0];
+            // console.log('waterMarkCell');
+            // console.log(waterMarkCell);
+            const signPath = { path: null, pic: null };
+            signPathArr[pageIdx].push(signPath);
+            signPath.pic = waterMarkCell.pic;
+            signPath.path = waterMarkCell.path;
+            signKeyArr[pageIdx].push(waterMarkCell.signature_name);
+            rst = true;
+            signSheetIdxArr[pageIdx] = true;
+        }
     }
     return rst;
 }
@@ -1288,7 +1367,7 @@ module.exports = {
         const signSheetIdxArr = []; // 确定哪些sheet有签名(在多表导出的时候,有些表可能没有签名,但其他的表有签名)
         // console.log('in exportExcel!');
         // console.log(roleRel);
-        // const hasSignature = (roleRel !== null) ? checkAndSetSignatureCache(pageData, signKeyArr, signPathArr, roleRel, signSheetIdxArr) : false;
+        // const hasSignature = (roleRel !== null) ? _checkAndSetSignatureCache(pageData, signKeyArr, signPathArr, roleRel, signSheetIdxArr) : false;
         let thisRoleRel = [];
         if (roleRel !== null && roleRel !== undefined) {
             thisRoleRel = roleRel;
@@ -1297,7 +1376,7 @@ module.exports = {
         // console.log(thisRoleRel);
 
         const hasSignature = false; // 暂时不支持电子签名、草图导出excel
-        // const hasSignature = checkAndSetSignatureCache(pageData, signKeyArr, signPathArr, thisRoleRel, signSheetIdxArr); // 因草图的关系,thisRoleRel是否为null就不是充要的条件
+        // const hasSignature = _checkAndSetSignatureCache(pageData, signKeyArr, signPathArr, thisRoleRel, signSheetIdxArr); // 因草图的关系,thisRoleRel是否为null就不是充要的条件
         // console.log('signKeyArr');
         // console.log(signKeyArr);
         // console.log('signPathArr');
@@ -1307,6 +1386,9 @@ module.exports = {
             createDummySignatureCell(pageData);
         }
         if (isSinglePage) {
+            // 加分页符(分页符要在外部处理好)-------------------
+            _setupPgBrks(pageData);
+            // 加分页符结束-------------------
             sheets.push({ sheetName: '全部页' });
         } else {
             if (custSheetNames && custSheetNames.length === pageData.items.length) {
@@ -1501,6 +1583,9 @@ module.exports = {
                 mergeBand[JV.PROP_RIGHT].push(pageDataArray[i][JV.BAND_PROP_MERGE_BAND][JV.PROP_RIGHT]);
                 sheetNames.push(pageDataArray[i][JV.NODE_PAGE_INFO][JV.NODE_MAIN_INFO_RPT_NAME]);
 
+                // 加分页符(分页符要在外部处理好)-------------------
+                _setupPgBrks(pageDataArray[i]);
+                // 加分页符结束-------------------
                 for (let j = 0; j < pageDataArray[i].items.length; j++) {
                     let maxY = 0;
                     let minY = 100000;
@@ -1563,8 +1648,9 @@ module.exports = {
                     }
                 }
             }
-            // 2. newPageData的items属性
+            // 2. newPageData的items属性及相关分页符
             newPageData.items = [];
+            newPageData.pageBreaks = [];
             for (let i = 0; i < pageDataArray.length; i++) {
                 const pageItem = {};
                 pageItem[JV.PROP_PAGE_SEQ] = i + 1;
@@ -1592,9 +1678,22 @@ module.exports = {
                         pageItem[JV.PROP_SIGNATURE_AUDIT_CELLS].push(pageDataArray[i].items[j][JV.PROP_SIGNATURE_AUDIT_CELLS][k]);
                     }
                 }
+                pageItem[JV.PROP_WATERMARK_CELLS] = [];
+                for (let j = 0; j < pageDataArray[i].items.length; j++) {
+                    for (let k = 0; k < pageDataArray[i].items[j][JV.PROP_WATERMARK_CELLS].length; k++) {
+                        pageItem[JV.PROP_WATERMARK_CELLS].push(pageDataArray[i].items[j][JV.PROP_WATERMARK_CELLS][k]);
+                    }
+                }
                 newPagePos[i][JV.NODE_PAGE_SIZE] = pageDataArray[i][JV.NODE_PAGE_INFO][JV.NODE_PAGE_SIZE];
                 pageItem[JV.PAGE_SPECIAL_MERGE_POS] = newPagePos[i];
                 newPageData.items.push(pageItem);
+                // 重新分配分页符()------------------
+                if (pageDataArray[i].pageBreaks.length === 1) {
+                    newPageData.pageBreaks.push(pageDataArray[i].pageBreaks[0]);
+                } else {
+                    newPageData.pageBreaks.push([]);
+                }
+                // 重新分配分页符结束------------------
             }
             // 3. everything is ok, then call me
             // let roleRel = null; // 未来调用的时候,这个属性要从外面给!!!

+ 139 - 133
app/router.js

@@ -9,6 +9,7 @@ module.exports = app => {
     const projectManagerCheck = app.middlewares.projectManagerCheck();
     // 标段读取中间件
     const tenderCheck = app.middlewares.tenderCheck();
+    const uncheckTenderCheck = app.middlewares.uncheckTenderCheck();
     // 期读取中间件
     const stageCheck = app.middlewares.stageCheck();
     // 材料调差读取中间件
@@ -127,156 +128,157 @@ module.exports = app => {
     app.get('/tender/:id/cooperation', sessionAuth, tenderCheck, 'tenderController.tenderCooperation');
 
     // 台账管理相关
-    app.get('/tender/:id/ledger', sessionAuth, tenderCheck, 'ledgerController.explode');
-    app.post('/tender/:id/ledger/load', sessionAuth, tenderCheck, 'ledgerController.loadExplodeData');
-    app.post('/tender/:id/ledger/get-children', sessionAuth, tenderCheck, 'ledgerController.getChildren');
-    app.post('/tender/:id/ledger/update', sessionAuth, tenderCheck, 'ledgerController.update');
-    app.post('/tender/:id/ledger/upload-excel/:ueType', sessionAuth, tenderCheck, 'ledgerController.uploadExcel');
-    app.get('/tender/:id/ledger/download/:file', sessionAuth, tenderCheck, 'ledgerController.download');
-    app.post('/tender/:id/pos/update', sessionAuth, tenderCheck, 'ledgerController.posUpdate');
-    app.post('/tender/:id/pos/paste', sessionAuth, tenderCheck, 'ledgerController.posPaste');
-    app.post('/tender/:id/ledger/deal2sgfh', sessionAuth, tenderCheck, 'ledgerController.deal2sgfh');
-    app.post('/tender/:id/ledger/check', sessionAuth, tenderCheck, 'ledgerController.check');
+    app.get('/tender/:id/ledger', sessionAuth, tenderCheck, uncheckTenderCheck, 'ledgerController.explode');
+    app.post('/tender/:id/ledger/load', sessionAuth, tenderCheck, uncheckTenderCheck, 'ledgerController.loadExplodeData');
+    app.post('/tender/:id/ledger/get-children', sessionAuth, tenderCheck, uncheckTenderCheck, 'ledgerController.getChildren');
+    app.post('/tender/:id/ledger/update', sessionAuth, tenderCheck, uncheckTenderCheck, 'ledgerController.update');
+    app.post('/tender/:id/ledger/upload-excel/:ueType', sessionAuth, tenderCheck, uncheckTenderCheck, 'ledgerController.uploadExcel');
+    app.get('/tender/:id/ledger/download/:file', sessionAuth, tenderCheck, uncheckTenderCheck, 'ledgerController.download');
+    app.post('/tender/:id/pos/update', sessionAuth, tenderCheck, uncheckTenderCheck, 'ledgerController.posUpdate');
+    app.post('/tender/:id/pos/paste', sessionAuth, tenderCheck, uncheckTenderCheck, 'ledgerController.posPaste');
+    app.post('/tender/:id/ledger/deal2sgfh', sessionAuth, tenderCheck, uncheckTenderCheck, 'ledgerController.deal2sgfh');
+    app.post('/tender/:id/ledger/check', sessionAuth, tenderCheck, uncheckTenderCheck, 'ledgerController.check');
 
     // 台账审批相关
-    app.get('/tender/:id/ledger/audit', sessionAuth, tenderCheck, 'ledgerAuditController.index');
-    app.post('/tender/:id/ledger/audit/add', sessionAuth, tenderCheck, 'ledgerAuditController.add');
-    app.post('/tender/:id/ledger/audit/delete', sessionAuth, tenderCheck, 'ledgerAuditController.remove');
-    app.post('/tender/:id/ledger/audit/start', sessionAuth, tenderCheck, 'ledgerAuditController.start');
-    app.post('/tender/:id/ledger/audit/check', sessionAuth, tenderCheck, 'ledgerAuditController.check');
+    app.get('/tender/:id/ledger/audit', sessionAuth, tenderCheck, uncheckTenderCheck, 'ledgerAuditController.index');
+    app.post('/tender/:id/ledger/audit/add', sessionAuth, tenderCheck, uncheckTenderCheck, 'ledgerAuditController.add');
+    app.post('/tender/:id/ledger/audit/delete', sessionAuth, tenderCheck, uncheckTenderCheck, 'ledgerAuditController.remove');
+    app.post('/tender/:id/ledger/audit/start', sessionAuth, tenderCheck, uncheckTenderCheck, 'ledgerAuditController.start');
+    app.post('/tender/:id/ledger/audit/check', sessionAuth, tenderCheck, uncheckTenderCheck, 'ledgerAuditController.check');
 
     // 部位台账
-    app.get('/tender/:id/ledger/bwtz', sessionAuth, tenderCheck, 'ledgerController.bwtz');
-    app.post('/tender/:id/ledger/bwtz/load', sessionAuth, tenderCheck, 'ledgerController.loadBwtz');
+    app.get('/tender/:id/ledger/bwtz', sessionAuth, tenderCheck, uncheckTenderCheck, 'ledgerController.bwtz');
+    app.post('/tender/:id/ledger/bwtz/load', sessionAuth, tenderCheck, uncheckTenderCheck, 'ledgerController.loadBwtz');
 
     // 台账对比
-    app.get('/tender/:id/ledger/gather', sessionAuth, tenderCheck, 'ledgerController.gather');
-    app.post('/tender/:id/ledger/gather/load', sessionAuth, tenderCheck, 'ledgerController.loadGatherData');
+    app.get('/tender/:id/ledger/gather', sessionAuth, tenderCheck, uncheckTenderCheck, 'ledgerController.gather');
+    app.post('/tender/:id/ledger/gather/load', sessionAuth, tenderCheck, uncheckTenderCheck, 'ledgerController.loadGatherData');
 
     // 台账修订
-    app.get('/tender/:id/revise', sessionAuth, tenderCheck, 'reviseController.index');
-    app.post('/tender/:id/revise/add', sessionAuth, tenderCheck, 'reviseController.add');
-    app.post('/tender/:id/revise/cancel', sessionAuth, tenderCheck, 'reviseController.cancel');
-    app.post('/tender/:id/revise/save', sessionAuth, tenderCheck, 'reviseController.save');
-    // app.post('/tender/:id/revise/deal2sgfh', sessionAuth, tenderCheck, 'reviseController.deal2sgfh');
+    app.get('/tender/:id/revise', sessionAuth, tenderCheck, uncheckTenderCheck, 'reviseController.index');
+    app.post('/tender/:id/revise/add', sessionAuth, tenderCheck, uncheckTenderCheck, 'reviseController.add');
+    app.post('/tender/:id/revise/cancel', sessionAuth, tenderCheck, uncheckTenderCheck, 'reviseController.cancel');
+    app.post('/tender/:id/revise/save', sessionAuth, tenderCheck, uncheckTenderCheck, 'reviseController.save');
+    // app.post('/tender/:id/revise/deal2sgfh', sessionAuth, tenderCheck, uncheckTenderCheck, 'reviseController.deal2sgfh');
 
     // 台账修订页面
-    app.get('/tender/:id/revise/info', sessionAuth, tenderCheck, 'reviseController.info');
-    app.post('/tender/:id/revise/auditors', sessionAuth, tenderCheck, 'reviseController.reviseAuditors');
-    app.post('/tender/:id/revise/info/load', sessionAuth, tenderCheck, 'reviseController.loadInfoData');
-    app.post('/tender/:id/revise/info/update', sessionAuth, tenderCheck, 'reviseController.update');
-    app.post('/tender/:id/revise/info/upload-excel/:ueType', sessionAuth, tenderCheck, 'reviseController.uploadExcel');
-    app.post('/tender/:id/revise/info/check', sessionAuth, tenderCheck, 'reviseController.checkData');
+    app.get('/tender/:id/revise/info', sessionAuth, tenderCheck, uncheckTenderCheck, 'reviseController.info');
+    app.post('/tender/:id/revise/auditors', sessionAuth, tenderCheck, uncheckTenderCheck, 'reviseController.reviseAuditors');
+    app.post('/tender/:id/revise/info/load', sessionAuth, tenderCheck, uncheckTenderCheck, 'reviseController.loadInfoData');
+    app.post('/tender/:id/revise/info/update', sessionAuth, tenderCheck, uncheckTenderCheck, 'reviseController.update');
+    app.post('/tender/:id/revise/info/upload-excel/:ueType', sessionAuth, tenderCheck, uncheckTenderCheck, 'reviseController.uploadExcel');
+    app.post('/tender/:id/revise/info/check', sessionAuth, tenderCheck, uncheckTenderCheck, 'reviseController.checkData');
 
     // 查看修订数据
-    app.get('/tender/:id/revise/history', sessionAuth, tenderCheck, 'reviseController.history');
-    app.post('/tender/:id/revise/history/load', sessionAuth, tenderCheck, 'reviseController.loadHistoryData');
-    app.post('/tender/:id/revise/history/info', sessionAuth, tenderCheck, 'reviseController.historyInfo');
+    app.get('/tender/:id/revise/history', sessionAuth, tenderCheck, uncheckTenderCheck, 'reviseController.history');
+    app.post('/tender/:id/revise/history/load', sessionAuth, tenderCheck, uncheckTenderCheck, 'reviseController.loadHistoryData');
+    app.post('/tender/:id/revise/history/info', sessionAuth, tenderCheck, uncheckTenderCheck, 'reviseController.historyInfo');
 
     // 修订审批
-    app.post('/tender/:id/revise/audit/add', sessionAuth, tenderCheck, 'reviseController.addAuditor');
-    app.post('/tender/:id/revise/audit/remove', sessionAuth, tenderCheck, 'reviseController.removeAuditor');
-    app.post('/tender/:id/revise/audit/start', sessionAuth, tenderCheck, 'reviseController.start');
-    app.post('/tender/:id/revise/audit/check', sessionAuth, tenderCheck, 'reviseController.check');
+    app.post('/tender/:id/revise/audit/add', sessionAuth, tenderCheck, uncheckTenderCheck, 'reviseController.addAuditor');
+    app.post('/tender/:id/revise/audit/remove', sessionAuth, tenderCheck, uncheckTenderCheck, 'reviseController.removeAuditor');
+    app.post('/tender/:id/revise/audit/start', sessionAuth, tenderCheck, uncheckTenderCheck, 'reviseController.start');
+    app.post('/tender/:id/revise/audit/check', sessionAuth, tenderCheck, uncheckTenderCheck, 'reviseController.check');
 
     // 签约清单
-    app.post('/tender/:id/deal/get-data', sessionAuth, tenderCheck, 'dealBillsController.getData');
-    app.post('/tender/:id/deal/upload-excel', sessionAuth, tenderCheck, 'dealBillsController.loadExcel');
-    app.get('/tender/:id/deal/download/:file', sessionAuth, tenderCheck, 'dealBillsController.download');
+    app.post('/tender/:id/deal/get-data', sessionAuth, tenderCheck, uncheckTenderCheck, 'dealBillsController.getData');
+    app.post('/tender/:id/deal/upload-excel', sessionAuth, tenderCheck, uncheckTenderCheck, 'dealBillsController.loadExcel');
+    app.get('/tender/:id/deal/download/:file', sessionAuth, tenderCheck, uncheckTenderCheck, 'dealBillsController.download');
+    app.post('/tender/:id/deal/update', sessionAuth, tenderCheck, uncheckTenderCheck, 'dealBillsController.update');
 
     // 计量台账
     // 期计量
-    app.get('/tender/:id/measure/stage', sessionAuth, tenderCheck, 'measureController.stage');
-    app.post('/tender/:id/measure/stage/auditors', sessionAuth, tenderCheck, 'measureController.stageAuditors');
-    app.post('/tender/:id/measure/add', sessionAuth, tenderCheck, 'measureController.add');
-    app.post('/tender/:id/measure/save', sessionAuth, tenderCheck, 'measureController.save');
-    app.post('/tender/:id/measure/stage/delete', sessionAuth, tenderCheck, 'measureController.delete');
+    app.get('/tender/:id/measure/stage', sessionAuth, tenderCheck, uncheckTenderCheck, 'measureController.stage');
+    app.post('/tender/:id/measure/stage/auditors', sessionAuth, tenderCheck, uncheckTenderCheck, 'measureController.stageAuditors');
+    app.post('/tender/:id/measure/add', sessionAuth, tenderCheck, uncheckTenderCheck, 'measureController.add');
+    app.post('/tender/:id/measure/save', sessionAuth, tenderCheck, uncheckTenderCheck, 'measureController.save');
+    app.post('/tender/:id/measure/stage/delete', sessionAuth, tenderCheck, uncheckTenderCheck, 'measureController.delete');
 
     // 计量台账 -- 清单汇总
-    app.get('/tender/:id/measure/gather', sessionAuth, tenderCheck, 'measureController.gather');
+    app.get('/tender/:id/measure/gather', sessionAuth, tenderCheck, uncheckTenderCheck, 'measureController.gather');
 
     // 计量台账 -- 审核比较
-    app.get('/tender/:id/measure/compare', sessionAuth, tenderCheck, 'measureController.compare');
-    app.post('/tender/:id/measure/compare/load', sessionAuth, tenderCheck, 'measureController.loadCompareData');
+    app.get('/tender/:id/measure/compare', sessionAuth, tenderCheck, uncheckTenderCheck, 'measureController.compare');
+    app.post('/tender/:id/measure/compare/load', sessionAuth, tenderCheck, uncheckTenderCheck, 'measureController.loadCompareData');
 
     // 期计量详细
     // 本期计量台账
-    app.get('/tender/:id/measure/stage/:order', sessionAuth, tenderCheck, stageCheck, 'stageController.index');
-    app.post('/tender/:id/measure/stage/:order/load', sessionAuth, tenderCheck, stageCheck, 'stageController.getStageData');
-    app.post('/tender/:id/measure/stage/:order/pos', sessionAuth, tenderCheck, stageCheck, 'stageController.getStagePosData');
-    app.post('/tender/:id/measure/stage/:order/update', sessionAuth, tenderCheck, stageCheck, 'stageController.updateStageData');
-    app.post('/tender/:id/measure/stage/:order/valid-change', sessionAuth, tenderCheck, stageCheck, 'stageController.searchValidChange');
-    app.post('/tender/:id/measure/stage/:order/use-change', sessionAuth, tenderCheck, stageCheck, 'stageController.useChange');
-    app.post('/tender/:id/measure/stage/:order/check', sessionAuth, tenderCheck, stageCheck, 'stageController.check');
+    app.get('/tender/:id/measure/stage/:order', sessionAuth, tenderCheck, uncheckTenderCheck, stageCheck, 'stageController.index');
+    app.post('/tender/:id/measure/stage/:order/load', sessionAuth, tenderCheck, uncheckTenderCheck, stageCheck, 'stageController.getStageData');
+    app.post('/tender/:id/measure/stage/:order/pos', sessionAuth, tenderCheck, uncheckTenderCheck, stageCheck, 'stageController.getStagePosData');
+    app.post('/tender/:id/measure/stage/:order/update', sessionAuth, tenderCheck, uncheckTenderCheck, stageCheck, 'stageController.updateStageData');
+    app.post('/tender/:id/measure/stage/:order/valid-change', sessionAuth, tenderCheck, uncheckTenderCheck, stageCheck, 'stageController.searchValidChange');
+    app.post('/tender/:id/measure/stage/:order/use-change', sessionAuth, tenderCheck, uncheckTenderCheck, stageCheck, 'stageController.useChange');
+    app.post('/tender/:id/measure/stage/:order/check', sessionAuth, tenderCheck, uncheckTenderCheck, stageCheck, 'stageController.check');
 
     // 计量附件
-    app.post('/tender/:id/measure/stage/:order/upload/file', sessionAuth, tenderCheck, stageCheck, 'stageController.uploadFile');
+    app.post('/tender/:id/measure/stage/:order/upload/file', sessionAuth, tenderCheck, uncheckTenderCheck, stageCheck, 'stageController.uploadFile');
     app.get('/tender/:id/measure/stage/:order/download/file/:fid', sessionAuth, 'stageController.downloadFile');
-    app.post('/tender/:id/measure/stage/:order/delete/file', sessionAuth, tenderCheck, stageCheck, 'stageController.deleteFile');
-    app.post('/tender/:id/measure/stage/:order/save/file', sessionAuth, tenderCheck, stageCheck, 'stageController.saveFile');
-    app.post('/tender/:id/measure/stage/:order/check/file', sessionAuth, tenderCheck, stageCheck, 'stageController.checkFile');
+    app.post('/tender/:id/measure/stage/:order/delete/file', sessionAuth, tenderCheck, uncheckTenderCheck, stageCheck, 'stageController.deleteFile');
+    app.post('/tender/:id/measure/stage/:order/save/file', sessionAuth, tenderCheck, uncheckTenderCheck, stageCheck, 'stageController.saveFile');
+    app.post('/tender/:id/measure/stage/:order/check/file', sessionAuth, tenderCheck, uncheckTenderCheck, stageCheck, 'stageController.checkFile');
 
     // 中间计量
-    app.get('/tender/:id/measure/stage/:order/detail', sessionAuth, tenderCheck, stageCheck, 'stageController.detail');
-    app.post('/tender/:id/measure/stage/:order/detail/build', sessionAuth, tenderCheck, stageCheck, 'stageController.buildDetailData');
-    app.post('/tender/:id/measure/stage/:order/detail/adv', sessionAuth, tenderCheck, stageCheck, 'stageController.setAdvancedConfig');
-    app.post('/tender/:id/measure/stage/:order/detail/load', sessionAuth, tenderCheck, stageCheck, 'stageController.loadDetailRelaData');
-    app.post('/tender/:id/measure/stage/:order/detail/save', sessionAuth, tenderCheck, stageCheck, 'stageController.saveDetailData');
-    app.post('/tender/:id/measure/stage/:order/detail/add-img', sessionAuth, tenderCheck, stageCheck, 'stageController.addCalcImage');
-    app.post('/tender/:id/measure/stage/:order/detail/merge-img', sessionAuth, tenderCheck, stageCheck, 'stageController.mergeCalcImage');
+    app.get('/tender/:id/measure/stage/:order/detail', sessionAuth, tenderCheck, uncheckTenderCheck, stageCheck, 'stageController.detail');
+    app.post('/tender/:id/measure/stage/:order/detail/build', sessionAuth, tenderCheck, uncheckTenderCheck, stageCheck, 'stageController.buildDetailData');
+    app.post('/tender/:id/measure/stage/:order/detail/adv', sessionAuth, tenderCheck, uncheckTenderCheck, stageCheck, 'stageController.setAdvancedConfig');
+    app.post('/tender/:id/measure/stage/:order/detail/load', sessionAuth, tenderCheck, uncheckTenderCheck, stageCheck, 'stageController.loadDetailRelaData');
+    app.post('/tender/:id/measure/stage/:order/detail/save', sessionAuth, tenderCheck, uncheckTenderCheck, stageCheck, 'stageController.saveDetailData');
+    app.post('/tender/:id/measure/stage/:order/detail/add-img', sessionAuth, tenderCheck, uncheckTenderCheck, stageCheck, 'stageController.addCalcImage');
+    app.post('/tender/:id/measure/stage/:order/detail/merge-img', sessionAuth, tenderCheck, uncheckTenderCheck, stageCheck, 'stageController.mergeCalcImage');
 
     // 合同支付
-    app.get('/tender/:id/measure/stage/:order/pay', sessionAuth, tenderCheck, stageCheck, 'stageController.pay');
-    app.post('/tender/:id/measure/stage/:order/pay/detail', sessionAuth, tenderCheck, stageCheck, 'stageController.chapterDetail');
-    app.post('/tender/:id/measure/stage/:order/pay/save', sessionAuth, tenderCheck, stageCheck, 'stageController.savePayData');
-    app.post('/tender/:id/measure/stage/:order/pay/upload/file', sessionAuth, tenderCheck, stageCheck, 'stageController.payUploadFile');
+    app.get('/tender/:id/measure/stage/:order/pay', sessionAuth, tenderCheck, uncheckTenderCheck, stageCheck, 'stageController.pay');
+    app.post('/tender/:id/measure/stage/:order/pay/detail', sessionAuth, tenderCheck, uncheckTenderCheck, stageCheck, 'stageController.chapterDetail');
+    app.post('/tender/:id/measure/stage/:order/pay/save', sessionAuth, tenderCheck, uncheckTenderCheck, stageCheck, 'stageController.savePayData');
+    app.post('/tender/:id/measure/stage/:order/pay/upload/file', sessionAuth, tenderCheck, uncheckTenderCheck, stageCheck, 'stageController.payUploadFile');
     app.get('/tender/:id/measure/stage/:order/pay/download/file/:pid/:index', sessionAuth, 'stageController.payDownloadFile');
-    app.post('/tender/:id/measure/stage/:order/pay/delete/file', sessionAuth, tenderCheck, stageCheck, 'stageController.payDeleteFile');
+    app.post('/tender/:id/measure/stage/:order/pay/delete/file', sessionAuth, tenderCheck, uncheckTenderCheck, stageCheck, 'stageController.payDeleteFile');
 
     // 变更概况
-    app.get('/tender/:id/measure/stage/:order/change', sessionAuth, tenderCheck, stageCheck, 'stageController.change');
-    app.post('/tender/:id/measure/stage/:order/change/data', sessionAuth, tenderCheck, stageCheck, 'stageController.getChangeData');
-    app.post('/tender/:id/measure/stage/:order/change/detail', sessionAuth, tenderCheck, stageCheck, 'stageController.changeDetail');
+    app.get('/tender/:id/measure/stage/:order/change', sessionAuth, tenderCheck, uncheckTenderCheck, stageCheck, 'stageController.change');
+    app.post('/tender/:id/measure/stage/:order/change/data', sessionAuth, tenderCheck, uncheckTenderCheck, stageCheck, 'stageController.getChangeData');
+    app.post('/tender/:id/measure/stage/:order/change/detail', sessionAuth, tenderCheck, uncheckTenderCheck, stageCheck, 'stageController.changeDetail');
 
     // 审批
-    app.post('/tender/:id/measure/stage/:order/audit/add', sessionAuth, tenderCheck, stageCheck, 'stageController.addAudit');
-    app.post('/tender/:id/measure/stage/:order/audit/delete', sessionAuth, tenderCheck, stageCheck, 'stageController.deleteAudit');
-    app.post('/tender/:id/measure/stage/:order/audit/start', sessionAuth, tenderCheck, stageCheck, 'stageController.startAudit');
-    app.post('/tender/:id/measure/stage/:order/audit/check', sessionAuth, tenderCheck, stageCheck, 'stageController.checkAudit');
-    app.get('/tender/:id/measure/stage/:order/audit/check/again', sessionAuth, tenderCheck, stageCheck, 'stageController.checkAuditAgain');
+    app.post('/tender/:id/measure/stage/:order/audit/add', sessionAuth, tenderCheck, uncheckTenderCheck, stageCheck, 'stageController.addAudit');
+    app.post('/tender/:id/measure/stage/:order/audit/delete', sessionAuth, tenderCheck, uncheckTenderCheck, stageCheck, 'stageController.deleteAudit');
+    app.post('/tender/:id/measure/stage/:order/audit/start', sessionAuth, tenderCheck, uncheckTenderCheck, stageCheck, 'stageController.startAudit');
+    app.post('/tender/:id/measure/stage/:order/audit/check', sessionAuth, tenderCheck, uncheckTenderCheck, stageCheck, 'stageController.checkAudit');
+    app.get('/tender/:id/measure/stage/:order/audit/check/again', sessionAuth, tenderCheck, uncheckTenderCheck, stageCheck, 'stageController.checkAuditAgain');
 
     // 部位台账
-    app.get('/tender/:id/measure/stage/:order/bwtz', sessionAuth, tenderCheck, stageCheck, 'stageController.bwtz');
-    app.post('/tender/:id/measure/stage/:order/bwtz/load', sessionAuth, tenderCheck, stageCheck, 'stageController.loadBwtz');
+    app.get('/tender/:id/measure/stage/:order/bwtz', sessionAuth, tenderCheck, uncheckTenderCheck, stageCheck, 'stageController.bwtz');
+    app.post('/tender/:id/measure/stage/:order/bwtz/load', sessionAuth, tenderCheck, uncheckTenderCheck, stageCheck, 'stageController.loadBwtz');
 
     // 清单汇总
-    app.get('/tender/:id/measure/stage/:order/gather', sessionAuth, tenderCheck, stageCheck, 'stageController.gather');
+    app.get('/tender/:id/measure/stage/:order/gather', sessionAuth, tenderCheck, uncheckTenderCheck, stageCheck, 'stageController.gather');
 
     // 审核比较
-    app.get('/tender/:id/measure/stage/:order/compare', sessionAuth, tenderCheck, stageCheck, 'stageController.compare');
-    app.post('/tender/:id/measure/stage/:order/compare/load', sessionAuth, tenderCheck, stageCheck, 'stageController.compareAuditor');
+    app.get('/tender/:id/measure/stage/:order/compare', sessionAuth, tenderCheck, uncheckTenderCheck, stageCheck, 'stageController.compare');
+    app.post('/tender/:id/measure/stage/:order/compare/load', sessionAuth, tenderCheck, uncheckTenderCheck, stageCheck, 'stageController.compareAuditor');
 
     // 附加功能
-    app.get('/tender/:id/measure/stage/:order/extra/jgcl', sessionAuth, tenderCheck, stageCheck, 'stageExtraController.jgcl');
-    app.post('/tender/:id/measure/stage/:order/extra/jgcl/load', sessionAuth, tenderCheck, stageCheck, 'stageExtraController.loadJgcl');
-    app.post('/tender/:id/measure/stage/:order/extra/jgcl/update', sessionAuth, tenderCheck, stageCheck, 'stageExtraController.updateJgcl');
-    app.get('/tender/:id/measure/stage/:order/extra/bonus', sessionAuth, tenderCheck, stageCheck, 'stageExtraController.bonus');
-    app.post('/tender/:id/measure/stage/:order/extra/bonus/load', sessionAuth, tenderCheck, stageCheck, 'stageExtraController.loadBonus');
-    app.post('/tender/:id/measure/stage/:order/extra/bonus/update', sessionAuth, tenderCheck, stageCheck, 'stageExtraController.updateBonus');
-    app.get('/tender/:id/measure/stage/:order/extra/other', sessionAuth, tenderCheck, stageCheck, 'stageExtraController.other');
-    app.post('/tender/:id/measure/stage/:order/extra/other/load', sessionAuth, tenderCheck, stageCheck, 'stageExtraController.loadOther');
-    app.post('/tender/:id/measure/stage/:order/extra/other/update', sessionAuth, tenderCheck, stageCheck, 'stageExtraController.updateOther');
-    app.post('/tender/:id/measure/stage/:order/extra/upload/file', sessionAuth, tenderCheck, stageCheck, 'stageExtraController.uploadFile');
-    app.get('/tender/:id/measure/stage/:order/extra/download/file', sessionAuth, tenderCheck, stageCheck, 'stageExtraController.downloadFile');
-    app.post('/tender/:id/measure/stage/:order/extra/delete/file', sessionAuth, tenderCheck, stageCheck, 'stageExtraController.deleteFile');
+    app.get('/tender/:id/measure/stage/:order/extra/jgcl', sessionAuth, tenderCheck, uncheckTenderCheck, stageCheck, 'stageExtraController.jgcl');
+    app.post('/tender/:id/measure/stage/:order/extra/jgcl/load', sessionAuth, tenderCheck, uncheckTenderCheck, stageCheck, 'stageExtraController.loadJgcl');
+    app.post('/tender/:id/measure/stage/:order/extra/jgcl/update', sessionAuth, tenderCheck, uncheckTenderCheck, stageCheck, 'stageExtraController.updateJgcl');
+    app.get('/tender/:id/measure/stage/:order/extra/bonus', sessionAuth, tenderCheck, uncheckTenderCheck, stageCheck, 'stageExtraController.bonus');
+    app.post('/tender/:id/measure/stage/:order/extra/bonus/load', sessionAuth, tenderCheck, uncheckTenderCheck, stageCheck, 'stageExtraController.loadBonus');
+    app.post('/tender/:id/measure/stage/:order/extra/bonus/update', sessionAuth, tenderCheck, uncheckTenderCheck, stageCheck, 'stageExtraController.updateBonus');
+    app.get('/tender/:id/measure/stage/:order/extra/other', sessionAuth, tenderCheck, uncheckTenderCheck, stageCheck, 'stageExtraController.other');
+    app.post('/tender/:id/measure/stage/:order/extra/other/load', sessionAuth, tenderCheck, uncheckTenderCheck, stageCheck, 'stageExtraController.loadOther');
+    app.post('/tender/:id/measure/stage/:order/extra/other/update', sessionAuth, tenderCheck, uncheckTenderCheck, stageCheck, 'stageExtraController.updateOther');
+    app.post('/tender/:id/measure/stage/:order/extra/upload/file', sessionAuth, tenderCheck, uncheckTenderCheck, stageCheck, 'stageExtraController.uploadFile');
+    app.get('/tender/:id/measure/stage/:order/extra/download/file', sessionAuth, tenderCheck, uncheckTenderCheck, stageCheck, 'stageExtraController.downloadFile');
+    app.post('/tender/:id/measure/stage/:order/extra/delete/file', sessionAuth, tenderCheck, uncheckTenderCheck, stageCheck, 'stageExtraController.deleteFile');
 
     // 期审批管理
-    app.get('/tender/:id/measure/stage/:order/manager', sessionAuth, tenderCheck, stageCheck, 'stageController.manager');
-    app.post('/tender/:id/measure/stage/:order/manager/audit/delete', sessionAuth, tenderCheck, stageCheck, 'stageController.managerAuditDelete');
+    app.get('/tender/:id/measure/stage/:order/manager', sessionAuth, tenderCheck, uncheckTenderCheck, stageCheck, 'stageController.manager');
+    app.post('/tender/:id/measure/stage/:order/manager/audit/delete', sessionAuth, tenderCheck, uncheckTenderCheck, stageCheck, 'stageController.managerAuditDelete');
 
     // 报表
-    app.get('/tender/:id/report', sessionAuth, tenderCheck, 'reportController.index');
-    app.get('/tender/:id/measure/stage/:order/report', sessionAuth, tenderCheck, stageCheck, 'reportController.index');
+    app.get('/tender/:id/report', sessionAuth, tenderCheck, uncheckTenderCheck, 'reportController.index');
+    app.get('/tender/:id/measure/stage/:order/report', sessionAuth, tenderCheck, uncheckTenderCheck, stageCheck, 'reportController.index');
     app.get('/printReport/:size', sessionAuth, 'reportController.showPrintPage');
     app.post('/tender/report_api/getReport', sessionAuth, 'reportController.getReport');
     app.post('/tender/report_api/getMultiReports', sessionAuth, 'reportController.getMultiReportsEx');
@@ -292,52 +294,56 @@ module.exports = app => {
     app.post('/report/cDefine', sessionAuth, 'reportController.setCustomDefine');
 
     // 变更管理
-    app.get('/tender/:id/change', sessionAuth, tenderCheck, 'changeController.index');
-    app.get('/tender/:id/change/status/:status', sessionAuth, tenderCheck, 'changeController.status');
-    app.post('/tender/:id/change/newCode', sessionAuth, tenderCheck, 'changeController.newCode');
-    app.post('/tender/:id/change/add', sessionAuth, tenderCheck, 'changeController.add');
-    app.post('/tender/:id/change/defaultBills', sessionAuth, tenderCheck, 'changeController.defaultBills');
-    app.get('/tender/:id/change/:cid/info', sessionAuth, tenderCheck, 'changeController.info');
+    app.get('/tender/:id/change', sessionAuth, tenderCheck, uncheckTenderCheck, 'changeController.index');
+    app.get('/tender/:id/change/status/:status', sessionAuth, tenderCheck, uncheckTenderCheck, 'changeController.status');
+    app.post('/tender/:id/change/newCode', sessionAuth, tenderCheck, uncheckTenderCheck, 'changeController.newCode');
+    app.post('/tender/:id/change/add', sessionAuth, tenderCheck, uncheckTenderCheck, 'changeController.add');
+    app.post('/tender/:id/change/defaultBills', sessionAuth, tenderCheck, uncheckTenderCheck, 'changeController.defaultBills');
+    app.get('/tender/:id/change/:cid/info', sessionAuth, tenderCheck, uncheckTenderCheck, 'changeController.info');
     app.post('/change/upload/file', sessionAuth, 'changeController.uploadFile');
     app.get('/change/download/file/:id', sessionAuth, 'changeController.downloadFile');
+    app.post('/change/download/file/:id', sessionAuth, 'changeController.checkFile');
     app.post('/change/delete/file', sessionAuth, 'changeController.deleteFile');
-    app.post('/tender/:id/change/delete', sessionAuth, tenderCheck, 'changeController.delete');
-    app.post('/tender/:id/change/bills', sessionAuth, tenderCheck, 'changeController.bills');
+    app.post('/tender/:id/change/delete', sessionAuth, tenderCheck, uncheckTenderCheck, 'changeController.delete');
+    app.post('/tender/:id/change/bills', sessionAuth, tenderCheck, uncheckTenderCheck, 'changeController.bills');
 
-    app.post('/tender/:id/change/save', sessionAuth, tenderCheck, 'changeController.save');
+    app.post('/tender/:id/change/save', sessionAuth, tenderCheck, uncheckTenderCheck, 'changeController.save');
 
-    app.post('/tender/:id/change/approval', sessionAuth, tenderCheck, 'changeController.approval');
-    app.post('/tender/:id/change/check/again', sessionAuth, tenderCheck, 'changeController.checkAgain');
+    app.post('/tender/:id/change/approval', sessionAuth, tenderCheck, uncheckTenderCheck, 'changeController.approval');
+    app.post('/tender/:id/change/check/again', sessionAuth, tenderCheck, uncheckTenderCheck, 'changeController.checkAgain');
 
-    app.post('/tender/:id/change/:cid/check/codeRepeat', sessionAuth, tenderCheck, 'changeController.checkCodeRepeat');
+    app.post('/tender/:id/change/:cid/check/codeRepeat', sessionAuth, tenderCheck, uncheckTenderCheck, 'changeController.checkCodeRepeat');
 
     // 变更单位管理
     app.post('/change/update/company', sessionAuth, 'changeController.updateCompany');
 
     // 材料调差
-    app.get('/tender/:id/measure/material', sessionAuth, tenderCheck, 'materialController.index');
-    app.post('/tender/:id/measure/material/add', sessionAuth, tenderCheck, 'materialController.add');
-    app.post('/tender/:id/measure/material/delete', sessionAuth, tenderCheck, 'materialController.delete');
-    app.post('/tender/:id/measure/material/auditors', sessionAuth, tenderCheck, 'materialController.materialAuditors');
+    app.get('/tender/:id/measure/material', sessionAuth, tenderCheck, uncheckTenderCheck, 'materialController.index');
+    app.post('/tender/:id/measure/material/add', sessionAuth, tenderCheck, uncheckTenderCheck, 'materialController.add');
+    app.post('/tender/:id/measure/material/delete', sessionAuth, tenderCheck, uncheckTenderCheck, 'materialController.delete');
+    app.post('/tender/:id/measure/material/auditors', sessionAuth, tenderCheck, uncheckTenderCheck, 'materialController.materialAuditors');
     // 审批
-    app.post('/tender/:id/measure/material/:order/audit/add', sessionAuth, tenderCheck, materialCheck, 'materialController.addAudit');
-    app.post('/tender/:id/measure/material/:order/audit/delete', sessionAuth, tenderCheck, materialCheck, 'materialController.deleteAudit');
-    app.post('/tender/:id/measure/material/:order/audit/start', sessionAuth, tenderCheck, materialCheck, 'materialController.startAudit');
-    app.post('/tender/:id/measure/material/:order/audit/check', sessionAuth, tenderCheck, materialCheck, 'materialController.checkAudit');
+    app.post('/tender/:id/measure/material/:order/audit/add', sessionAuth, tenderCheck, uncheckTenderCheck, materialCheck, 'materialController.addAudit');
+    app.post('/tender/:id/measure/material/:order/audit/delete', sessionAuth, tenderCheck, uncheckTenderCheck, materialCheck, 'materialController.deleteAudit');
+    app.post('/tender/:id/measure/material/:order/audit/start', sessionAuth, tenderCheck, uncheckTenderCheck, materialCheck, 'materialController.startAudit');
+    app.post('/tender/:id/measure/material/:order/audit/check', sessionAuth, tenderCheck, uncheckTenderCheck, materialCheck, 'materialController.checkAudit');
     // 调差工料
-    app.get('/tender/:id/measure/material/:order', sessionAuth, tenderCheck, materialCheck, 'materialController.info');
-    app.post('/tender/:id/measure/material/:order/save', sessionAuth, tenderCheck, materialCheck, 'materialController.saveBillsData');
+    app.get('/tender/:id/measure/material/:order', sessionAuth, tenderCheck, uncheckTenderCheck, materialCheck, 'materialController.info');
+    app.post('/tender/:id/measure/material/:order/save', sessionAuth, tenderCheck, uncheckTenderCheck, materialCheck, 'materialController.saveBillsData');
     // 月信息价
-    app.post('/tender/:id/measure/material/:order/month/save', sessionAuth, tenderCheck, materialCheck, 'materialController.saveMonth');
+    app.post('/tender/:id/measure/material/:order/month/save', sessionAuth, tenderCheck, uncheckTenderCheck, materialCheck, 'materialController.saveMonth');
+    // 指数调差
+    app.get('/tender/:id/measure/material/:order/exponent', sessionAuth, tenderCheck, uncheckTenderCheck, materialCheck, 'materialController.exponent');
+    app.post('/tender/:id/measure/material/:order/exponent/save', sessionAuth, tenderCheck, uncheckTenderCheck, materialCheck, 'materialController.saveExponentData');
     // 调差清单
-    app.get('/tender/:id/measure/material/:order/list', sessionAuth, tenderCheck, materialCheck, 'materialController.list');
-    app.post('/tender/:id/measure/material/:order/list/save', sessionAuth, tenderCheck, materialCheck, 'materialController.saveListsData');
+    app.get('/tender/:id/measure/material/:order/list', sessionAuth, tenderCheck, uncheckTenderCheck, materialCheck, 'materialController.list');
+    app.post('/tender/:id/measure/material/:order/list/save', sessionAuth, tenderCheck, uncheckTenderCheck, materialCheck, 'materialController.saveListsData');
 
     // 附件
-    app.get('/tender/:id/measure/material/:order/file', sessionAuth, tenderCheck, materialCheck, 'materialController.file');
-    app.post('/tender/:id/measure/material/:order/file/upload', sessionAuth, tenderCheck, materialCheck, 'materialController.upload');
-    app.get('/tender/:id/measure/material/:order/file/:fid/download', sessionAuth, tenderCheck, 'materialController.downloadFile');
-    app.post('/tender/:id/measure/material/:order/file/find', sessionAuth, tenderCheck, materialCheck, 'materialController.getCurMatericalFiles');
+    app.get('/tender/:id/measure/material/:order/file', sessionAuth, tenderCheck, uncheckTenderCheck, materialCheck, 'materialController.file');
+    app.post('/tender/:id/measure/material/:order/file/upload', sessionAuth, tenderCheck, uncheckTenderCheck, materialCheck, 'materialController.upload');
+    app.get('/tender/:id/measure/material/:order/file/:fid/download', sessionAuth, tenderCheck, uncheckTenderCheck, 'materialController.downloadFile');
+    app.post('/tender/:id/measure/material/:order/file/find', sessionAuth, tenderCheck, uncheckTenderCheck, materialCheck, 'materialController.getCurMatericalFiles');
     app.post('/tender/measure/material/file/delete', sessionAuth, 'materialController.deleteFile');
 
     // 个人账号相关
@@ -384,10 +390,10 @@ module.exports = app => {
     app.get('/wap/logout', 'wapController.logout');
     app.get('/wap/dashboard', sessionAuth, 'wapController.dashboard');
     app.get('/wap/list', sessionAuth, 'wapController.list');
-    app.get('/wap/tender/:id', sessionAuth, tenderCheck, 'wapController.tender');
-    app.get('/wap/tender/:id/stage/:order', sessionAuth, tenderCheck, 'wapController.stage');
-    app.get('/wap/tender/:id/change/:cid/info', sessionAuth, tenderCheck, 'wapController.change');
-    app.post('/wap/tender/:id/change/approval', sessionAuth, tenderCheck, 'wapController.changeApproval');
+    app.get('/wap/tender/:id', sessionAuth, tenderCheck, uncheckTenderCheck, 'wapController.tender');
+    app.get('/wap/tender/:id/stage/:order', sessionAuth, tenderCheck, uncheckTenderCheck, 'wapController.stage');
+    app.get('/wap/tender/:id/change/:cid/info', sessionAuth, tenderCheck, uncheckTenderCheck, 'wapController.change');
+    app.post('/wap/tender/:id/change/approval', sessionAuth, tenderCheck, uncheckTenderCheck, 'wapController.changeApproval');
 
     // 微信
     app.get('/wx', 'wechatController.index');

+ 39 - 8
app/service/advance.js

@@ -10,10 +10,12 @@ module.exports = app => {
         }
         /**
          * 获取预付款列表
-         * @param {Number} tid 标段id
-         * @param {Number} type 预付款类型
+         * @param {Number} tid - 标段id
+         * @param {Number} type - 预付款类型
+         * @param {Number} decimal - 小数位数
+         * @param {Number} advancePayTotal - 预付款总额
          */
-        async getAdvanceList(tid, type) {
+        async getAdvanceList(tid, type, decimal, advancePayTotal) {
             this.initSqlBuilder();
             this.sqlBuilder.setAndWhere('tid', {
                 value: tid,
@@ -33,6 +35,23 @@ module.exports = app => {
             const [sql, sqlParam] = this.sqlBuilder.build(this.tableName);
             const advance = await this.db.query(sql, sqlParam);
             for (const item of advance) {
+                let s1,
+                    s3;
+                const s2 = item.prev_amount.toString().split('.')[1];
+                item.pay_ratio = this.ctx.helper.mul(this.ctx.helper.div(item.cur_amount, advancePayTotal), 100, 2) || 0;
+                if (item.status === auditConst.status.uncheck || item.status === auditConst.status.checkNo) {
+                    const cur_amount = item.cur_amount && this.ctx.helper.round(item.cur_amount, decimal) || 0;
+                    s1 = parseFloat(cur_amount).toString().split('.')[1];
+                    s3 = parseFloat(this.ctx.helper.add(cur_amount, item.prev_amount)).toString().split('.')[1];
+                    item.cur_amount = this.ctx.helper.formatMoney(cur_amount, ',', s1 && s1.length || 0);
+                    item.prev_total_amount = this.ctx.helper.formatMoney(this.ctx.helper.add(cur_amount, item.prev_amount), ',', s3 && s3.length || 0);
+                } else {
+                    s1 = item.cur_amount && item.cur_amount.toString().split('.')[1];
+                    s3 = item.prev_total_amount.toString().split('.')[1];
+                    item.cur_amount = this.ctx.helper.formatMoney(item.cur_amount, ',', s1 && s1.length || 0);
+                    item.prev_total_amount = this.ctx.helper.formatMoney(item.prev_total_amount, ',', s3 && s3.length || 0);
+                }
+                item.prev_amount = this.ctx.helper.formatMoney(item.prev_amount, ',', s2 && s2.length || 0);
                 item.curAuditor = await this.ctx.service.advanceAudit.getAuditorByStatus(item.id, item.status, item.times);
                 if (item.status === auditConst.status.checkNoPre) {
                     item.curAuditor2 = await this.ctx.service.advanceAudit.getAuditorByStatus(item.id, auditConst.status.checking);
@@ -71,6 +90,12 @@ module.exports = app => {
             return advance;
         }
 
+        async getSumAdvance(tid) {
+            const sql = 'Select sum(cur_amount) as tp From ' + this.tableName + ' where tid = ?';
+            const result = await this.db.queryOne(sql, [tid]);
+            return result ? result.tp : 0;
+        }
+
         /**
          * 创建一条新的记录
          * @param {String} type 类型: 开工预付款|材料预付款 (start|material)
@@ -84,20 +109,26 @@ module.exports = app => {
             if (!latestOrder) {
                 latestOrder = {
                     order: 1,
-                    prev_amount: 0.00,
-                    prev_total_amount: 0.00,
+                    prev_amount: 0,
+                    prev_total_amount: 0,
                 };
             } else {
                 latestOrder.order = latestOrder.order + 1;
             }
             const record = await this.db.insert(this.tableName, { type, uid, tid, status: auditConst.status.uncheck, order: latestOrder.order, prev_amount: latestOrder.prev_total_amount, prev_total_amount: latestOrder.prev_total_amount });
             const auditors = await ctx.service.advanceAudit.getAuditGroupByList(latestOrder.id, latestOrder.times);
-            auditors.forEach(async (auditor, idx) => {
-                const { audit_id } = auditor;
+            for (let idx = 0; idx < auditors.length; idx++) {
+                const { audit_id } = auditors[idx];
                 await ctx.service.advanceAudit.db.insert(ctx.service.advanceAudit.tableName, {
                     tid: latestOrder.tid, audit_id, type: latestOrder.type, vid: record.insertId, times: 1, order: idx + 1, status: 1, create_time: new Date(),
                 });
-            });
+            }
+            // auditors.forEach(async (auditor, idx) => {
+            //     const { audit_id } = auditor;
+            //     await ctx.service.advanceAudit.db.insert(ctx.service.advanceAudit.tableName, {
+            //         tid: latestOrder.tid, audit_id, type: latestOrder.type, vid: record.insertId, times: 1, order: idx + 1, status: 1, create_time: new Date(),
+            //     });
+            // });
             return await this.getDataById(record.insertId);
         }
 

+ 67 - 29
app/service/change.js

@@ -441,7 +441,7 @@ module.exports = app => {
                 let total_price = 0;
                 if (changeList.length > 0) {
                     for (const cl of changeList) {
-                        const clInfo = cl.split(';');
+                        const clInfo = cl.split('*;*');
                         const clArray = {
                             tid: tenderId,
                             cid: changeInfo.cid,
@@ -456,6 +456,8 @@ module.exports = app => {
                             samount: '',
                             detail: clInfo[7],
                             spamount: clInfo[6],
+                            xmj_code: clInfo[9] !== '' ? clInfo[9] : null,
+                            xmj_jldy: clInfo[10] !== '' ? clInfo[10] : null,
                         };
                         if (clInfo[4] === '') {
                             delete clArray.unit_price;
@@ -956,51 +958,87 @@ module.exports = app => {
         async getValidChanges(tid, bills, pos) {
             const timesLen = 100;
             const filter =
-                'cb.`code` = ' +
-                this.db.escape(bills.b_code) +
-                ' And cb.`name` = ' +
-                this.db.escape(bills.name) +
-                ' And cb.`unit` = ' +
-                this.db.escape(bills.unit) +
-                ' And cb.`unit_price` = ' +
-                this.db.escape(bills.unit_price) +
+                'cb.`code` = ' + this.db.escape(bills.b_code) +
+                ' And cb.`name` = ' + this.db.escape(bills.name) +
+                ' And cb.`unit` = ' + this.db.escape(bills.unit) +
+                ' And cb.`unit_price` = ' + this.db.escape(bills.unit_price) +
                 (pos ? ' And cb.`bwmx` = ' + this.db.escape(pos.name) : '');
             const sql =
                 'SELECT c.cid, c.code, c.name, c.w_code, c.p_code, c.peg, c.org_name, c.org_code, c.new_name, c.new_code,' +
                 '    c.content, c.basis, c.memo, c.type, c.class, c.quality, c.company, c.charge, ' +
                 '    cb.id As cbid, cb.code As b_code, cb.name As b_name, cb.unit As b_unit, cb.samount As b_amount, cb.detail As b_detail, cb.bwmx As b_bwmx, ' +
                 '    scb.used_amount' +
-                '  FROM ' +
-                this.tableName +
-                ' As c ' +
-                '  Left Join ' +
-                this.ctx.service.changeAuditList.tableName +
-                ' As cb On c.cid = cb.cid ' +
+                '  FROM ' + this.tableName + ' As c ' +
+                '  Left Join ' + this.ctx.service.changeAuditList.tableName + ' As cb On c.cid = cb.cid ' +
                 '  Left Join (' +
                 '    SELECT SUM(sc.qty) As used_amount, sc.cbid' +
-                '      FROM ' +
-                this.ctx.service.stageChange.tableName +
-                ' As sc' +
-                '      INNER JOIN (SELECT MAX(`stimes` * ' +
-                timesLen +
-                ' + `sorder`) As `flow`, cbid, sid ' +
-                '        FROM ' +
-                this.ctx.service.stageChange.tableName +
+                '      FROM ' + this.ctx.service.stageChange.tableName + ' As sc' +
+                '      INNER JOIN (SELECT MAX(`stimes` * ' + timesLen + ' + `sorder`) As `flow`, cbid, sid ' +
+                '        FROM ' + this.ctx.service.stageChange.tableName +
                 '        WHERE tid = ?' +
                 '        GROUP BY cbid, sid' +
                 '      ) As MF' +
-                '      ON (sc.stimes * ' +
-                timesLen +
-                ' + sc.sorder) = MF.flow And sc.cbid = MF.cbid And sc.sid = MF.sid' +
+                '      ON (sc.stimes * ' + timesLen + ' + sc.sorder) = MF.flow And sc.cbid = MF.cbid And sc.sid = MF.sid' +
                 '    GROUP BY sc.cbid' +
                 '  ) As scb ON cb.id = scb.cbid' +
-                '  WHERE c.tid = ? And c.status = ? And c.valid And ' +
-                filter +
+                '  WHERE c.tid = ? And c.status = ? And c.valid And ' + filter +
                 '  ORDER BY c.in_time';
             const sqlParam = [tid, tid, audit.flow.status.checked];
             const changes = await this.db.query(sql, sqlParam);
             for (const c of changes) {
-                const aSql = 'SELECT ca.*, pa.name As u_name, pa.role As u_role ' + '  FROM ?? As ca ' + '  Left Join ?? As pa ' + '  On ca.uid = pa.id ' + '  Where ca.cid = ?';
+                const aSql = 'SELECT ca.*, pa.name As u_name, pa.role As u_role ' +
+                    '  FROM ?? As ca ' +
+                    '  Left Join ?? As pa ' +
+                    '  On ca.uid = pa.id ' +
+                    '  Where ca.cid = ?';
+                const aSqlParam = [this.ctx.service.changeAtt.tableName, this.ctx.service.projectAccount.tableName, c.cid];
+                c.attachments = await this.db.query(aSql, aSqlParam);
+            }
+            return changes;
+        }
+
+        /**
+         * 查询审批人可用的变更令
+         * @param bills - 查询的清单
+         * @param pos - 查询的部位
+         * @return {Promise<*>} - 可用的变更令列表
+         */
+        async getAuditValidChanges(tid, bills, pos, times, order) {
+            const timesLen = 100;
+            const filter =
+                'cb.`code` = ' + this.db.escape(bills.b_code) +
+                ' And cb.`name` = ' + this.db.escape(bills.name) +
+                ' And cb.`unit` = ' + this.db.escape(bills.unit) +
+                ' And cb.`unit_price` = ' + this.db.escape(bills.unit_price) +
+                (pos ? ' And cb.`bwmx` = ' + this.db.escape(pos.name) : '');
+            const sql =
+                'SELECT c.cid, c.code, c.name, c.w_code, c.p_code, c.peg, c.org_name, c.org_code, c.new_name, c.new_code,' +
+                '    c.content, c.basis, c.memo, c.type, c.class, c.quality, c.company, c.charge, ' +
+                '    cb.id As cbid, cb.code As b_code, cb.name As b_name, cb.unit As b_unit, cb.samount As b_amount, cb.detail As b_detail, cb.bwmx As b_bwmx, ' +
+                '    scb.used_amount' +
+                '  FROM ' + this.tableName + ' As c ' +
+                '  Left Join ' + this.ctx.service.changeAuditList.tableName + ' As cb On c.cid = cb.cid ' +
+                '  Left Join (' +
+                '    SELECT SUM(sc.qty) As used_amount, sc.cbid' +
+                '      FROM ' + this.ctx.service.stageChange.tableName + ' As sc' +
+                '      INNER JOIN (SELECT MAX(`stimes` * ' + timesLen + ' + `sorder`) As `flow`, cbid, sid ' +
+                '        FROM ' + this.ctx.service.stageChange.tableName +
+                '        WHERE tid = ? And (`stimes` < ? OR (`stimes` = ? AND `sorder` <= ?)) ' +
+                '        GROUP BY cbid, sid' +
+                '      ) As MF' +
+                '      ON (sc.stimes * ' + timesLen + ' + sc.sorder) = MF.flow And sc.cbid = MF.cbid And sc.sid = MF.sid' +
+                '    GROUP BY sc.cbid' +
+                '  ) As scb ON cb.id = scb.cbid' +
+                '  WHERE c.tid = ? And c.status = ? And c.valid And ' + filter +
+                '  ORDER BY c.in_time';
+            const sqlParam = [tid, times, times, order, tid, audit.flow.status.checked];
+            const changes = await this.db.query(sql, sqlParam);
+            for (const c of changes) {
+                const aSql = 'SELECT ca.*, pa.name As u_name, pa.role As u_role ' +
+                    '  FROM ?? As ca ' +
+                    '  Left Join ?? As pa ' +
+                    '  On ca.uid = pa.id ' +
+                    '  Where ca.cid = ?';
                 const aSqlParam = [this.ctx.service.changeAtt.tableName, this.ctx.service.projectAccount.tableName, c.cid];
                 c.attachments = await this.db.query(aSql, aSqlParam);
             }

+ 1 - 1
app/service/change_att.js

@@ -65,7 +65,7 @@ module.exports = app => {
                 if (!ctx.helper.canPreview(item.fileext)) {
                     item.filepath = `/change/download/file/${item.id}`;
                 } else {
-                    item.filepath = '/' + item.filepath;
+                    item.filepath = item.filepath.replace(/^app|\/app/, '');
                 }
                 return item;
             });

+ 87 - 2
app/service/deal_bills.js

@@ -161,7 +161,8 @@ module.exports = app => {
                     //if (this.ctx.helper.validBillsCode(code)) {
                     if (code) {
                         const data = {
-                            deal_id: bills.length + 1,
+                            id: this.uuid.v4(),
+                            order: bills.length + 1,
                             tender_id: tenderId,
                             code: code,
                             name: this.ctx.helper.replaceReturn(row[iName]),
@@ -206,12 +207,96 @@ module.exports = app => {
         * @param {Number} tenderId - 所属标段Id
         */
         async getDataByTenderId(tenderId) {
-            const sql = 'SELECT Bills.* FROM ' + this.tableName + ' As Bills WHERE tender_id = ?';
+            const sql = 'SELECT Bills.* FROM ' + this.tableName + ' As Bills WHERE tender_id = ? ORDER BY `order` ASC';
             const sqlParam = [tenderId];
             return await this.db.query(sql, sqlParam);
             // let rst = await this.getDataByCondition({tender_id: tenderId});
             // return rst;
         }
+
+        async _addDatas(data) {
+            const info =  this.ctx.tender.info;
+
+            const datas = data instanceof Array ? data : [data];
+            const insertData = [];
+            for (const d of datas) {
+                if (!d.code || !d.order) throw '新增签约清单,提交的数据错误';
+                const nd = { id: this.uuid.v4(), tender_id: this.ctx.tender.id };
+                nd.code = d.code;
+                nd.order = d.order;
+                if (d.name) nd.name = d.name;
+                if (d.unit) nd.unit = d.unit;
+                if (d.unit_price) nd.unit_price = this.ctx.helper.round(d.unit_price, info.decimal.up);
+                const precision = this.ctx.helper.findPrecision(info.precision, d.unit);
+                if (d.quantity) {
+                    nd.quantity = this.ctx.helper.round(d.quantity, precision.value);
+                    nd.total_price = this.ctx.helper.mul(nd.unit_price, nd.quantity, info.decimal.tp);
+                }
+                insertData.push(nd);
+            }
+            const result = await this.db.insert(this.tableName, insertData);
+            return insertData;
+        }
+
+        async _delDatas (data) {
+            await this.db.delete(this.tableName, {id: data});
+            return data;
+        }
+
+        async _updateDatas (data) {
+            const info =  this.ctx.tender.info;
+
+            const datas = data instanceof Array ? data : [data];
+            const orgDatas = await this.getAllDataByCondition({where: {id: this.ctx.helper._.map(datas, 'id')}});
+
+            const uDatas = [];
+            for (const d of datas) {
+                const od = this.ctx.helper._.find(orgDatas, {id: d.id});
+                if (!od) continue;
+
+                const nd = {id: od.id};
+
+                if (d.code !== undefined) nd.code = d.code;
+                if (d.order !== undefined) nd.order = d.order;
+                if (d.name !== undefined) nd.name = d.name;
+                if (d.unit !== undefined) nd.unit = d.unit;
+                nd.unit_price = d.unit_price !== undefined ? this.ctx.helper.round(d.unit_price, info.decimal.up) : od.unit_price;
+                const precision = this.ctx.helper.findPrecision(info.precision, d.unit);
+                if (d.quantity !== undefined) {
+                    nd.quantity = this.ctx.helper.round(d.quantity, precision.value);
+                    nd.total_price = this.ctx.helper.mul(nd.unit_price, nd.quantity, info.decimal.tp);
+                } else {
+                    nd.quantity = this.ctx.helper.round(od.quantity, precision.value);
+                    nd.total_price = this.ctx.helper.mul(nd.unit_price, nd.quantity, info.decimal.tp);
+                }
+                uDatas.push(nd);
+            }
+            if (uDatas.length > 0) {
+                await this.db.updateRows(this.tableName, uDatas);
+                return uDatas;
+            } else {
+                return [];
+            }
+        }
+
+        async updateDatas(data) {
+            const result = {add: [], del: [], update: []};
+            try {
+                if (data.add) {
+                    result.add = await this._addDatas(data.add);
+                }
+                if (data.update) {
+                    result.update = await this._updateDatas(data.update);
+                }
+                if (data.del) {
+                    result.del = await this._delDatas(data.del);
+                }
+                return result;
+            } catch (err) {
+                if (err) result.err = err;
+                return result;
+            }
+        }
     }
 
     return DealBills;

+ 22 - 0
app/service/ledger_audit.js

@@ -46,6 +46,28 @@ module.exports = app => {
             return await this.db.queryOne(sql, sqlParam);
         }
 
+        async getAuditorByOrder(tenderId, order, times = 1) {
+            const sql =
+                'SELECT la.`audit_id`, pa.`name`, pa.`company`, pa.`role`, pa.`mobile`, pa.`telephone`, la.`times`, la.`audit_order`, la.`status`, la.`opinion`, la.`begin_time`, la.`end_time` ' +
+                'FROM ?? AS la, ?? AS pa ' +
+                'WHERE la.`tender_id` = ? and la.`audit_order` = ? and la.`times` = ?' +
+                '    and la.`audit_id` = pa.`id`';
+            const sqlParam = [this.tableName, this.ctx.service.projectAccount.tableName, tenderId, order, times];
+            return await this.db.queryOne(sql, sqlParam);
+        }
+
+        async getLastestAuditor(tenderId, times, status) {
+            const sql =
+                'SELECT la.`audit_id`, pa.`name`, pa.`company`, pa.`role`, pa.`mobile`, pa.`telephone`,' +
+                '    la.`times`, la.`audit_order`, la.`status`, la.`opinion`, la.`begin_time`, la.`end_time` ' +
+                '    FROM ' + this.tableName + ' AS la' +
+                '    Left Join ' + this.ctx.service.projectAccount.tableName + ' AS pa ON la.`audit_id` = pa.`id`' +
+                '  WHERE la.`tender_id` = ? and la.`status` = ? and la.`times` = ?' +
+                '  ORDER BY `audit_order` DESC';
+            const sqlParam = [tenderId, status, times ? times : 1];
+            return await this.db.queryOne(sql, sqlParam);
+        }
+
         /**
          * 获取标段审核人最后一位的名称
          *

+ 137 - 0
app/service/login_logging.js

@@ -0,0 +1,137 @@
+'use strict';
+
+/**
+ * 登录日志-数据模型
+ *
+ * @author lanjianrong
+ * @date 2020/8/31
+ * @version
+ */
+const UAParser = require('ua-parser-js');
+
+module.exports = app => {
+    class LoginLogging extends app.BaseService {
+        constructor(ctx) {
+            super(ctx);
+            this.tableName = 'login_logging';
+        }
+
+        /**
+         * 创建记录
+         * @param {Object} payload - 载荷
+         */
+        async createLog(payload) {
+            const transaction = await this.db.beginTransaction();
+            try {
+                transaction.insert(this.tableName, payload);
+                await transaction.commit();
+            } catch (error) {
+                await transaction.rollback();
+                throw error;
+            }
+        }
+
+        /**
+         * 创建登录日志
+         * @return {Boolean} 日志是否创建成功
+         * @param {Number} type - 登录类型
+         * @param {Number} status - 是否显示记录
+         */
+        async addLoginLog(type, status) {
+            const { ctx } = this;
+            const ip = ctx.request.ip ? ctx.request.ip : '';
+            const ipInfo = await this.getIpInfoFromApi(ip);
+            const parser = new UAParser(ctx.header['user-agent']);
+            const osInfo = parser.getOS();
+            const cpuInfo = parser.getCPU();
+            const browserInfo = parser.getBrowser();
+            const payload = {
+                os: `${osInfo.name} ${osInfo.version} ${cpuInfo.architecture}`,
+                browser: `${browserInfo.name} ${browserInfo.version}`,
+                ip,
+                address: ipInfo,
+                uid: ctx.session.sessionUser.accountId,
+                pid: ctx.session.sessionProject.id,
+                type,
+                show: status,
+            };
+            return await this.createLog(payload);
+        }
+
+        /**
+         * 根据ip请求获取详细地址
+         * @param {String} a_ip - ip地址
+         * @return {String} 详细地址
+         */
+        async getIpInfoFromApi(a_ip = '') {
+            console.log('a_ip', a_ip);
+            if (!a_ip) return '';
+            if (a_ip === '127.0.0.1' || a_ip === '::1' || a_ip.indexOf('192.168') !== -1) return '服务器本机访问';
+            const { ip = '', region = '', city = '', isp = '' } = await this.sendRequest(a_ip);
+            let address = '';
+            region && (address += region + '省');
+            city && (address += city + '市 ');
+            isp && (address += isp + ' ');
+            ip && (address += `(${ip})`);
+            return address;
+        }
+
+        /**
+         * 发送请求获取详细地址
+         * @param {String} ip - ip地址
+         * @param {Number} max - 最大重试次数
+         * @return {Object} the result of request
+         * @private
+         */
+        async sendRequest(ip, max = 3) {
+            return new Promise(resolve => {
+                const start = () => {
+                    if (max <= 0) {
+                        resolve(); // 已达到最大重试次数,返回空的执行承若
+                    }
+                    max--;
+                    this.ctx.curl(`https://api01.aliyun.venuscn.com/ip?ip=${ip}`, {
+                        dateType: 'json',
+                        encoding: 'utf8',
+                        timeout: 2000,
+                        headers: {
+                            Authorization: 'APPCODE 85c64bffe70445c4af9df7ae31c7bfcc',
+                        },
+                    }).then(({ status, data }) => {
+                        if (status === 200) {
+                            const result = JSON.parse(data.toString()).data;
+                            if (!result.ip) {
+                                start();
+                            } else {
+                                max++;
+                                resolve(result);
+                            }
+                        } else {
+                            max--;
+                            start();
+                        }
+                    }).catch(() => {
+                        start();
+                    });
+                };
+                start();
+            });
+        }
+
+        /**
+         * 获取登录日志
+         * @param {Number} pid - 项目id
+         * @param {Number} uid - 用户id
+         * @return {Promise<Array>} 日志数组
+         */
+        async getLoginLogs(pid, uid) {
+            return this.db.select(this.tableName, {
+                where: { pid, uid, show: 0 },
+                orders: [['create_time', 'desc']],
+                columns: ['browser', 'create_time', 'ip', 'os', 'address'],
+                limit: 10, offset: 0,
+            });
+        }
+    }
+    return LoginLogging;
+};

+ 58 - 3
app/service/material.js

@@ -127,6 +127,7 @@ module.exports = app => {
                 if (preMaterial) {
                     newMaterial.rate = preMaterial.rate;
                     newMaterial.pre_tp = this.ctx.helper.add(preMaterial.m_tp, preMaterial.pre_tp);
+                    newMaterial.ex_pre_tp = this.ctx.helper.add(preMaterial.ex_tp, preMaterial.ex_pre_tp);
                 }
                 // 新增期记录
                 const result = await transaction.insert(this.tableName, newMaterial);
@@ -148,10 +149,13 @@ module.exports = app => {
                     await this.ctx.service.materialList.copyPreMaterialList(transaction, preMaterial, newMaterial);
                     // 修改本期应耗数量值和有效价差,需要剔除不参与调差的清单数据,并返回总金额
                     const m_tp = await this.ctx.service.materialBills.updateNewMaterial(transaction, this.ctx.tender.id, newMaterial.id, this.ctx, newMaterial.stage_id);
+                    // 修改现行价格指数,并返回调差基数json
+                    const ex_calc = await this.ctx.service.materialExponent.updateNewMaterial(transaction, newMaterial.id, this.ctx, newMaterial.stage_id, preMaterial.ex_calc);
                     // 计算得出本期总金额
                     const updateMaterialData = {
                         id: newMaterial.id,
                         m_tp,
+                        ex_calc: JSON.stringify(ex_calc),
                     };
                     await transaction.update(this.tableName, updateMaterialData);
                 }
@@ -182,15 +186,31 @@ module.exports = app => {
                 await transaction.delete(this.ctx.service.materialListNotjoin.tableName, { mid: id });
                 await transaction.delete(this.ctx.service.materialBillsHistory.tableName, { mid: id });
                 await transaction.delete(this.ctx.service.materialFile.tableName, { mid: id });
-                // 如果存在上一期,把上一期的quantity,pre_tp添加到bill中
+                await transaction.delete(this.ctx.service.materialExponent.tableName, { mid: id });
+                await transaction.delete(this.ctx.service.materialExponentHistory.tableName, { mid: id });
+                // 如果存在上一期,把上一期的quantity,expr,msg_tp,msg_times,msg_spread,m_up_risk,m_down_risk,m_spread,m_tp,pre_tp,is_summary添加到bill中
                 const materialInfo = await this.getDataById(id);
                 if (materialInfo.order > 1) {
                     const sql = 'UPDATE ' + this.ctx.service.materialBills.tableName + ' as mb, ' +
                         this.ctx.service.materialBillsHistory.tableName + ' as mbh ' +
-                        'SET mb.`quantity` = mbh.`quantity`, mb.`pre_tp` = mbh.`pre_tp` ' +
+                        'SET mb.`quantity` = mbh.`quantity`, mb.`expr` = mbh.`expr`, ' +
+                        'mb.`msg_tp` = mbh.`msg_tp`, mb.`msg_times` = mbh.`msg_times`, ' +
+                        'mb.`msg_spread` = mbh.`msg_spread`, mb.`m_up_risk` = mbh.`m_up_risk`, ' +
+                        'mb.`m_down_risk` = mbh.`m_down_risk`, mb.`m_spread` = mbh.`m_spread`, ' +
+                        'mb.`m_tp` = mbh.`m_tp`, mb.`pre_tp` = mbh.`pre_tp`, ' +
+                        'mb.`is_summary` = mbh.`is_summary` ' +
                         'WHERE mbh.`tid` = ? AND mbh.`order` = ? AND mbh.`mb_id` = mb.`id`';
                     const sqlParam = [this.ctx.tender.id, materialInfo.order - 1];
                     await transaction.query(sql, sqlParam);
+
+                    const sql2 = 'UPDATE ' + this.ctx.service.materialExponent.tableName + ' as me, ' +
+                        this.ctx.service.materialExponentHistory.tableName + ' as meh ' +
+                        'SET me.`weight_num` = meh.`weight_num`, me.`basic_price` = meh.`basic_price`, ' +
+                        'me.`basic_times` = meh.`basic_times`, me.`m_price` = meh.`m_price`, ' +
+                        'me.`calc_num` = meh.`calc_num`, me.`is_summary` = meh.`is_summary` ' +
+                        'WHERE meh.`tid` = ? AND meh.`order` = ? AND meh.`me_id` = me.`id`';
+                    const sqlParam2 = [this.ctx.tender.id, materialInfo.order - 1];
+                    await transaction.query(sql2, sqlParam2);
                 }
                 await transaction.delete(this.tableName, { id });
                 await transaction.commit();
@@ -235,7 +255,29 @@ module.exports = app => {
         }
 
         /**
-         * 修改增税税率
+         * 修改调差基数
+         * @param {int} rate 税率
+         * @return {Promise<*>}
+         */
+        async changeExCalc(ex_calc) {
+            const transaction = await this.db.beginTransaction();
+            try {
+                const updateData = {
+                    id: this.ctx.material.id,
+                    ex_calc: JSON.stringify(ex_calc),
+                };
+                await transaction.update(this.tableName, updateData);
+                const [ex_tp, ex_expr] = await this.ctx.service.materialExponent.calcMaterialExTp(transaction, ex_calc);
+                await transaction.commit();
+                return [ex_tp, ex_expr];
+            } catch (err) {
+                await transaction.rollback();
+                throw err;
+            }
+        }
+
+        /**
+         * 取当前期截止上期含税金额
          * @param {int} tid 标段id
          * @param {int} order 调差期数
          * @return {Promise<*>}
@@ -246,6 +288,19 @@ module.exports = app => {
             const result = await this.db.queryOne(sql, sqlParam);
             return result.pre_tp_hs;
         }
+
+        /**
+         * 取当前期截止上期含税指数金额
+         * @param {int} tid 标段id
+         * @param {int} order 调差期数
+         * @return {Promise<*>}
+         */
+        async getExPreTpHs(tid, order) {
+            const sql = 'SELECT SUM(ROUND(`ex_tp`*(1+ `rate`/100),2)) AS `ex_pre_tp_hs` FROM ?? WHERE `tid` = ? AND `order` < ?';
+            const sqlParam = [this.tableName, tid, order];
+            const result = await this.db.queryOne(sql, sqlParam);
+            return result.ex_pre_tp_hs;
+        }
     }
 
     return Material;

+ 36 - 10
app/service/material_audit.js

@@ -219,11 +219,32 @@ module.exports = app => {
                         m_spread: mb.m_spread,
                         m_tp: mb.m_tp,
                         pre_tp: mb.pre_tp,
+                        is_summary: mb.is_summary,
                     };
                     mbhList.push(newMbh);
                 }
                 await transaction.insert(this.ctx.service.materialBillsHistory.tableName, mbhList);
 
+                const materialExponentData = await this.ctx.service.materialExponent.getAllDataByCondition({ where: { tid: this.ctx.tender.id } });
+                const mehList = [];
+                for (const me of materialExponentData) {
+                    const newMeh = {
+                        tid: this.ctx.tender.id,
+                        mid: this.ctx.material.id,
+                        order: this.ctx.material.order,
+                        me_id: me.id,
+                        type: me.type,
+                        weight_num: me.weight_num,
+                        basic_price: me.basic_price,
+                        basic_times: me.basic_times,
+                        m_price: me.m_price,
+                        calc_num: me.calc_num,
+                        is_summary: me.is_summary,
+                    };
+                    mehList.push(newMeh);
+                }
+                await transaction.insert(this.ctx.service.materialExponentHistory.tableName, mehList);
+
                 // 微信模板通知
                 const materialInfo = await this.ctx.service.material.getDataById(materialId);
                 const wechatData = {
@@ -231,8 +252,8 @@ module.exports = app => {
                     status: wxConst.status.check,
                     tips: wxConst.tips.check,
                     begin_time: Date.parse(new Date()),
-                    m_tp: this.ctx.helper.round(materialInfo.m_tp, 2),
-                    hs_m_tp: this.ctx.helper.round(this.ctx.helper.mul(materialInfo.m_tp, 1+materialInfo.rate/100), 2),
+                    m_tp: this.ctx.helper.add(this.ctx.helper.round(materialInfo.m_tp, 2), this.ctx.helper.round(materialInfo.ex_tp, 2)),
+                    hs_m_tp: this.ctx.helper.add(this.ctx.helper.round(this.ctx.helper.mul(materialInfo.m_tp, 1+materialInfo.rate/100), 2), this.ctx.helper.round(this.ctx.helper.mul(materialInfo.ex_tp, 1+materialInfo.rate/100), 2)),
                 };
                 await this.ctx.helper.sendWechat(audit.aid, smsTypeConst.const.TC, smsTypeConst.judge.approval.toString(), wxConst.template.material, wechatData);
 
@@ -313,8 +334,8 @@ module.exports = app => {
                         status: wxConst.status.check,
                         tips: wxConst.tips.check,
                         begin_time: Date.parse(begin_audit.begin_time),
-                        m_tp: this.ctx.helper.round(materialInfo.m_tp, 2),
-                        hs_m_tp: this.ctx.helper.round(this.ctx.helper.mul(materialInfo.m_tp, 1+materialInfo.rate/100), 2),
+                        m_tp: this.ctx.helper.add(this.ctx.helper.round(materialInfo.m_tp, 2), this.ctx.helper.round(materialInfo.ex_tp, 2)),
+                        hs_m_tp: this.ctx.helper.add(this.ctx.helper.round(this.ctx.helper.mul(materialInfo.m_tp, 1+materialInfo.rate/100), 2), this.ctx.helper.round(this.ctx.helper.mul(materialInfo.ex_tp, 1+materialInfo.rate/100), 2)),
                     };
                     await this.ctx.helper.sendWechat(nextAudit.aid, smsTypeConst.const.TC, smsTypeConst.judge.approval.toString(), wxConst.template.material, wechatData);
 
@@ -351,8 +372,8 @@ module.exports = app => {
                         status: wxConst.status.success,
                         tips: wxConst.tips.success,
                         begin_time: Date.parse(begin_audit.begin_time),
-                        m_tp: this.ctx.helper.round(materialInfo.m_tp, 2),
-                        hs_m_tp: this.ctx.helper.round(this.ctx.helper.mul(materialInfo.m_tp, 1+materialInfo.rate/100), 2),
+                        m_tp: this.ctx.helper.add(this.ctx.helper.round(materialInfo.m_tp, 2), this.ctx.helper.round(materialInfo.ex_tp, 2)),
+                        hs_m_tp: this.ctx.helper.add(this.ctx.helper.round(this.ctx.helper.mul(materialInfo.m_tp, 1+materialInfo.rate/100), 2), this.ctx.helper.round(this.ctx.helper.mul(materialInfo.ex_tp, 1+materialInfo.rate/100), 2)),
                     };
                     await this.ctx.helper.sendWechat(users, smsTypeConst.const.TC, smsTypeConst.judge.result.toString(), wxConst.template.material, wechatData);
 
@@ -430,6 +451,11 @@ module.exports = app => {
                     tid: this.ctx.tender.id,
                     order: this.ctx.material.order,
                 });
+                // 删除material_exponent_history信息
+                await transaction.delete(this.ctx.service.materialExponentHistory.tableName, {
+                    tid: this.ctx.tender.id,
+                    order: this.ctx.material.order,
+                });
 
                 // 微信模板通知
                 const begin_audit = await this.getDataByCondition({
@@ -443,8 +469,8 @@ module.exports = app => {
                     status: wxConst.status.back,
                     tips: wxConst.tips.back,
                     begin_time: Date.parse(begin_audit.begin_time),
-                    m_tp: this.ctx.helper.round(materialInfo.m_tp, 2),
-                    hs_m_tp: this.ctx.helper.round(this.ctx.helper.mul(materialInfo.m_tp, 1+materialInfo.rate/100), 2),
+                    m_tp: this.ctx.helper.add(this.ctx.helper.round(materialInfo.m_tp, 2), this.ctx.helper.round(materialInfo.ex_tp, 2)),
+                    hs_m_tp: this.ctx.helper.add(this.ctx.helper.round(this.ctx.helper.mul(materialInfo.m_tp, 1+materialInfo.rate/100), 2), this.ctx.helper.round(this.ctx.helper.mul(materialInfo.ex_tp, 1+materialInfo.rate/100), 2)),
                 };
                 await this.ctx.helper.sendWechat(users, smsTypeConst.const.TC, smsTypeConst.judge.result.toString(), wxConst.template.material, wechatData);
 
@@ -540,8 +566,8 @@ module.exports = app => {
                     status: wxConst.status.check,
                     tips: wxConst.tips.check,
                     begin_time: Date.parse(begin_audit.begin_time),
-                    m_tp: this.ctx.helper.round(materialInfo.m_tp, 2),
-                    hs_m_tp: this.ctx.helper.round(this.ctx.helper.mul(materialInfo.m_tp, 1+materialInfo.rate/100), 2),
+                    m_tp: this.ctx.helper.add(this.ctx.helper.round(materialInfo.m_tp, 2), this.ctx.helper.round(materialInfo.ex_tp, 2)),
+                    hs_m_tp: this.ctx.helper.add(this.ctx.helper.round(this.ctx.helper.mul(materialInfo.m_tp, 1+materialInfo.rate/100), 2), this.ctx.helper.round(this.ctx.helper.mul(materialInfo.ex_tp, 1+materialInfo.rate/100), 2)),
                 };
                 await this.ctx.helper.sendWechat(preAuditor.aid, smsTypeConst.const.TC, smsTypeConst.judge.approval.toString(), wxConst.template.material, wechatData);
 

+ 5 - 3
app/service/material_bills.js

@@ -193,10 +193,11 @@ module.exports = app => {
                     msg_times: null,
                     msg_spread: newmsg_spread,
                     m_spread: newm_spread,
+                    m_tp: this.ctx.helper.round(this.ctx.helper.mul(this.ctx.helper.round(mb_quantity.quantity, 3), newm_spread), 2),
                     pre_tp: mb.m_tp !== null ? this.ctx.helper.round(this.ctx.helper.add(mb.pre_tp, mb.m_tp), 2) : mb.pre_tp,
                 };
                 await transaction.update(this.tableName, updateData);
-                return await this.ctx.helper.round(this.ctx.helper.mul(mb_quantity.quantity, newm_spread), 2);
+                return mb.is_summary === 1 ? await this.ctx.helper.round(this.ctx.helper.mul(mb_quantity.quantity, newm_spread), 2) : 0;
             } else if (mb.t_type === materialConst.t_type[1].value) {
                 const quantity = await materialCalculator.calculateExpr(mb.expr);
                 const updateData = {
@@ -206,10 +207,11 @@ module.exports = app => {
                     msg_times: null,
                     msg_spread: newmsg_spread,
                     m_spread: newm_spread,
+                    m_tp: quantity !== 0 && quantity !== null ? this.ctx.helper.round(this.ctx.helper.mul(this.ctx.helper.round(quantity, 3), newm_spread), 2) : null,
                     pre_tp: mb.m_tp !== null ? this.ctx.helper.round(this.ctx.helper.add(mb.pre_tp, mb.m_tp), 2) : mb.pre_tp,
                 };
                 await transaction.update(this.tableName, updateData);
-                return await this.ctx.helper.round(this.ctx.helper.mul(quantity, newm_spread), 2);
+                return mb.is_summary === 1 ? await this.ctx.helper.round(this.ctx.helper.mul(quantity, newm_spread), 2) : 0;
             }
         }
 
@@ -259,7 +261,7 @@ module.exports = app => {
         // 更改计算总金额并返回值
         async calcMaterialMTp(transaction) {
             // 金额发生变化,则重新计算本期金额
-            const sql = 'SELECT SUM(`m_tp`) as total_price FROM ' + this.tableName + ' WHERE `tid` = ?';
+            const sql = 'SELECT SUM(`m_tp`) as total_price FROM ' + this.tableName + ' WHERE `tid` = ? AND `is_summary` = 1';
             const sqlParam = [this.ctx.tender.id];
             const tp = await transaction.queryOne(sql, sqlParam);
             console.log(tp);

+ 253 - 0
app/service/material_exponent.js

@@ -0,0 +1,253 @@
+'use strict';
+
+/**
+ * 期计量 数据模型
+ *
+ * @author Mai
+ * @date 2018/8/13
+ * @version
+ */
+
+const auditConst = require('../const/audit').material;
+const materialConst = require('../const/material');
+const MaterialCalculator = require('../lib/material_calc');
+
+module.exports = app => {
+    class MaterialExponent extends app.BaseService {
+        /**
+         * 构造函数
+         *
+         * @param {Object} ctx - egg全局变量
+         * @return {void}
+         */
+        constructor(ctx) {
+            super(ctx);
+            this.tableName = 'material_exponent';
+        }
+
+        /**
+         * 添加指数清单
+         * @return {void}
+         */
+        async add() {
+            if (!this.ctx.tender || !this.ctx.material) {
+                throw '数据错误';
+            }
+            const transaction = await this.db.beginTransaction();
+            try {
+                const newExponent = {
+                    tid: this.ctx.tender.id,
+                    mid: this.ctx.material.id,
+                    in_time: new Date(),
+                };
+                // 新增工料
+                const result = await transaction.insert(this.tableName, newExponent);
+                if (result.affectedRows !== 1) {
+                    throw '新增指数数据失败';
+                }
+                await transaction.commit();
+                return await this.getDataById(result.insertId);
+            } catch (error) {
+                console.log(error);
+                await transaction.rollback();
+                throw error;
+            }
+        }
+
+        /**
+         * 删除指数清单
+         * @param {int} id 工料id
+         * @return {void}
+         */
+        async del(id) {
+            if (!this.ctx.tender || !this.ctx.material) {
+                throw '数据错误';
+            }
+            // 判断t_type是否为费用,且存在quantity,m_spread值
+            const transaction = await this.db.beginTransaction();
+            try {
+                const meInfo = await this.getDataById(id);
+                await transaction.delete(this.tableName, { id });
+                if (meInfo.weight_num === null) {
+                    await transaction.delete(this.ctx.service.materialExponentHistory.tableName, { mid: id });
+                }
+                let ex_tp = this.ctx.material.ex_tp;
+                let ex_expr = this.ctx.material.ex_expr;
+                if (meInfo.is_summary === materialConst.is_summary.yes) {
+                    // 金额发生变化,则重新计算本期金额
+                    [ex_tp, ex_expr] = await this.ctx.service.materialExponent.calcMaterialExTp(transaction);
+                }
+                await transaction.commit();
+                return [ex_tp, ex_expr];
+            } catch (err) {
+                await transaction.rollback();
+                throw err;
+            }
+        }
+
+        /**
+         * 修改指数清单信息
+         * @param {Object} data 工料内容
+         * @return {void}
+         */
+        async save(data) {
+            if (!this.ctx.tender || !this.ctx.material) {
+                throw '数据错误';
+            }
+            delete data.in_time;
+            // delete data.m_tp;
+            // 判断是否可修改
+            // 判断t_type是否为费用
+            const transaction = await this.db.beginTransaction();
+            try {
+                await transaction.update(this.tableName, data);
+                const [ex_tp, ex_expr] = await this.calcMaterialExTp(transaction);
+                await transaction.commit();
+                return [ex_tp, ex_expr];
+            } catch (err) {
+                await transaction.rollback();
+                throw err;
+            }
+        }
+
+        /**
+         * 复制粘贴指数清单
+         * @param {Object} data 工料内容
+         * @return {void}
+         */
+        async saveDatas(datas) {
+            if (!this.ctx.tender || !this.ctx.material) {
+                throw '数据错误';
+            }
+            // 判断是否可修改
+            // 判断t_type是否为费用
+            const transaction = await this.db.beginTransaction();
+            try {
+                await transaction.updateRows(this.tableName, datas);
+                const [ex_tp, ex_expr] = await this.calcMaterialExTp(transaction);
+                await transaction.commit();
+                return [ex_tp, ex_expr];
+            } catch (err) {
+                await transaction.rollback();
+                throw err;
+            }
+        }
+
+        /**
+         * 旧数据补充定值和历史数据
+         * @returns {Promise<void>}
+         */
+        async addOldData() {
+            if (!this.ctx.tender || !this.ctx.material) {
+                throw '数据错误';
+            }
+            const transaction = await this.db.beginTransaction();
+            try {
+                // 先获取第一期的mid
+                const first_material = await this.ctx.service.material.getDataByCondition({
+                    tid: this.ctx.tender.id,
+                    order: 1,
+                });
+                const insert_data = {
+                    tid: this.ctx.tender.id,
+                    mid: first_material.id,
+                    type: materialConst.ex_type[0].value,
+                    symbol: 'X',
+                    symbol_desc: '非可调因子',
+                    code: 'A',
+                    weight_num: 0,
+                    is_summary: materialConst.is_summary.yes,
+                    in_time: new Date(),
+                };
+                const result = await transaction.insert(this.tableName, insert_data);
+                // 获取最新期数据
+                const high_material = await this.ctx.service.material.getDataByCondition({
+                    tid: this.ctx.tender.id,
+                    order: this.ctx.material.highOrder,
+                });
+                const qi_order = high_material.status === auditConst.status.uncheck || high_material.status === auditConst.status.checkNo ? high_material.order - 1 : high_material;
+                const insert_history_data = [];
+                for (let i = 1; i <= qi_order; i++) {
+                    const one_insert = {
+                        tid: this.ctx.tender.id,
+                        mid: first_material.id,
+                        order: i,
+                        me_id: result.insertId,
+                        type: materialConst.ex_type[0].value,
+                        weight_num: 0,
+                        is_summary: materialConst.is_summary.yes,
+                    };
+                    insert_history_data.push(one_insert);
+                }
+                if (insert_history_data.length > 0) await transaction.insert(this.ctx.service.materialExponentHistory.tableName, insert_history_data);
+                await transaction.commit();
+            } catch (err) {
+                await transaction.rollback();
+                throw err;
+            }
+        }
+
+        // 更改计算总指数金额并返回值和公式
+        async calcMaterialExTp(transaction, ex_calc = (this.ctx.material.ex_calc ? JSON.parse(this.ctx.material.ex_calc) : null), mid = null) {
+            let basic_calc = 0;
+            if (ex_calc) {
+                for (const calc of ex_calc) {
+                    if (calc.select) {
+                        basic_calc = this.ctx.helper.add(basic_calc, calc.value);
+                    }
+                }
+            }
+            let expr = basic_calc + '*[';
+            const exponentList = await transaction.select(this.tableName, { where: { tid: this.ctx.tender.id, is_summary: materialConst.is_summary.yes } });
+            let sumByChange = 0;
+            let constant = 0;
+            for (const ex of exponentList) {
+                if (ex.type === materialConst.ex_type[0].value) {
+                    constant = ex.weight_num ? ex.weight_num : 0;
+                    expr += constant.toString();
+                } else if (ex.type === materialConst.ex_type[1].value) {
+                    const change = ex.calc_num ? ex.calc_num : 0;
+                    expr = expr + (change !== 0 ? '+' + ex.weight_num.toString() + '*(' + ex.m_price.toString() + '/' + ex.basic_price.toString() + ')' : '');
+                    sumByChange = this.ctx.helper.add(sumByChange, change);
+                }
+            }
+            expr += '-1]';
+            const ex_tp = this.ctx.helper.round(this.ctx.helper.mul(basic_calc, this.ctx.helper.sub(this.ctx.helper.add(constant, sumByChange), 1)), 2);
+            await transaction.update(this.ctx.service.material.tableName, {
+                id: mid ? mid : this.ctx.material.id,
+                ex_tp,
+                ex_expr: expr,
+            });
+            console.log(ex_tp, expr);
+            return [ex_tp, expr];
+        }
+
+        /**
+         * 更新新一期的现行价格指数,并返回指数总金额和公式
+         * @param transaction
+         * @param tid
+         * @param mid
+         * @returns {Promise<number>}
+         */
+        async updateNewMaterial(transaction, mid, ctx, stage_id, ex_calc) {
+            const stage_list = await ctx.service.stage.getStageMsgByStageId(stage_id);
+            const calcBase = await ctx.service.stage.getMaterialCalcBase(stage_list, ctx.tender.info);
+            const old_ex_calc = ex_calc ? JSON.parse(ex_calc) : null;
+            const new_ex_calc = materialConst.ex_calc;
+            for (const bq of new_ex_calc) {
+                const calc = this._.find(calcBase, function(item) {
+                    return bq.code === item.code;
+                });
+                const oldcalc = old_ex_calc ? this._.find(old_ex_calc, function(item) {
+                    return bq.code === item.code;
+                }) : null;
+                bq.value = calc.value;
+                bq.select = oldcalc ? oldcalc.select : false;
+            }
+            await this.calcMaterialExTp(transaction, new_ex_calc, mid);
+            return new_ex_calc;
+        }
+    }
+
+    return MaterialExponent;
+};

+ 37 - 0
app/service/material_exponent_history.js

@@ -0,0 +1,37 @@
+'use strict';
+
+/**
+ * 指数历史期数据表 数据模型
+ *
+ * @author Mai
+ * @date 2018/8/13
+ * @version
+ */
+
+
+module.exports = app => {
+    class MaterialExponentHistory extends app.BaseService {
+        /**
+         * 构造函数
+         *
+         * @param {Object} ctx - egg全局变量
+         * @return {void}
+         */
+        constructor(ctx) {
+            super(ctx);
+            this.tableName = 'material_exponent_history';
+        }
+
+        /**
+         * 获取历史本期应耗数量和上期调差金额
+         * @param mid
+         * @param order
+         * @param mb_id
+         * @returns {Promise<*[]>}
+         */
+        async getByMeId(mid, order, me_id) {
+            return await this.getDataByCondition({ mid, order, me_id });
+        }
+    }
+    return MaterialExponentHistory;
+};

+ 50 - 20
app/service/material_list.js

@@ -139,29 +139,59 @@ module.exports = app => {
             }
             const transaction = await this.db.beginTransaction();
             try {
+                // 先删除addxmj里所有的清单工料再添加新工料,并重新计算每个工料的单价数量
+                // 还要找出删除的工料,更新单价数量
                 const list = [];
-                const select = data.select;
+                const delList = [];
+                const materialBills = data.materialBills;
+                const mb_idList = [];
                 for (const xmj of data.addXmj) {
-                    const newLists = {
-                        tid: this.ctx.tender.id,
-                        order: this.ctx.material.order,
-                        mid: this.ctx.material.id,
-                        mb_id: select.mb_id,
-                        gcl_id: xmj.gcl_id,
-                        xmj_id: xmj.id,
-                        mx_id: xmj.mx_id,
-                        gather_qty: xmj.gather_qty,
-                        quantity: select.quantity,
-                        in_time: new Date(),
-                    };
-                    list.push(newLists);
+                    const mlList = await this.getAllDataByCondition({
+                        where: {
+                            mid: this.ctx.material.id,
+                            gcl_id: xmj.gcl_id,
+                            xmj_id: xmj.id,
+                            mx_id: xmj.mx_id ? xmj.mx_id : [null, ''],
+                        },
+                    });
+                    const mbIdList = this._.map(mlList, 'mb_id');
+                    mb_idList.push(...mbIdList);
+                    delList.push(...this._.map(mlList, 'id'));
+                    for (const mb of materialBills) {
+                        const newLists = {
+                            tid: this.ctx.tender.id,
+                            order: this.ctx.material.order,
+                            mid: this.ctx.material.id,
+                            mb_id: mb.mb_id,
+                            gcl_id: xmj.gcl_id,
+                            xmj_id: xmj.id,
+                            mx_id: xmj.mx_id ? xmj.mx_id : '',
+                            gather_qty: xmj.gather_qty ? xmj.gather_qty : null,
+                            quantity: mb.quantity,
+                            in_time: new Date(),
+                        };
+                        list.push(newLists);
+                        mb_idList.push(mb.mb_id);
+                    }
                 }
-                // 新增工料
-                const result = await transaction.insert(this.tableName, list);
-                if (result.affectedRows === 0) {
-                    throw '新增工料数据失败';
+                // 删除工料清单关联
+                if (delList.length > 0) await transaction.delete(this.tableName, { id: delList });
+                // 新增工料清单关联
+                if (list.length > 0) {
+                    const result = await transaction.insert(this.tableName, list);
+                    if (result.affectedRows === 0) {
+                        throw '新增工料数据失败';
+                    }
                 }
-                await this.calcQuantityByML(transaction, select.mb_id);
+                // 重算工料和总金额
+                const calcMBIdList = this._.uniq(mb_idList);
+                if (calcMBIdList.length > 0) {
+                    for (const select of calcMBIdList) {
+                        await this.calcQuantityByML(transaction, select);
+                    }
+                }
+                // throw 'fail';
+                // await this.calcQuantityByML(transaction, select.mb_id);
                 await transaction.commit();
                 return await this.getMaterialData(this.ctx.tender.id, this.ctx.material.id);
             } catch (err) {
@@ -193,7 +223,7 @@ module.exports = app => {
             };
             await transaction.update(this.ctx.service.materialBills.tableName, updateData);
             // 计算本期总金额
-            const sql2 = 'SELECT SUM(`m_tp`) as total_price FROM ' + this.ctx.service.materialBills.tableName + ' WHERE `tid` = ?';
+            const sql2 = 'SELECT SUM(`m_tp`) as total_price FROM ' + this.ctx.service.materialBills.tableName + ' WHERE `tid` = ? AND `is_summary` = 1';
             const sqlParam2 = [this.ctx.tender.id];
             const tp = await transaction.queryOne(sql2, sqlParam2);
             console.log(tp);

+ 1 - 0
app/service/material_month.js

@@ -45,6 +45,7 @@ module.exports = app => {
             try {
                 const material_month = this.ctx.material.months ? this.ctx.material.months.split(',') : [];
                 material_month.push(data.yearmonth);
+                material_month.sort();
                 if (mbList.length !== 0) {
                     const insertArray = [];
                     const updateArray = [];

+ 5 - 1
app/service/project_account.js

@@ -14,6 +14,7 @@ const SSO = require('../lib/sso');
 const SMS = require('../lib/sms');
 const SmsAliConst = require('../const/sms_alitemplate');
 const thirdPartyConst = require('../const/third_party');
+const loginWay = require('../const/setting').loginWay;
 
 module.exports = app => {
 
@@ -235,13 +236,16 @@ module.exports = app => {
                         projectInfo.gxby_status = thirdParty.gxby_option && thirdParty.gxby_option.status
                             ? thirdParty.gxby_option.status : thirdPartyConst.gxby;
 
-                        thirdParty.dagl_option = thirdParty.dagl_option ? JSON.parse(thirdParty.dagl_option): null;
+                        thirdParty.dagl_option = thirdParty.dagl_option ? JSON.parse(thirdParty.dagl_option) : null;
                         projectInfo.dagl = thirdParty.dagl;
                         projectInfo.dagl_status = thirdParty.dagl_option && thirdParty.dagl_option.status
                             ? thirdParty.dagl_option.status : thirdPartyConst.dagl;
                     }
                     this.ctx.session.sessionProject = projectInfo;
                     this.ctx.session.sessionProjectList = projectList;
+                    // 记录登录日志
+                    await this.ctx.service.loginLogging.addLoginLog(loginType, loginStatus);
+
                 }
             } catch (error) {
                 console.log(error);

+ 4 - 4
app/service/report_memory.js

@@ -361,12 +361,12 @@ module.exports = app => {
                     const curStage = await this.ctx.service.stageBills.getAuditorStageData(this.ctx.tender.id,
                         this.ctx.stage.id, this.ctx.stage.curTimes, this.ctx.stage.curOrder);
                     this.ctx.helper.assignRelaData(billsData, [
-                        {data: curStage, fields: ['contract_qty', 'contract_tp', 'contract_expr', 'qc_qty', 'qc_tp'], prefix: '', relaId: 'lid'}
+                        {data: curStage, fields: ['contract_qty', 'contract_tp', 'contract_expr', 'qc_qty', 'qc_tp', 'postil'], prefix: '', relaId: 'lid'}
                     ]);
                 } else {
                     const curStage = await this.ctx.service.stageBills.getLastestStageData(this.ctx.tender.id, this.ctx.stage.id);
                     this.ctx.helper.assignRelaData(billsData, [
-                        {data: curStage, fields: ['contract_qty', 'contract_tp', 'contract_expr', 'qc_qty', 'qc_tp'], prefix: '', relaId: 'lid'}
+                        {data: curStage, fields: ['contract_qty', 'contract_tp', 'contract_expr', 'qc_qty', 'qc_tp', 'postil'], prefix: '', relaId: 'lid'}
                     ]);
                 }
             }
@@ -441,12 +441,12 @@ module.exports = app => {
                     const curPosStage = await this.ctx.service.stagePos.getAuditorStageData2(this.ctx.tender.id,
                         this.ctx.stage.id, this.ctx.stage.curTimes, this.ctx.stage.curOrder);
                     this.ctx.helper.assignRelaData(posData, [
-                        {data: curPosStage, fields: ['contract_qty', 'qc_qty', 'contract_expr'], prefix: '', relaId: 'pid'}
+                        {data: curPosStage, fields: ['contract_qty', 'qc_qty', 'contract_expr', 'postil'], prefix: '', relaId: 'pid'}
                     ]);
                 } else {
                     const curPosStage = await this.ctx.service.stagePos.getLastestStageData2(this.ctx.tender.id, this.ctx.stage.id);
                     this.ctx.helper.assignRelaData(posData, [
-                        {data: curPosStage, fields: ['contract_qty', 'qc_qty', 'contract_expr'], prefix: '', relaId: 'pid'}
+                        {data: curPosStage, fields: ['contract_qty', 'qc_qty', 'contract_expr', 'postil'], prefix: '', relaId: 'pid'}
                     ]);
                 }
             }

+ 16 - 0
app/service/stage.js

@@ -298,6 +298,16 @@ module.exports = app => {
             return stages;
         }
 
+        async _getSumTp(condition, ...field) {
+            const fieldSql = [];
+            for (const f of field) {
+                fieldSql.push('SUM(`' + f + '`) as ' + '`' + f + '`');
+            }
+
+            const sql = 'SELECT ' + fieldSql.join(', ') + ' FROM ' + this.tableName + ' ' + this.ctx.helper.whereSql(condition);
+            return await this.db.queryOne(sql);
+        }
+
         /**
          *
          * @param tenderId - 标段id
@@ -337,6 +347,12 @@ module.exports = app => {
                 newStage.pre_contract_tp = this.ctx.helper.add(preStage.pre_contract_tp, preStage.contract_tp);
                 newStage.pre_qc_tp = this.ctx.helper.add(preStage.pre_qc_tp, preStage.qc_tp);
                 newStage.pre_yf_tp = this.ctx.helper.add(preStage.pre_yf_tp, preStage.yf_tp);
+                if (preStage.order === 1 || preStage.pre_sf_tp) {
+                    newStage.pre_sf_tp = this.ctx.helper.add(preStage.pre_sf_tp, preStage.sf_tp);
+                } else {
+                    const sumTp = await this._getSumTp({tid: preStage.tid}, 'sf_tp');
+                    newStage.pre_sf_tp = sumTp.sf_tp || 0;
+                }
             }
             const transaction = await this.db.beginTransaction();
             try {

+ 3 - 11
app/service/stage_att.js

@@ -63,20 +63,12 @@ module.exports = app => {
          */
         async getDataByTenderIdAndStageId(tid, sid) {
             const { ctx } = this;
-            const sql = 'SELECT att.id, att.lid, att.uid, att.filepath, att.filename, att.fileext, att.filesize, att.re_upload, att.remark, att.in_time,' +
+            const sql = 'SELECT att.id, att.lid, att.uid, att.filename, att.fileext, att.filesize, att.re_upload, att.remark, att.in_time,' +
                 ' pa.name as `username`, leg.name as `lname`, leg.code as `code`, leg.ledger_id as `ledger_id`, leg.b_code as `b_code`' +
                 ' FROM ?? AS att,?? AS pa,?? AS leg' +
                 ' WHERE leg.id = att.lid AND pa.id = att.uid AND att.tid = ? AND att.sid = ? ORDER BY att.id DESC';
             const sqlParam = [this.tableName, this.ctx.service.projectAccount.tableName, this.ctx.service.ledger.tableName, tid, sid];
-            const result = await this.db.query(sql, sqlParam);
-            return result.map(item => {
-                if (!ctx.helper.canPreview(item.fileext)) {
-                    item.filepath = `/tender/${ctx.tender.id}/measure/stage/${ctx.params.order}/download/file/${item.id}`;
-                } else {
-                    item.filepath = '/' + item.filepath;
-                }
-                return item;
-            });
+            return await this.db.query(sql, sqlParam);
         }
 
         /**
@@ -94,7 +86,7 @@ module.exports = app => {
             const sqlParam = [this.tableName, this.ctx.service.projectAccount.tableName, this.ctx.service.ledger.tableName, id];
             const result = await this.db.queryOne(sql, sqlParam);
             if (!ctx.helper.canPreview(result.fileext)) result.filepath = `/tender/${ctx.tender.id}/measure/stage/${ctx.params.order}/download/file/${result.id}`;
-            else result.filepath = '/' + result.filepath;
+            else result.filepath = result.filepath.replace(/^app|\/app/, '');
             return result;
         }
     }

+ 26 - 7
app/service/stage_audit.js

@@ -47,6 +47,30 @@ module.exports = app => {
             return await this.db.queryOne(sql, sqlParam);
         }
 
+        async getAuditorByOrder(stageId, order, times) {
+            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` ' +
+                '    FROM ' + this.tableName + ' AS la' +
+                '    Left Join ' + this.ctx.service.projectAccount.tableName + ' AS pa ON la.`aid` = pa.`id`' +
+                '  WHERE la.`sid` = ? and la.`order` = ? and la.`times` = ?' +
+                '  ORDER BY `order` DESC';
+            const sqlParam = [stageId, order, times ? times: 1];
+            return await this.db.queryOne(sql, sqlParam);
+        }
+
+        async getLastestAuditor(stageId, times, status) {
+            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` ' +
+                '    FROM ' + this.tableName + ' AS la' +
+                '    Left Join ' + this.ctx.service.projectAccount.tableName + ' AS pa ON la.`aid` = pa.`id`' +
+                '  WHERE la.`sid` = ? and la.`status` = ? and la.`times` = ?' +
+                '  ORDER BY `order` DESC';
+            const sqlParam = [stageId, status, times ? times: 1];
+            return await this.db.queryOne(sql, sqlParam);
+        }
+
         /**
          * 获取 审核列表信息
          *
@@ -74,12 +98,8 @@ module.exports = app => {
 
         async getAllAuditors(tenderId) {
             const sql =
-                'SELECT sa.aid, sa.tid FROM ' +
-                this.tableName +
-                ' sa' +
-                '  LEFT JOIN ' +
-                this.ctx.service.tender.tableName +
-                ' t On sa.tid = t.id' +
+                'SELECT sa.aid, sa.tid FROM ' + this.tableName + ' sa' +
+                '  LEFT JOIN ' + this.ctx.service.tender.tableName + ' t On sa.tid = t.id' +
                 '  WHERE t.id = ?' +
                 '  GROUP BY  sa.aid';
             const sqlParam = [tenderId];
@@ -478,7 +498,6 @@ module.exports = app => {
         }
 
         async _checkNo(pid, stageId, checkData, times) {
-            console.log('checkData', checkData);
             const time = new Date();
             // 整理当前流程审核人状态更新
             const audit = await this.getDataByCondition({ sid: stageId, times, status: auditConst.status.checking });

+ 13 - 31
app/service/stage_bills.js

@@ -407,21 +407,17 @@ module.exports = app => {
          * @return {Promise<void>}
          */
         async getStagesData(tid, stage_id_list) {
-            let stage_id_listSql = '';
-            stage_id_list = stage_id_list.indexOf(',') !== -1 ? stage_id_list.split(',') : stage_id_list;
-            if (stage_id_list) {
-                if (stage_id_list instanceof Array) {
-                    stage_id_listSql = stage_id_list.length > 0 ? ' And sid in (' + this.ctx.helper.getInArrStrSqlFilter(stage_id_list) + ')' : '';
-                } else {
-                    stage_id_listSql = ' And sid in (' + this.db.escape(stage_id_list) + ')';
-                }
-            }
-            const sql = 'SELECT `lid`, `tid`, `sid`, SUM(`contract_qty`) as `contract_qty`, SUM(`contract_tp`) as `contract_tp`, ' +
-                'SUM(`qc_qty`) as `qc_qty`, SUM(`qc_tp`) as `qc_tp` FROM ' + this.tableName + ' WHERE `tid` = ? ' +
-                stage_id_listSql + ' GROUP BY `lid`';
-            const sqlParam = [tid];
-            const result = await this.db.query(sql, sqlParam);
-            console.log('result', result);
+            const whereSql = this.ctx.helper.whereSql({tid: tid, sid: stage_id_list.split(',')});
+            const sql = 'SELECT Bills.lid, Bills.tid, Bills.sid,' +
+                '    Sum(Bills.contract_qty) As contract_qty, Sum(Bills.contract_tp) As contract_tp,' +
+                '    Sum(Bills.qc_qty) As qc_qty, Sum(Bills.qc_tp) As qc_tp' +
+                '  FROM ' + this.tableName + ' As Bills ' +
+                '  INNER JOIN ( ' +
+                '    SELECT MAX(`times` * ' + timesLen + ' + `order`) As `progress`, `tid`, `lid`, `sid` From ' + this.tableName + whereSql + ' GROUP BY `lid`, `sid`' +
+                '  ) As MaxFilter ' +
+                '  ON (Bills.times * ' + timesLen + ' + `order`) = MaxFilter.progress And Bills.tid = MaxFilter.tid And Bills.lid = MaxFilter.lid And Bills.`sid` = MaxFilter.`sid`' +
+                '  GROUP BY Bills.lid';
+            const result = await this.db.query(sql);
             return result;
         }
 
@@ -458,16 +454,7 @@ module.exports = app => {
             let contract_tp = 0;
             let qc_tp = 0;
             for (const stage of stage_list) {
-                const sql = 'SELECT Sum(`contract_tp`) As `contract_tp`, Sum(`qc_tp`) As `qc_tp` FROM ' + this.tableName + ' As Bills ' +
-                    '  INNER JOIN ( ' +
-                    '    SELECT MAX(`times` * ' + timesLen + ' + `order`) As `flow`, `lid` From ' + this.tableName +
-                    '      WHERE `times` <= ? AND `order` <= ?' +
-                    '      GROUP BY `lid`' +
-                    '  ) As MaxFilter ' +
-                    '  ON (Bills.times * ' + timesLen + ' + `order`) = MaxFilter.flow And Bills.lid = MaxFilter.lid' +
-                    '  WHERE Bills.sid = ?';
-                const sqlParam = [stage.curTimes, stage.curOrder, stage.id];
-                const result = await this.db.queryOne(sql, sqlParam);
+                const result = await this.getSumTotalPrice(stage);
                 if (result) {
                     contract_tp = this.ctx.helper.add(contract_tp, result.contract_tp);
                     qc_tp = this.ctx.helper.add(qc_tp, result.qc_tp);
@@ -480,12 +467,7 @@ module.exports = app => {
             let contract_tp = 0;
             let qc_tp = 0;
             for (const stage of stage_list) {
-                let result = null;
-                if (regText) {
-                    result = await this.getSumTotalPriceFilter(stage, 'REGEXP', regText);
-                } else {
-                    result = await this.getSumTotalPriceFilter(stage, '<>', this.db.escape(''));
-                }
+                const result = await this.getSumTotalPriceGcl(stage, regText);
                 if (result) {
                     contract_tp = this.ctx.helper.add(contract_tp, result.contract_tp);
                     qc_tp = this.ctx.helper.add(qc_tp, result.qc_tp);

+ 13 - 3
app/service/stage_bonus.js

@@ -72,6 +72,10 @@ module.exports = app => {
         }
 
         async _addDatas(data) {
+            const tpDecimal = this.ctx.tender.info.decimal.extra
+                ? this.ctx.tender.info.decimal.extraTp
+                : this.ctx.tender.info.decimal.tp;
+
             const datas = data instanceof Array ? data : [data];
             const insertData = [];
             for (const d of datas) {
@@ -87,7 +91,7 @@ module.exports = app => {
                     order: d.order,
                 };
                 nd.b_type = d.b_type ? d.b_type : null;
-                nd.tp = d.tp ? this.ctx.helper.round(d.to, this.ctx.tender.info.decimal.tp) : 0;
+                nd.tp = d.tp ? this.ctx.helper.round(d.tp, tpDecimal) : 0;
                 nd.code = d.code ? d.code: null;
                 nd.proof = d.proof ? d.proof : null;
                 nd.real_time = d.real_time ? d.real_time : null;
@@ -112,8 +116,14 @@ module.exports = app => {
         }
 
         async _updateDatas (data) {
+            const tpDecimal = this.ctx.tender.info.decimal.extra
+                ? this.ctx.tender.info.decimal.extraTp
+                : this.ctx.tender.info.decimal.tp;
+
             const datas = data instanceof Array ? data : [data];
-            const orgDatas = await this.getAllDataByCondition({sid: this.ctx.stage.id, id: this.ctx.helper._.map(datas, 'id')});
+            const orgDatas = await this.getAllDataByCondition({
+                where: { sid: this.ctx.stage.id, id: this.ctx.helper._.map(datas, 'id') }
+            });
 
             const uDatas = [];
             for (const d of datas) {
@@ -123,7 +133,7 @@ module.exports = app => {
                 const nd = {id: od.id};
                 if (d.name !== undefined) nd.name = d.name;
                 if (d.b_type !== undefined) nd.b_type = d.b_type;
-                if (d.tp !== undefined) nd.tp = this.ctx.helper.round(d.tp, this.ctx.tender.info.decimal.tp);
+                if (d.tp !== undefined) nd.tp = this.ctx.helper.round(d.tp, tpDecimal);
                 if (d.code !== undefined) nd.code = d.code;
                 if (d.proof !== undefined) nd.proof = d.proof;
                 if (d.proof_file !== undefined) nd.proof_file = JSON.stringify(d.proof_file);

+ 9 - 9
app/service/stage_change.js

@@ -10,7 +10,7 @@
 
 const defaultPid = -1; // 非pid
 const audit = require('../const/audit');
-const timesLen = 100;
+const timesLen = audit.stage.timesLen;
 
 module.exports = app => {
 
@@ -40,11 +40,11 @@ module.exports = app => {
                 '  oc.p_code As c_code, oc.new_code As c_new_code' +
                 '  FROM ' + this.tableName + ' As c ' +
                 '  INNER JOIN ( ' +
-                '    SELECT MAX(`stimes`) As `stimes`, MAX(`sorder`) As `sorder`, `lid`, `pid`, `sid` From ' + this.tableName +
+                '    SELECT MAX(`stimes` * ' + timesLen + ' + `sorder`) As `progress`, `lid`, `pid`, `sid` From ' + this.tableName +
                 '      WHERE tid = ? And sid = ? And lid = ? And pid = ?' +
                 '      GROUP By `lid`, `pid`' +
                 '  ) As m ' +
-                '  ON c.stimes = m.stimes And c.sorder = m.sorder And c.lid = m.lid And c.pid = m.pid And c.`sid` = m.`sid`' +
+                '  ON (c.stimes * ' + timesLen + ' + c.sorder) = m.progress And c.lid = m.lid And c.pid = m.pid And c.`sid` = m.`sid`' +
                 '  LEFT JOIN ' + this.ctx.service.change.tableName + ' As oc' +
                 '  ON c.cid = oc.cid';
             const sqlParam = [tid, sid, lid, pid ? pid : -1];
@@ -66,11 +66,11 @@ module.exports = app => {
                 '  oc.p_code As c_code, oc.new_code As c_new_code' +
                 '  FROM ' + this.tableName + ' As c ' +
                 '  INNER JOIN ( ' +
-                '    SELECT MAX(`stimes`) As `stimes`, MAX(`sorder`) As `sorder`, `lid`, `pid`, `sid` From ' + this.tableName +
+                '    SELECT MAX(`stimes` * ' + timesLen + ' + `sorder`) As `progress`, `lid`, `pid`, `sid` From ' + this.tableName +
                 '      WHERE tid = ? And sid = ? And (`stimes` < ? OR (`stimes` = ? AND `sorder` <= ?)) And lid = ? And pid = ?' +
                 '      GROUP By `lid`, `pid`' +
                 '  ) As m ' +
-                '  ON c.stimes = m.stimes And c.sorder = m.sorder And c.lid = m.lid And c.pid = m.pid And c.`sid` = m.`sid`' +
+                '  ON (c.stimes * ' + timesLen + ' + c.sorder) = m.progress And c.lid = m.lid And c.pid = m.pid And c.`sid` = m.`sid`' +
                 '  LEFT JOIN ' + this.ctx.service.change.tableName + ' As oc' +
                 '  ON c.cid = oc.cid';
             const sqlParam = [tid, sid, times, times, order, lid, pid ? pid : -1];
@@ -82,11 +82,11 @@ module.exports = app => {
                 '  oc.p_code As c_code, oc.new_code As c_new_code' +
                 '  FROM ' + this.tableName + ' As c ' +
                 '  INNER JOIN ( ' +
-                '    SELECT MAX(`stimes`) As `stimes`, MAX(`sorder`) As `sorder`, `lid`, `pid`, `sid` From ' + this.tableName +
+                '    SELECT MAX(`stimes` * ' + timesLen + ' + `sorder`) As `progress`, `lid`, `pid`, `sid` From ' + this.tableName +
                 '      WHERE tid = ? And sid = ?' +
                 '      GROUP By `lid`, `pid`' +
                 '  ) As m ' +
-                '  ON c.stimes = m.stimes And c.sorder = m.sorder And c.`sid` = m.`sid` And c.lid = m.lid And c.pid = m.pid' +
+                '  ON (c.stimes * ' + timesLen + ' + c.sorder) = m.progress And c.lid = m.lid And c.pid = m.pid And c.`sid` = m.`sid`' +
                 '  LEFT JOIN ' + this.ctx.service.change.tableName + ' As oc' +
                 '  ON c.cid = oc.cid';
             const sqlParam = [tid, sid];
@@ -98,11 +98,11 @@ module.exports = app => {
                 '  oc.p_code As c_code, oc.new_code As c_new_code' +
                 '  FROM ' + this.tableName + ' As c ' +
                 '  INNER JOIN ( ' +
-                '    SELECT MAX(`stimes`) As `stimes`, MAX(`sorder`) As `sorder`, `lid`, `pid`, `sid` From ' + this.tableName +
+                '    SELECT MAX(`stimes` * ' + timesLen + ' + `sorder`) As `progress`, `lid`, `pid`, `sid` From ' + this.tableName +
                 '      WHERE tid = ? And sid = ? And (`stimes` < ? OR (`stimes` = ? AND `sorder` <= ?))' +
                 '      GROUP By `lid`, `pid`' +
                 '  ) As m ' +
-                '  ON c.stimes = m.stimes And c.sorder = m.sorder And c.`sid` = m.`sid` And c.lid = m.lid And c.pid = m.pid' +
+                '  ON (c.stimes * ' + timesLen + ' + c.sorder) = m.progress And c.lid = m.lid And c.pid = m.pid And c.`sid` = m.`sid`' +
                 '  LEFT JOIN ' + this.ctx.service.change.tableName + ' As oc' +
                 '  ON c.cid = oc.cid';
             const sqlParam = [tid, sid, times, times, order];

+ 23 - 10
app/service/stage_jgcl.js

@@ -71,6 +71,10 @@ module.exports = app => {
         }
 
         async _addDatas(data) {
+            const tpDecimal = this.ctx.tender.info.decimal.extra
+                ? this.ctx.tender.info.decimal.extraTp
+                : this.ctx.tender.info.decimal.tp;
+
             const datas = data instanceof Array ? data : [data];
             const insertData = [];
             for (const d of datas) {
@@ -79,20 +83,21 @@ module.exports = app => {
                     uuid: this.uuid.v4(),
                     add_sid: this.ctx.stage.id,
                     add_uid: this.ctx.session.sessionUser.accountId,
+                    tid: this.ctx.tender.id,
                     sid: this.ctx.stage.id,
                 };
                 nd.name = d.name;
                 nd.order = d.order;
                 if (d.unit) nd.unit = d.unit;
                 if (d.unit_price) nd.unit_price = d.unit_price;
-                const precision = this.ctx.helper.findPrecision(this.ctx.tender.info.precision, d.unit_price);
+                const precision = this.ctx.helper.findPrecision(this.ctx.tender.info.precision, d.unit);
                 if (d.arrive_qty) {
                     nd.arrive_qty = this.ctx.helper.round(d.arrive_qty, precision.value);
-                    nd.arrive_tp = this.ctx.helper.mul(nd.unit_price, nd.arrive_qty, this.ctx.tender.info.decimal.tp);
+                    nd.arrive_tp = this.ctx.helper.mul(nd.unit_price, nd.arrive_qty, tpDecimal);
                 }
                 if (d.deduct_qty) {
                     nd.deduct_qty = this.ctx.helper.round(d.deduct_qty, precision.value);
-                    nd.deduct_tp = this.ctx.helper.mul(nd.unit_price, nd.deduct_qty, this.ctx.tender.info.decimal.tp);
+                    nd.deduct_tp = this.ctx.helper.mul(nd.unit_price, nd.deduct_qty, tpDecimal);
                 }
                 if (d.source) nd.source = d.source;
                 if (d.bills_code) nd.bills_code = d.bills_code;
@@ -117,8 +122,14 @@ module.exports = app => {
         }
 
         async _updateDatas (data) {
+            const tpDecimal = this.ctx.tender.info.decimal.extra
+                ? this.ctx.tender.info.decimal.extraTp
+                : this.ctx.tender.info.decimal.tp;
+
             const datas = data instanceof Array ? data : [data];
-            const orgDatas = await this.getAllDataByCondition({sid: this.ctx.stage.id, id: this.ctx.helper._.map(datas, 'id')});
+            const orgDatas = await this.getAllDataByCondition({
+                where: { sid: this.ctx.stage.id, id: this.ctx.helper._.map(datas, 'id') }
+            });
             const info = this.ctx.tender.info;
 
             const uDatas = [];
@@ -134,18 +145,18 @@ module.exports = app => {
                 } else {
                     nd.unit_price = od.unit_price;
                 }
-                const precision = this.ctx.helper.findPrecision(info.precision, d.unit_price);
+                const precision = this.ctx.helper.findPrecision(info.precision, d.unit);
                 if (d.arrive_qty !== undefined) {
                     nd.arrive_qty = this.ctx.helper.round(d.arrive_qty, precision.value);
-                    nd.arrive_tp = this.ctx.helper.mul(nd.unit_price, nd.arrive_qty, info.decimal.tp);
+                    nd.arrive_tp = this.ctx.helper.mul(nd.unit_price, nd.arrive_qty, tpDecimal);
                 } else if (d.unit_price !== undefined) {
-                    nd.arrive_tp = this.ctx.helper.mul(nd.unit_price, od.arrive_qty, info.decimal.tp);
+                    nd.arrive_tp = this.ctx.helper.mul(nd.unit_price, od.arrive_qty, tpDecimal);
                 }
                 if (d.deduct_qty !== undefined) {
                     nd.deduct_qty = this.ctx.helper.round(d.deduct_qty, precision.value);
-                    nd.deduct_tp = this.ctx.helper.mul(nd.unit_price, nd.deduct_qty, info.decimal.tp);
+                    nd.deduct_tp = this.ctx.helper.mul(nd.unit_price, nd.deduct_qty, tpDecimal);
                 } else if (d.unit_price !== undefined) {
-                    nd.deduct_tp = this.ctx.helper.mul(nd.unit_price, od.deduct_qty, info.decimal.tp);
+                    nd.deduct_tp = this.ctx.helper.mul(nd.unit_price, od.deduct_qty, tpDecimal);
                 }
                 if (d.source !== undefined) nd.source = d.source;
                 if (d.bills_code !== undefined) nd.bills_code = d.bills_code;
@@ -215,12 +226,14 @@ module.exports = app => {
                 throw '标段数据有误';
             }
             const preDatas = await this.getAllDataByCondition({
-                columns: ['uuid', 'name', 'unit', 'unit_price', 'source', 'bills_code', 'check_code', 'memo', 'add_uid', 'add_sid', 'order'],
+                columns: ['uuid', 'name', 'unit', 'unit_price', 'source', 'bills_code', 'check_code', 'memo', 'add_uid', 'add_sid', 'order', 'arrive_qty', 'deduct_qty'],
                 where: { sid: preStage.id },
             });
             if (preDatas.length > 0) {
                 for (const pd of preDatas) {
                     pd.pre_used = pd.pre_used || !this.ctx.helper.checkZero(pd.arrive_qty) || !this.ctx.helper.checkZero(pd.deduct_qty);
+                    delete pd.arrive_qty;
+                    delete pd.deduct_qty;
                     pd.sid = stage.id;
                 }
                 const result = await transaction.insert(this.tableName, preDatas);

+ 17 - 6
app/service/stage_other.js

@@ -59,6 +59,10 @@ module.exports = app => {
         }
 
         async _addDatas(data) {
+            const tpDecimal = this.ctx.tender.info.decimal.extra
+                ? this.ctx.tender.info.decimal.extraTp
+                : this.ctx.tender.info.decimal.tp;
+
             const datas = data instanceof Array ? data : [data];
             const insertData = [];
             for (const d of datas) {
@@ -76,8 +80,8 @@ module.exports = app => {
                 nd.order = d.order;
 
                 if (d.o_type) nd.o_type = d.o_type;
-                if (d.total_price) nd.total_price = this.ctx.helper.round(d.total_price, this.ctx.tender.info.decimal.tp);
-                if (d.tp) nd.tp = this.ctx.helper.round(d.tp, this.ctx.tender.info.decimal.tp);
+                if (d.total_price) nd.total_price = this.ctx.helper.round(d.total_price, tpDecimal);
+                if (d.tp) nd.tp = this.ctx.helper.round(d.tp, tpDecimal);
                 if (d.real_time) nd.real_time = d.real_time;
                 if (d.memo) nd.memo = d.memo;
                 insertData.push(nd);
@@ -99,8 +103,14 @@ module.exports = app => {
         }
 
         async _updateDatas (data) {
+            const tpDecimal = this.ctx.tender.info.decimal.extra
+                ? this.ctx.tender.info.decimal.extraTp
+                : this.ctx.tender.info.decimal.tp;
+
             const datas = data instanceof Array ? data : [data];
-            const orgDatas = await this.getAllDataByCondition({sid: this.ctx.stage.id, id: this.ctx.helper._.map(datas, 'id')});
+            const orgDatas = await this.getAllDataByCondition({
+                where: { sid: this.ctx.stage.id, id: this.ctx.helper._.map(datas, 'id') }
+            });
 
             const uDatas = [];
             for (const d of datas) {
@@ -116,9 +126,9 @@ module.exports = app => {
                 if (d.o_type !== undefined) nd.o_type = d.o_type;
                 if (d.total_price !== undefined) {
                     if (od.pre_used) throw '往期已使用,不可修改金额';
-                    nd.total_price = this.ctx.helper.round(d.total_price, this.ctx.tender.info.decimal.tp);
+                    nd.total_price = this.ctx.helper.round(d.total_price, tpDecimal);
                 }
-                if (d.tp !== undefined) nd.tp = this.ctx.helper.round(d.tp, this.ctx.tender.info.decimal.tp);
+                if (d.tp !== undefined) nd.tp = this.ctx.helper.round(d.tp, tpDecimal);
                 if (d.real_time !== undefined) nd.real_time = new Date(d.real_time);
                 if (d.memo !== undefined) nd.memo = d.memo;
                 uDatas.push(nd);
@@ -184,12 +194,13 @@ module.exports = app => {
                 throw '标段数据有误';
             }
             const preDatas = await this.getAllDataByCondition({
-                columns: ['uuid', 'tid', 'add_uid', 'add_sid', 'add_time', 'name', 'o_type', 'total_price', 'order', 'memo', 'real_time'],
+                columns: ['uuid', 'tid', 'add_uid', 'add_sid', 'add_time', 'name', 'o_type', 'tp', 'total_price', 'order', 'memo', 'real_time'],
                 where: { sid: preStage.id }
             });
             if (preDatas.length > 0) {
                 for (const pd of preDatas) {
                     pd.pre_used = pd.pre_used || !this.ctx.helper.checkZero(pd.tp);
+                    delete pd.tp;
                     pd.sid = stage.id;
                     pd.sorder = stage.order;
                 }

+ 21 - 13
app/service/stage_pos.js

@@ -88,7 +88,7 @@ module.exports = app => {
             return await this.db.query(sql, sqlParam);
         }
 
-        async _filterLastestData(stagePos) {
+        _filterLastestData(stagePos) {
             const stagePosIndex = {};
             for (const sp of stagePos) {
                 const key = 'sp-' + sp.pid;
@@ -545,20 +545,28 @@ module.exports = app => {
          * @returns {Promise<void>}
          */
         async getStagesData(tid, stage_id_list) {
-            let stage_id_listSql = '';
-            stage_id_list = stage_id_list.indexOf(',') !== -1 ? stage_id_list.split(',') : stage_id_list;
-            if (stage_id_list) {
-                if (stage_id_list instanceof Array) {
-                    stage_id_listSql = stage_id_list.length > 0 ? ' And sid in (' + this.ctx.helper.getInArrStrSqlFilter(stage_id_list) + ')' : '';
-                } else {
-                    stage_id_listSql = ' And sid in (' + this.db.escape(stage_id_list) + ')';
+            const sids = stage_id_list.split(',');
+            const result = [];
+            for (const sid of sids) {
+                const sql = 'SELECT id, tid, sid, pid, lid, contract_qty, qc_qty, postil, `times`, `order`, `contract_expr`' +
+                    '  FROM ' + this.tableName +
+                    '  WHERE tid = ? And sid = ? ';
+                const sqlParam = [tid, sid];
+                const stagePos = await this.db.query(sql, sqlParam);
+                const stagePosFilter = this._filterLastestData(stagePos);
+                for (const sp of stagePosFilter) {
+                    const rsp = result.find(function (x) { return x.pid === sp.pid});
+                    if (rsp) {
+                        rsp.contract_qty = this.ctx.helper.add(rsp.contract_qty, sp.contract_qty);
+                        rsp.qc_qty = this.ctx.helper.add(rsp.qc_qty, sp.qc_qty);
+                    } else {
+                        result.push({
+                            id: sp.id, tid: sp.tid, lid: sp.lid, pid: sp.pid,
+                            contract_qty: sp.contract_qty, qc_qty: sp.qc_qty,
+                        });
+                    }
                 }
             }
-            const sql = 'SELECT `id`, `tid`, `sid`, `pid`, `lid`, SUM(`contract_qty`) as `contract_qty`, SUM(`qc_qty`) as `qc_qty`' +
-                ' FROM ' + this.tableName + ' WHERE `tid` = ? ' +
-                stage_id_listSql + 'GROUP BY `pid`';
-            const sqlParam = [tid];
-            const result = await this.db.query(sql, sqlParam);
             return result;
         }
 

+ 79 - 9
app/service/tender_info.js

@@ -12,7 +12,7 @@ const infoConst = require('../const/tender_info');
 const parseInfo = infoConst.parseInfo;
 const arrayInfo = infoConst.arrayInfo;
 const defaultInfo = infoConst.defaultInfo;
-
+const advanceConst = require('../const/audit').advance;
 module.exports = app => {
 
     class TenderInfo extends app.BaseService {
@@ -86,13 +86,11 @@ module.exports = app => {
          */
         async getTenderInfoEx(tenderId) {
             const sql = 'select t2.name, t1.* from zh_tender_info t1 inner join zh_tender t2 on t2.id = t1.tid where t1.tid = ?';
-            // console.log('getTenderInfoEx: ' + tenderId);
             const sqlParam = [tenderId];
             const list = await this.db.query(sql, sqlParam);
             const info = list[0];
             const len = info.deal_info.length;
             info.deal_info = info.deal_info.slice(0, len - 1) + ',"name":"' + info.name + '"' + info.deal_info.slice(len - 1);
-            // console.log(info);
             for (const pi of parseInfo) {
                 info[pi] = !info[pi] || info[pi] === '' ? defaultInfo[pi] : JSON.parse(info[pi]);
                 this.ctx.helper._.defaults(info[pi], defaultInfo[pi]);
@@ -200,10 +198,9 @@ module.exports = app => {
             }
         }
 
-        async saveDecimal(tenderId, newDecimal, oldDecimal) {
-            const changeBills = [],
-                calcUp = newDecimal.up < oldDecimal.up,
-                calcTp = newDecimal.tp !== oldDecimal.tp;
+        async _reCalcLedger(tenderId, newDecimal, oldDecimal) {
+            const changeBills = [];
+            const calcUp = newDecimal.up < oldDecimal.up, calcTp = newDecimal.tp !== oldDecimal.tp;
             if (calcUp || calcTp) {
                 const bills = await this.ctx.service.ledger.getAllDataByCondition({
                     columns: ['id', 'unit_price', 'sgfh_qty', 'sjcl_qty', 'qtcl_qty', 'deal_qty', 'quantity'],
@@ -220,12 +217,85 @@ module.exports = app => {
                     changeBills.push(cb);
                 }
             }
-            if (changeBills.length > 0) {
+            return changeBills;
+        }
+        async _reCalcStageExtra(tenderId, newDecimal, oldDecimal) {
+            let changeSj = [], changeSb = [], changeSo = [];
+            const stage = await this.ctx.service.stage.getLastestStage(tenderId, true);
+            if (!stage) return [changeSj, changeSb, changeSo];
+
+            const upDecimal = newDecimal.up, tpDecimal = newDecimal.extra ? newDecimal.extraTp : newDecimal.tp;
+            const calcUp = upDecimal < oldDecimal.up,
+                calcTp = tpDecimal < (oldDecimal.extra ? oldDecimal.extraTp : oldDecimal.tp);
+            console.log(calcUp, calcTp);
+
+            if (calcUp || calcTp) {
+                const stageJgcl = await this.ctx.service.stageJgcl.getAllDataByCondition({
+                    columns: ['id', 'unit_price', 'arrive_qty', 'deduct_qty'],
+                    where: { sid: stage.id }
+                });
+                for (const sj of stageJgcl) {
+                    const cj = { id: sj.id };
+                    cj.unit_price = calcUp ? this.ctx.helper.round(sj.unit_price, upDecimal) : sj.unit_price;
+                    cj.arrive_tp = this.ctx.helper.mul(sj.arrive_qty, sj.unit_price, tpDecimal);
+                    cj.deduct_tp = this.ctx.helper.mul(sj.deduct_qty, sj.unit_price, tpDecimal);
+                    changeSj.push(cj);
+                }
+            }
+            if (calcTp) {
+                changeSb = await this.ctx.service.stageBonus.getAllDataByCondition({
+                    columns: ['id', 'tp'],
+                    where: { sid: stage.id }
+                });
+                for (const cb of changeSb) {
+                    cb.tp = this.ctx.helper.round(cb.tp, tpDecimal);
+                }
+                changeSo = await this.ctx.service.stageOther.getAllDataByCondition({
+                    columns: ['id', 'total_price', 'tp'],
+                    where: { sid: stage.id }
+                });
+                for (const co of changeSo) {
+                    if (stage.order === 1) co.total_price = this.ctx.helper.round(co.total_price, tpDecimal);
+                    co.tp = this.ctx.helper.round(co.tp, tpDecimal);
+                }
+            }
+            return [changeSj, changeSb, changeSo];
+        }
+
+        async saveDecimal(tenderId, newDecimal, oldDecimal) {
+            const changeAdvanceBills = [];
+            const caclPayTp = (newDecimal.pay ? newDecimal.payTp : newDecimal.tp) < (oldDecimal.pay ? oldDecimal.payTp : oldDecimal.tp);
+            if (caclPayTp) {
+                // 获取预付款需要修改的相关记录
+                const ad_bills = await this.ctx.service.advance.getAllDataByCondition({
+                    columns: ['id', 'cur_amount', 'prev_amount', 'prev_total_amount'],
+                    where: { status: [advanceConst.status.uncheck, advanceConst.status.checkNo], tid: tenderId },
+                });
+                const decimal = newDecimal.pay ? newDecimal.payTp : newDecimal.tp;
+                // 根据精度重新计算相关金额
+                for (const ad of ad_bills) {
+                    const cb = { id: ad.id };
+                    const cur_amount = this.ctx.helper.round(ad.cur_amount, decimal);
+                    cb.cur_amount = cur_amount;
+                    cb.prev_total_amount = this.ctx.helper.add(cur_amount, ad.prev_amount);
+                    changeAdvanceBills.push(cb);
+                }
+            }
+            const changeBills = await this._reCalcLedger(tenderId, newDecimal, oldDecimal);
+            const [changeSj, changeSb, changeSo] = await this._reCalcStageExtra(tenderId, newDecimal, oldDecimal);
+            if (changeBills.length > 0 ||
+                changeAdvanceBills.length > 0 ||
+                changeSj.length > 0 || changeSb.length > 0 || changeSo.length > 0) {
                 const transaction = await this.db.beginTransaction();
                 try {
                     await transaction.update(this.tableName,
                         { decimal: JSON.stringify(newDecimal) }, { where: { tid: tenderId } });
-                    await transaction.updateRows(this.ctx.service.ledger.tableName, changeBills);
+                    if (changeBills.length > 0) await transaction.updateRows(this.ctx.service.ledger.tableName, changeBills);
+                    if (changeSj.length > 0) await transaction.updateRows(this.ctx.service.stageJgcl.tableName, changeSj);
+                    if (changeSb.length > 0) await transaction.updateRows(this.ctx.service.stageBonus.tableName, changeSb);
+                    if (changeSo.length > 0) await transaction.updateRows(this.ctx.service.stageOther.tableName, changeSo);
+
+                    if (changeAdvanceBills.length) await transaction.updateRows(this.ctx.service.advance.tableName, changeAdvanceBills);
                     await transaction.commit();
                 } catch (error) {
                     await transaction.rollback();

+ 9 - 9
app/view/advance/detail.ejs

@@ -22,7 +22,7 @@
                         <thead>
                             <tr>
                                 <th colspan="4" class="text-center">
-                                    签约<%- advance.type === auditConst.type.start ? '开工' : '材料' %>预付款:<%- ctx.helper.formatMoney(advancePayTotal) %>
+                                    签约<%- advance.type === auditConst.type.start ? '开工' : '材料' %>预付款:<%- ctx.helper.formatMoney(advancePayTotal, ',', parseFloat(advancePayTotal.toFixed(decimal)).toString().split('.')[1] && parseFloat(advancePayTotal.toFixed(decimal)).toString().split('.')[1].length || 0) %>
                                 </th>
                             </tr>
@@ -33,17 +33,17 @@
                                 <td class="text-right" width="405">
                                     <div class="input-group input-group-sm">
                                         <input type="number" class="pay-input form-control nospin text-right"
-                                            max="<%- max_pr %>" min="1" step="0.01" placeholder="请填写支付比例,将自动计算本期金额" data-type="0" <%- isEdited && ctx.session.sessionUser.accountId === ctx.advance.uid ? '' : 'disabled' %>
-                                            value="<%- advance.cur_amount && ctx.helper.mul(ctx.helper.div(advance.cur_amount, advancePayTotal), 100, 2) || 0 %>">
+                                            max="<%- max_pr %>" min="1" step="0.01" placeholder="请填写支付比例,将自动计算本期金额" data-type="0" <%- isEdited ? '' : 'disabled' %>
+                                            value="<%- advance.pay_ratio && ctx.helper.mul(ctx.helper.div(advance.cur_amount, advancePayTotal), 100, 2) || 0 %>">
                                         <div class="input-group-append"><span class="input-group-text">%</span></div>
                                     </div>
                                 </td>
                                 <th width="150" class="text-center">本期金额</th>
                                 <td class="text-right" width="405">
                                     <div class="input-group input-group-sm">
-                                        <input type="number" class="pay-input form-control nospin text-right" min="1" <%- isEdited && ctx.session.sessionUser.accountId === ctx.advance.uid ? '' : 'disabled' %>
+                                        <input type="number" class="pay-input form-control nospin text-right" min="1" <%- isEdited  ? '' : 'disabled' %>
                                             placeholder="请填写本期金额,将自动计算支付比例" data-type="1"
-                                            value="<%- advance.cur_amount && advance.cur_amount.toFixed(decimal) %>">
+                                            value="<%- cur_amount %>">
                                         <div class="input-group-append"><span class="input-group-text">元</span></div>
                                     </div>
                                 </td>
@@ -51,18 +51,18 @@
                             <tr>
                                 <th class="text-center">截止上期</th>
                                 <td class="text-right" id="p_total1" width="405">
-                                    <%- ctx.helper.formatMoney((prevAdvance && prevAdvance.prev_total_amount || 0), ',', decimal) %>元
+                                    <%- prev_amount %>元
                                 </td>
                                 <th class="text-center">截止本期金额</th>
                                 <td class="text-right" id="p_total2" width="405">
-                                    <%- ctx.helper.formatMoney((prevAdvance && prevAdvance.prev_total_amount + advance.cur_amount || (advance.cur_amount || 0)), ',', decimal) %>元
+                                    <%- prev_total_amount %>元
                                 </td>
                             </tr>
                             <tr>
                                 <th class="text-center" >备注</th>
                                 <td colspan="3">
                                     <textarea id="ad-remark" class="form-control form-control-sm" rows="2"
-                                        <%- isEdited && ctx.session.sessionUser.accountId === ctx.advance.uid ? '' : 'disabled' %>></textarea>
+                                        <%- isEdited ? '' : 'disabled' %>></textarea>
                                 </td>
                             </tr>
                         </tbody>
@@ -76,7 +76,7 @@
                         <tbody id="file-content">
                         </tbody>
                     </table>
-                    <% if(isEdited && ctx.session.sessionUser.accountId === ctx.advance.uid) { %>
+                    <% if(isEdited) { %>
                     <table class="table table-bordered mt-3">
                         <thead>
                             <tr>

+ 8 - 8
app/view/advance/index.ejs

@@ -15,16 +15,16 @@
                 </div>
                 <div class="d-inline-block ml-2">
                     签约<%- type === 0 ? '开工' : '材料' %>预付款
-                    <b><%- ctx.helper.formatMoney(advancePayTotal) %></b> 元
+                    <b><%- ctx.helper.formatMoney(advancePayTotal, ',', parseFloat(advancePayTotal.toFixed(decimal)).toString().split('.')[1] && parseFloat(advancePayTotal.toFixed(decimal)).toString().split('.')[1].length || 0) %></b> 元
                 </div>
                 <div class="d-inline-block ml-4" style="width:300px">
                     <div class="progress">
                         <div class="progress-bar bg-success" style="width: <%- progress.p_ratio%>%;" data-placement="bottom"
-                            data-toggle="tooltip" data-original-title="截止上期金额:¥<%- ctx.helper.formatMoney(progress.p_amount, ',', decimal) %>"><%- progress.p_ratio.toFixed() %>%</div>
+                            data-toggle="tooltip" data-original-title="截止上期金额:¥<%- ctx.helper.formatMoney(progress.p_amount, ',', parseFloat(progress.p_amount.toFixed(decimal)).toString().split('.')[1] && parseFloat(progress.p_amount.toFixed(decimal)).toString().split('.')[1].length || 0) %>"><%- progress.p_ratio.toFixed() %>%</div>
                         <div class="progress-bar bg-info" style="width:<%- progress.c_ratio%>%;" data-placement="bottom"
-                            data-toggle="tooltip" data-original-title="本期金额:¥<%- ctx.helper.formatMoney(progress.c_amount, ',', decimal) %>"><%- progress.c_ratio.toFixed() %>%</div>
+                            data-toggle="tooltip" data-original-title="本期金额:¥<%- ctx.helper.formatMoney(progress.c_amount, ',', progress.c_amount && parseFloat(progress.c_amount.toFixed(decimal)).toString().split('.')[1] && parseFloat(progress.c_amount.toFixed(decimal)).toString().split('.')[1].length || 0) %>"><%- progress.c_ratio.toFixed() %>%</div>
                         <div class="progress-bar bg-gray" style="width:<%- progress.s_ratio%>%;" data-placement="bottom"
-                            data-toggle="tooltip" data-original-title="未完成:¥<%- ctx.helper.formatMoney(progress.s_amount, ',', decimal) %>"><%- progress.s_ratio.toFixed() %>%</div>
+                            data-toggle="tooltip" data-original-title="未完成:¥<%- ctx.helper.formatMoney(progress.s_amount, ',', parseFloat(progress.s_amount.toFixed(decimal)).toString().split('.')[1] && parseFloat(progress.s_amount.toFixed(decimal)).toString().split('.')[1].length || 0) %>"><%- progress.s_ratio.toFixed() %>%</div>
                     </div>
                 </div>
             </div>
@@ -59,10 +59,10 @@
                         <% advanceList.forEach(item => { %>
                             <tr>
                                 <td><a href="/tender/<%- ctx.tender.id %>/advance/<%- item.id %>/detail" data-id="<%- item.id %>">第<%- item.order %>期</a></td>
-                                <td><%- item.cur_amount && ctx.helper.mul(ctx.helper.div(item.cur_amount, advancePayTotal), 100, 2) || 0 %>%</td>
-                                <td class="text-right"><%- ctx.helper.formatMoney(item.cur_amount, ',', decimal)%></td>
-                                <td class="text-right"><%- ctx.helper.formatMoney(item.prev_amount, ',', decimal)%></td>
-                                <td class="text-right"><%- ctx.helper.formatMoney(item.prev_total_amount, ',', decimal)%></td>
+                                <td><%- item.pay_ratio %>%</td>
+                                <td class="text-right"><%- item.cur_amount %></td>
+                                <td class="text-right"><%- item.prev_amount %></td>
+                                <td class="text-right"><%- item.prev_total_amount %></td>
                                 <td><a class="btn btn-sm" href="#file" data-toggle="modal" data-target="#file" data-id="<%- item.id %>"><i
                                             class="fa fa-paperclip"></i> <%- item.fileList.length %></a></td>
                                 <td>

+ 1 - 1
app/view/advance/modal.ejs

@@ -23,7 +23,7 @@
                 <h5 class="modal-title">附件</h5>
             </div>
             <div class="modal-body">
-                <div class="modal-height-500">
+                <div class="modal-height-500" style="overflow: auto;">
                     <table class="table table-sm table-bordered" id="file-content">
                     </table>
                 </div>

+ 15 - 15
app/view/change/info.ejs

@@ -457,9 +457,9 @@
                         <td data-site="4"><%= cl.unit %></td>
                         <td data-site="5"><%= ctx.helper.roundNum(cl.unit_price, upUnit) %></td>
                         <td data-site="6"><%= ctx.helper.roundNum(cl.oamount, ctx.helper.findDecimal(cl.unit)) %></td>
-                        <td data-site="7"><%= ctx.helper.roundNum(ctx.helper.accMul(cl.unit_price, cl.oamount), tpUnit) %></td>
+                        <td data-site="7"><%= ctx.helper.mul(cl.unit_price, cl.oamount, tpUnit) %></td>
                         <td data-site="8"><input class="form-control form-control-sm" placeholder="变更数量" type="text" onkeyup="RegNum(this,event,<%= ctx.helper.findDecimal(cl.unit) %>)"  value="<%= ctx.helper.roundNum(cl.camount, ctx.helper.findDecimal(cl.unit)) %>"></td>
-                        <td data-site="9"><%= ctx.helper.roundNum(ctx.helper.accMul(cl.unit_price, cl.camount), tpUnit) %></td>
+                        <td data-site="9"><%= ctx.helper.mul(cl.unit_price, cl.camount, tpUnit) %></td>
                         <td><a class="text-danger">移除</a>
                         </td>
                     </tr>
@@ -483,9 +483,9 @@
                         </td>
                         <td data-site="5"><input class="form-control form-control-sm" placeholder="单价" onkeyup="RegNum(this,event, <%= upUnit %>)" type="text" value="<%= ctx.helper.roundNum(cl.unit_price, upUnit) %>"></td>
                         <td data-site="6"><input class="form-control form-control-sm" placeholder="数量" onkeyup="RegNum(this,event,<%= ctx.helper.findDecimal(cl.unit) %>)" type="text" value="<%= ctx.helper.roundNum(cl.oamount, ctx.helper.findDecimal(cl.unit)) %>"></td>
-                        <td data-site="7"><%= ctx.helper.roundNum(ctx.helper.accMul(cl.unit_price, cl.oamount), tpUnit) %></td>
+                        <td data-site="7"><%= ctx.helper.mul(cl.unit_price, cl.oamount, tpUnit) %></td>
                         <td data-site="8"><input class="form-control form-control-sm" placeholder="变更数量" onkeyup="RegNum(this,event,<%= ctx.helper.findDecimal(cl.unit) %>)" type="text" value="<%= ctx.helper.roundNum(cl.camount, ctx.helper.findDecimal(cl.unit)) %>"></td>
-                        <td data-site="9"><%= ctx.helper.roundNum(ctx.helper.accMul(cl.unit_price, cl.camount), tpUnit) %></td>
+                        <td data-site="9"><%= ctx.helper.mul(cl.unit_price, cl.camount, tpUnit) %></td>
                         <td><a class="text-danger">移除</a>
                         </td>
                     </tr>
@@ -534,11 +534,11 @@
                             <td><%= cl.unit %></td>
                             <td><%= ctx.helper.roundNum(cl.unit_price, upUnit) %></td>
                             <td><%= ctx.helper.roundNum(cl.oamount, ctx.helper.findDecimal(cl.unit)) %></td>
-                            <td><%= ctx.helper.roundNum(ctx.helper.accMul(cl.unit_price, cl.oamount), tpUnit) %></td>
+                            <td><%= ctx.helper.mul(cl.unit_price, cl.oamount, tpUnit) %></td>
                             <td><%= ctx.helper.roundNum(cl.camount, ctx.helper.findDecimal(cl.unit)) %></td>
-                            <td><%= ctx.helper.roundNum(ctx.helper.accMul(cl.unit_price, cl.camount), tpUnit) %></td>
+                            <td><%= ctx.helper.mul(cl.unit_price, cl.camount, tpUnit) %></td>
                             <td><%= cl.samount !== '' ? ctx.helper.roundNum(cl.samount, ctx.helper.findDecimal(cl.unit)) : '' %></td>
-                            <td><%= cl.samount !== '' ? ctx.helper.roundNum(ctx.helper.accMul(cl.unit_price, cl.samount), tpUnit) : '' %></td>
+                            <td><%= cl.samount !== '' ? ctx.helper.mul(cl.unit_price, cl.samount, tpUnit) : '' %></td>
                             </td>
                         </tr>
                     <% } %>
@@ -593,13 +593,13 @@
                             <td><%= cl.unit %></td>
                             <td><%= ctx.helper.roundNum(cl.unit_price, upUnit) %></td>
                             <td><%= ctx.helper.roundNum(cl.oamount, ctx.helper.findDecimal(cl.unit)) %></td>
-                            <td><%= ctx.helper.roundNum(ctx.helper.accMul(cl.unit_price, cl.oamount), tpUnit) %></td>
+                            <td><%= ctx.helper.mul(cl.unit_price, cl.oamount, tpUnit) %></td>
                             <td><%= ctx.helper.roundNum(cl.camount, ctx.helper.findDecimal(cl.unit)) %></td>
-                            <td><%= ctx.helper.roundNum(ctx.helper.accMul(cl.unit_price, cl.camount), tpUnit) %></td>
+                            <td><%= ctx.helper.mul(cl.unit_price, cl.camount, tpUnit) %></td>
                             <% for (const audit of auditList2) { %>
                                 <% if (audit.usite !== 0) { %>
                                 <td><%= audit.list_amount[cindex] !== undefined ? ctx.helper.roundNum(audit.list_amount[cindex], ctx.helper.findDecimal(cl.unit)) : '' %></td>
-                                <td><%= audit.list_amount[cindex] !== undefined ? ctx.helper.roundNum(ctx.helper.accMul(cl.unit_price, audit.list_amount[cindex]), tpUnit) : '' %></td>
+                                <td><%= audit.list_amount[cindex] !== undefined ? ctx.helper.mul(cl.unit_price,  audit.list_amount[cindex], tpUnit) : '' %></td>
                                 <% } %>
                             <% } %>
                             </td>
@@ -661,9 +661,9 @@
                             <td><%= cl.unit %></td>
                             <td data-site="5"><%= ctx.helper.roundNum(cl.unit_price, upUnit) %></td>
                             <td><%= ctx.helper.roundNum(cl.oamount, ctx.helper.findDecimal(cl.unit)) %></td>
-                            <td><%= ctx.helper.roundNum(ctx.helper.accMul(cl.unit_price, cl.oamount), tpUnit) %></td>
+                            <td><%= ctx.helper.mul(cl.unit_price, cl.oamount, tpUnit) %></td>
                             <td><%= ctx.helper.roundNum(cl.camount, ctx.helper.findDecimal(cl.unit)) %></td>
-                            <td><%= ctx.helper.roundNum(ctx.helper.accMul(cl.unit_price, cl.camount), tpUnit) %></td>
+                            <td><%= ctx.helper.mul(cl.unit_price, cl.camount, tpUnit) %></td>
                             <% for (const audit of auditList) { %>
                                 <% if (audit.usite !== 0) { %>
                                     <% if (uid === audit.uid && audit.status === 2) { %>
@@ -676,7 +676,7 @@
                                     <% } else { %>
                                     <td><%= audit.list_amount[cindex] !== undefined ? ctx.helper.roundNum(audit.list_amount[cindex], ctx.helper.findDecimal(cl.unit)) : '' %></td>
                                     <% } %>
-                                    <td<% if (uid === audit.uid) { %> class="amount_cost"<% } %>><%= audit.list_amount[cindex] !== undefined ? ctx.helper.roundNum(ctx.helper.accMul(cl.unit_price, audit.list_amount[cindex]), tpUnit) : '' %></td>
+                                    <td<% if (uid === audit.uid) { %> class="amount_cost"<% } %>><%= audit.list_amount[cindex] !== undefined ? ctx.helper.mul(cl.unit_price, audit.list_amount[cindex], tpUnit) : '' %></td>
                                 <% } %>
                             <% } %>
                             </td>
@@ -699,7 +699,7 @@
             <% } %>
             </div>
         </div>
-
+        <a href="" id="file-upload" target="_blank" style="display: none;"></a>
         <div class="c-body tab-pane" role="tabpanel" id="files">
             <div class="sjs-height-0">
             <table class="table table-bordered">
@@ -717,7 +717,7 @@
                 <% for (const [index,att] of attList.entries()) { %>
                 <tr>
                     <td><%- index+1 %></td>
-                    <td><a href="<%- att.filepath %>" target="_black"><%- att.filename %><%- att.fileext %></a></td>
+                    <td><a href="javascript: void(0);" class="file-atn" f-id="<%- att.id %>"><%- att.filename %><%- att.fileext %></a></td>
                     <td><%- ctx.helper.bytesToSize(att.filesize) %></td>
                     <td><%- moment(att.in_time * 1000).format('YYYY-MM-DD') %></td>
                     <td>

+ 3 - 0
app/view/layout/menu.ejs

@@ -24,6 +24,9 @@
     </div>
     <div class="nav-bottom mt-auto">
         <ul class="nav nav-pills nav-stacked bg-nav">
+            <li><a href="http://doc.zhzdwd.com/docs/yunjiliangAPI/yunjiliangAPI-1ccjk7h426enp" target="_blank" data-toggle="tooltip" data-placement="right" title="" data-original-title="数据接口">API</a></li>
+        </ul>
+        <ul class="nav nav-pills nav-stacked bg-nav">
             <li <% if (ctx.controllerName === 'setting') { %>class="active"<% } %>><a href="/setting/info" data-toggle="tooltip" data-placement="right" title="" data-original-title="项目信息"><i class="fa fa-cogs"></i><span>项目信息</span></a></li>
         </ul>
         <div class="dropup mb-1 ml-1">

+ 5 - 2
app/view/ledger/bwtz.ejs

@@ -19,9 +19,12 @@
                         </div>
                     </div>
                 </div>
-                <div class="d-inline-block ml-3">
+                <div class="d-inline-block">
                     <a id="exportBwtz" class="btn btn-primary btn-sm" href="javascript: void(0)">导出部位台账Excel</a>
                 </div>
+                <div class="d-inline-block ml-2">
+                    <div class="alert alert-warning p-1"><i class="fa Example of exclamation-circle fa-exclamation-circle "></i> 台账分解与部位台账金额,会存在数学误差,属正常现象。软件以台账分解金额为准。</div>
+                </div>
             </div>
             <div class="ml-auto">
             </div>
@@ -125,4 +128,4 @@
     };
     const thousandth = <%- ctx.tender.info.display.thousandth %>;
     const decimal = <%- ctx.tender.info.decimal.tp %>;
-</script>
+</script>

+ 2 - 11
app/view/ledger/explode_modal.ejs

@@ -136,7 +136,7 @@
                     </div>
                 </div>
             </div>
-            <form class="modal-footer" method="post" action="<%- preUrl %>/ledger/audit/start" onsubmit="return checkAuditorFrom()">
+            <form class="modal-footer" method="post" action="<%- preUrl %>/ledger/audit/start" name="audit-start">
                 <button type="button" class="btn btn-secondary btn-sm" data-dismiss="modal">关闭</button>
                 <input type="hidden" name="_csrf" value="<%= ctx.csrf %>">
                 <button class="btn btn-primary btn-sm" type="submit" >确认上报</button>
@@ -320,7 +320,7 @@
                     </div>
                 </div>
             </div>
-            <form class="modal-footer" method="post" action="<%- preUrl %>/ledger/audit/start" onsubmit="return checkAuditorFrom()">
+            <form class="modal-footer" method="post" action="<%- preUrl %>/ledger/audit/start" name="audit-start">
                 <input type="hidden" name="_csrf" value="<%= ctx.csrf %>">
                 <button type="button" class="btn btn-secondary btn-sm" data-dismiss="modal">关闭</button>
                 <% if(tender.ledger_status === auditConst.status.checkNo && ctx.session.sessionUser.accountId === tender.user_id) { %>
@@ -349,15 +349,6 @@
         return false;
     }
 
-    // 检查上报情况
-    function checkAuditorFrom () {
-        if ($('#auditors li').length === 0) {
-            toast('请先选择审批人,再上报数据', 'error', 'exclamation-circle');
-            return false;
-        }
-        $('#hide-all').show();
-    }
-
     $('#hideSp').click(function () {
         $('#sp-list').modal('hide');
     });

+ 2 - 1
app/view/material/audit_modal.ejs

@@ -678,10 +678,11 @@
 <% if (ctx.session.sessionUser.accountId === ctx.material.user_id && (ctx.material.status === auditConst.status.uncheck || ctx.material.status === auditConst.status.checkNo)) { %>
     <script>
         const accountGroup = JSON.parse('<%- JSON.stringify(accountGroup) %>');
+        const accountList = JSON.parse('<%- JSON.stringify(accountList) %>');
     </script>
 <% } %>
+<script>const cur_uid = parseInt('<%- ctx.session.sessionUser.accountId %>');</script>
 <script>
-    const cur_uid  = parseInt('<%- ctx.session.sessionUser.accountId %>');
     $('.sp-location-list').on('shown.bs.modal', function () {
         const scrollBox = $(this).find('div[class="col-8 modal-height-500"]');
         const bdiv = (scrollBox.offset() && scrollBox.offset().top) || 0;

+ 123 - 0
app/view/material/exponent.ejs

@@ -0,0 +1,123 @@
+<% include ./material_sub_menu.ejs %>
+<div class="panel-content">
+    <div class="panel-title">
+        <div class="title-main d-flex justify-content-between">
+            <% include ./material_sub_mini_menu.ejs %>
+            <div>
+                <% if ((material.status === auditConst.status.uncheck || material.status === auditConst.status.checkNo) && ctx.session.sessionUser.accountId === material.user_id) { %>
+                    <div class="d-inline-block">
+                        <a href="javascript: void(0);" id="add" class="btn btn-sm btn-light text-primary" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="新增"><i class="fa fa-plus" aria-hidden="true"></i></a>
+                        <a href="javascript: void(0);" id="del" class="btn btn-sm btn-light text-primary" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="删除"><i class="fa fa-remove" aria-hidden="true"></i></a>
+                    </div>
+                <% } %>
+                <div class="d-inline-block ml-3">
+                    本期调差计量期:第<span class="mx-2"><%= material.s_order.split(',').join(',') %></span>期
+                </div>
+                <div class="d-inline-block ml-3">
+                    本期价差:<b id="ex_expr"><%- material.ex_expr %></b>
+                </div>
+            </div>
+            <div class="ml-auto">
+            </div>
+        </div>
+    </div>
+    <div class="content-wrap pr-46">
+        <div class="c-header p-0">
+        </div>
+        <div class="row w-100 sub-content">
+            <div id="left-view" class="c-body" style="width: 100%">
+                <!--上部分-->
+                <div class="sjs-height-1" id="material-exponent-spread">
+                </div>
+                <!--下部分-->
+                <div class="bcontent-wrap">
+                    <div class="bc-bar mb-1">
+                        <div class="input-group input-group-sm ">
+                            <div class="input-group-prepend">
+                                <span class="input-group-text" id="basic-addon1">增值税税率</span>
+                            </div>
+                            <select class="form-control form-control-sm col-1" id="changeRate">
+                                <% if (!material.readOnly) { %>
+                                    <option value="9" <% if(material.rate === 9) { %>selected<% } %>>9%</option>
+                                    <option value="10" <% if(material.rate === 10) { %>selected<% } %>>10%</option>
+                                    <option value="11" <% if(material.rate === 11) { %>selected<% } %>>11%</option>
+                                <% } else { %>
+                                    <option value="<%= material.rate %>" selected><%= material.rate %>%</option>
+                                <% } %>
+                            </select>
+                        </div>
+                    </div>
+                    <div class="sp-wrap">
+                        <div class="col-7 p-0">
+                            <table class="table table-sm table-bordered">
+                                <tr><th rowspan="2"></th><th colspan="2" class="text-center">信息价</th><th colspan="2" class="text-center">价格指数</th></tr>
+                                <tr class="text-center"><th>本期金额</th><th>截止本期金额</th><th>本期金额</th><th>截止本期金额</th></tr>
+                                <tr id="tp_set"><td>材料价差费用</td>
+                                    <td class="text-center"><%= material.m_tp !== null ? ctx.helper.round(material.m_tp, 2) : null %></td>
+                                    <td class="text-center"><%= material.m_tp !== null || material.pre_tp !== null ? ctx.helper.round(ctx.helper.add(material.pre_tp, material.m_tp), 2) : null %></td>
+                                    <td class="text-center"><%= material.ex_tp !== null ? ctx.helper.round(material.ex_tp, 2) : null %></td>
+                                    <td class="text-center"><%= material.ex_tp !== null || material.ex_pre_tp !== null ? ctx.helper.round(ctx.helper.add(material.ex_pre_tp, material.ex_tp), 2) : null %></td>
+                                </tr>
+                                <tr id="rate_set"><td>材料价差费用(含税)</td>
+                                    <td class="text-center"><%= material.m_tp !== null ? ctx.helper.round(ctx.helper.mul(material.m_tp, 1+material.rate/100), 2) : null %></td>
+                                    <td class="text-center"><%= material.m_tp !== null || pre_tp_hs !== null ? ctx.helper.round(ctx.helper.add(pre_tp_hs, ctx.helper.round(ctx.helper.mul(material.m_tp, 1+material.rate/100), 2)), 2) : null %></td>
+                                    <td class="text-center"><%= material.ex_tp !== null ? ctx.helper.round(ctx.helper.mul(material.ex_tp, 1+material.rate/100), 2) : null %></td>
+                                    <td class="text-center"><%= material.ex_tp !== null || ex_pre_tp_hs !== null ? ctx.helper.round(ctx.helper.add(ex_pre_tp_hs, ctx.helper.round(ctx.helper.mul(material.ex_tp, 1+material.rate/100), 2)), 2) : null %></td>
+                                </tr>
+                            </table>
+                        </div>
+                    </div>
+                </div>
+            </div>
+            <div id="right-view" class="c-body" style="display:none;width: 33%">
+                <div class="resize-x" id="right-spr" r-Type="width" div1="#left-view" div2="#right-view" title="调整大小" a-type="percent"><!--调整左右高度条--></div>
+                <div class="tab-content" style="width: 100%">
+                    <div id="tiaochaje" class="tab-pane active">
+                        <div class="sjs-sh-1">
+                            <table class="table table-bordered">
+                                <thead>
+                                <tr><th>需调整的价差</th><th>金额</th><th>选择</th></tr>
+                                </thead>
+                                <tbody id="calc_basic_select">
+                                <% for (const bq of ex_calc) { %>
+                                    <tr>
+                                        <td><%- bq.text %></td><td><%- bq.value %></td>
+                                        <td><input type="checkbox" value="<%- bq.code %>" <% if (material.readOnly) { %>disabled<% } %> class="calc_select" <% if (bq.select) { %>checked<% } %>></td>
+                                    </tr>
+                                <% } %>
+                                </tbody>
+                            </table>
+                        </div>
+                    </div>
+                </div>
+            </div>
+        </div>
+        <div class="side-menu">
+            <!--右侧菜单-->
+            <ul class="nav flex-column right-nav">
+                <li class="nav-item">
+                    <a class="nav-link" content="#base-tab" href="javascript: void(0);">调差基数</a>
+                </li>
+            </ul>
+        </div>
+    </div>
+</div>
+<div style="display: none">
+    <img src="/public/images/ellipsis_horizontal.png" id="ellipsis-icon" />
+    <img src="/public/images/icon-ok.png" id="icon-ok" />
+</div>
+<script>
+    const readOnly = <%- material.readOnly %>;
+    const materialID = <%- material.id %>;
+    const materialOrder = <%- material.order %>;
+    let m_tp = <%= material.m_tp !== null ? material.m_tp : 0 %>;
+    let ex_tp = <%= material.ex_tp !== null ? material.ex_tp : 0 %>;
+    const pre_tp = <%= material.pre_tp !== null ? material.pre_tp : 0 %>;
+    const ex_pre_tp = <%= material.ex_pre_tp !== null ? material.ex_pre_tp : 0 %>;
+    const pre_tp_hs = <%= pre_tp_hs !== null ? pre_tp_hs : 0 %>;
+    const ex_pre_tp_hs = <%= ex_pre_tp_hs !== null ? ex_pre_tp_hs : 0 %>;
+    const materialType = JSON.parse('<%- JSON.stringify(materialType) %>');
+    const ex_calc = JSON.parse('<%- JSON.stringify(ex_calc) %>');
+    let ex_expr = '<%- material.ex_expr %>';
+    let materialExponentData = JSON.parse('<%- JSON.stringify(materialExponentData) %>');
+</script>

+ 1 - 0
app/view/material/exponent_modal.ejs

@@ -0,0 +1 @@
+<% include ./audit_modal.ejs %>

+ 16 - 5
app/view/material/file.ejs

@@ -1,15 +1,26 @@
 <% include ./material_sub_menu.ejs %>
 <div class="panel-content">
   <div class="panel-title">
-    <div class="title-main d-flex justify-content-between">
-      <div class="d-flex justify-content-start align-items-center">
+    <div class="title-main d-flex">
+      <% include ./material_sub_mini_menu.ejs %>
+      <div>
+        <div class="d-inline-block">
           <a href="#addfujian" data-toggle="modal" class="btn btn-sm btn-light text-primary" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="添加清单"><i class="fa fa-cloud-upload" aria-hidden="true"></i> 上传附件</a>
+        </div>
+        <div class="d-inline-block">
           <span class="d-flex align-items-center" style="margin-left: 5px;">
             <input type="checkbox" id="file-checkbox">
             <span class="text-primary" style="margin-left: 5px;">所有期</span>
-
           </span>
         </div>
+      </div>
+      <!--<div class="d-flex justify-content-start align-items-center">-->
+          <!--<a href="#addfujian" data-toggle="modal" class="btn btn-sm btn-light text-primary" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="添加清单"><i class="fa fa-cloud-upload" aria-hidden="true"></i> 上传附件</a>-->
+          <!--<span class="d-flex align-items-center" style="margin-left: 5px;">-->
+            <!--<input type="checkbox" id="file-checkbox">-->
+            <!--<span class="text-primary" style="margin-left: 5px;">所有期</span>-->
+          <!--</span>-->
+        <!--</div>-->
     </div>
   </div>
   <div class="content-wrap">
@@ -90,9 +101,9 @@
 <script>
   const auditors = JSON.parse('<%- JSON.stringify(auditors) %>');
   const curAuditor = JSON.parse('<%- JSON.stringify(ctx.material.curAuditor) %>');
-  const material = JSON.parse('<%- JSON.stringify(ctx.material) %>');
+  const materialStatus = parseInt('<%- ctx.material.status %>');
+  const materialUid = parseInt('<%- ctx.material.user_id %>');
   const auditConst = JSON.parse('<%- JSON.stringify(auditConst) %>');
-  const cur_uid = '<%- ctx.session.sessionUser.accountId %>';
   const tid = '<%- ctx.tender.id %>';
   const mid = '<%- ctx.material.id %>';
   const fileList = JSON.parse('<%- JSON.stringify(fileList) %>');

+ 19 - 6
app/view/material/index.ejs

@@ -20,13 +20,22 @@
                 <table class="table table-bordered">
                     <thead>
                     <tr>
-                        <th>期数</th>
-                        <th class="text-center">添加时间</th>
-                        <th class="text-center">计量期</th>
+                        <th rowspan="2">期数</th>
+                        <th class="text-center" rowspan="2">添加时间</th>
+                        <th class="text-center" rowspan="2">计量期</th>
+                        <th class="text-center" colspan="2">信息价调差</th>
+                        <th class="text-center" colspan="2">指数法调差</th>
+                        <th class="text-center" colspan="2">合计</th>
+                        <th class="text-center" rowspan="2">审批进度</th>
+                        <th class="text-center" rowspan="2" width="140">操作</th>
+                    </tr>
+                    <tr>
+                        <th class="text-center">价差费用</th>
+                        <th class="text-center">价差费用(含税)</th>
+                        <th class="text-center">价差费用</th>
                         <th class="text-center">价差费用(含税)</th>
                         <th class="text-center">价差费用</th>
-                        <th class="text-center">审批进度</th>
-                        <th class="text-center">操作</th>
+                        <th class="text-center">价差费用(含税)</th>
                     </tr>
                     </thead>
                     <tbody>
@@ -37,8 +46,12 @@
                             </td>
                             <td class="text-center"><%= moment(m.in_time).format('YYYY-MM-DD') %></td>
                             <td class="text-center">第 <%= m.s_order %> 期</td>
-                            <td class="text-right"><%= m.m_tp !== null ? ctx.helper.round(ctx.helper.mul(m.m_tp, 1+m.rate/100), 2) : null %></td>
                             <td class="text-right"><%= m.m_tp !== null ? ctx.helper.round(m.m_tp, 2) : null %></td>
+                            <td class="text-right"><%= m.m_tp !== null ? ctx.helper.round(ctx.helper.mul(m.m_tp, 1+m.rate/100), 2) : null %></td>
+                            <td class="text-right"><%= m.ex_tp !== null ? ctx.helper.round(m.ex_tp, 2) : null %></td>
+                            <td class="text-right"><%= m.ex_tp !== null ? ctx.helper.round(ctx.helper.mul(m.ex_tp, 1+m.rate/100), 2) : null %></td>
+                            <td class="text-right"><%= ctx.helper.add(ctx.helper.round(m.ex_tp, 2), ctx.helper.round(m.m_tp, 2)) %></td>
+                            <td class="text-right"><%= ctx.helper.add(ctx.helper.round(ctx.helper.mul(m.ex_tp, 1+m.rate/100), 2), ctx.helper.round(ctx.helper.mul(m.m_tp, 1+m.rate/100), 2)) %></td>
                             <td class="<%- auditConst.auditProgressClass[m.status] %>">
                                 <% if (m.curAuditor) { %>
                                     <a href="#sp-list" data-toggle="modal" data-target="#sp-list" m-order="<%- m.order %>"><%- m.curAuditor.name %><%if (m.curAuditor.role !== '' && m.curAuditor.role !== null) { %>-<%- m.curAuditor.role %><% } %></a>

+ 19 - 10
app/view/material/info.ejs

@@ -10,7 +10,7 @@
                         <a href="javascript: void(0);" id="del" class="btn btn-sm btn-light text-primary" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="删除材料"><i class="fa fa-remove" aria-hidden="true"></i></a>
                     </div>
                 <% } %>
-                <div class="d-inline-block">
+                <div class="d-inline-block ml-3">
                     本期调差计量期:第<span class="mx-2"><%= material.s_order.split(',').join(',') %></span>期
                     <span id="qi-month">
                         <% if (months.length > 0) { %>
@@ -50,11 +50,22 @@
                         </div>
                     </div>
                     <div class="sp-wrap">
-                        <div class="col-4 p-0">
+                        <div class="col-7 p-0">
                             <table class="table table-sm table-bordered">
-                                <tr><th></th><th>本期金额</th><th>截止本期金额</th></tr>
-                                <tr id="tp_set"><td>材料价差费用</td><td><%= material.m_tp !== null ? ctx.helper.round(material.m_tp, 2) : null %></td><td><%= material.m_tp !== null || material.pre_tp !== null ? ctx.helper.round(ctx.helper.add(material.pre_tp, material.m_tp), 2) : null %></td></tr>
-                                <tr id="rate_set"><td>材料价差费用(含税)</td><td><%= material.m_tp !== null ? ctx.helper.round(ctx.helper.mul(material.m_tp, 1+material.rate/100), 2) : null %></td><td><%= material.m_tp !== null || pre_tp_hs !== null ? ctx.helper.round(ctx.helper.add(pre_tp_hs, ctx.helper.round(ctx.helper.mul(material.m_tp, 1+material.rate/100), 2)), 2) : null %></td></tr>
+                                <tr><th rowspan="2"></th><th colspan="2" class="text-center">信息价</th><th colspan="2" class="text-center">价格指数</th></tr>
+                                <tr class="text-center"><th>本期金额</th><th>截止本期金额</th><th>本期金额</th><th>截止本期金额</th></tr>
+                                <tr id="tp_set"><td>材料价差费用</td>
+                                    <td class="text-center"><%= material.m_tp !== null ? ctx.helper.round(material.m_tp, 2) : null %></td>
+                                    <td class="text-center"><%= material.m_tp !== null || material.pre_tp !== null ? ctx.helper.round(ctx.helper.add(material.pre_tp, material.m_tp), 2) : null %></td>
+                                    <td class="text-center"><%= material.ex_tp !== null ? ctx.helper.round(material.ex_tp, 2) : null %></td>
+                                    <td class="text-center"><%= material.ex_tp !== null || material.ex_pre_tp !== null ? ctx.helper.round(ctx.helper.add(material.ex_pre_tp, material.ex_tp), 2) : null %></td>
+                                </tr>
+                                <tr id="rate_set"><td>材料价差费用(含税)</td>
+                                    <td class="text-center"><%= material.m_tp !== null ? ctx.helper.round(ctx.helper.mul(material.m_tp, 1+material.rate/100), 2) : null %></td>
+                                    <td class="text-center"><%= material.m_tp !== null || pre_tp_hs !== null ? ctx.helper.round(ctx.helper.add(pre_tp_hs, ctx.helper.round(ctx.helper.mul(material.m_tp, 1+material.rate/100), 2)), 2) : null %></td>
+                                    <td class="text-center"><%= material.ex_tp !== null ? ctx.helper.round(ctx.helper.mul(material.ex_tp, 1+material.rate/100), 2) : null %></td>
+                                    <td class="text-center"><%= material.ex_tp !== null || ex_pre_tp_hs !== null ? ctx.helper.round(ctx.helper.add(ex_pre_tp_hs, ctx.helper.round(ctx.helper.mul(material.ex_tp, 1+material.rate/100), 2)), 2) : null %></td>
+                                </tr>
                             </table>
                         </div>
                     </div>
@@ -95,11 +106,6 @@
 <!--<link rel="stylesheet" href="/public/css/jquery-ui/datepicker.css">-->
 <!--<script src="/public/js/jquery-ui/datepicker.js"></script>-->
 <!--<script src="/public/js/jquery-ui/datepicker-zh-CN.js"></script>-->
-<% if ((material.status === auditConst.status.uncheck || material.status === auditConst.status.checkNo) && ctx.session.sessionUser.accountId === material.user_id) {%>
-    <script>
-        const accountList = JSON.parse('<%- JSON.stringify(accountList) %>');
-    </script>
-<% } %>
 <script>
     const materialType = JSON.parse('<%- materialType %>');
     let materialBillsData = JSON.parse('<%- JSON.stringify(materialBillsData) %>');
@@ -107,8 +113,11 @@
     const readOnly = <%- material.readOnly %>;
     const materialID = <%- material.id %>;
     let m_tp = <%= material.m_tp !== null ? material.m_tp : 0 %>;
+    let ex_tp = <%= material.ex_tp !== null ? material.ex_tp : 0 %>;
     const pre_tp = <%= material.pre_tp !== null ? material.pre_tp : 0 %>;
+    const ex_pre_tp = <%= material.ex_pre_tp !== null ? material.ex_pre_tp : 0 %>;
     const pre_tp_hs = <%= pre_tp_hs !== null ? pre_tp_hs : 0 %>;
+    const ex_pre_tp_hs = <%= ex_pre_tp_hs !== null ? ex_pre_tp_hs : 0 %>;
     const calcBase = JSON.parse('<%- JSON.stringify(calcBase) %>');
     const months = JSON.parse('<%- JSON.stringify(months) %>');
     let monthsList = JSON.parse('<%- JSON.stringify(monthsList) %>');

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

@@ -42,7 +42,7 @@
             <div class="modal-body">
                 <div class="form-group">
                     <label>信息价月份</label>
-                    <input class="datepicker-here form-control" id="months" placeholder="点击选择年月" data-view="months" data-min-view="months" data-date-format="yyyy-MM" data-language="zh" type="text">
+                    <input class="datepicker-here form-control" id="months" placeholder="点击选择年月" data-view="months" data-min-view="months" data-date-format="yyyy-MM" data-language="zh" type="text" data-auto-close="true">
                 </div>
                 <!--首次创建-->
                 <div class="alert alert-primary">创建月信息价后,「本期信息价」 「单价」列将无法自行填写,而是从「月信息价」中获取,如果有多个「月信息价」,将取平均值。</div>

+ 0 - 5
app/view/material/list.ejs

@@ -61,11 +61,6 @@
         </div>
     </div>
 </div>
-<% if ((ctx.material.status === auditConst.status.uncheck || ctx.material.status === auditConst.status.checkNo) && ctx.session.sessionUser.accountId === ctx.material.user_id) {%>
-<script>
-    const accountList = JSON.parse('<%- JSON.stringify(accountList) %>');
-</script>
-<% } %>
 <script>
     const materialType = JSON.parse('<%- materialType %>');
     const materialBillsData = JSON.parse('<%- JSON.stringify(materialBillsData) %>');

+ 8 - 1
app/view/material/material_sub_menu.ejs

@@ -11,11 +11,18 @@
         <div class="nav-box">
                 <ul class="nav-list list-unstyled">
                     <li class="<% if (ctx.url === '/tender/' + ctx.tender.id + '/measure/material/' + ctx.material.order) { %>active<% } %>">
-                        <a href="/tender/<%- ctx.tender.id %>/measure/material/<%- ctx.material.order %>"><span class="ml-3">调差工料</span></a>
+                        <a href="/tender/<%- ctx.tender.id %>/measure/material/<%- ctx.material.order %>"><span class="ml-3">信息价调差</span></a>
                     </li>
                 </ul>
             </div>
         <div class="nav-box">
+            <ul class="nav-list list-unstyled">
+                <li class="<% if (ctx.url === '/tender/' + ctx.tender.id + '/measure/material/' + ctx.material.order + '/exponent') { %>active<% } %>">
+                    <a href="/tender/<%- ctx.tender.id %>/measure/material/<%- ctx.material.order %>/exponent"><span class="ml-3">指数法调差</span></a>
+                </li>
+            </ul>
+        </div>
+        <div class="nav-box">
                 <ul class="nav-list list-unstyled">
                     <li class="<% if (ctx.url === '/tender/' + ctx.tender.id + '/measure/material/' + ctx.material.order + '/list') { %>active<% } %>">
                         <a href="/tender/<%- ctx.tender.id %>/measure/material/<%- material.order %>/list"><span class="ml-3">调差清单</span></a>

+ 15 - 1
app/view/material/material_sub_mini_menu.ejs

@@ -12,7 +12,14 @@
         <div class="nav-box">
             <ul class="nav-list list-unstyled">
                 <li class="<% if (ctx.url === '/tender/' + ctx.tender.id + '/measure/material/' + ctx.material.order) { %>active<% } %>">
-                    <a href="/tender/<%- ctx.tender.id %>/measure/material/<%- ctx.material.order %>"><span class="ml-3">调差工料</span></a>
+                    <a href="/tender/<%- ctx.tender.id %>/measure/material/<%- ctx.material.order %>"><span class="ml-3">信息价调差</span></a>
+                </li>
+            </ul>
+        </div>
+        <div class="nav-box">
+            <ul class="nav-list list-unstyled">
+                <li class="<% if (ctx.url === '/tender/' + ctx.tender.id + '/measure/material/' + ctx.material.order + '/exponent') { %>active<% } %>">
+                    <a href="/tender/<%- ctx.tender.id %>/measure/material/<%- ctx.material.order %>/exponent"><span class="ml-3">指数法调差</span></a>
                 </li>
             </ul>
         </div>
@@ -23,6 +30,13 @@
                 </li>
             </ul>
         </div>
+        <div class="nav-box">
+            <ul class="nav-list list-unstyled">
+                <li class="<% if (ctx.url === '/tender/' + ctx.tender.id + '/measure/material/' + ctx.material.order + '/file') { %>active<% } %>">
+                    <a href="/tender/<%- ctx.tender.id %>/measure/material/<%- material.order %>/file"><span class="ml-3">附件</span></a>
+                </li>
+            </ul>
+        </div>
         <% include ./audit_btn.ejs %>
         <div class="side-fold"><a href="javascript: void(0);" data-toggle="tooltip" data-placement="top" data-original-title="展开侧栏" id="to-menu"><i class="fa fa-thumb-tack"></i></a></div>
     </div>

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

@@ -9,7 +9,11 @@
             <div class="ml-auto">
                 <% if (ctx.session.sessionUser.accountId === ctx.tender.data.user_id && ctx.tender.data.ledger_status === auditConst.status.checked &&
                         (stages.length === 0 || stages[0].status === auditConst.status.checked)) { %>
-                <a href="#add-qi" data-toggle="modal" data-target="#add-qi" class="btn btn-primary btn-sm pull-right">开始新一期</a>
+                <% if ((ctx.helper.checkZero(ctx.tender.info.deal_param.contractPrice) || ctx.helper.checkZero(ctx.tender.info.deal_param.startAdvance)) && stages.length === 0) { %>
+                    <a href="#add-qi" data-toggle="modal" data-target="#tips" class="btn btn-primary btn-sm pull-right">开始新一期</a>
+                <% } else { %>
+                    <a href="#add-qi" data-toggle="modal" data-target="#add-qi" class="btn btn-primary btn-sm pull-right">开始新一期</a>
+                <% } %>
                 <% } %>
             </div>
         </div>

+ 17 - 0
app/view/measure/stage_modal.ejs

@@ -1,5 +1,22 @@
 <% if (ctx.session.sessionUser.accountId === ctx.tender.data.user_id && ctx.tender.data.ledger_status === auditConst.status.checked &&
         (stages.length === 0 || stages[stages.length- 1].status === auditConst.status.checked)) { %>
+<!--弹出填写合同参数-->
+<div class="modal fade" id="tips" data-backdrop="static">
+    <div class="modal-dialog" role="document">
+        <div class="modal-content">
+            <div class="modal-header">
+                <h5 class="modal-title">添加新一期</h5>
+            </div>
+            <div class="modal-body">
+                <h6>请先去“标段概况-合同参数”填写数据,再开始第一期计量</h6>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-sm btn-secondary" data-dismiss="modal">关闭</button>
+                <a type="button" class="btn btn-sm btn-primary" href="/tender/<%- ctx.tender.id %>" >进入标段概况</a>
+            </div>
+        </div>
+    </div>
+</div>
 <!--弹出添加期-->
 <div class="modal fade" id="add-qi" data-backdrop="static">
     <div class="modal-dialog" role="document">

+ 26 - 0
app/view/profile/safe.ejs

@@ -22,6 +22,32 @@
                                 <p>SSO用户请到<a href="#">此处</a>修改密码</p>
                             <% } %>
                         </form>
+                        <!-- 访问日志 -->
+                        <div class="col-12 mt-5">
+                            <h4>访问日志</h4>
+                            <table class="table table-hover">
+                                <thead>
+                                    <tr>
+                                        <th></th>
+                                        <th>系统</th>
+                                        <th>浏览器</th>
+                                        <th>登录时间</th>
+                                        <th>登录地址</th>
+                                    </tr>
+                                </thead>
+                                <tbody>
+                                    <% loginLogging.forEach((item, idx) => { %>
+                                        <tr>
+                                            <td><%- idx + 1 %></td>
+                                            <td><%- item.os %></td>
+                                            <td><%- item.browser %></td>
+                                            <td><%- ctx.helper.formatFullDate(item.create_time) %></td>
+                                            <td><%- item.address %></td>
+                                        </tr>
+                                    <% }) %>
+                                </tbody>
+                            </table>
+                        </div>
                     </div>
                 </div>
             </div>

+ 5 - 4
app/view/profile/sms.ejs

@@ -2,7 +2,7 @@
 <div class="panel-content" id="app">
     <div class="panel-title">
         <div class="title-main">
-            <h2>短信通知</h2>
+            <h2>认证手机</h2>
         </div>
     </div>
     <div class="content-wrap">
@@ -13,7 +13,7 @@
                     <% if (accountData.auth_mobile !== '') { %>
                     <!--已绑定手机-->
                     <div class="form-group">
-                        <label>已认证手机(用于 找回密码、接收通知)</label>
+                        <label>已认证手机</label>
                         <div class="input-group mb-3">
                             <input class="form-control form-control-sm" readonly="" value="<%= accountData.auth_mobile %>">
                             <div class="input-group-append">
@@ -23,9 +23,10 @@
                     </div>
                     <% } %>
                     <!--绑定手机-->
+                    <% if (accountData.auth_mobile === '') { %><div class="alert alert-warning">认证手机用户找回密码操作,请优先设置。</div><% } %>
                     <form id="mobile-form" <% if (accountData.auth_mobile !== '') { %>style="display: none" <% } %>>
                         <div class="form-group">
-                            <label>认证手机(用于 找回密码、接收通知)</label>
+                            <label>认证手机</label>
                             <div class="input-group mb-3">
                                 <input class="form-control form-control-sm" placeholder="输入11位手机号码" value="" name="auth_mobile" maxlength="11"/>
                                 <div class="input-group-append">
@@ -41,7 +42,7 @@
                         </div>
                         <button type="button" class="btn btn-secondary btn-sm disabled" id="bind-btn">确认绑定</button>
                     </form>
-                    <% if (accountData.auth_mobile !== '') { %>
+                    <% if (accountData.auth_mobile !== '' && false) { %>
                     <!--短信通知开关(已有认证手机后显示)-->
                     <div class="mt-5">
                         <h4>通知类型</h4>

+ 0 - 0
app/view/report/index.ejs


部分文件因为文件数量过多而无法显示