浏览代码

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

MaiXinRong 4 年之前
父节点
当前提交
94eb01d9c3
共有 100 个文件被更改,包括 113181 次插入305 次删除
  1. 1 0
      .gitignore
  2. 1 1
      app/base/base_bills_service.js
  3. 9 1
      app/const/audit.js
  4. 1 0
      app/const/tender_info.js
  5. 52 9
      app/controller/advance_controller.js
  6. 85 9
      app/controller/change_controller.js
  7. 13 4
      app/controller/ledger_controller.js
  8. 0 1
      app/controller/login_controller.js
  9. 299 0
      app/controller/report_archive_controller.js
  10. 38 108
      app/controller/report_controller.js
  11. 8 1
      app/controller/revise_controller.js
  12. 67 1
      app/controller/setting_controller.js
  13. 72 30
      app/controller/stage_controller.js
  14. 39 1
      app/controller/tender_controller.js
  15. 0 16
      app/controller/wechat_controller.js
  16. 41 9
      app/extend/helper.js
  17. 187 0
      app/lib/ledger.js
  18. 388 0
      app/lib/rptCustomData.js
  19. 22 0
      app/lib/s2b.js
  20. 5 8
      app/lib/stage_im.js
  21. 3 2
      app/middleware/change_check.js
  22. 12 0
      app/middleware/material_check.js
  23. 18 1
      app/middleware/stage_check.js
  24. 11 2
      app/middleware/tender_check.js
  25. 2 2
      app/middleware/uncheck_tender_check.js
  26. 二进制
      app/public/css/ztree/img/diy/10.png
  27. 二进制
      app/public/images/wechat.png
  28. 12 0
      app/public/js/advance.js
  29. 21 13
      app/public/js/advance_audit.js
  30. 42 0
      app/public/js/change.js
  31. 40 19
      app/public/js/change_information_set.js
  32. 2 1
      app/public/js/change_information_show.js
  33. 9 0
      app/public/js/global.js
  34. 6 1
      app/public/js/ledger.js
  35. 170 44
      app/public/js/ledger_check.js
  36. 22 5
      app/public/js/path_tree.js
  37. 1 1
      app/public/js/se_jgcl.js
  38. 1 1
      app/public/js/setting.js
  39. 6 0
      app/public/js/shares/cs_tools.js
  40. 19 1
      app/public/js/shenpi.js
  41. 97 5
      app/public/js/stage.js
  42. 3 3
      app/public/js/stage_pay.js
  43. 9 5
      app/public/js/tender.js
  44. 237 0
      app/public/netcasign/example/appPackage.js
  45. 1 0
      app/public/netcasign/js/base64.min.js
  46. 615 0
      app/public/netcasign/js/debugger.js
  47. 506 0
      app/public/netcasign/js/json2.js
  48. 11012 0
      app/public/netcasign/js/netca-jquery-client.js
  49. 191 0
      app/public/netcasign/js/netcaseal.js
  50. 2048 0
      app/public/netcasign/js/netcasealpdf.js
  51. 2185 0
      app/public/netcasign/js/netcawebsocket.js
  52. 23599 0
      app/public/netcasign/js/pdf.js
  53. 1 0
      app/public/netcasign/js/pdf.js.map
  54. 55370 0
      app/public/netcasign/js/pdf.worker.js
  55. 1 0
      app/public/netcasign/js/pdf.worker.js.map
  56. 15580 0
      app/public/netcasign/js/viewer.js
  57. 1 0
      app/public/netcasign/js/viewer.js.map
  58. 二进制
      app/public/netcasign/ui/cmaps/78-EUC-H.bcmap
  59. 二进制
      app/public/netcasign/ui/cmaps/78-EUC-V.bcmap
  60. 二进制
      app/public/netcasign/ui/cmaps/78-H.bcmap
  61. 二进制
      app/public/netcasign/ui/cmaps/78-RKSJ-H.bcmap
  62. 二进制
      app/public/netcasign/ui/cmaps/78-RKSJ-V.bcmap
  63. 二进制
      app/public/netcasign/ui/cmaps/78-V.bcmap
  64. 二进制
      app/public/netcasign/ui/cmaps/78ms-RKSJ-H.bcmap
  65. 二进制
      app/public/netcasign/ui/cmaps/78ms-RKSJ-V.bcmap
  66. 二进制
      app/public/netcasign/ui/cmaps/83pv-RKSJ-H.bcmap
  67. 二进制
      app/public/netcasign/ui/cmaps/90ms-RKSJ-H.bcmap
  68. 二进制
      app/public/netcasign/ui/cmaps/90ms-RKSJ-V.bcmap
  69. 二进制
      app/public/netcasign/ui/cmaps/90msp-RKSJ-H.bcmap
  70. 二进制
      app/public/netcasign/ui/cmaps/90msp-RKSJ-V.bcmap
  71. 二进制
      app/public/netcasign/ui/cmaps/90pv-RKSJ-H.bcmap
  72. 二进制
      app/public/netcasign/ui/cmaps/90pv-RKSJ-V.bcmap
  73. 二进制
      app/public/netcasign/ui/cmaps/Add-H.bcmap
  74. 二进制
      app/public/netcasign/ui/cmaps/Add-RKSJ-H.bcmap
  75. 二进制
      app/public/netcasign/ui/cmaps/Add-RKSJ-V.bcmap
  76. 二进制
      app/public/netcasign/ui/cmaps/Add-V.bcmap
  77. 二进制
      app/public/netcasign/ui/cmaps/Adobe-CNS1-0.bcmap
  78. 二进制
      app/public/netcasign/ui/cmaps/Adobe-CNS1-1.bcmap
  79. 二进制
      app/public/netcasign/ui/cmaps/Adobe-CNS1-2.bcmap
  80. 二进制
      app/public/netcasign/ui/cmaps/Adobe-CNS1-3.bcmap
  81. 二进制
      app/public/netcasign/ui/cmaps/Adobe-CNS1-4.bcmap
  82. 二进制
      app/public/netcasign/ui/cmaps/Adobe-CNS1-5.bcmap
  83. 二进制
      app/public/netcasign/ui/cmaps/Adobe-CNS1-6.bcmap
  84. 二进制
      app/public/netcasign/ui/cmaps/Adobe-CNS1-UCS2.bcmap
  85. 二进制
      app/public/netcasign/ui/cmaps/Adobe-GB1-0.bcmap
  86. 二进制
      app/public/netcasign/ui/cmaps/Adobe-GB1-1.bcmap
  87. 二进制
      app/public/netcasign/ui/cmaps/Adobe-GB1-2.bcmap
  88. 二进制
      app/public/netcasign/ui/cmaps/Adobe-GB1-3.bcmap
  89. 二进制
      app/public/netcasign/ui/cmaps/Adobe-GB1-4.bcmap
  90. 二进制
      app/public/netcasign/ui/cmaps/Adobe-GB1-5.bcmap
  91. 二进制
      app/public/netcasign/ui/cmaps/Adobe-GB1-UCS2.bcmap
  92. 二进制
      app/public/netcasign/ui/cmaps/Adobe-Japan1-0.bcmap
  93. 二进制
      app/public/netcasign/ui/cmaps/Adobe-Japan1-1.bcmap
  94. 二进制
      app/public/netcasign/ui/cmaps/Adobe-Japan1-2.bcmap
  95. 二进制
      app/public/netcasign/ui/cmaps/Adobe-Japan1-3.bcmap
  96. 二进制
      app/public/netcasign/ui/cmaps/Adobe-Japan1-4.bcmap
  97. 二进制
      app/public/netcasign/ui/cmaps/Adobe-Japan1-5.bcmap
  98. 二进制
      app/public/netcasign/ui/cmaps/Adobe-Japan1-6.bcmap
  99. 二进制
      app/public/netcasign/ui/cmaps/Adobe-Japan1-UCS2.bcmap
  100. 0 0
      app/public/netcasign/ui/cmaps/Adobe-Korea1-0.bcmap

+ 1 - 0
.gitignore

@@ -11,6 +11,7 @@ app/public/upload/
 *.swp
 *.swp
 package-lock.json
 package-lock.json
 app/public/js/web
 app/public/js/web
+app/public/archive
 .vscode/
 .vscode/
 /report_temp
 /report_temp
 /file
 /file

+ 1 - 1
app/base/base_bills_service.js

@@ -14,7 +14,7 @@ const rootId = -1;
 const calcFields = ['unit_price', 'sgfh_qty', 'sgfh_tp', 'sjcl_qty', 'sjcl_tp', 'qtcl_qty', 'qtcl_tp', 'deal_qty', 'deal_tp', 'dgn_qty1', 'dgn_qty2'];
 const calcFields = ['unit_price', 'sgfh_qty', 'sgfh_tp', 'sjcl_qty', 'sjcl_tp', 'qtcl_qty', 'qtcl_tp', 'deal_qty', 'deal_tp', 'dgn_qty1', 'dgn_qty2'];
 const readOnlyFields = ['id', 'tender_id', 'ledger_id', 'ledger_pid', 'order', 'level', 'full_path', 'is_leaf'];
 const readOnlyFields = ['id', 'tender_id', 'ledger_id', 'ledger_pid', 'order', 'level', 'full_path', 'is_leaf'];
 const upFields = ['unit_price'];
 const upFields = ['unit_price'];
-const qtyFields = ['sgfh_qty', 'sjcl_qty', 'qtcl_qty', 'quantity', 'deal_qty', 'dgn_qty1', 'dgn_qty2'];
+const qtyFields = ['sgfh_qty', 'sjcl_qty', 'qtcl_qty', 'quantity', 'deal_qty'];
 const tpFields = ['sgfh_tp', 'sjcl_tp', 'qtcl_tp', 'total_price', 'deal_tp'];
 const tpFields = ['sgfh_tp', 'sjcl_tp', 'qtcl_tp', 'total_price', 'deal_tp'];
 const measureType = require('../const/tender').measureType;
 const measureType = require('../const/tender').measureType;
 const billsUtils = require('../lib/bills_utils');
 const billsUtils = require('../lib/bills_utils');

+ 9 - 1
app/const/audit.js

@@ -230,6 +230,7 @@ const status = {
     // checkNo: 4,     // 审批终止
     // checkNo: 4,     // 审批终止
     back: 5, // 重新上报
     back: 5, // 重新上报
     backnew: 6, // 退回
     backnew: 6, // 退回
+    revise: 9, // 修订变更
 };
 };
 const statusButton = [];
 const statusButton = [];
 statusButton[status.uncheck] = '上报';
 statusButton[status.uncheck] = '上报';
@@ -238,6 +239,7 @@ statusButton[status.checked] = '';
 // statusButton[status.checkNo] = '';
 // statusButton[status.checkNo] = '';
 statusButton[status.back] = '重新上报';
 statusButton[status.back] = '重新上报';
 statusButton[status.backnew] = '审批';
 statusButton[status.backnew] = '审批';
+statusButton[status.revise] = '修订';
 
 
 const statusButtonClass = [];
 const statusButtonClass = [];
 statusButtonClass[status.uncheck] = 'btn-primary';
 statusButtonClass[status.uncheck] = 'btn-primary';
@@ -246,6 +248,7 @@ statusButtonClass[status.checked] = '';
 // statusButtonClass[status.checkNo] = '';
 // statusButtonClass[status.checkNo] = '';
 statusButtonClass[status.back] = 'btn-warning';
 statusButtonClass[status.back] = 'btn-warning';
 statusButtonClass[status.backnew] = 'btn-success';
 statusButtonClass[status.backnew] = 'btn-success';
+statusButtonClass[status.revise] = 'btn-warning';
 
 
 const statusString = [];
 const statusString = [];
 statusString[status.uncheck] = '未上报';
 statusString[status.uncheck] = '未上报';
@@ -254,6 +257,7 @@ statusString[status.checked] = '审批通过';
 // statusString[status.checkNo] = '终止';
 // statusString[status.checkNo] = '终止';
 statusString[status.back] = '审批退回';
 statusString[status.back] = '审批退回';
 statusString[status.backnew] = '审批退回';
 statusString[status.backnew] = '审批退回';
+statusString[status.revise] = '修订中';
 
 
 const statusClass = [];
 const statusClass = [];
 statusClass[status.uncheck] = '';
 statusClass[status.uncheck] = '';
@@ -262,18 +266,20 @@ statusClass[status.checked] = 'text-success';
 // statusClass[status.checkNo] = 'text-danger';
 // statusClass[status.checkNo] = 'text-danger';
 statusClass[status.back] = 'text-warning';
 statusClass[status.back] = 'text-warning';
 statusClass[status.backnew] = 'text-warning';
 statusClass[status.backnew] = 'text-warning';
+statusClass[status.revise] = 'text-warning';
 
 
 /* ------------------------------------------------------- */
 /* ------------------------------------------------------- */
 
 
 // 变更令审批人状态
 // 变更令审批人状态
 const auditStatus = {
 const auditStatus = {
     uncheck: 1, // 待审批
     uncheck: 1, // 待审批
-    checking: 2, // 审批中或者原报人待上报
+    checking: 2, // 审批中或者原报人待上报或者原报上报修订中
     checked: 3, // 审批通过或者原报人上报完成
     checked: 3, // 审批通过或者原报人上报完成
     // checkNo: 4,     // 审批终止
     // checkNo: 4,     // 审批终止
     back: 5, // 退回到原报人重新上报
     back: 5, // 退回到原报人重新上报
     backnew: 6, // 退回到上一个审批人
     backnew: 6, // 退回到上一个审批人
     checkAgain: 7, // 重新审批
     checkAgain: 7, // 重新审批
+    revise: 9, // 修订变更
 };
 };
 
 
 const auditStatusString = [];
 const auditStatusString = [];
@@ -284,6 +290,7 @@ auditStatusString[auditStatus.checked] = '审批通过';
 auditStatusString[auditStatus.back] = '退回';
 auditStatusString[auditStatus.back] = '退回';
 auditStatusString[auditStatus.backnew] = '审批退回';
 auditStatusString[auditStatus.backnew] = '审批退回';
 auditStatusString[auditStatus.checkAgain] = '重新审批';
 auditStatusString[auditStatus.checkAgain] = '重新审批';
+auditStatusString[auditStatus.revise] = '待修订';
 
 
 const auditStatusClass = [];
 const auditStatusClass = [];
 auditStatusClass[auditStatus.uncheck] = '';
 auditStatusClass[auditStatus.uncheck] = '';
@@ -293,6 +300,7 @@ auditStatusClass[auditStatus.checked] = 'text-success';
 auditStatusClass[auditStatus.back] = 'text-warning';
 auditStatusClass[auditStatus.back] = 'text-warning';
 auditStatusClass[auditStatus.backnew] = 'text-warning';
 auditStatusClass[auditStatus.backnew] = 'text-warning';
 auditStatusClass[auditStatus.checkAgain] = 'text-warning';
 auditStatusClass[auditStatus.checkAgain] = 'text-warning';
+auditStatusClass[auditStatus.revise] = '';
 
 
 /* ------------------------------------------------------- */
 /* ------------------------------------------------------- */
 
 

+ 1 - 0
app/const/tender_info.js

@@ -100,6 +100,7 @@ const defaultInfo = {
         zanLiePrice: 0,
         zanLiePrice: 0,
         startAdvance: 0,
         startAdvance: 0,
         materialAdvance: 0,
         materialAdvance: 0,
+        safeAdvance: 0,
     },
     },
     // 显示设置
     // 显示设置
     display: {
     display: {

+ 52 - 9
app/controller/advance_controller.js

@@ -119,15 +119,20 @@ module.exports = app => {
          * @private
          * @private
          */
          */
         async _checkCanEntry(ctx) {
         async _checkCanEntry(ctx) {
+            ctx.advance.filePermission = false;
             if (ctx.advance.status === auditConst.status.uncheck) {
             if (ctx.advance.status === auditConst.status.uncheck) {
-                if (ctx.session.sessionUser.accountId !== ctx.advance.uid) {
+                if (ctx.session.sessionUser.accountId !== ctx.advance.uid && !ctx.tender.isTourist) {
                     throw '无权访问';
                     throw '无权访问';
+                } else if (ctx.session.sessionUser.accountId === ctx.advance.uid) {
+                    ctx.advance.filePermission = true;
                 }
                 }
             } else {
             } else {
                 const auditors = await ctx.service.advanceAudit.getAuditorsWithOwner(ctx.advance.id, ctx.advance.times);
                 const auditors = await ctx.service.advanceAudit.getAuditorsWithOwner(ctx.advance.id, ctx.advance.times);
                 const cur_uid = ctx.session.sessionUser.accountId;
                 const cur_uid = ctx.session.sessionUser.accountId;
-                if (auditors.findIndex(item => item.audit_id === cur_uid) === -1) {
+                if (auditors.findIndex(item => item.audit_id === cur_uid) === -1 && !ctx.tender.isTourist) {
                     throw '无权访问';
                     throw '无权访问';
+                } else if (auditors.findIndex(item => item.audit_id === cur_uid) !== -1 || ctx.session.sessionUser.accountId === ctx.advance.uid) {
+                    ctx.advance.filePermission = true;
                 }
                 }
             }
             }
         }
         }
@@ -148,19 +153,22 @@ module.exports = app => {
                 const prevAdvance = await ctx.service.advance.getPreviousRecord(ctx.tender.id, ctx.advance.type);
                 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);
                 const max_pr = ctx.helper.mul(ctx.helper.div(ctx.helper.sub(advancePayTotal, (prevAdvance && prevAdvance.prev_total_amount || 0)), advancePayTotal, 10), 100);
+
+                const isLimitMax = ctx.helper.round(max_pr, 2) === ctx.advance.pay_ratio;
                 // 特殊处理金额的显示(formatMoney)
                 // 特殊处理金额的显示(formatMoney)
                 let cur_amount,
                 let cur_amount,
                     prev_total_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);
+                const s1 = parseFloat(ctx.advance.prev_amount).toString().split('.')[1] || '';
+                const prev_amount = ctx.helper.formatMoney(ctx.advance.prev_amount, ',', isLimitMax ? s1.length : 2);
                 if (status === uncheck || status === checkNo) {
                 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);
+                    // cur_amount = parseFloat(ctx.helper.round(ctx.advance.cur_amount, 2)) || 0;
+                    cur_amount = ctx.advance.cur_amount || 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), ',', isLimitMax ? s2.length : 2);
                 } else {
                 } else {
                     cur_amount = ctx.advance.cur_amount;
                     cur_amount = ctx.advance.cur_amount;
                     const s2 = parseFloat(ctx.advance.prev_total_amount).toString().split('.')[1];
                     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);
+                    prev_total_amount = ctx.helper.formatMoney(ctx.advance.prev_total_amount, ',', isLimitMax ? s2.length : 2);
                 }
                 }
 
 
                 renderData.isEdited = isEdited;
                 renderData.isEdited = isEdited;
@@ -169,7 +177,7 @@ module.exports = app => {
                 renderData.prev_amount = prev_amount;
                 renderData.prev_amount = prev_amount;
                 renderData.prev_total_amount = prev_total_amount;
                 renderData.prev_total_amount = prev_total_amount;
                 renderData.max_pr = max_pr;
                 renderData.max_pr = max_pr;
-                renderData.decimal = this.decimal;
+                renderData.decimal = 2;
                 renderData.advancePayTotal = advancePayTotal;
                 renderData.advancePayTotal = advancePayTotal;
                 renderData.prevAdvance = prevAdvance;
                 renderData.prevAdvance = prevAdvance;
                 await this.layout('advance/detail.ejs', renderData, 'advance/modal_audit.ejs');
                 await this.layout('advance/detail.ejs', renderData, 'advance/modal_audit.ejs');
@@ -219,6 +227,41 @@ module.exports = app => {
         }
         }
 
 
         /**
         /**
+         * 删除期
+         * @param {*} ctx 全局上下文
+         */
+        async delete(ctx) {
+            try {
+                const data = JSON.parse(ctx.request.body.data);
+                const id = data && this.app._.toInteger(data.id);
+                if (isNaN(id) || id <= 0) {
+                    throw '参数错误';
+                }
+                const advance = await ctx.service.advance.getDataById(id);
+                if (!advance) {
+                    throw '参数错误';
+                }
+                // 检查权限等
+                if (advance.uid !== ctx.session.sessionUser.accountId) {
+                    throw '您无权删除';
+                }
+                if (advance.status !== auditConst.status.uncheck && advance.status !== auditConst.status.checkNo) {
+                    console.log(advance.status !== auditConst.status.uncheck);
+                    console.log(advance.status !== auditConst.status.checkNo);
+                    throw '当前不允许删除';
+                }
+                const isDeleted = await ctx.service.advance.deleteAdvance(id);
+                if (!isDeleted) {
+                    throw '删除失败,请重试';
+                }
+                ctx.body = { err: 0, msg: '' };
+            } catch (error) {
+                this.log(error);
+                ctx.body = { err: 1, msg: error.toString(), data: null };
+            }
+        }
+
+        /**
          * 添加审批人
          * 添加审批人
          * @param {Object} ctx 全局上下文
          * @param {Object} ctx 全局上下文
          */
          */

+ 85 - 9
app/controller/change_controller.js

@@ -41,7 +41,9 @@ module.exports = app => {
             // const tenderList = await this.service.tender.getList();
             // const tenderList = await this.service.tender.getList();
 
 
             const page = ctx.page;
             const page = ctx.page;
-            const changes = await ctx.service.change.getListByStatus(tender.id, status);
+            const sorts = ctx.query.sort ? ctx.query.sort : 0;
+            const orders = ctx.query.order ? ctx.query.order : 0;
+            const changes = await ctx.service.change.getListByStatus(tender.id, status, 1, sorts, orders);
             const total = await ctx.service.change.getCountByStatus(tender.id, status);
             const total = await ctx.service.change.getCountByStatus(tender.id, status);
             if (changes !== null) {
             if (changes !== null) {
                 let i = 0;
                 let i = 0;
@@ -72,6 +74,11 @@ module.exports = app => {
                             const checkingAudit = await ctx.service.changeAudit.getLastUser(c.cid, c.times, status);
                             const checkingAudit = await ctx.service.changeAudit.getLastUser(c.cid, c.times, status);
                             auditStatus = checkingAudit.uid === ctx.session.sessionUser.accountId ? 1 : 0;
                             auditStatus = checkingAudit.uid === ctx.session.sessionUser.accountId ? 1 : 0;
                             break;
                             break;
+                        case 9:
+                            auditStatus = 9;
+                            const changeUsedData = await ctx.service.stageChange.getFinalUsedData(ctx.tender.id, c.cid);
+                            c.stageChangeNum = this.ctx.helper.sum(changeUsedData.map(x => { return Math.abs(x.used_qty); }));
+                            break;
                         default:
                         default:
                             break;
                             break;
                     }
                     }
@@ -299,7 +306,7 @@ module.exports = app => {
                     shenpiConst,
                     shenpiConst,
                 };
                 };
                 // 根据auditStatus状态获取的不同的数据
                 // 根据auditStatus状态获取的不同的数据
-                if (auditStatus === 1 || auditStatus === 2) {
+                if (auditStatus === 1 || auditStatus === 2 || auditStatus === 9) {
                     renderData.changeUnits = changeConst.units;
                     renderData.changeUnits = changeConst.units;
                     renderData.precision = ctx.tender.info.precision;
                     renderData.precision = ctx.tender.info.precision;
                     // renderData.accountGroup = accountGroup;
                     // renderData.accountGroup = accountGroup;
@@ -314,7 +321,7 @@ module.exports = app => {
                         return { groupName: item, groupList };
                         return { groupName: item, groupList };
                     });
                     });
                     // 重新上报获取审批流程
                     // 重新上报获取审批流程
-                    if (auditStatus === 2) {
+                    if (auditStatus === 2 || auditStatus === 9) {
                         const auditList2 = await ctx.service.changeAudit.getListByBack(change.cid, change.times);
                         const auditList2 = await ctx.service.changeAudit.getListByBack(change.cid, change.times);
                         // 展示页右侧审批流程列表
                         // 展示页右侧审批流程列表
                         const auditList3 = [];
                         const auditList3 = [];
@@ -568,7 +575,7 @@ module.exports = app => {
                     precision: ctx.tender.info.precision,
                     precision: ctx.tender.info.precision,
                 };
                 };
                 // 根据auditStatus状态获取的不同的数据
                 // 根据auditStatus状态获取的不同的数据
-                if (auditStatus === 1 || auditStatus === 2) {
+                if (auditStatus === 1 || auditStatus === 2 || auditStatus === 9) {
                     renderData.changeUnits = changeConst.units;
                     renderData.changeUnits = changeConst.units;
                     // renderData.accountGroup = accountGroup;
                     // renderData.accountGroup = accountGroup;
                     // 获取所有项目参与者
                     // 获取所有项目参与者
@@ -582,7 +589,7 @@ module.exports = app => {
                         return { groupName: item, groupList };
                         return { groupName: item, groupList };
                     });
                     });
                     // 重新上报获取审批流程
                     // 重新上报获取审批流程
-                    if (auditStatus === 2) {
+                    if (auditStatus === 2 || auditStatus === 9) {
                         const auditList2 = await ctx.service.changeAudit.getListByBack(change.cid, change.times);
                         const auditList2 = await ctx.service.changeAudit.getListByBack(change.cid, change.times);
                         // 展示页右侧审批流程列表
                         // 展示页右侧审批流程列表
                         const auditList3 = [];
                         const auditList3 = [];
@@ -604,13 +611,14 @@ module.exports = app => {
                         }
                         }
                         renderData.auditList3 = auditList3;
                         renderData.auditList3 = auditList3;
                     }
                     }
+
                     // 获取公司列表
                     // 获取公司列表
                     const companyList = await ctx.service.changeCompany.getAllDataByCondition({ where: { tid: ctx.tender.id } });
                     const companyList = await ctx.service.changeCompany.getAllDataByCondition({ where: { tid: ctx.tender.id } });
                     renderData.companyList = companyList;
                     renderData.companyList = companyList;
                     // 获取已选清单
                     // 获取已选清单
                     const changeList = await ctx.service.changeAuditList.getList(change.cid);
                     const changeList = await ctx.service.changeAuditList.getList(change.cid);
                     renderData.changeList = changeList;
                     renderData.changeList = changeList;
-                } else if (auditStatus === 3 || auditStatus === 4 || auditStatus === 5 || auditStatus === 7) {
+                } else if (auditStatus === 3 || auditStatus === 4 || auditStatus === 5 || auditStatus === 7 || auditStatus === 8) {
                     // 展示页左侧审批流程列表和清单审批列表数据
                     // 展示页左侧审批流程列表和清单审批列表数据
                     const times = change.status === audit.flow.status.back ?
                     const times = change.status === audit.flow.status.back ?
                         change.times - 1 : change.times;
                         change.times - 1 : change.times;
@@ -705,6 +713,7 @@ module.exports = app => {
 
 
                 // 获取是否已存在调用变更令
                 // 获取是否已存在调用变更令
                 const changeUsedData = await ctx.service.stageChange.getFinalUsedData(ctx.tender.id, change.cid);
                 const changeUsedData = await ctx.service.stageChange.getFinalUsedData(ctx.tender.id, change.cid);
+                renderData.changeUsedData = changeUsedData;
                 renderData.stageChangeNum = this.ctx.helper.sum(changeUsedData.map(x => { return Math.abs(x.used_qty); }));
                 renderData.stageChangeNum = this.ctx.helper.sum(changeUsedData.map(x => { return Math.abs(x.used_qty); }));
                 await this.layout('change/information.ejs', renderData, 'change/information_modal.ejs');
                 await this.layout('change/information.ejs', renderData, 'change/information_modal.ejs');
             } catch (err) {
             } catch (err) {
@@ -747,7 +756,8 @@ module.exports = app => {
                     case 'ledger_list':
                     case 'ledger_list':
                         await ctx.service.changeAuditList.saveLedgerListDatas(data.updateData);
                         await ctx.service.changeAuditList.saveLedgerListDatas(data.updateData);
                         // 取所有工料表
                         // 取所有工料表
-                        responseData.data = await ctx.service.changeAuditList.getList(ctx.change.cid);
+                        responseData.data = { changeList: await ctx.service.changeAuditList.getList(ctx.change.cid),
+                            usedList: await ctx.service.stageChange.getFinalUsedData(ctx.tender.id, ctx.change.cid) };
                         break;
                         break;
                     case 'remove_list':
                     case 'remove_list':
                         await ctx.service.changeAuditList.removeLedgerListDatas(data.updateData);
                         await ctx.service.changeAuditList.removeLedgerListDatas(data.updateData);
@@ -800,10 +810,20 @@ module.exports = app => {
                 if (ctx.change.uid !== ctx.session.sessionUser.accountId) {
                 if (ctx.change.uid !== ctx.session.sessionUser.accountId) {
                     throw '您无权上报该变更令数据';
                     throw '您无权上报该变更令数据';
                 }
                 }
-                if (!(ctx.change.status === audit.flow.status.uncheck || ctx.change.status === audit.flow.status.back)) {
+                if (!(ctx.change.status === audit.flow.status.uncheck || ctx.change.status === audit.flow.status.back || ctx.change.status === audit.flow.status.revise)) {
                     throw '该变更令当前无法上报';
                     throw '该变更令当前无法上报';
                 }
                 }
-
+                // 判断是否是修订,判断有无审批人员作弊
+                if (ctx.change.status === audit.flow.status.revise && ctx.tender.info.shenpi.change === shenpiConst.sp_status.sqspr) {
+                    // 获取上一次的审批人流程
+                    const lastUserList = await ctx.service.changeAudit.getListGroupByTimes(ctx.change.cid, ctx.change.times - 1);
+                    const lastUidList = ctx.helper._.map(lastUserList, 'uid');
+                    // 判断上一次审批是否为非原报为审批人
+                    const nowUidList = ctx.helper._.map(ctx.change.auditors, 'uid');
+                    if (!ctx.helper._.isEqual(lastUidList, nowUidList) && nowUidList.length === 2 && nowUidList[0] === nowUidList[1]) {
+                        throw '该变更令不能指定原报人为单独审批人';
+                    }
+                }
                 await ctx.service.changeAudit.start(ctx.change.cid, ctx.change.times);
                 await ctx.service.changeAudit.start(ctx.change.cid, ctx.change.times);
 
 
                 ctx.redirect(ctx.request.header.referer);
                 ctx.redirect(ctx.request.header.referer);
@@ -1291,6 +1311,62 @@ module.exports = app => {
         }
         }
 
 
         /**
         /**
+         * 变更令修订重新上报
+         * @param {Object} ctx - egg全局变量
+         * @return {void}
+         */
+        async checkRevise(ctx) {
+            try {
+                const changeData = await ctx.service.change.getDataByCondition({ cid: ctx.request.body.cid });
+                if (!changeData) {
+                    throw '变更令数据错误';
+                }
+                if (changeData.status !== audit.flow.status.checked || ctx.session.sessionUser.accountId !== changeData.uid) {
+                    throw '您无权进行该操作';
+                }
+                if (ctx.session.sessionUser.loginStatus === 0) {
+                    const code = ctx.request.body.code;
+                    const pa = await ctx.service.projectAccount.getDataById(ctx.session.sessionUser.accountId);
+                    if (!pa.auth_mobile) {
+                        throw '未绑定手机号';
+                    }
+                    const cacheKey = 'smsCode:' + ctx.session.sessionUser.accountId;
+                    const cacheCode = await app.redis.get(cacheKey);
+                    // console.log(cacheCode);
+                    if (cacheCode === null || code === undefined || cacheCode !== (code + pa.auth_mobile)) {
+                        throw '验证码不正确!';
+                    }
+                }
+
+                // 获取是否已存在调用变更令
+                // const changeUsedData = await ctx.service.stageChange.getFinalUsedData(ctx.tender.id, changeData.cid);
+                // const stageChangeNum = this.ctx.helper.sum(changeUsedData.map(x => { return Math.abs(x.used_qty); }));
+                // if (stageChangeNum !== 0) {
+                //     throw '该变更令已被调用,无法重新审批';
+                // }
+                // 重新审批
+                const result = await ctx.service.change.checkRevise(changeData.cid);
+                if (!result) {
+                    throw '修订发起失败';
+                }
+                // ctx.redirect('/tender/' + changeData.tid + '/change/' + changeData.cid + '/info');
+                ctx.body = {
+                    err: 0,
+                    url: ctx.request.header.referer,
+                    msg: '',
+                };
+            } catch (err) {
+                console.log(err);
+                // ctx.redirect(ctx.request.header.referer);
+                ctx.body = {
+                    err: 1,
+                    // url: ctx.request.header.referer,
+                    msg: err,
+                };
+            }
+        }
+
+        /**
          * 获取变更清单
          * 获取变更清单
          * @param ctx
          * @param ctx
          * @return {Promise<void>}
          * @return {Promise<void>}

+ 13 - 4
app/controller/ledger_controller.js

@@ -62,7 +62,7 @@ module.exports = app => {
             const isUser = tender.user_id === this.ctx.session.sessionUser.accountId;
             const isUser = tender.user_id === this.ctx.session.sessionUser.accountId;
             const auditorsId = this.ctx.helper._.map(auditors, 'audit_id');
             const auditorsId = this.ctx.helper._.map(auditors, 'audit_id');
             const isAuditor  = auditorsId.indexOf(this.ctx.session.sessionUser.accountId) >= 0;
             const isAuditor  = auditorsId.indexOf(this.ctx.session.sessionUser.accountId) >= 0;
-            const upPermission = this.ctx.session.sessionUser.permission
+            const upPermission = this.ctx.session.sessionUser.permission && this.ctx.session.sessionUser.permission.tender
                 ? this.ctx.session.sessionUser.permission.tender.indexOf('3') >= 0
                 ? this.ctx.session.sessionUser.permission.tender.indexOf('3') >= 0
                 : false;
                 : false;
             return ((tender.ledger_status === auditConst.status.uncheck || tender.ledger_status === auditConst.status.checkNo) && isUser) ||
             return ((tender.ledger_status === auditConst.status.uncheck || tender.ledger_status === auditConst.status.checkNo) && isUser) ||
@@ -133,7 +133,7 @@ module.exports = app => {
                 const times = tender.data.ledger_status === auditConst.status.checkNo ? tender.data.ledger_times - 1 : tender.data.ledger_times;
                 const times = tender.data.ledger_status === auditConst.status.checkNo ? tender.data.ledger_times - 1 : tender.data.ledger_times;
 
 
                 const curAuditor = await ctx.service.ledgerAudit.getCurAuditor(tender.id, tender.data.ledger_times);
                 const curAuditor = await ctx.service.ledgerAudit.getCurAuditor(tender.id, tender.data.ledger_times);
-                const auditors = tender.data.ledger_status === auditConst.status.checkNo && tender.data.user_id !== ctx.session.sessionUser.accountId ?
+                const auditors = tender.data.ledger_status === auditConst.status.checkNo && tender.data.user_id !== ctx.session.sessionUser.accountId && !ctx.tender.isTourist ?
                     await ctx.service.ledgerAudit.getAuditorsWithOwner(tender.id, times) :
                     await ctx.service.ledgerAudit.getAuditorsWithOwner(tender.id, times) :
                     await ctx.service.ledgerAudit.getAuditorsWithOwner(tender.id, tender.data.ledger_times);
                     await ctx.service.ledgerAudit.getAuditorsWithOwner(tender.id, tender.data.ledger_times);
                 const user = await ctx.service.projectAccount.getAccountInfoById(ctx.tender.data.user_id);
                 const user = await ctx.service.projectAccount.getAccountInfoById(ctx.tender.data.user_id);
@@ -183,7 +183,8 @@ module.exports = app => {
             } catch (err) {
             } catch (err) {
                 ctx.helper.log(err);
                 ctx.helper.log(err);
                 this.postError(err, '标段数据错误');
                 this.postError(err, '标段数据错误');
-                await this.redirect('/dashboard');
+                // await this.redirect('/dashboard');
+                ctx.redirect(ctx.request.header.referer);
             }
             }
         }
         }
 
 
@@ -356,6 +357,7 @@ module.exports = app => {
                         responseData.data = await this._base(ctx, data.postType, data.postData);
                         responseData.data = await this._base(ctx, data.postType, data.postData);
                         break;
                         break;
                     case 'update':
                     case 'update':
+                        ctx.helper.checkDgnQtyPrecision(data.postData);
                         responseData.data = await ctx.service.ledger.updateCalc(ctx.tender.id, data.postData);
                         responseData.data = await ctx.service.ledger.updateCalc(ctx.tender.id, data.postData);
                         break;
                         break;
                     case 'paste-block':
                     case 'paste-block':
@@ -468,6 +470,13 @@ module.exports = app => {
                 const ledgerData = await ctx.service.ledger.getData(ctx.tender.id);
                 const ledgerData = await ctx.service.ledger.getData(ctx.tender.id);
                 const posData = this.ctx.tender.data.measure_type === measureType.tz.value
                 const posData = this.ctx.tender.data.measure_type === measureType.tz.value
                     ? await ctx.service.pos.getPosData({ tid: ctx.tender.id }) : [];
                     ? await ctx.service.pos.getPosData({ tid: ctx.tender.id }) : [];
+
+                const checkDataModel = require('../lib/ledger').checkData;
+                const checkData = new checkDataModel(ctx);
+                checkData.loadData(ledgerData, posData);
+                const sameCodeError = checkData.checkSameCode();
+                const siblingError = checkData.checkSibling();
+
                 const qtyData = ctx.helper.checkBillsWithPos(ledgerData, posData,
                 const qtyData = ctx.helper.checkBillsWithPos(ledgerData, posData,
                     ['sgfh_qty', 'qtcl_qty', 'sjcl_qty', 'quantity']);
                     ['sgfh_qty', 'qtcl_qty', 'sjcl_qty', 'quantity']);
                 qtyData.error.forEach(x => { x.errorType = 'qty'; });
                 qtyData.error.forEach(x => { x.errorType = 'qty'; });
@@ -477,7 +486,7 @@ module.exports = app => {
                 ], this.ctx.tender.info.decimal);
                 ], this.ctx.tender.info.decimal);
                 tpData.error.forEach(x => { x.errorType = 'tp'; });
                 tpData.error.forEach(x => { x.errorType = 'tp'; });
                 ctx.body = { err: 0, msg: '', data: {
                 ctx.body = { err: 0, msg: '', data: {
-                    error: [...qtyData.error, ...tpData.error],
+                    error: [...qtyData.error, ...tpData.error, ...sameCodeError, ...siblingError],
                     source: {
                     source: {
                         bills: [...qtyData.source.bills, ...tpData.source.bills],
                         bills: [...qtyData.source.bills, ...tpData.source.bills],
                         pos: [...qtyData.source.pos, ...tpData.source.pos],
                         pos: [...qtyData.source.pos, ...tpData.source.pos],

+ 0 - 1
app/controller/login_controller.js

@@ -56,7 +56,6 @@ module.exports = app => {
             try {
             try {
                 const client = new OAuth(ctx.app.config.wxCode.appid, ctx.app.config.wxCode.appsecret);
                 const client = new OAuth(ctx.app.config.wxCode.appid, ctx.app.config.wxCode.appsecret);
                 const token = await client.getAccessToken(code);
                 const token = await client.getAccessToken(code);
-                ctx.session.wxbindtoken = token;
                 // const user = await client.getUser(token.data.openid);
                 // const user = await client.getUser(token.data.openid);
                 // console.log(user);
                 // console.log(user);
                 if (!token) {
                 if (!token) {

+ 299 - 0
app/controller/report_archive_controller.js

@@ -0,0 +1,299 @@
+'use strict';
+
+/**
+ * Created by Tony on 2021/3/31.
+ */
+
+const path = require('path');
+const uuidV1 = require('uuid').v1;
+const fs = require('fs');
+const MAX_ARCHIVE = 3;
+const tenderMenu = require('../../config/menu').tenderMenu;
+const measureType = require('../const/tender').measureType;
+const fsUtil = require('../public/js/fsUtil');
+const auditConst = require('../const/audit');
+
+module.exports = app => {
+    class ReportArchiveController extends app.BaseController {
+
+        async index(ctx) {
+            const tender = ctx.tender;
+            const stage = ctx.stage;
+            let stage_id = -1;
+            let stage_order = -1;
+            let stage_times = -1;
+            let stage_status = -1;
+            const treeNodes = await ctx.service.rptTreeNode.getNodesByProjectId([-1, tender.data.project_id]);
+            const custTreeNodes = await ctx.service.rptTreeNodeCust.getCustFoldersByUserId(this.ctx.session.sessionUser.accountId);
+            const stageList = await ctx.service.stage.getValidStagesShort(tender.id);
+            //
+            // 。。。
+            let archiveList = [];
+            // console.log('tender.data.project_id: ' + tender.data.project_id);
+            if (stage) {
+                // console.log('ctx.stage.id: ' + ctx.stage.id);
+                const archives = await ctx.service.rptArchive.getPrjStgArchive(tender.data.project_id, ctx.stage.id);
+                stage_id = stage.id;
+                stage_order = stage.order;
+                stage_times = stage.times;
+                stage_status = stage.status;
+                if (archives.length > 0) {
+                    archiveList = JSON.parse(archives[0].content);
+                }
+            } else {
+                // console.log('stageList[0].id: ' + stageList[0].id);
+                const archives = await ctx.service.rptArchive.getPrjStgArchive(tender.data.project_id, stageList[stageList.length - 1].id);
+                // stage_id = stageList[0].id;
+                // stage_order = stageList[0].order;
+                // stage_times = stageList[0].times;
+                // stage_status = stageList[0].status;
+                if (archives && archives.length > 0) {
+                    archiveList = JSON.parse(archives[0].content);
+                }
+            }
+            let rpt_tpl_items = '{ customize: [], common: [] }';
+            if (custTreeNodes.length > 0) {
+                rpt_tpl_items = custTreeNodes[0].rpt_tpl_items;
+            }
+            //
+            const renderData = {
+                tender: tender.data,
+                rpt_tpl_data: JSON.stringify(treeNodes),
+                cust_tpl_data: rpt_tpl_items,
+                project_id: tender.data.project_id,
+                tender_id: tender.id,
+                stg_id: stage_id,
+                stg_order: stage_order,
+                stg_times: stage_times,
+                stg_status: stage_status,
+                stage_list: JSON.stringify(stageList),
+                tenderMenu,
+                measureType,
+                jsFiles: this.app.jsFiles.common.concat(this.app.jsFiles.report.main),
+                stages: stageList,
+                auditConst: auditConst.stage,
+                archiveList,
+            };
+            await this.layout('report/index_archive.ejs', renderData);
+        }
+
+        async getReportArchive(ctx) {
+            const params = JSON.parse(ctx.request.body.params);
+            // ctx.body = await this._getReport(ctx, params);
+
+            const archives = await ctx.service.rptArchive.getPrjStgArchive(params.prjId, params.stgId);
+            let archiveList = [];
+            if (archives.length > 0) {
+                archiveList = JSON.parse(archives[0].content);
+            }
+            ctx.body = {
+                data: archiveList,
+            };
+        }
+
+        async addReportArchive(ctx) {
+            try {
+                const stream = await ctx.getFileStream();
+                const prjId = ctx.params.prjId;
+                const stgId = ctx.params.stgId;
+                const rptId = ctx.params.rptId;
+                const newName = uuidV1();
+                const fileName = newName + '.PDF';
+                // console.log('adding fileName: ' + fileName);
+                // await ctx.helper.saveStreamFile(stream, path.join(this.app.baseDir, 'app', 'public/archive', fileName));
+                const oss_result = await ctx.oss.put('archive/' + fileName, stream);
+                if (!(oss_result && oss_result.url && oss_result.res.status === 200)) {
+                    throw '上传文件失败';
+                }
+                // const url = await ctx.oss.delete('archive/52d3e7f0-c7fb-11eb-b8c2-51b890b95d23.PDF');
+                // console.log(url);
+                // const flag = true;
+                // if (flag) {
+                //     throw 'ok';
+                // }
+                const updateDate = new Date();
+                const montStr = (updateDate.getMonth() + 1) < 10 ? ('0' + (updateDate.getMonth() + 1)) : ((updateDate.getMonth() + 1));
+                const dateStr = (updateDate.getDate()) < 10 ? ('0' + (updateDate.getDate())) : ((updateDate.getDate()));
+                const dtStr = updateDate.getFullYear() + '-' + montStr + '-' + dateStr;
+                const orgArchiveList = await ctx.service.rptArchive.getPrjStgArchive(prjId, stgId);
+                if (orgArchiveList.length > 0) {
+                    const contentArr = JSON.parse(orgArchiveList[0].content);
+                    let hasArchive = false;
+                    for (const item of contentArr) {
+                        if (item.rpt_id === rptId) {
+                            hasArchive = true;
+                            if (item.items.length >= MAX_ARCHIVE) {
+                                // 超出界限,需要删除时间最旧的那个
+                                let rmIdx = 0;
+                                for (let idx = 1; idx < item.items.length; idx++) {
+                                    if (item.items[rmIdx].updateDate_time < item.items[idx].updateDate_time) {
+                                        rmIdx = idx;
+                                    }
+                                }
+                                // 同时删除oss文件
+                                await ctx.oss.delete('archive/' + item.items[rmIdx].uuid + '.PDF');
+                                item.items.splice(rmIdx, 1);
+                            }
+                            const newItem = { uuid: newName, updateDate_time: dtStr };
+                            item.items.push(newItem);
+                            break;
+                        }
+                    }
+                    if (!hasArchive) {
+                        // 表示有新的模板需要添加
+                        contentArr.push({ rpt_id: rptId, items: [{ uuid: newName, updateDate_time: dtStr }] });
+                    }
+                    const updatedRst = await ctx.service.rptArchive.updateArchive(orgArchiveList[0].id, prjId, stgId, contentArr);
+                    // console.log(updatedRst);
+                    ctx.body = { err: 0, msg: '', data: { fileName, updateDate, addedRst: contentArr } };
+                } else {
+                    // 需要增加
+                    const archiveArr = [];
+                    archiveArr.push({ rpt_id: rptId, items: [{ uuid: newName, updateDate_time: dtStr }] });
+                    const addedRst = await ctx.service.rptArchive.createArchive(prjId, stgId, archiveArr);
+                    // console.log(addedRst);
+                    ctx.body = { err: 0, msg: '', data: { fileName, updateDate, addedRst: archiveArr } };
+                }
+            } catch (err) {
+                this.log(err);
+                ctx.body = { err: 1, msg: err.toString(), data: null };
+            }
+        }
+
+        async updateReportArchive(ctx) {
+            try {
+                const stream = await ctx.getFileStream();
+                const prjId = ctx.params.prjId;
+                const stgId = ctx.params.stgId;
+                const rptId = ctx.params.rptId;
+                const orgName = ctx.params.orgName;
+                const fileName = orgName + '.PDF';
+                console.log('updating fileName: ' + fileName);
+                // await ctx.helper.saveStreamFile(stream, path.join(this.app.baseDir, 'app', 'public/archive', fileName));
+                const oss_result = await ctx.oss.put('archive/' + fileName, stream);
+                if (!(oss_result && oss_result.url && oss_result.res.status === 200)) {
+                    throw '上传文件失败';
+                }
+
+                const updateDate = new Date();
+                const montStr = (updateDate.getMonth() + 1) < 10 ? ('0' + (updateDate.getMonth() + 1)) : ((updateDate.getMonth() + 1));
+                const dateStr = (updateDate.getDate()) < 10 ? ('0' + (updateDate.getDate())) : ((updateDate.getDate()));
+                const dtStr = updateDate.getFullYear() + '-' + montStr + '-' + dateStr;
+                const orgArchiveList = await ctx.service.rptArchive.getPrjStgArchive(prjId, stgId);
+                if (orgArchiveList.length > 0) {
+                    const contentArr = JSON.parse(orgArchiveList[0].content);
+                    for (const item of contentArr) {
+                        if (item.rpt_id === rptId) {
+                            if (item.items && item.items.length > 0) {
+                                for (const subItem of item.items) {
+                                    if (subItem.uuid === orgName) {
+                                        subItem.updateDate_time = dtStr;
+                                        break;
+                                    }
+                                }
+                            } else {
+                                item.items = [{ uuid: orgName, updateDate_time: dtStr }];
+                            }
+                            break;
+                        }
+                    }
+                    const updatedRst = await ctx.service.rptArchive.updateArchive(orgArchiveList[0].id, prjId, stgId, contentArr);
+                    // console.log(updatedRst);
+                    ctx.body = { err: 0, msg: '', data: { fileName, updateDate, updatedRst: contentArr } };
+                } else {
+                    // 需要增加
+                    const archiveArr = [];
+                    archiveArr.push({ rpt_id: rptId, items: [{ uuid: orgName, updateDate_time: dtStr }] });
+                    const updatedRst = await ctx.service.rptArchive.createArchive(prjId, stgId, archiveArr);
+                    // console.log(updatedRst);
+                    ctx.body = { err: 0, msg: '', data: { fileName, updateDate, updatedRst: archiveArr } };
+                }
+            } catch (err) {
+                this.log(err);
+                ctx.body = { err: 1, msg: err.toString(), data: null };
+            }
+        }
+
+        async removeReportArchive(ctx) {
+            try {
+                const prjId = ctx.params.prjId;
+                const stgId = ctx.params.stgId;
+                const rptId = ctx.params.rptId;
+                const orgName = ctx.params.orgName;
+                const fileName = orgName + '.PDF';
+                console.log('removing fileName: ' + fileName);
+                const fullName = path.join(this.app.baseDir, 'app', 'public/archive', fileName);
+                // await ctx.helper.saveStreamFile(stream, path.join(this.app.baseDir, 'app', 'public/archive', fileName));
+                // fs.stat(fullName, function(err, data) {
+                //     if (err) {
+                //         console.log(err);
+                //     } else {
+                //         fs.unlink(fullName, function(err) {
+                //             if (err) {
+                //                 console.log(err);
+                //             }
+                //         });
+                //     }
+                // });
+                const oss_result = await ctx.oss.delete('archive/' + fileName);
+                if (!(oss_result && oss_result.res.status === 204)) {
+                    throw '删除归档文件失败';
+                }
+                const orgArchiveList = await ctx.service.rptArchive.getPrjStgArchive(prjId, stgId);
+                if (orgArchiveList.length > 0) {
+                    const contentArr = JSON.parse(orgArchiveList[0].content);
+                    for (const item of contentArr) {
+                        if (item.rpt_id === rptId) {
+                            if (item.items && item.items.length > 0) {
+                                for (const subIdx in item.items) {
+                                    if (item.items[subIdx].uuid === orgName) {
+                                        item.items.splice(subIdx, 1);
+                                        break;
+                                    }
+                                }
+                            }
+                            break;
+                        }
+                    }
+                    const updatedRst = await ctx.service.rptArchive.updateArchive(prjId, stgId, contentArr);
+                    ctx.body = { err: 0, msg: '', data: { fileName, updatedRst } };
+                } else {
+                    ctx.body = { err: 0, msg: '', data: { fileName, updatedRst: null } };
+                }
+            } catch (err) {
+                this.log(err);
+                ctx.body = { err: 1, msg: err.toString(), data: null };
+            }
+        }
+
+        async addMultiReportArchive(ctx, params) {
+            // 暂时不支持
+        }
+
+        async getArchivedFileByUUID(ctx) {
+            // console.log('downloading : ' + ctx.params.uuid);
+            const uuid = ctx.params.uuid;
+            const rptName = ctx.params.rptName;
+            const suffix = '.PDF';
+            try {
+                const rptNameURI = encodeURI(rptName);
+                const filePath = this.app.baseDir + '/app/public/archive/';
+                // console.log('filePath: ' + filePath);
+                // await this.ctx.helper.recursiveMkdirSync(this.app.baseDir + '/app/public/download');
+                ctx.set({
+                    'Content-Type': 'application/vnd.openxmlformats',
+                    'Content-Disposition': 'attachment; filename="' + rptNameURI + suffix + "\"; filename*=utf-8''" + rptNameURI + suffix,
+                });
+                ctx.body = await fs.readFileSync(filePath + uuid + suffix);
+            } catch (e) {
+                console.log(e);
+            }
+        }
+
+        async pdfShow(ctx) {
+            await ctx.render('report/archive_pdf.ejs');
+        }
+    }
+    return ReportArchiveController;
+};
+

+ 38 - 108
app/controller/report_controller.js

@@ -20,7 +20,12 @@ const RPT_DEF_PROPERTIES = require('../const/report_defined_properties');
 const reportConst = require('../const/report');
 const reportConst = require('../const/report');
 // const stringUtil = require('../public/js/string_util_light');
 // const stringUtil = require('../public/js/string_util_light');
 const scheduleJob = require('node-schedule');
 const scheduleJob = require('node-schedule');
-const needCustomTables = ['mem_gather_stage_bills', 'mem_gather_deal_bills', 'mem_gather_stage_pay', 'mem_gather_tender_info', 'mem_stage_sum_bills', 'mem_stage_sum_pay'];
+const needCustomTables = [
+    'mem_custom_select',
+    'mem_gather_stage_bills', 'mem_gather_deal_bills', 'mem_gather_stage_pay', 'mem_gather_tender_info',
+    'mem_stage_sum_bills', 'mem_stage_sum_pay',
+    'mem_jh_gather_im_change', 'mem_jh_im_change',
+];
 
 
 module.exports = app => {
 module.exports = app => {
     class ReportController extends app.BaseController {
     class ReportController extends app.BaseController {
@@ -69,7 +74,7 @@ module.exports = app => {
                 await this._getStageAuditViewData(ctx);
                 await this._getStageAuditViewData(ctx);
                 const pageShow = ctx.session.sessionProject.page_show;
                 const pageShow = ctx.session.sessionProject.page_show;
                 // console.log(pageShow);
                 // console.log(pageShow);
-                // pageShow.showArchive = 1;
+                pageShow.showArchive = 0;
                 const tender = ctx.tender;
                 const tender = ctx.tender;
                 const stage = ctx.stage;
                 const stage = ctx.stage;
                 let stage_id = -1;
                 let stage_id = -1;
@@ -81,6 +86,10 @@ module.exports = app => {
                 const custTreeNodes = await ctx.service.rptTreeNodeCust.getCustFoldersByUserId(this.ctx.session.sessionUser.accountId);
                 const custTreeNodes = await ctx.service.rptTreeNodeCust.getCustFoldersByUserId(this.ctx.session.sessionUser.accountId);
                 const custCfg = await ctx.service.rptCustomizeCfg.getCustomizeCfgByUserId('Administrator');
                 const custCfg = await ctx.service.rptCustomizeCfg.getCustomizeCfgByUserId('Administrator');
                 const stageList = await ctx.service.stage.getValidStagesShort(tender.id);
                 const stageList = await ctx.service.stage.getValidStagesShort(tender.id);
+                // console.log(stage);
+                // console.log(stageList);
+                // console.log('ctx.stage.id: ' + ctx.stage.id);
+                // console.log('ctx.stage.stage_id: ' + ctx.stage.stage_id);
                 // console.log('this.ctx.session.sessionUser.accountId: ' + this.ctx.session.sessionUser.accountId);
                 // console.log('this.ctx.session.sessionUser.accountId: ' + this.ctx.session.sessionUser.accountId);
                 if (stage && stage.status === auditConst.stage.status.uncheck) {
                 if (stage && stage.status === auditConst.stage.status.uncheck) {
                     // 得判断账号是否在审核人列表中(不含原报)
                     // 得判断账号是否在审核人列表中(不含原报)
@@ -88,6 +97,8 @@ module.exports = app => {
                         stage.auditorList = await ctx.service.stageAudit.getAuditors(ctx.stage.id, ctx.stage.times);
                         stage.auditorList = await ctx.service.stageAudit.getAuditors(ctx.stage.id, ctx.stage.times);
                     }
                     }
                     let isAudit = false;
                     let isAudit = false;
+                    // console.log('stage.auditorList');
+                    // console.log(stage.auditorList);
                     for (const audit of stage.auditorList) {
                     for (const audit of stage.auditorList) {
                         if (audit.aid === this.ctx.session.sessionUser.accountId) {
                         if (audit.aid === this.ctx.session.sessionUser.accountId) {
                             isAudit = true;
                             isAudit = true;
@@ -105,6 +116,8 @@ module.exports = app => {
                                 stageList[idx].auditorList = await ctx.service.stageAudit.getAuditors(stageList[idx].id, stageList[idx].times);
                                 stageList[idx].auditorList = await ctx.service.stageAudit.getAuditors(stageList[idx].id, stageList[idx].times);
                             }
                             }
                             let isAudit = false;
                             let isAudit = false;
+                            // console.log('stageList[idx].auditorList');
+                            // console.log(stageList[idx].auditorList);
                             for (const audit of stageList[idx].auditorList) {
                             for (const audit of stageList[idx].auditorList) {
                                 if (audit.aid === this.ctx.session.sessionUser.accountId) {
                                 if (audit.aid === this.ctx.session.sessionUser.accountId) {
                                     isAudit = true;
                                     isAudit = true;
@@ -117,6 +130,26 @@ module.exports = app => {
                         }
                         }
                     }
                     }
                 }
                 }
+                let lastAuditor = null;
+                if (stage) {
+                    lastAuditor = await ctx.service.stageAudit.getLastestAuditor(ctx.stage.id, ctx.stage.times, auditConst.stage.status.checked);
+                } else {
+                    lastAuditor = await ctx.service.stageAudit.getLastestAuditor(stageList[0].id, stageList[0].times, auditConst.stage.status.checked);
+                }
+                let archiveList = [];
+                if (stage) {
+                    const archives = await ctx.service.rptArchive.getPrjStgArchive(tender.data.project_id, ctx.stage.id);
+                    if (archives.length > 0) {
+                        archiveList = JSON.parse(archives[0].content);
+                    }
+                } else {
+                    const archives = await ctx.service.rptArchive.getPrjStgArchive(tender.data.project_id, stageList[stageList.length - 1].id);
+                    // console.log(archives);
+                    if (archives && archives.length > 0) {
+                        archiveList = JSON.parse(archives[0].content);
+                    }
+                }
+                // const archiveList = await ctx.service.rptArchive.getPrjStgArchive(tender.data.project_id, ctx.stage.id);
                 const prjAccList = await ctx.service.projectAccount.getAllAccountByProjectId(tender.data.project_id);
                 const prjAccList = await ctx.service.projectAccount.getAllAccountByProjectId(tender.data.project_id);
                 const roleList = await ctx.service.signatureRole.getSignatureRolesByTenderId(tender.id);
                 const roleList = await ctx.service.signatureRole.getSignatureRolesByTenderId(tender.id);
                 const usedList = await ctx.service.signatureUsed.getSignatureUsedByTenderId(tender.id);
                 const usedList = await ctx.service.signatureUsed.getSignatureUsedByTenderId(tender.id);
@@ -260,6 +293,8 @@ module.exports = app => {
                     pageShow,
                     pageShow,
                     authMobile: accountInfo.auth_mobile,
                     authMobile: accountInfo.auth_mobile,
                     shenpiConst,
                     shenpiConst,
+                    archiveList,
+                    lastAuditor,
                 };
                 };
                 await this.layout('report/index.ejs', renderData, 'report/rpt_all_popup.ejs');
                 await this.layout('report/index.ejs', renderData, 'report/rpt_all_popup.ejs');
                 // await this.layout('report/index.ejs', renderData);
                 // await this.layout('report/index.ejs', renderData);
@@ -700,116 +735,11 @@ async function checkStg(ctx, params) {
         }
         }
     }
     }
 }
 }
-/*
-async function getReportData(ctx, params, filters, memFieldKeys) {
-    const rst = {};
-    const runnableRst = [];
-    const runnableKey = []; // 这个配合runnableRst用,未来考虑并行查询优化
-    // console.log('params');
-    // console.log(params);
-    // console.log('memFieldKeys');
-    // console.log(memFieldKeys);
-    for (const filter of filters) {
-        if (runnableKey.indexOf(filter) < 0) {
-            switch (filter) {
-                case 'project' :
-                    runnableRst.push(ctx.service.project.getProjectById(params.project_id));
-                    runnableKey.push('project');
-                    break;
-                case 'tender_info' :
-                    runnableRst.push(ctx.service.tenderInfo.getTenderInfo(params.tender_id));
-                    runnableKey.push('tender_info');
-                    break;
-                case 'ledger' :
-                    runnableRst.push(ctx.service.ledger.getData(params.tender_id, 0));
-                    runnableKey.push('ledger');
-                    break;
-                case 'deal_bills' :
-                    // console.log('has push deal_bills! ' + params.tender_id);
-                    runnableRst.push(ctx.service.dealBills.getDataByTenderId(params.tender_id));
-                    runnableKey.push('deal_bills');
-                    break;
-                case 'stage_bills':
-                    runnableRst.push(ctx.service.stageBills.getLastestStageData(params.tender_id, params.stage_id));
-                    runnableKey.push('stage_bills');
-                    break;
-                case 'stage_bills_final':
-                    await checkStg(ctx, params);
-                    // const stage = ctx.stage;
-                    // runnableRst.push(ctx.service.stageBillsFinal.getFinalDataEx(params.tender_id, params.stage_order));
-                    runnableRst.push(ctx.service.stageBillsFinal.getFinalDataEx(params.tender_id, ctx.stage.order));
-                    runnableKey.push('stage_bills_final');
-                    break;
-                case 'stage':
-                    runnableRst.push(ctx.service.stage.getStageById(params.stage_id));
-                    runnableKey.push('stage');
-                    break;
-                case 'stage_pay':
-                    await checkStg(ctx, params);
-                    // const stage2 = ctx.stage;
-                    // runnableRst.push(ctx.service.stagePay.getAuditorStageData(params.stage_id, params.stage_times, params.stage_order));
-                    runnableRst.push(ctx.service.stagePay.getAuditorStageData(params.stage_id, ctx.stage.times, ctx.stage.order));
-                    runnableKey.push('stage_pay');
-                    break;
-                case 'mem_stage_im_zl':
-                    // memFieldKeys[filter]
-                    runnableRst.push(ctx.service.reportMemory.getStageImZlData(params.tender_id, params.stage_id, memFieldKeys[filter]));
-                    runnableKey.push(filter);
-                    break;
-                case 'mem_month_progress':
-                    runnableRst.push(ctx.service.reportMemory.getMonthProgress(params.tender_id, memFieldKeys[filter]));
-                    runnableKey.push(filter);
-                    break;
-                case 'mem_stage_bills':
-                    runnableRst.push(ctx.service.reportMemory.getStageBillsData(params.tender_id, params.stage_id, memFieldKeys[filter]));
-                    runnableKey.push(filter);
-                    break;
-                case 'mem_stage_pos':
-                    runnableRst.push(ctx.service.reportMemory.getStagePosData(params.tender_id, params.stage_id, memFieldKeys[filter]));
-                    runnableKey.push(filter);
-                    break;
-                case 'mem_stage_bills_compare':
-                    runnableRst.push(ctx.service.reportMemory.getStagePosData(params.tender_id, params.stage_id, memFieldKeys[filter]));
-                    runnableKey.push(filter);
-                    break;
-                case 'change':
-                    runnableRst.push(ctx.service.change.getListByStatus(params.tender_id, 3)); // 获取所有审核通过的变更主信息
-                    runnableKey.push('change');
-                    break;
-                case 'change_audit_list':
-                    runnableRst.push(ctx.service.changeAuditList.getChangeAuditBills(params.tender_id)); // 获取所有审核通过的变更清单
-                    runnableKey.push('change_audit_list');
-                    break;
-                default:
-                    break;
-            }
-        }
-    }
-    const queryRst = await Promise.all(runnableRst);
-    for (let idx = 0; idx < runnableKey.length; idx++) {
-        rst[runnableKey[idx]] = queryRst[idx];
-    }
-    for (const filter of filters) {
-        switch (filter) {
-            case 'mem_stage_im_tz':
-                rst[filter] = await ctx.service.reportMemory.getStageImTzData(params.tender_id, params.stage_id, memFieldKeys[filter]);
-                break;
-            case 'mem_stage_im_tz_bills':
-                rst[filter] = await ctx.service.reportMemory.getStageImTzBillsData(params.tender_id, params.stage_id, memFieldKeys[filter]);
-                break;
-            default:
-                break;
-        }
-    }
-    return rst;
-}
-*/
 
 
 async function getAllPagesCommon(ctx, rptTpl, params, option, outputType, baseDir, customSelect) {
 async function getAllPagesCommon(ctx, rptTpl, params, option, outputType, baseDir, customSelect) {
     const rptDataUtil = new rptDataExtractor();
     const rptDataUtil = new rptDataExtractor();
     rptDataUtil.initialize(rptTpl);
     rptDataUtil.initialize(rptTpl);
     const filter = rptDataUtil.getDataRequestFilter();
     const filter = rptDataUtil.getDataRequestFilter();
-    // console.log(filter.tables);
     const rawDataObj = await ctx.service.report.getReportData(params, filter.tables, filter.memFieldKeys,
     const rawDataObj = await ctx.service.report.getReportData(params, filter.tables, filter.memFieldKeys,
         rptTpl[JV.NODE_CUSTOM_DEFINE], customSelect);
         rptTpl[JV.NODE_CUSTOM_DEFINE], customSelect);
     // console.log(rawDataObj);
     // console.log(rawDataObj);
@@ -926,7 +856,7 @@ async function getMultiRptsCommon(ctx, params, outputType, baseDir) {
                 curRawDataObj[table] = ctx.helper.clone(rawDataObj[table]);
                 curRawDataObj[table] = ctx.helper.clone(rawDataObj[table]);
             }
             }
             // 如果是用户交互类型的表,则应该单独获取数据
             // 如果是用户交互类型的表,则应该单独获取数据
-            if (params.customSelect && params.customSelect[tplIdx]) {
+            if ((params.customSelect && params.customSelect[tplIdx]) || rptTpl[JV.NODE_CUSTOM_DEFINE][JV.NODE_CUS_OPTION]) {
                 const cfTables = [],
                 const cfTables = [],
                     cmFieldKeys = [];
                     cmFieldKeys = [];
                 const curFilter = rptDataUtil.getDataRequestFilter();
                 const curFilter = rptDataUtil.getDataRequestFilter();

+ 8 - 1
app/controller/revise_controller.js

@@ -459,6 +459,12 @@ module.exports = app => {
                 const reviseBills = await ctx.service.reviseBills.getData(ctx.tender.id);
                 const reviseBills = await ctx.service.reviseBills.getData(ctx.tender.id);
                 const revisePos = await ctx.service.revisePos.getData(ctx.tender.id);
                 const revisePos = await ctx.service.revisePos.getData(ctx.tender.id);
 
 
+                const checkDataModel = require('../lib/ledger').checkData;
+                const checkData = new checkDataModel(ctx);
+                checkData.loadData(reviseBills, revisePos);
+                const sameCodeError = checkData.checkSameCode();
+                const siblingError = checkData.checkSibling();
+
                 const qtyData = ctx.helper.checkBillsWithPos(reviseBills, revisePos,
                 const qtyData = ctx.helper.checkBillsWithPos(reviseBills, revisePos,
                     ['sgfh_qty', 'qtcl_qty', 'sjcl_qty', 'quantity']);
                     ['sgfh_qty', 'qtcl_qty', 'sjcl_qty', 'quantity']);
                 qtyData.error.forEach(x => { x.errorType = 'qty'; });
                 qtyData.error.forEach(x => { x.errorType = 'qty'; });
@@ -468,7 +474,7 @@ module.exports = app => {
                 ], this.ctx.tender.info.decimal);
                 ], this.ctx.tender.info.decimal);
                 tpData.error.forEach(x => { x.errorType = 'tp'; });
                 tpData.error.forEach(x => { x.errorType = 'tp'; });
                 ctx.body = { err: 0, msg: '', data: {
                 ctx.body = { err: 0, msg: '', data: {
-                        error: [...qtyData.error, ...tpData.error],
+                        error: [...qtyData.error, ...tpData.error, ...sameCodeError, ...siblingError],
                         source: {
                         source: {
                             bills: [...qtyData.source.bills, ...tpData.source.bills],
                             bills: [...qtyData.source.bills, ...tpData.source.bills],
                             pos: [...qtyData.source.pos, ...tpData.source.pos],
                             pos: [...qtyData.source.pos, ...tpData.source.pos],
@@ -701,6 +707,7 @@ module.exports = app => {
                         responseData.data = await this._billsBase(revise, data.postType, data.postData);
                         responseData.data = await this._billsBase(revise, data.postType, data.postData);
                         break;
                         break;
                     case 'update':
                     case 'update':
+                        ctx.helper.checkDgnQtyPrecision(data.postData);
                         responseData.data = await this.ctx.service.reviseBills.updateCalc(revise.tid, data.postData);
                         responseData.data = await this.ctx.service.reviseBills.updateCalc(revise.tid, data.postData);
                         break;
                         break;
                     case 'batch-insert':
                     case 'batch-insert':

+ 67 - 1
app/controller/setting_controller.js

@@ -1,4 +1,4 @@
-'use strict';
+ 'use strict';
 
 
 /**
 /**
  *
  *
@@ -15,6 +15,8 @@ const accountGroup = require('../const/account_group').group;
 const permission = require('../const/account_permission').permission;
 const permission = require('../const/account_permission').permission;
 const projectLog = require('../const/project_log');
 const projectLog = require('../const/project_log');
 const imType = require('../const/tender').imType;
 const imType = require('../const/tender').imType;
+const S2b = require('../lib/s2b');
+const measureType = require('../const/tender').measureType;
 
 
 module.exports = app => {
 module.exports = app => {
 
 
@@ -31,6 +33,14 @@ module.exports = app => {
             ctx.subMenu = settingMenu;
             ctx.subMenu = settingMenu;
         }
         }
 
 
+        async _checkMenu(pid) {
+            const s2bData = new S2b(this.ctx);
+            const s2bProj = await s2bData.getS2bProj(pid);
+            // this.ctx.subMenu.s2b.display = !!s2bProj && (!!s2bProj.gxby || !!s2bProj.dagl);
+            this.ctx.subMenu.s2b.display = false;
+            return s2bProj;
+        }
+
         /**
         /**
          * 项目信息页面(Get)
          * 项目信息页面(Get)
          *
          *
@@ -41,6 +51,7 @@ module.exports = app => {
             try {
             try {
                 // 获取项目数据
                 // 获取项目数据
                 const projectId = ctx.session.sessionProject.id;
                 const projectId = ctx.session.sessionProject.id;
+                await this._checkMenu(projectId);
                 const projectData = await ctx.service.project.getDataById(projectId);
                 const projectData = await ctx.service.project.getDataById(projectId);
                 if (projectData === null) {
                 if (projectData === null) {
                     throw '没有对应的项目数据';
                     throw '没有对应的项目数据';
@@ -85,6 +96,7 @@ module.exports = app => {
             try {
             try {
                 // 获取项目数据
                 // 获取项目数据
                 const projectId = ctx.session.sessionProject.id;
                 const projectId = ctx.session.sessionProject.id;
+                await this._checkMenu(projectId);
                 const projectData = await ctx.service.project.getDataById(projectId);
                 const projectData = await ctx.service.project.getDataById(projectId);
                 if (projectData === null) {
                 if (projectData === null) {
                     throw '没有对应的项目数据';
                     throw '没有对应的项目数据';
@@ -410,6 +422,7 @@ module.exports = app => {
             try {
             try {
                 // 获取项目数据
                 // 获取项目数据
                 const projectId = ctx.session.sessionProject.id;
                 const projectId = ctx.session.sessionProject.id;
+                await this._checkMenu(projectId);
                 const projectData = await ctx.service.project.getDataById(projectId);
                 const projectData = await ctx.service.project.getDataById(projectId);
                 if (projectData === null) {
                 if (projectData === null) {
                     throw '没有对应的项目数据';
                     throw '没有对应的项目数据';
@@ -626,6 +639,7 @@ module.exports = app => {
             try {
             try {
                 // 获取项目数据
                 // 获取项目数据
                 const projectId = ctx.session.sessionProject.id;
                 const projectId = ctx.session.sessionProject.id;
+                await this._checkMenu(projectId);
                 const projectData = await ctx.service.project.getDataById(projectId);
                 const projectData = await ctx.service.project.getDataById(projectId);
                 if (!projectData) {
                 if (!projectData) {
                     throw '没有对应的项目数据';
                     throw '没有对应的项目数据';
@@ -666,6 +680,7 @@ module.exports = app => {
             try {
             try {
                 // 获取项目数据
                 // 获取项目数据
                 const projectId = ctx.session.sessionProject.id;
                 const projectId = ctx.session.sessionProject.id;
+                await this._checkMenu(projectId);
                 const projectData = await ctx.service.project.getDataById(projectId);
                 const projectData = await ctx.service.project.getDataById(projectId);
                 if (projectData === null) {
                 if (projectData === null) {
                     throw '没有对应的项目数据';
                     throw '没有对应的项目数据';
@@ -692,6 +707,7 @@ module.exports = app => {
         async fun(ctx) {
         async fun(ctx) {
             try {
             try {
                 const projectId = ctx.session.sessionProject.id;
                 const projectId = ctx.session.sessionProject.id;
+                await this._checkMenu(projectId);
                 const projectData = await ctx.service.project.getDataById(projectId);
                 const projectData = await ctx.service.project.getDataById(projectId);
                 const funRela = await ctx.service.project.getFunRela(projectId);
                 const funRela = await ctx.service.project.getFunRela(projectId);
                 if (projectData === null) {
                 if (projectData === null) {
@@ -741,6 +757,56 @@ module.exports = app => {
                 this.ajaxErrorBody(error, '保存数据失败');
                 this.ajaxErrorBody(error, '保存数据失败');
             }
             }
         }
         }
+
+        async s2b (ctx) {
+            try {
+                const projectId = ctx.session.sessionProject.id;
+                await this._checkMenu(projectId);
+                const projectData = await ctx.service.project.getDataById(projectId);
+                if (projectData === null) throw '没有对应的项目数据';
+                if (ctx.session.sessionUser.is_admin === 0) throw '没有访问权限';
+
+                const tenders = await ctx.service.tender.getAllDataByCondition({ where: { project_id: projectId } });
+                for (const t of tenders) {
+                    t.measure_type_str = t.measure_type === measureType.tz.value ? measureType.tz.title : measureType.gcl.title;
+                    const stages = await ctx.service.stage.getAllDataByCondition({ where: { tid: t.id } });
+                    t.stage_count_str = `第${stages.count || 0}期`;
+                    const user = await ctx.service.projectAccount.getAccountInfoById(t.user_id);
+                    t.user_str = user.name + '-' + user.company;
+                    t.show_time = stages.length > 0 ? stages[stages.length - 1].cache_time_r || stages[stages.length - 1].in_time : t.create_time;
+                }
+                await this.layout('setting/s2b.ejs', {
+                    projectData,
+                    tenders,
+                })
+            } catch (error) {
+                ctx.helper.log(error);
+                ctx.redirect('/dashboard');
+            }
+        }
+
+        async s2bUpdate(ctx) {
+            try {
+                const projectId = ctx.session.sessionProject.id;
+                const projectData = await ctx.service.project.getDataById(projectId);
+                if (projectData === null) throw '没有对应的项目数据';
+                if (ctx.session.sessionUser.is_admin === 0) throw '没有访问权限';
+
+                const data = JSON.parse(ctx.request.body.data);
+                if (!data.tid) throw '提交数据错误';
+
+                const updateData = {};
+                if (data.gxby_limit !== undefined) updateData.s2b_gxby_limit = data.gxby_limit;
+                if (data.gxby_check !== undefined) updateData.s2b_gxby_check = data.gxby_check;
+                if (data.dagl_limit !== undefined) updateData.s2b_dagl_limit = data.dagl_limit;
+                if (data.dagl_check !== undefined) updateData.s2b_dagl_check = data.dagl_check;
+                await ctx.service.tender.saveApiRela(data.tid, updateData);
+                ctx.body = { err: 0, msg: '', data: null };
+            } catch (error) {
+                ctx.helper.log(error);
+                ctx.ajaxError(error, '保存数据错误');
+            }
+        }
     }
     }
 
 
     return SettingController;
     return SettingController;

+ 72 - 30
app/controller/stage_controller.js

@@ -193,6 +193,7 @@ module.exports = app => {
                     attData[index].in_time = moment(attData[index].in_time * 1000).format('YYYY-MM-DD');
                     attData[index].in_time = moment(attData[index].in_time * 1000).format('YYYY-MM-DD');
                 }
                 }
                 renderData.attData = attData;
                 renderData.attData = attData;
+                renderData.coopwd = ((ctx.stage.status === auditConst.status.uncheck || ctx.stage.status === auditConst.status.checkNo) && ctx.session.sessionUser.accountId === ctx.stage.user_id) || (ctx.stage.status === auditConst.status.checking && ctx.stage.curAuditor && ctx.stage.curAuditor.aid === ctx.session.sessionUser.accountId);
                 await this.layout('stage/index.ejs', renderData, 'stage/modal.ejs');
                 await this.layout('stage/index.ejs', renderData, 'stage/modal.ejs');
             } catch (err) {
             } catch (err) {
                 this.log(err);
                 this.log(err);
@@ -329,8 +330,16 @@ module.exports = app => {
                             responseData.data.tags = await ctx.service.ledgerTag.getDatas(ctx.tender.id, ctx.stage.id);
                             responseData.data.tags = await ctx.service.ledgerTag.getDatas(ctx.tender.id, ctx.stage.id);
                             break;
                             break;
                         case 'cooperation':
                         case 'cooperation':
-                            responseData.data.cooperation = await this.ctx.service.ledgerCooperation.getValidData(
-                                ctx.tender.id, ctx.session.sessionUser.accountId);
+                            const uid = ctx.stage.curAuditor ?
+                                ctx.stage.curAuditor.aid :
+                                (ctx.stage.status === auditConst.status.uncheck ||
+                                (ctx.stage.status === auditConst.status.checkNo && ctx.stage.user_id === ctx.session.sessionUser.accountId) ?
+                                    ctx.stage.user_id : (ctx.stage.status === auditConst.status.checkNo && ctx.stage.user_id !== ctx.session.sessionUser.accountId) ?
+                                        ctx.session.sessionUser.accountId : null);
+                            responseData.data.cooperation = uid !== null ? await ctx.service.ledgerCooperation.getValidData(
+                                ctx.tender.id, uid) : [];
+                            const stageTimes = (ctx.stage.status === auditConst.status.checkNo && ctx.stage.user_id !== ctx.session.sessionUser.accountId) ? ctx.stage.times - 1 : ctx.stage.times;
+                            responseData.data.cooperationConfirm = uid !== null ? await ctx.service.cooperationConfirm.getValidData(ctx.tender.id, ctx.stage.id, stageTimes, uid) : [];
                             break;
                             break;
                         case 'spec':
                         case 'spec':
                             const spec = {zlj: JSON.parse(JSON.stringify(stdConst.zlj)), jrg: stdConst.jrg};
                             const spec = {zlj: JSON.parse(JSON.stringify(stdConst.zlj)), jrg: stdConst.jrg};
@@ -353,6 +362,10 @@ module.exports = app => {
                 const ledgerData = await this._getStageLedgerData(ctx);
                 const ledgerData = await this._getStageLedgerData(ctx);
                 const posData = await this._getStagePosData(ctx);
                 const posData = await this._getStagePosData(ctx);
 
 
+                const checkDataModel = require('../lib/ledger').checkData;
+                const checkData = new checkDataModel(ctx);
+                checkData.loadData(ledgerData, posData);
+                const check3fResult = checkData.check3fLimit(ctx.tender.data);
                 const [qtyData, overData] = ctx.helper.checkBillsWithPos2(ledgerData, posData,
                 const [qtyData, overData] = ctx.helper.checkBillsWithPos2(ledgerData, posData,
                     ['contract_qty', 'qc_qty'], projRela.banOver, this.ctx.tender.data.measure_type === measureType.tz.value);
                     ['contract_qty', 'qc_qty'], projRela.banOver, this.ctx.tender.data.measure_type === measureType.tz.value);
                 qtyData.error.forEach(x => { x.errorType = 'qty'; });
                 qtyData.error.forEach(x => { x.errorType = 'qty'; });
@@ -362,10 +375,10 @@ module.exports = app => {
                 ], this.ctx.tender.info.decimal);
                 ], this.ctx.tender.info.decimal);
                 tpData.error.forEach(x => { x.errorType = 'tp'; });
                 tpData.error.forEach(x => { x.errorType = 'tp'; });
                 ctx.body = { err: 0, msg: '', data: {
                 ctx.body = { err: 0, msg: '', data: {
-                    error: [...qtyData.error, ...tpData.error, ...overData.error],
+                    error: [...qtyData.error, ...tpData.error, ...overData.error, ...check3fResult.error],
                     source: {
                     source: {
-                        bills: [...qtyData.source.bills, ...tpData.source.bills, ...overData.source.bills],
-                        pos: [...qtyData.source.pos, ...tpData.source.pos, ...overData.source.pos],
+                        bills: ctx.helper._.uniqBy([...qtyData.source.bills, ...tpData.source.bills, ...overData.source.bills, ...check3fResult.source.bills], 'id'),
+                        pos: ctx.helper._.uniqBy([...qtyData.source.pos, ...tpData.source.pos, ...overData.source.pos, ...check3fResult.source.pos], 'id'),
                     },
                     },
                 } };
                 } };
             } catch (err) {
             } catch (err) {
@@ -442,6 +455,7 @@ module.exports = app => {
                         responseData.data.bills = await ctx.service.ledger.getDataByIds(this.ctx.helper._.map(updateDatas, 'id'));
                         responseData.data.bills = await ctx.service.ledger.getDataByIds(this.ctx.helper._.map(updateDatas, 'id'));
                     }
                     }
                     if (data.bills.dgn) {
                     if (data.bills.dgn) {
+                        ctx.helper.checkDgnQtyPrecision(data.bills.dgn);
                         responseData.data.dgn = await ctx.service.stageBillsDgn.saveDgnData(data.bills.dgn);
                         responseData.data.dgn = await ctx.service.stageBillsDgn.saveDgnData(data.bills.dgn);
                     }
                     }
                     if (data.bills.stage) {
                     if (data.bills.stage) {
@@ -1287,27 +1301,27 @@ module.exports = app => {
          * 检查当前期当前用户是否在审核列表中,如果是的话允许再次上传附件
          * 检查当前期当前用户是否在审核列表中,如果是的话允许再次上传附件
          * @param {Object} ctx 上下文
          * @param {Object} ctx 上下文
          */
          */
-        // _checkStageCanModifyRe(ctx) {
-        //     // 检查登录用户,是否可操作
-        //     if (ctx.stage.readOnly) {
-        //         if (ctx.stage.status === auditConst.status.checked) {
-        //             // 当前期状态为完成,且提交人是审核列表中的则可再次上传
-        //             if (ctx.stage.user_id === ctx.session.sessionUser.accountId || ctx.stage.auditors.findIndex(auditor => auditor.aid === ctx.session.sessionUser.accountId) !== -1) {
-        //                 // 再次上传的图片要给个标识,方便给前端进行编辑操作
-        //                 ctx.reUploadPermission = true;
-        //                 return;
-        //             }
-
-        //             throw '该计量期当前您无权操作';
-
-        //         } else {
-        //             throw '该计量期当前您无权操作';
-        //         }
-        //     }
-        //     if (ctx.stage.revising) {
-        //         throw '台账修订中,请勿修改提交期数据';
-        //     }
-        // }
+        _checkStageCanModifyRe(ctx) {
+            // 检查登录用户,是否可操作
+            if (ctx.stage.readOnly) {
+                if (ctx.stage.status === auditConst.status.checked) {
+                    // 当前期状态为完成,且提交人是审核列表中的则可再次上传
+                    if (ctx.stage.user_id === ctx.session.sessionUser.accountId || ctx.stage.auditors.findIndex(auditor => auditor.aid === ctx.session.sessionUser.accountId) !== -1) {
+                        // 再次上传的图片要给个标识,方便给前端进行编辑操作
+                        // ctx.reUploadPermission = true;
+                        return;
+                    }
+
+                    throw '该计量期当前您无权操作';
+
+                } else {
+                    throw '该计量期当前您无权操作';
+                }
+            }
+            if (ctx.stage.revising) {
+                throw '台账修订中,请勿修改提交期数据';
+            }
+        }
         /**
         /**
          * 上传附件
          * 上传附件
          * @param {Object} ctx - egg全局变量
          * @param {Object} ctx - egg全局变量
@@ -1321,7 +1335,7 @@ module.exports = app => {
             };
             };
             let stream;
             let stream;
             try {
             try {
-                // this._checkStageCanModifyRe(ctx);
+                this._checkStageCanModifyRe(ctx);
 
 
                 const parts = ctx.multipart({ autoFields: true });
                 const parts = ctx.multipart({ autoFields: true });
                 const files = [];
                 const files = [];
@@ -1422,7 +1436,6 @@ module.exports = app => {
                     }
                     }
                 } catch (err) {
                 } catch (err) {
                     this.log(err);
                     this.log(err);
-                    console.log(err);
                     this.setMessage(err.toString(), this.messageType.ERROR);
                     this.setMessage(err.toString(), this.messageType.ERROR);
                 }
                 }
             }
             }
@@ -1470,7 +1483,7 @@ module.exports = app => {
                 data: '',
                 data: '',
             };
             };
             try {
             try {
-                // this._checkStageCanModifyRe(ctx);
+                this._checkStageCanModifyRe(ctx);
 
 
                 const data = JSON.parse(ctx.request.body.data);
                 const data = JSON.parse(ctx.request.body.data);
                 const fileInfo = await ctx.service.stageAtt.getDataById(data.id);
                 const fileInfo = await ctx.service.stageAtt.getDataById(data.id);
@@ -1516,7 +1529,7 @@ module.exports = app => {
             };
             };
             let stream;
             let stream;
             try {
             try {
-                // this._checkStageCanModifyRe(ctx);
+                this._checkStageCanModifyRe(ctx);
                 stream = await ctx.getFileStream({ requireFile: false });
                 stream = await ctx.getFileStream({ requireFile: false });
                 let fileData = {};
                 let fileData = {};
                 if (stream.filename !== undefined) {
                 if (stream.filename !== undefined) {
@@ -1806,6 +1819,35 @@ module.exports = app => {
                 ctx.redirect(ctx.request.header.referer);
                 ctx.redirect(ctx.request.header.referer);
             }
             }
         }
         }
+
+        /**
+         * 删除本次审批 - 期审批管理页面
+         * @param ctx
+         * @return {Promise<void>}
+         */
+        async saveCooperationData(ctx) {
+            try {
+                const data = JSON.parse(ctx.request.body.data);
+                const responseData = { err: 0, msg: '', data: {} };
+                switch (data.type) {
+                    case 'save-confirm':
+                        await ctx.service.cooperationConfirm.save(data.postData);
+                        responseData.data.cooperationConfirm = await ctx.service.cooperationConfirm.getValidData(ctx.tender.id, ctx.stage.id, ctx.stage.times, ctx.session.sessionUser.accountId);
+                        break;
+                    case 'del-confirm':
+                        await ctx.service.cooperationConfirm.del(data.postData);
+                        responseData.data.cooperationConfirm = await ctx.service.cooperationConfirm.getValidData(ctx.tender.id, ctx.stage.id, ctx.stage.times, ctx.session.sessionUser.accountId);
+                        break;
+                    default:
+                        throw '参数有误';
+                }
+
+                ctx.body = responseData;
+            } catch (err) {
+                this.log(err);
+                ctx.body = { err: 1, msg: err.toString(), data: null };
+            }
+        }
     }
     }
 
 
     return StageController;
     return StageController;

+ 39 - 1
app/controller/tender_controller.js

@@ -454,7 +454,7 @@ module.exports = app => {
                     jsFiles: this.app.jsFiles.common.concat(this.app.jsFiles.tender.tenderInfo),
                     jsFiles: this.app.jsFiles.common.concat(this.app.jsFiles.tender.tenderInfo),
                 };
                 };
                 if (ctx.session.sessionUser.is_admin) {
                 if (ctx.session.sessionUser.is_admin) {
-                    // renderData.tourists = await ctx.service.tenderTourist.getTourists(tender.id);
+                    renderData.tourists = await ctx.service.tenderTourist.getTourists(tender.id);
                     // 获取所有项目参与者
                     // 获取所有项目参与者
                     const accountList = await ctx.service.projectAccount.getAllDataByCondition({
                     const accountList = await ctx.service.projectAccount.getAllDataByCondition({
                         where: { project_id: ctx.session.sessionProject.id, enable: 1 },
                         where: { project_id: ctx.session.sessionProject.id, enable: 1 },
@@ -918,6 +918,9 @@ module.exports = app => {
                     case 'pwd':
                     case 'pwd':
                         info = await ctx.service.ledgerCooperation.save(data);
                         info = await ctx.service.ledgerCooperation.save(data);
                         break;
                         break;
+                    case 'company':
+                        info = await ctx.service.ledgerCooperation.saveCompany(data);
+                        break;
                     default:break;
                     default:break;
                 }
                 }
                 ctx.body = { err: 0, msg: '', data: info };
                 ctx.body = { err: 0, msg: '', data: info };
@@ -968,6 +971,41 @@ module.exports = app => {
         }
         }
 
 
         /**
         /**
+         * 游客账号设置
+         * @param {object} ctx - 上下文
+         */
+        async saveTourist(ctx) {
+            try {
+                const data = JSON.parse(ctx.request.body.data);
+                if (!data) {
+                    throw '提交数据错误';
+                }
+                // 判断修改权限
+                if (ctx.session.sessionUser.is_admin === 0) {
+                    throw '你没有权限修改游客账号';
+                }
+                let info = '';
+                switch (data.type) {
+                    case 'add':
+                        const result = await ctx.service.tenderTourist.addAudit(data);
+                        if (!result) {
+                            throw '添加审批人失败';
+                        }
+                        info = result;
+                        break;
+                    case 'del':
+                        await ctx.service.tenderTourist.removeAudit(data);
+                        break;
+                    default:break;
+                }
+                ctx.body = { err: 0, msg: '', data: info };
+            } catch (err) {
+                this.log(err);
+                ctx.body = this.ajaxErrorBody(err, '保存游客账号设置失败');
+            }
+        }
+
+        /**
          * 获取部位明细数据(Ajax)
          * 获取部位明细数据(Ajax)
          *
          *
          * @param ctx
          * @param ctx

+ 0 - 16
app/controller/wechat_controller.js

@@ -254,22 +254,6 @@ module.exports = app => {
                 ctx.body = error;
                 ctx.body = error;
             }
             }
         }
         }
-
-        async batchUpdateUnionid(ctx) {
-            try {
-                const wxList = await ctx.service.projectAccount.getUnionIdList();
-                const updateList = [];
-                for (const wx of wxList) {
-                    const user = await app.wechat.api.getUser(wx.wx_openid);
-                    updateList.push({ id: wx.id, wx_unionid: user.unionid });
-                }
-                await ctx.service.projectAccount.updateUnionid(updateList);
-                ctx.body = 'success';
-            } catch (error) {
-                console.log(error);
-                ctx.body = error;
-            }
-        }
     }
     }
 
 
     return WechatController;
     return WechatController;

+ 41 - 9
app/extend/helper.js

@@ -635,6 +635,14 @@ module.exports = {
         }
         }
     },
     },
 
 
+    checkDgnQtyPrecision(data, precision = 3) {
+        const datas = data instanceof Array ? data : [data];
+        for (const d of datas) {
+            for (const prop in d) {
+                if (prop.indexOf('dgn_qty') >= 0) d[prop] = this.round(d[prop], precision);
+            }
+        }
+    },
     /**
     /**
      * 过滤无效数据
      * 过滤无效数据
      *
      *
@@ -1125,17 +1133,18 @@ module.exports = {
     },
     },
 
 
     /**
     /**
-     * 短链接生成
+     * 短链接生成(暂停生成)
      * @param url
      * @param url
      * @return {*}
      * @return {*}
      */
      */
     async urlToShort(url) {
     async urlToShort(url) {
-        const apiUrl = 'http://scn.ink/api/shorturl';
-        const data = {
-            url: encodeURI(url),
-        };
-        const result = await this.sendRequest(apiUrl, data, 'get');
-        return result && result.code === 200 && result.url ? result.url : url;
+        // const apiUrl = 'http://scn.ink/api/shorturl';
+        // const data = {
+        //     url: encodeURI(url),
+        // };
+        // const result = await this.sendRequest(apiUrl, data, 'get');
+        // return result && result.code === 200 && result.url ? result.url : url;
+        return url;
     },
     },
 
 
     /**
     /**
@@ -1364,7 +1373,30 @@ module.exports = {
         return counts(array, val);
         return counts(array, val);
     },
     },
 
 
-    filterLastestData(data, keyFields) {
+    filterTimesOrderData(data, keyFields, timesField, orderField, times, order) {
+        const dataIndex = {};
+        for (const d of data) {
+            if (d[timesField] > times || (d[timesField] = times && d[orderField] > order)) continue;
+            let key = 'd';
+            for (const kf of keyFields) {
+                key = key + '.' + (d[kf] || '');
+            }
+
+            const di = dataIndex[key];
+            if (di) {
+                if ((di[timesField] * timesLen + di[orderField]) < (d[timesField] * timesLen + d[orderField])) dataIndex[key] = d;
+            } else {
+                dataIndex[key] = d;
+            }
+        }
+        const result = [];
+        for (const prop in dataIndex) {
+            result.push(dataIndex[prop]);
+        }
+        return result;
+    },
+
+    filterLastestData(data, keyFields, timesField = 'times', orderField = 'order') {
         const dataIndex = {};
         const dataIndex = {};
         for (const d of data) {
         for (const d of data) {
             let key = 'd';
             let key = 'd';
@@ -1374,7 +1406,7 @@ module.exports = {
 
 
             const di = dataIndex[key];
             const di = dataIndex[key];
             if (di) {
             if (di) {
-                if ((di.times * timesLen + di.order) < (d.times * timesLen + d.order)) dataIndex[key] = d;
+                if ((di[timesField] * timesLen + di[orderField]) < (d[timesField] * timesLen + d[orderField])) dataIndex[key] = d;
             } else {
             } else {
                 dataIndex[key] = d;
                 dataIndex[key] = d;
             }
             }

+ 187 - 0
app/lib/ledger.js

@@ -27,6 +27,16 @@ class baseTree {
         // 树设置
         // 树设置
         this.setting = setting;
         this.setting = setting;
     }
     }
+    clear() {
+        // 无索引
+        this.datas = [];
+        // 以key为索引
+        this.items = {};
+        // 以排序为索引
+        this.nodes = [];
+        // 根节点
+        this.children = [];
+    }
 
 
     /**
     /**
      * 根据id获取树结构节点数据
      * 根据id获取树结构节点数据
@@ -562,10 +572,187 @@ class pos {
     }
     }
 }
 }
 
 
+class checkData {
+    constructor(ctx) {
+        this.ctx = ctx;
+        this.checkBills = new billsTree(ctx, { id: 'ledger_id', pid: 'ledger_pid', order: 'order', level: 'level', rootId: -1 });
+        this.checkPos = new pos({ id: 'id', ledgerId: 'lid' });
+    }
+
+    _check3f(data, limit, ratio) {
+        if (limit === 0) {
+            if (data.contract_tp || data.pre_contract_tp) return 1; // 违规
+        }
+        if (limit === 1) {
+            if (ratio === 0) {
+                if (!data.contract_tp && !data.pre_contract_tp) return 2; // 漏计
+            } else {
+                const tp = this.ctx.helper.mul(data.total_price, ratio, this.ctx.tender.info.decimal.tp);
+                const checkTp = this.ctx.helper.add(data.contract_tp, data.pre_contract_tp);
+                if (tp > checkTp) return 1; // 违规
+                if (tp < checkTp) return 2; // 漏计
+            }
+        }
+        return 0; // 合法
+    }
+    _check3fQty(data, limit, ratio, unit) {
+        if (limit === 0) {
+            if (data.contract_qty || data.qc_qty || data.pre_contract_qty || data.pre_qc_qty) return 1; // 违规
+        }
+        if (limit === 1) {
+            if (ratio === 0) {
+                if (!data.contract_qty && !data.qc_qty && !data.pre_contract_qty && !data.pre_qc_qty) return 2; // 漏计
+            } else {
+                const precision = this.ctx.helper.findPrecision(this.ctx.tender.info.precision, unit);
+                const checkQty = this.ctx.helper.mul(data.quantity, ratio, precision.value);
+                const qty = this.ctx.helper.add(data.contract_qty, data.pre_contract_qty);
+                if (qty > checkQty) return 1; // 违规
+                if (qty < checkQty) return 2; // 漏计
+            }
+        }
+        return 0; // 合法
+    }
+
+    _checkLeafBills3fLimit(checkType, bills, result, checkInfo) {
+        const over = [], lost = [];
+        const posRange = this.checkPos.getLedgerPos(bills.id);
+        if (posRange && posRange.length > 0) {
+            for (const p of posRange) {
+                const posCheckInfo = this.ctx.helper._.assign({}, checkInfo);
+                for (const ct of checkType) {
+                    if (p[ct + '_limit'] >= 0) {
+                        posCheckInfo[ct + '_limit'] = p[ct + '_limit'];
+                        posCheckInfo[ct + '_ratio'] = p[ct + '_ratio'];
+                    }
+                }
+                for (const ct of checkType) {
+                    const checkResult = this._check3fQty(p, posCheckInfo[ct + '_limit'], posCheckInfo[ct + '_ratio'], bills.unit);
+                    if (checkResult === 1) {
+                        if (over.indexOf(ct) === -1) over.push(ct);
+                    }
+                    if (checkResult === 2) {
+                        if (lost.indexOf(ct) === -1) lost.push(ct);
+                    }
+                }
+            }
+        } else {
+            for (const ct of checkType) {
+                const checkResult = bills.is_tp
+                    ? this._check3f(bills, checkInfo[ct + '_limit'], checkInfo[ct + '_ratio'])
+                    : this._check3fQty(bills, checkInfo[ct + '_limit'], checkInfo[ct + '_ratio'], bills.unit);
+                if (checkResult === 1) {
+                    if (over.indexOf(ct) === -1) over.push(ct);
+                }
+                if (checkResult === 2) {
+                    if (lost.indexOf(ct) === -1) lost.push(ct);
+                }
+            }
+        }
+        if (over.length + lost.length > 0) {
+            for (const o of over) {
+                result.error.push({
+                    ledger_id: bills.ledger_id,
+                    b_code: bills.b_code,
+                    name: bills.name,
+                    errorType: 's2b_over_' + o,
+                });
+            }
+            for (const l of lost) {
+                result.error.push({
+                    ledger_id: bills.ledger_id,
+                    b_code: bills.b_code,
+                    name: bills.name,
+                    errorType: 's2b_lost_' + l,
+                });
+            }
+            result.source.bills.push(bills);
+            if (posRange && posRange.length > 0) result.source.pos.push(...posRange);
+        }
+    }
+    _recursiveCheckBills3fLimit(checkType, bills, result, parentCheckInfo) {
+        const checkInfo = this.ctx.helper._.assign({}, parentCheckInfo);
+        for (const ct of checkType) {
+            if (bills[ct + '_limit'] >= 0) {
+                checkInfo[ct + '_limit'] = bills[ct + '_limit'];
+                checkInfo[ct + '_ratio'] = bills[ct + '_ratio'];
+            }
+        }
+        if (bills.children && bills.children.length > 0) {
+            for (const c of bills.children) {
+                this._recursiveCheckBills3fLimit(checkType, c, result, checkInfo);
+            }
+        } else {
+            this._checkLeafBills3fLimit(checkType, bills, result, checkInfo);
+        }
+    }
+
+    loadData(bills, pos) {
+        this.checkBills.loadDatas(bills);
+        this.checkPos.loadDatas(pos);
+    }
+
+    checkSibling() {
+        const error = [];
+        for (const node of this.checkBills.nodes) {
+            if (!node.children || node.children.length === 0) continue;
+            let hasXmj, hasGcl;
+            for (const child of node.children) {
+                if (child.b_code) hasXmj = true;
+                if (!child.b_code) hasGcl = true;
+            }
+            if (hasXmj && hasGcl) error.push({
+                ledger_id: node.ledger_id,
+                b_code: node.b_code,
+                name: node.name,
+                errorType: 'sibling',
+            });
+        }
+        return error;
+    }
+
+    checkSameCode() {
+        const error = [];
+        let xmj = this.checkBills.nodes.filter(x => { return /^((GD*)|G)?[0-9]+/.test(x.code); });
+        let check = null;
+        while (xmj.length > 0) {
+            [check, xmj] = this.ctx.helper._.partition(xmj, x => { return x.code === xmj[0].code; });
+            if (check.length > 1) {
+                for (const c of check) {
+                    error.push({
+                        ledger_id: c.ledger_id,
+                        b_code: c.b_code,
+                        name: c.name,
+                        errorType: 'same_code',
+                    })
+                }
+            }
+        }
+        return error;
+    }
+
+    check3fLimit(tender) {
+        const result = {
+            error: [],
+            source: {bills: [], pos: []},
+        };
+
+        const check = [];
+        if (tender.s2b_gxby_limit) check.push('gxby');
+        if (tender.s2b_dagl_limit) check.push('dagl');
+        if (check.length === 0) return result;
+
+        for (const b of this.checkBills.children) {
+            this._recursiveCheckBills3fLimit(check, b, result, {});
+        }
+        return result;
+    }
+}
+
 module.exports = {
 module.exports = {
     billsTree,
     billsTree,
     pos,
     pos,
     filterTree,
     filterTree,
     filterGatherTree,
     filterGatherTree,
     gatherTree,
     gatherTree,
+    checkData,
 };
 };

+ 388 - 0
app/lib/rptCustomData.js

@@ -0,0 +1,388 @@
+'use strict';
+
+/**
+ * 定制报表 注意不做任何混用,也不做任何继承
+ *
+ * @author Mai
+ * @date
+ * @version
+ */
+const auditConst = require('../const/audit');
+
+/**
+ * 季华项目 定制报表
+ * 汇总表,流水汇总2个标段(N个),汇总到期,每期汇总3个人的数据(3个),工程量清单流水表,并根据截至本期变更令使用情况筛选
+ *
+ * 借用 通用汇总标段选的界面
+ * 故配置可与汇总标段选择相同
+ *
+ * define: {
+ *     "title": "请选择汇总的标段", "type": "month/final/checked-final/stage",
+ *    "defaultCompare": [1, 2, 3], // 结果按序 rn_qty, rn_tp
+ *    "match": { "quality": [2, 3], "qty": "<0" }, // class根据变更类型过滤,qty根据数量过滤
+ *    "merge": true,
+ * }
+ * defaultCompare为默认选择的审批人,0为原报,1-N为1-N审
+ * match为保留的类型
+ * 如需要用户选择审批人,则应配置selectCompare(目前不可用,不可配置,如需使用,则需要额外界面),为后期可能的变动预留
+ *
+ */
+class jhHelper {
+
+    constructor (ctx) {
+        this.ctx = ctx;
+        this.result = [];
+    }
+
+    async _getValidStages(tenderId) {
+        const stages = await this.ctx.service.stage.db.select(this.ctx.service.stage.tableName, {
+            where: { tid: tenderId },
+            orders: [['order', 'desc']],
+        });
+        if (stages.length !== 0) {
+            const lastStage = stages[0];
+            if (lastStage.status === auditConst.stage.status.uncheck && lastStage.user_id !== this.ctx.session.sessionUser.accountId) {
+                stages.splice(0, 1);
+            }
+        }
+        return stages;
+    }
+
+    async _getCheckedStages(tenderId) {
+        const stages = await this.db.select(this.ctx.service.stage.tableName, {
+            where: { tid: tenderId },
+            orders: [['order', 'desc']],
+        });
+        if (stages.length !== 0) {
+            const lastStage = stages[0];
+            if (lastStage.status !== auditConst.stage.status.checked) {
+                stages.splice(0, 1);
+            }
+        }
+        return stages;
+    }
+
+    /**
+     * 查询本期所有变更明细
+     * @param tid
+     * @param sid
+     * @returns {Promise<void>}
+    */
+    async getCurChangeDetailData(tid, sid) {
+        const sql = 'SELECT sc.*, c.type As c_type, c.class As c_class, c.quality As c_quality FROM ' + this.ctx.service.stageChange.tableName + ' sc' +
+                    '  Left Join ' + this.ctx.service.change.tableName + ' c ON sc.cid = c.cid' +
+                    '  WHERE sc.tid = ? and sc.sid = ?';
+        return await this.ctx.service.stageChange.db.query(sql, [tid, sid]);
+    }
+
+    async getPreChangeDetailData(tid, sOrder) {
+        const sql = 'SELECT sc.*, c.type As c_type, c.class As c_class, c.quality As c_quality FROM ' + this.ctx.service.stageChangeFinal.tableName + ' sc' +
+                    '  Left Join ' + this.ctx.service.change.tableName + ' c ON sc.cid = c.cid' +
+                    '  Left Join ' + this.ctx.service.stage.tableName + ' s ON sc.sid = s.id' +
+                    '  WHERE sc.tid = ? and s.order < ?';
+        return await this.ctx.service.stageChangeFinal.db.query(sql, [tid, sOrder]);
+    }
+
+    getLastestAuditors(auditors) {
+        const index = {};
+        for (const auditor of auditors) {
+            if (!index[auditor.aid] || auditor.order > index[auditor.aid].order) index[auditor.aid] = auditor;
+        }
+        const result = [];
+        for (const i in index) {
+            result.push(index[i]);
+        }
+        result.sort((x, y) => { return x.order - y.order; });
+        return result;
+    }
+
+    _loadChangeDetail(billsIndex, changeDetail, gsDefine, prefix) {
+        for (const cd of changeDetail) {
+            if (!cd.qty) continue;
+
+            let match = false;
+            for (const m of gsDefine.match) {
+                if (m.quality === cd.c_quality && ((m.minus && cd.qty < 0) || (!m.minus && cd.qty > 0))) match = true;
+            }
+            if (!match) continue;
+
+            const bills = billsIndex[cd.lid];
+            if (!bills) continue;
+
+            if (!bills[prefix + 'cd']) bills[prefix + 'cd'] = [];
+            bills[prefix + 'cd'].push(cd);
+            if (cd.pid) {
+                const pos = bills.pos.find(x => {return x.id === cd.pid});
+                if (pos) {
+                    if (!pos[prefix + 'cd']) pos[prefix + 'cd'] = [];
+                    pos[prefix + 'cd'].push(cd);
+                }
+            }
+        }
+    }
+
+    _loadMergeResult(bills, prefixes, decimal) {
+        const rst = {
+            id: bills.id,
+            tid: bills.tender_id,
+            b_code: bills.b_code,
+            name: bills.name,
+            unit: bills.unit,
+            unit_price: bills.unit_price
+        };
+        if (bills.pos && bills.pos.length > 0) {
+            for (const p of bills.pos) {
+                let gather = false;
+                if (p.pre_cd && p.pre_cd.length > 0) gather = true;
+                for (const prefix of prefixes) {
+                    if (p[prefix + 'cd'] && p[prefix + 'cd'].length > 0) gather = true;
+                }
+                if (gather) {
+                    rst.qc_qty = this.ctx.helper.add(rst.qc_qty, p.qc_qty);
+                    rst.pre_qc_qty = this.ctx.helper.add(rst.pre_qc_qty, p.pre_qc_qty);
+                    rst.end_qc_qty = this.ctx.helper.add(rst.end_qc_qty, p.end_qc_qty);
+                    for (const prefix of prefixes) {
+                        rst[prefix + 'qc_qty'] = this.ctx.helper.add(rst[prefix + 'qc_qty'], p[prefix + 'qc_qty']);
+                    }
+                }
+            }
+            rst.qc_tp = this.ctx.helper.mul(rst.unit_price, rst.qc_qty, decimal.tp);
+            rst.pre_qc_tp = this.ctx.helper.mul(rst.unit_price, rst.pre_qc_qty, decimal.tp);
+            rst.end_qc_tp = this.ctx.helper.add(rst.pre_qc_tp, rst.qc_tp);
+            for (const prefix of prefixes) {
+                rst[prefix + 'qc_tp'] = this.ctx.helper.mul(rst.unit_price, rst[prefix + 'qc_qty'], decimal.tp);
+            }
+        } else {
+            rst.qc_qty = bills.qc_qty;
+            rst.qc_tp = bills.qc_tp;
+            rst.pre_qc_qty = bills.pre_qc_qty;
+            rst.pre_qc_tp = bills.pre_qc_tp;
+            rst.end_qc_qty = bills.end_qc_qty;
+            rst.end_qc_tp = bills.end_qc_tp;
+            for (const prefix of prefixes) {
+                rst[prefix + 'qc_qty'] = bills[prefix + 'qc_qty'];
+                rst[prefix + 'qc_tp'] = bills[prefix + 'qc_tp'];
+            }
+        }
+        this.result.push(rst);
+    }
+
+    _loadResult(bills, prefixes, decimal) {
+        if (bills.pos) {
+            for (const p of bills.pos) {
+                let load = false;
+                if (p.pre_cd && p.pre_cd.length > 0) load = true;
+                for (const prefix of prefixes) {
+                    if (p[prefix + 'cd'] && p[prefix + 'cd'].length > 0) load = true;
+                }
+                if (!load) continue;
+
+                const rst = {
+                    b_code: bills.b_code,
+                    name: bills.name,
+                    unit: bills.unit,
+                    unit_price: bills.unit_price, 
+                };
+                rst.qc_qty = p.qc_qty;
+                rst.qc_tp = this.ctx.helper.mul(bills.unit_price, p.qc_qty, decimal.tp);
+                rst.pre_qc_qty = p.pre_qc_qty;
+                rst.pre_qc_tp = this.ctx.helper.mul(bills.unit_price, p.pre_qc_qty, decimal.tp);
+                rst.end_qc_qty = p.end_qc_qty;
+                rst.end_qc_tp = this.ctx.helper.add(rst.qc_tp, p.pre_qc_tp);
+                for (const prefix of prefixes) {
+                    rst[prefix + 'qc_qty'] = p[prefix + 'qc_qty'];
+                    rst[prefix + 'qc_tp'] = this.ctx.helper.mul(bills.unit_price, p[prefix + 'qc_qty'], decimal.tp);
+                }
+                this.result.push(rst);
+            }
+        } else {
+            const rst = {
+                b_code: bills.b_code,
+                name: bills.name,
+                unit: bills.unit,
+                unit_price: bills.unit_price, 
+            };
+            rst.qc_qty = bills.qc_qty;
+            rst.qc_tp = bills.qc_tp;
+            rst.pre_qc_qty = bills.pre_qc_qty;
+            rst.pre_qc_tp = bills.pre_qc_tp;
+            rst.end_qc_qty = bills.end_qc_qty;
+            rst.end_qc_tp = bills.end_qc_tp;
+            for (const prefix of prefixes) {
+                rst[prefix + 'qc_qty'] = bills[prefix + 'qc_qty'];
+                rst[prefix + 'qc_tp'] = bills[prefix + 'qc_tp'];
+            }
+            this.result.push(rst);
+        }
+    }
+
+    _generateResult(billsData, gsDefine, decimal) {
+        for (const bills of billsData) {
+            let load = false;
+            if (bills.pre_cd && bills.pre_cd.length > 0) load = true;
+            for (const dc of gsDefine.defaultCompare) {
+                if (bills['r' + dc + '_cd'] && bills['r' + dc + '_cd'].length > 0) load = true;
+            }
+            if (!load) continue;
+            gsDefine.merge ? this._loadMergeResult(bills, this.prefixes, decimal) : this._loadResult(bills, this.prefixes, decimal);
+        }
+    }
+
+    async _loadStageBillsData(tender, stage, gsDefine, auditors) {
+        const helper = this.ctx.helper;
+        // 加载截止上期/本期
+        let billsData = await this.ctx.service.ledger.getData(tender.id);
+        billsData = billsData.filter(x => { return x.b_code && x.is_leaf });
+        const curStage = await this.ctx.service.stageBills.getLastestStageData(tender.id, stage.id);
+        const preStage = stage.order > 1 ? await this.ctx.service.stageBillsFinal.getFinalData(tender, stage.order - 1) : [];
+        const loadData = [
+            { data: curStage, fields: ['qc_qty', 'qc_tp'], prefix: '', relaId: 'lid' },
+            { data: preStage, fields: ['qc_qty', 'qc_tp'], prefix: 'pre_', relaId: 'lid' }
+        ];
+        for (const dc of gsDefine.defaultCompare) {
+            const auditor = auditors[dc];
+            const auditorStage = await this.ctx.service.stageBills.getAuditorStageData(tender.id, stage.id, auditor.times, auditor.order);
+            loadData.push({ data: auditorStage, fields: ['qc_qty', 'qc_tp'], prefix: `r${dc}_`, relaId: 'lid' });
+        }
+        helper.assignRelaData(billsData, loadData);
+        // 计算截止本期
+        billsData.forEach(x => {
+            x.end_qc_qty = helper.add(x.qc_qty, x.pre_qc_qty);
+            x.end_qc_tp = helper.add(x.qc_tp, x.pre_qc_tp);
+        });
+        return billsData;
+    }
+
+    async _loadStagePosData(tender, stage, gsDefine, auditors) {
+        const helper = this.ctx.helper;
+        const posData = await this.ctx.service.pos.getPosData({tid: tender.id});
+        const curStage = await this.ctx.service.stagePos.getLastestStageData2(tender.id, stage.id);
+        const preStage = stage.order > 1 ? await this.ctx.service.stagePosFinal.getFinalData(tender, stage.order - 1) : [];
+        const loadData = [
+            { data: curStage, fields: ['qc_qty'], prefix: '', relaId: 'pid' },
+            { data: preStage, fields: ['qc_qty'], prefix: 'pre_', relaId: 'pid' }
+        ];
+        for (const dc of gsDefine.defaultCompare) {
+            const auditor = auditors[dc];
+            const auditorStage = await this.ctx.service.stagePos.getAuditorStageData2(tender.id, stage.id, auditor.times, auditor.order);
+            loadData.push({ data: auditorStage, fields: ['qc_qty'], prefix: `r${dc}_`, relaId: 'pid' });
+        }
+        helper.assignRelaData(posData, loadData);
+        posData.forEach(x => {
+            x.end_qc_qty = helper.add(x.qc_qty, x.pre_qc_qty);
+        });
+        return posData;
+    }
+
+    async _gatherStageData(tender, stage, gsDefine) {
+        if (!stage) return;
+        const helper = this.ctx.helper;
+
+        await this.ctx.service.stage.doCheckStage(stage);
+
+        const auditors = this.getLastestAuditors(stage.auditors);
+        const user = await this.ctx.service.projectAccount.getDataById(stage.user_id);
+        auditors.unshift({
+            aid: user.id, name: user.name, company: user.company, role: user.role,
+            times: stage.curTimes, order: 0
+        });
+        const billsData = await this._loadStageBillsData(tender, stage, gsDefine, auditors);
+        const posData = await this._loadStagePosData(tender, stage, gsDefine, auditors);
+        // 创建索引
+        const billsIndex = {};
+        for (const b of billsData) {
+            billsIndex[b.id] = b;
+            b.pos = posData.filter(x => { return x.lid === b.id; });
+        }
+
+        // 查询比较人数据
+        this.prefixes = [];
+        const stageChangeDetail = await this.getCurChangeDetailData(tender.id, stage.id);
+        this.ctx.helper.saveBufferFile(JSON.stringify(stageChangeDetail, '', '\t'), this.ctx.app.baseDir + '/temp.json');
+        for (const dc of gsDefine.defaultCompare) {
+            const scd = helper.filterTimesOrderData(stageChangeDetail, ['lid', 'pid', 'cid', 'cbid'], 'stimes', 'sorder', auditors[dc].times, auditors[dc].order);
+            this._loadChangeDetail(billsIndex, scd, gsDefine, `r${dc}_`);
+            this.prefixes.push(`r${dc}_`);
+        }
+        const finalChangeData = await this.getPreChangeDetailData(tender.id, stage.order);
+        this._loadChangeDetail(billsIndex, finalChangeData, gsDefine, 'pre_');
+        this._generateResult(billsData, gsDefine, tender.info.decimal);
+    }
+
+    async _gatherMonthData(tender, month, defaultCompare) {
+        const stages = await this._getValidStages(tender.id);
+        const stage = this.ctx.helper._.find(stages, {s_time: month});
+        await this._gatherStageData(tender, stage, defaultCompare);
+    }
+
+    async _gatherFinalData(tender, defaultCompare) {
+        const stages = await this._getValidStages(tender.id);
+        await this._gatherStageData(tender, stages[0], defaultCompare);
+    }
+
+    async _gatherCheckedFinalData(tender, defaultCompare) {
+        const stages = await this._getCheckedStages(tender.id);
+        await this._gatherStageData(tender, stages[0], defaultCompare);
+    }
+
+    async _gatherIndexData(tender, index, defaultCompare) {
+        const stages = await this._getValidStages(tender.id);
+        const stage = this.ctx.helper._.find(stages, {order: index});
+        await this._gatherStageData(tender, stage, defaultCompare);
+    }
+
+    /**
+     *
+     * @param {Array} memFieldKeys 报表添加的指标字段
+     * @param {object} gsDefine
+     * @param {object} gsCustom
+     * @returns {Promise<Array>}
+     */
+    async gather(memFieldKeys, gsDefine, gsCustom) {
+        if (!gsDefine || !gsDefine.enable) return [];
+        if (!gsCustom || !gsCustom.tenders || gsCustom.tenders.length === 0) return [];
+
+        const gsSetting = JSON.parse(gsDefine.setting);
+        if (!gsSetting.defaultCompare || !gsSetting.match) return[];
+        for (const t of gsCustom.tenders) {
+            const tender = await this.ctx.service.tender.getCheckTender(t.tid);
+
+            switch (gsSetting.type) {
+                case 'month':
+                    await this._gatherMonthData(tender, gsCustom.month, gsSetting);
+                    break;
+                case 'final':
+                    await this._gatherFinalData(tender, gsSetting);
+                    break;
+                case 'checked-final':
+                    await this._gatherCheckedFinalData(tender, gsSetting);
+                    break;
+                case 'stage':
+                    await this._gatherIndexData(tender, gsCustom.stage, gsSetting);
+                    break;
+                default: throw '未知汇总类型';
+            }
+        }
+        const helper = this.ctx.helper;
+        // 排序
+        this.result.sort((x, y) => { return helper.compareCode(x.b_code, y.b_code); });
+        return this.result;
+    }
+
+    async convert(tid, sid, memFieldKeys, option) {
+        if (!option) return [];
+        const setting = JSON.parse(option);
+        if (!setting || !setting.defaultCompare) return [];
+        const tender = await this.ctx.service.tender.getCheckTender(tid);
+        const stage = await this.ctx.service.stage.getDataById(sid);
+        await this._gatherStageData(tender, stage, setting);
+        const helper = this.ctx.helper;
+        // 排序
+        this.result.sort((x, y) => { return helper.compareCode(x.b_code, y.b_code); });
+        return this.result;
+    }
+}
+
+module.exports = {
+    jhHelper,
+};

+ 22 - 0
app/lib/s2b.js

@@ -0,0 +1,22 @@
+'use strict';
+
+/**
+ *
+ *
+ * @author Mai
+ * @date
+ * @version
+ */
+
+class S2b {
+    constructor(ctx) {
+        this.ctx = ctx;
+        this.db = ctx.app.mysql;
+    }
+
+    getS2bProj(pid) {
+        return this.db.get('zh_s2b_proj', { pid });
+    }
+};
+
+module.exports = S2b;

+ 5 - 8
app/lib/stage_im.js

@@ -308,7 +308,6 @@ class StageIm {
         }
         }
     }
     }
 
 
-
     _checkCustomDetail(im) {
     _checkCustomDetail(im) {
         const self = this;
         const self = this;
         const cd = this._.find(this.details, function(d) {
         const cd = this._.find(this.details, function(d) {
@@ -320,11 +319,14 @@ class StageIm {
                 (!im.pid || im.pid === d.pid) &&
                 (!im.pid || im.pid === d.pid) &&
                 (!im.pos_name || im.pos_name === d.pos_name);
                 (!im.pos_name || im.pos_name === d.pos_name);
         });
         });
+        if (im.code === '101-1-b') console.log(im, cd);
         if (cd) {
         if (cd) {
             im.custom_define = cd.custom_define ? cd.custom_define.split(',') : this.imFields;
             im.custom_define = cd.custom_define ? cd.custom_define.split(',') : this.imFields;
             this._.assignInWith(im, cd, function(oV, sV, key) {
             this._.assignInWith(im, cd, function(oV, sV, key) {
-                return im.custom_define.indexOf(key) > -1 && sV !== undefined && sV !== null ? sV : oV;
+                if (key === 'calc_memo') console.log(im.code, sV);
+                return im.custom_define.indexOf(key) > -1 ? sV : oV;
             });
             });
+            if (im.code === '101-1-b') console.log(im.calc_memo);
         }
         }
     }
     }
 
 
@@ -534,7 +536,6 @@ class StageIm {
             im.bw = this._getZlNormalBw(node, peg);
             im.bw = this._getZlNormalBw(node, peg);
             im.xm = node.name;
             im.xm = node.name;
         }
         }
-        this._checkCustomDetail(im);
         im.check = this.ctx.stage.im_gather && node.check;
         im.check = this.ctx.stage.im_gather && node.check;
         this._generateTzGclBillsData(node, im);
         this._generateTzGclBillsData(node, im);
         this.ImData.push(im);
         this.ImData.push(im);
@@ -633,7 +634,6 @@ class StageIm {
             changes: [], gclBills: [],
             changes: [], gclBills: [],
             dwgc: this._getDwgc(peg, node), fbgc: this._getFbgc(peg, node), fxgc: this._getFxgc(peg, node),
             dwgc: this._getDwgc(peg, node), fbgc: this._getFbgc(peg, node), fxgc: this._getFxgc(peg, node),
         };
         };
-        this._checkCustomDetail(im);
         return im;
         return im;
     }
     }
     _getBwBillsPosIm(imArr, node, peg, index, bw, posName) {
     _getBwBillsPosIm(imArr, node, peg, index, bw, posName) {
@@ -651,7 +651,6 @@ class StageIm {
                 changes: [], gclBills: [],
                 changes: [], gclBills: [],
             };
             };
             imArr.push(im);
             imArr.push(im);
-            this._checkCustomDetail(im);
         }
         }
         return im;
         return im;
     }
     }
@@ -828,7 +827,6 @@ class StageIm {
                     im.drawing_code = this._getDrawingCode(p);
                     im.drawing_code = this._getDrawingCode(p);
                 }
                 }
                 nodeImData.push(im);
                 nodeImData.push(im);
-                this._checkCustomDetail(im);
                 this.ImData.push(im);
                 this.ImData.push(im);
             }
             }
             // if (!this.ctx.stage.im_gather || !node.check) {
             // if (!this.ctx.stage.im_gather || !node.check) {
@@ -904,7 +902,6 @@ class StageIm {
                     im.end_contract_tp = this.ctx.helper.mul(im.end_contract_jl, im.unit_price, tp_decimal);
                     im.end_contract_tp = this.ctx.helper.mul(im.end_contract_jl, im.unit_price, tp_decimal);
                     im.end_qc_tp = this.ctx.helper.mul(im.end_qc_jl, im.unit_price, tp_decimal);
                     im.end_qc_tp = this.ctx.helper.mul(im.end_qc_jl, im.unit_price, tp_decimal);
                     im.calc_memo = '本期计量:' + (this.ctx.helper.checkZero(im.jl) ? 0 : im.jl) + ' ' + im.unit;
                     im.calc_memo = '本期计量:' + (this.ctx.helper.checkZero(im.jl) ? 0 : im.jl) + ' ' + im.unit;
-                    this._checkCustomDetail(im);
                     this.ImData.push(im);
                     this.ImData.push(im);
                     if (pp.qc_qty && pp.qc_qty !== 0) {
                     if (pp.qc_qty && pp.qc_qty !== 0) {
                         for (const c of this.changes) {
                         for (const c of this.changes) {
@@ -937,7 +934,6 @@ class StageIm {
                     lIndex: this.billsTree.getNodeSerialNo(node),
                     lIndex: this.billsTree.getNodeSerialNo(node),
                 };
                 };
                 im.calc_memo = '本期计量:' + (this.ctx.helper.checkZero(im.jl) ? 0 : im.jl) + ' ' + im.unit;
                 im.calc_memo = '本期计量:' + (this.ctx.helper.checkZero(im.jl) ? 0 : im.jl) + ' ' + im.unit;
-                this._checkCustomDetail(im);
                 this.ImData.push(im);
                 this.ImData.push(im);
                 if (p.qc_qty && p.qc_qty !== 0) {
                 if (p.qc_qty && p.qc_qty !== 0) {
                     for (const c of this.changes) {
                     for (const c of this.changes) {
@@ -1005,6 +1001,7 @@ class StageIm {
             }
             }
 
 
             this._getCalcMemo(im);
             this._getCalcMemo(im);
+            this._checkCustomDetail(im);
             delete im.leafXmjs;
             delete im.leafXmjs;
             delete im.gclBills;
             delete im.gclBills;
 
 

+ 3 - 2
app/middleware/change_check.js

@@ -32,7 +32,6 @@ module.exports = options => {
             // 读取原报、审核人数据
             // 读取原报、审核人数据
             change.auditors = yield this.service.changeAudit.getListGroupByTimes(change.cid, change.times);
             change.auditors = yield this.service.changeAudit.getListGroupByTimes(change.cid, change.times);
             change.curAuditor = yield this.service.changeAudit.getCurAuditor(change.cid, change.times);
             change.curAuditor = yield this.service.changeAudit.getCurAuditor(change.cid, change.times);
-            console.log(change.curAuditor);
 
 
             if (!change) throw '变更令数据有误';
             if (!change) throw '变更令数据有误';
             // 权限相关
             // 权限相关
@@ -45,8 +44,10 @@ module.exports = options => {
                 if (change.curAuditor) {
                 if (change.curAuditor) {
                     change.readOnly = change.curAuditor.uid !== accountId;
                     change.readOnly = change.curAuditor.uid !== accountId;
                 } else {
                 } else {
-                    change.readOnly = change.status !== status.uncheck && change.status !== status.back;
+                    change.readOnly = change.status !== status.uncheck && change.status !== status.back && change.status !== status.revise;
                 }
                 }
+            } else if (this.tender.isTourist) {
+                change.readOnly = true;
             } else if (auditorIds.indexOf(accountId) !== -1) { // 审批人
             } else if (auditorIds.indexOf(accountId) !== -1) { // 审批人
                 if (change.status === status.uncheck) {
                 if (change.status === status.uncheck) {
                     throw '您无权查看该数据';
                     throw '您无权查看该数据';

+ 12 - 0
app/middleware/material_check.js

@@ -70,6 +70,16 @@ module.exports = options => {
                 } else {
                 } else {
                     material.curOrder = material.curAuditor.aid === accountId ? material.curAuditor.order : material.curAuditor.order - 1;
                     material.curOrder = material.curAuditor.aid === accountId ? material.curAuditor.order : material.curAuditor.order - 1;
                 }
                 }
+                material.filePermission = true;
+            } else if (this.tender.isTourist) {
+                material.curTimes = material.times;
+                if (material.status === status.uncheck || material.status === status.checkNo) {
+                    material.curOrder = 0;
+                } else if (material.status === status.checked) {
+                    material.curOrder = _.max(_.map(material.auditors, 'order'));
+                } else {
+                    material.curOrder = material.curAuditor.order;
+                }
             } else if (auditorIds.indexOf(accountId) !== -1) { // 审批人
             } else if (auditorIds.indexOf(accountId) !== -1) { // 审批人
                 if (material.status === status.uncheck) {
                 if (material.status === status.uncheck) {
                     throw '您无权查看该数据';
                     throw '您无权查看该数据';
@@ -86,6 +96,7 @@ module.exports = options => {
                 } else {
                 } else {
                     material.curOrder = accountId === material.curAuditor.aid ? material.curAuditor.order : material.curAuditor.order - 1;
                     material.curOrder = accountId === material.curAuditor.aid ? material.curAuditor.order : material.curAuditor.order - 1;
                 }
                 }
+                material.filePermission = true;
             } else if (shareIds.indexOf(accountId) !== -1) { // 分享人
             } else if (shareIds.indexOf(accountId) !== -1) { // 分享人
                 if (material.status === status.uncheck) {
                 if (material.status === status.uncheck) {
                     throw '您无权查看该数据';
                     throw '您无权查看该数据';
@@ -93,6 +104,7 @@ module.exports = options => {
                 // material.readOnly = true;
                 // material.readOnly = true;
                 material.curTimes = material.status === status.checkNo ? material.times - 1 : material.times;
                 material.curTimes = material.status === status.checkNo ? material.times - 1 : material.times;
                 material.curOrder = material.status === status.checked ? _.max(_.map(material.auditors, 'order')) : material.curAuditor.order - 1;
                 material.curOrder = material.status === status.checked ? _.max(_.map(material.auditors, 'order')) : material.curAuditor.order - 1;
+                material.filePermission = false;
             } else { // 其他不可见
             } else { // 其他不可见
                 throw '您无权查看该数据';
                 throw '您无权查看该数据';
             }
             }

+ 18 - 1
app/middleware/stage_check.js

@@ -59,7 +59,7 @@ module.exports = options => {
                 return item.s_order.split(',').indexOf(stage.highOrder.toString()) !== -1;
                 return item.s_order.split(',').indexOf(stage.highOrder.toString()) !== -1;
             });
             });
             // 权限相关
             // 权限相关
-            // todo 校验权限 (标段参与人、分享)
+            // todo 校验权限 (标段参与人、分享、游客
             const accountId = this.session.sessionUser.accountId,
             const accountId = this.session.sessionUser.accountId,
                 auditorIds = _.map(stage.auditors, 'aid'),
                 auditorIds = _.map(stage.auditors, 'aid'),
                 shareIds = [];
                 shareIds = [];
@@ -79,6 +79,21 @@ module.exports = options => {
                 } else {
                 } else {
                     stage.curOrder = stage.curAuditor.aid === accountId ? stage.curAuditor.order : stage.curAuditor.order - 1;
                     stage.curOrder = stage.curAuditor.aid === accountId ? stage.curAuditor.order : stage.curAuditor.order - 1;
                 }
                 }
+                stage.filePermission = true;
+            } else if (this.tender.isTourist) {
+                if (auditorIds.indexOf(accountId) !== -1) {
+                    stage.readOnly = (stage.status !== status.checking && stage.status !== status.checkNoPre) || accountId !== stage.curAuditor.aid;
+                } else {
+                    stage.readOnly = true;
+                }
+                stage.curTimes = stage.times;
+                if (stage.status === status.uncheck || stage.status === status.checkNo) {
+                    stage.curOrder = 0;
+                } else if (stage.status === status.checked) {
+                    stage.curOrder = _.max(_.map(stage.auditors, 'order'));
+                } else {
+                    stage.curOrder = stage.curAuditor.order;
+                }
             } else if (auditorIds.indexOf(accountId) !== -1) { // 审批人
             } else if (auditorIds.indexOf(accountId) !== -1) { // 审批人
                 if (stage.status === status.uncheck) {
                 if (stage.status === status.uncheck) {
                     throw '您无权查看该数据';
                     throw '您无权查看该数据';
@@ -95,6 +110,7 @@ module.exports = options => {
                     stage.curOrder = accountId === stage.curAuditor.aid ? stage.curAuditor.order : stage.curAuditor.order - 1;
                     stage.curOrder = accountId === stage.curAuditor.aid ? stage.curAuditor.order : stage.curAuditor.order - 1;
                 }
                 }
                 stage.readOnly = (stage.status !== status.checking && stage.status !== status.checkNoPre) || accountId !== stage.curAuditor.aid;
                 stage.readOnly = (stage.status !== status.checking && stage.status !== status.checkNoPre) || accountId !== stage.curAuditor.aid;
+                stage.filePermission = true;
             } else if (shareIds.indexOf(accountId) !== -1 || (permission !== null && permission.tender !== undefined && permission.tender.indexOf('2') !== -1)) { // 分享人
             } else if (shareIds.indexOf(accountId) !== -1 || (permission !== null && permission.tender !== undefined && permission.tender.indexOf('2') !== -1)) { // 分享人
                 if (stage.status === status.uncheck) {
                 if (stage.status === status.uncheck) {
                     throw '您无权查看该数据';
                     throw '您无权查看该数据';
@@ -109,6 +125,7 @@ module.exports = options => {
                 } else {
                 } else {
                     stage.curOrder = stage.status === status.checked ? _.max(_.map(stage.auditors, 'order')) : stage.curAuditor.order - 1;
                     stage.curOrder = stage.status === status.checked ? _.max(_.map(stage.auditors, 'order')) : stage.curAuditor.order - 1;
                 }
                 }
+                stage.filePermission = false;
             } else { // 其他不可见
             } else { // 其他不可见
                 throw '您无权查看该数据';
                 throw '您无权查看该数据';
             }
             }

+ 11 - 2
app/middleware/tender_check.js

@@ -61,11 +61,14 @@ module.exports = options => {
             const materialAuditors = yield this.service.materialAudit.getAllAuditors(tender.id);
             const materialAuditors = yield this.service.materialAudit.getAllAuditors(tender.id);
             const materialAuditorsId = this.helper._.map(materialAuditors, 'aid');
             const materialAuditorsId = this.helper._.map(materialAuditors, 'aid');
             const tenderPermission = this.session.sessionUser.permission ? this.session.sessionUser.permission.tender : null;
             const tenderPermission = this.session.sessionUser.permission ? this.session.sessionUser.permission.tender : null;
+            const isTenderTourist = yield this.service.tenderTourist.getDataByCondition({ tid: tender.id, user_id: accountId });
+            // 判断访问人是否具有游客身份
+            tender.isTourist = isTenderTourist !== null;
             if (auditorsId.indexOf(accountId) === -1 && tender.data.user_id !== accountId &&
             if (auditorsId.indexOf(accountId) === -1 && tender.data.user_id !== accountId &&
                 (tenderPermission === null || tenderPermission === undefined || tenderPermission.indexOf('2') === -1) &&
                 (tenderPermission === null || tenderPermission === undefined || tenderPermission.indexOf('2') === -1) &&
                 stageAuditorsId.indexOf(accountId) === -1 && changeAuditorsId.indexOf(accountId) === -1 &&
                 stageAuditorsId.indexOf(accountId) === -1 && changeAuditorsId.indexOf(accountId) === -1 &&
                 reviseAuditorsId.indexOf(accountId) === -1 && materialAuditorsId.indexOf(accountId) === -1 &&
                 reviseAuditorsId.indexOf(accountId) === -1 && materialAuditorsId.indexOf(accountId) === -1 &&
-                advanceAuditorsId.indexOf(accountId) === -1 && !this.session.sessionUser.is_admin) {
+                advanceAuditorsId.indexOf(accountId) === -1 && !this.session.sessionUser.is_admin && !isTenderTourist) {
                 throw '您无权查看该项目';
                 throw '您无权查看该项目';
             }
             }
 
 
@@ -82,7 +85,13 @@ module.exports = options => {
             } else {
             } else {
                 const scheduleUser = yield this.service.scheduleAudit.getDataByCondition({ tid: tender.id, audit_id: this.session.sessionUser.accountId });
                 const scheduleUser = yield this.service.scheduleAudit.getDataByCondition({ tid: tender.id, audit_id: this.session.sessionUser.accountId });
                 if (scheduleUser) {
                 if (scheduleUser) {
-                    schedule_permission = scheduleUser.permission;
+                    if (tender.isTourist && scheduleUser.permission === scPermission.no) {
+                        schedule_permission = scPermission.show;
+                    } else {
+                        schedule_permission = scheduleUser.permission;
+                    }
+                } else if (tender.isTourist) {
+                    schedule_permission = scPermission.show;
                 }
                 }
             }
             }
             tender.schedule_permission = schedule_permission;
             tender.schedule_permission = schedule_permission;

+ 2 - 2
app/middleware/uncheck_tender_check.js

@@ -23,9 +23,9 @@ module.exports = options => {
     return function* uncheckTenderCheck(next) {
     return function* uncheckTenderCheck(next) {
         try {
         try {
             if (this.tender.data.ledger_status === auditConst.status.uncheck) {
             if (this.tender.data.ledger_status === auditConst.status.uncheck) {
-                if (this.tender.data.user_id !== this.session.sessionUser.accountId && !this.session.sessionUser.is_admin && this.tender.advanceAuditorsId.indexOf(this.session.sessionUser.accountId) === -1) {
+                if (this.tender.data.user_id !== this.session.sessionUser.accountId && !this.session.sessionUser.is_admin && this.tender.advanceAuditorsId.indexOf(this.session.sessionUser.accountId) === -1 && !this.tender.isTourist) {
                     throw '您无权查看该项目';
                     throw '您无权查看该项目';
-                } else if (this.tender.advanceAuditorsId.indexOf(this.session.sessionUser.accountId) !== -1 && !this.session.sessionUser.is_admin) {
+                } else if (this.tender.advanceAuditorsId.indexOf(this.session.sessionUser.accountId) !== -1 && !this.session.sessionUser.is_admin && !this.tender.isTourist) {
                     throw '您无权查看该内容';
                     throw '您无权查看该内容';
                 }
                 }
             }
             }

二进制
app/public/css/ztree/img/diy/10.png


二进制
app/public/images/wechat.png


+ 12 - 0
app/public/js/advance.js

@@ -213,6 +213,18 @@ $(document).ready(function () {
             $('#audit-list').append(historyHTML)
             $('#audit-list').append(historyHTML)
         })
         })
     })
     })
+
+    $('a[data-target="#del-qi"').on('click', function () {
+      const id = $(this).data('id')
+      const order = $(this).data('order') //期数
+      $('#del-qi').find('.modal-body').children()[0].innerText = `确认删除「第${order}期」?`
+      $('#del-qi #del-confirm').click(function() {
+        postData(`${window.location.pathname}/delete`, {id}, (res) => {
+          console.log(res);
+          window.location.reload()
+        })
+      })
+    })
     function formatMoney(s, dot = ',', decimal = 2) {
     function formatMoney(s, dot = ',', decimal = 2) {
         if (!s) {
         if (!s) {
             s = 0;
             s = 0;

+ 21 - 13
app/public/js/advance_audit.js

@@ -11,6 +11,7 @@
 $(document).ready(function () {
 $(document).ready(function () {
     autoFlashHeight()
     autoFlashHeight()
 
 
+    const decimal = 2
     let oldVal = null
     let oldVal = null
     let timer = null
     let timer = null
     let oldSearchVal = null
     let oldSearchVal = null
@@ -217,6 +218,7 @@ $(document).ready(function () {
         const type = parseInt($(this).data('type'))
         const type = parseInt($(this).data('type'))
         let pay_ratio = null
         let pay_ratio = null
         let cur_amount = null
         let cur_amount = null
+        let isLimitMax = false  // 用来判断是否达到最大
         if (val < min) {
         if (val < min) {
             // 限制最小值为min
             // 限制最小值为min
             $(this).val(min)
             $(this).val(min)
@@ -229,32 +231,38 @@ $(document).ready(function () {
         }
         }
         // 本期金额转化
         // 本期金额转化
         if (type === 1) {
         if (type === 1) {
-            if (val > re_amount) {
+            if (val >= re_amount) {
                 // 限制不能超过最大值
                 // 限制不能超过最大值
                 val = re_amount
                 val = re_amount
+                isLimitMax = true
             }
             }
-            $(this).val(fixedToSub(val, decimal)) // 重新赋值限制只有两位小数
+            
+            $(this).val(isLimitMax ? val : fixedToSub(val, decimal)) // 重新赋值限制只有两位小数
             const pay_a_input = $(`.pay-input[data-type=${reverse(type)}]`)
             const pay_a_input = $(`.pay-input[data-type=${reverse(type)}]`)
-            pay_ratio = parseFloat(ZhCalc.mul(ZhCalc.div(val, advancePayTotal), 100).toFixed(2))
-            cur_amount = ZhCalc.round(val, decimal)
+            pay_ratio = ZhCalc.mul(ZhCalc.div(val, advancePayTotal), 100).toFixed(2)
+            cur_amount = isLimitMax ? re_amount : ZhCalc.round(val, decimal)
             pay_a_input.val(pay_ratio)
             pay_a_input.val(pay_ratio)
-            const total = parseFloat(ZhCalc.add(cur_amount, p_amount)).toString().split('.')[1]
+            const total = ZhCalc.add(cur_amount, p_amount).toString().split('.')[1] || ''
             // 截止本期金额文案更新
             // 截止本期金额文案更新
-            $('#p_total2').text(formatMoney(ZhCalc.add(cur_amount, p_amount), ',', total && total.length || 0) + '元')
+            $('#p_total2').text(formatMoney(ZhCalc.add(cur_amount, p_amount), ',', isLimitMax ? total.length : 2) + '元')
         } else {
         } else {
+
             // 支付比例转化
             // 支付比例转化
             val = fixedToSub(val)
             val = fixedToSub(val)
-            if (val.toFixed(1) === max.toFixed(1)) {
-                val = max
+            if (val.toFixed(2) === max.toFixed(2)) {
+              // 比例达到最大,特殊处理金额的显示小数点
+                val = fixedToSub(max, 2);
+                isLimitMax = true
             }
             }
+
             $(this).val(val) // 重新赋值限制只有两位小数
             $(this).val(val) // 重新赋值限制只有两位小数
             const cur_m_input = $(`.pay-input[data-type=${reverse(type)}]`)
             const cur_m_input = $(`.pay-input[data-type=${reverse(type)}]`)
-            cur_amount = ZhCalc.round(ZhCalc.mul(advancePayTotal, ZhCalc.div(val, 100)), decimal)
+            cur_amount = isLimitMax ? re_amount : ZhCalc.round(ZhCalc.mul(advancePayTotal, ZhCalc.div(val, 100), 10), decimal)
             pay_ratio = val
             pay_ratio = val
-            cur_m_input.val(parseFloat(cur_amount.toFixed(decimal)))
-            const total = parseFloat(ZhCalc.add(cur_amount, p_amount)).toString().split('.')[1]
+            cur_m_input.val(cur_amount)
+            const total = ZhCalc.add(cur_amount, p_amount).toString().split('.')[1] || ''
             // 截止本期金额文案更新
             // 截止本期金额文案更新
-            $('#p_total2').text(formatMoney(ZhCalc.add(cur_amount, p_amount), ',', total && total.length || 0) + '元')
+            $('#p_total2').text(formatMoney(ZhCalc.add(cur_amount, p_amount), ',', isLimitMax ? total.length : 2) + '元')
         }
         }
         const data = {
         const data = {
             pay_ratio,
             pay_ratio,
@@ -353,7 +361,7 @@ $(document).ready(function () {
             }
             }
             return {...file, showDel}
             return {...file, showDel}
         })
         })
-        let html = `<tr><td colspan="3"><a href="#addfujian" data-toggle="modal" class="btn btn-sm btn-light text-primary" data-placement="bottom" title="" data-original-title="添加清单"><i class="fa fa-cloud-upload" aria-hidden="true"></i> 上传附件</a></td></tr>`
+        let html = advance.filePermission ? `<tr><td colspan="3"><a href="#addfujian" data-toggle="modal" class="btn btn-sm btn-light text-primary" data-placement="bottom" title="" data-original-title="添加清单"><i class="fa fa-cloud-upload" aria-hidden="true"></i> 上传附件</a></td></tr>` : '';
         newFiles.forEach((file, idx) => {
         newFiles.forEach((file, idx) => {
             if (file.showDel) {
             if (file.showDel) {
                 html += `<tr><td width="70">${idx + 1}</td><td><a href="/${file.filepath}" target="_blank">${file.filename}</a></td><td width="90"><a href="javascript: void(0);" class="text-danger file-del" data-id="${file.id}">移除</a></td></tr>`
                 html += `<tr><td width="70">${idx + 1}</td><td><a href="/${file.filepath}" target="_blank">${file.filename}</a></td><td width="90"><a href="javascript: void(0);" class="text-danger file-del" data-id="${file.id}">移除</a></td></tr>`

+ 42 - 0
app/public/js/change.js

@@ -274,6 +274,11 @@ $(document).ready(() => {
        if (status !== 0) {
        if (status !== 0) {
            url += '/status/'+ status;
            url += '/status/'+ status;
        }
        }
+       let orderSetting = getLocalCache('change-'+ $('#tenderId').val() +'-list-order');
+       if (orderSetting) {
+           const orders = orderSetting.split('|');
+           url += '?sort=' + orders[0] + '&order=' + orders[1];
+       }
        window.location.href = url;
        window.location.href = url;
     });
     });
     // 不再显示首次使用
     // 不再显示首次使用
@@ -290,4 +295,41 @@ $(document).ready(() => {
     $('.delete-cid-modal').on('click', function () {
     $('.delete-cid-modal').on('click', function () {
         $('#delete-cid').val($(this).attr('cid'));
         $('#delete-cid').val($(this).attr('cid'));
     });
     });
+
+    // 排序初始化
+    let orderSetting = getLocalCache('change-'+ $('#tenderId').val() +'-list-order');
+    if (!orderSetting) orderSetting = 'time|desc';
+    const orders = orderSetting.split('|');
+    $("#sort-radio input[value='"+ orders[0] +"']").prop('checked', true);
+    $("#order-radio input[value='"+ orders[1] +"']").prop('checked', true);
+    if (orders[0] === 'time') {
+        $('#bpaixu').text('排序:创建时间');
+    } else {
+        $('#bpaixu').text('排序:变更令号');
+    }
+    // let sortSetting = getLocalCache('change-'+ $('#tenderId').val() +'-list-sort');
+    // if (sortSetting && parseInt(sortSetting) === 1) {
+    //     $('#bpaixu').click();
+    // }
+    // $('#sort-dropdown').on('shown.bs.dropdown', function () {
+    //     setLocalCache('change-'+ $('#tenderId').val() +'-list-sort', 1);
+    // });
+    // $('#sort-dropdown').on('hidden.bs.dropdown', function () {
+    //     setLocalCache('change-'+ $('#tenderId').val() +'-list-sort', 0);
+    // });
+
+    $('#sort-radio input[name="paizhi"]').click(function () {
+        const orderStr = $(this).val() + '|' + $('#order-radio input[name="paixu"]:checked').val();
+        setLocalCache('change-'+ $('#tenderId').val() +'-list-order', orderStr);
+        // setLocalCache('change-'+ $('#tenderId').val() +'-list-sort', 1);
+        const link = window.location.origin + window.location.pathname + '?sort='+ $(this).val() + '&order=' + $('#order-radio input[name="paixu"]:checked').val();
+        window.location.href = link;
+    });
+    $('#order-radio input[name="paixu"]').click(function () {
+        const orderStr = $('#sort-radio input[name="paizhi"]:checked').val() + '|' + $(this).val();
+        setLocalCache('change-'+ $('#tenderId').val() +'-list-order', orderStr);
+        // setLocalCache('change-'+ $('#tenderId').val() +'-list-sort', 1);
+        const link = window.location.origin + window.location.pathname + '?sort='+ $('#sort-radio input[name="paizhi"]:checked').val() + '&order=' + $(this).val();
+        window.location.href = link;
+    })
 });
 });

+ 40 - 19
app/public/js/change_information_set.js

@@ -86,12 +86,12 @@ $.event.special.valuechange = {
 $(document).ready(() => {
 $(document).ready(() => {
     const changeSpreadSetting = {
     const changeSpreadSetting = {
         cols: [
         cols: [
-            {title: '清单编号', colSpan: '1', rowSpan: '2', field: 'code', hAlign: 0, width: 80, formatter: '@', readOnly: 'readOnly.isEdit'},
-            {title: '名称', colSpan: '1', rowSpan: '2', field: 'name', hAlign: 0, width: 120, formatter: '@', readOnly: 'readOnly.isEdit'},
+            {title: '清单编号', colSpan: '1', rowSpan: '2', field: 'code', hAlign: 0, width: 80, formatter: '@', readOnly: 'readOnly.isEdit2'},
+            {title: '名称', colSpan: '1', rowSpan: '2', field: 'name', hAlign: 0, width: 120, formatter: '@', readOnly: 'readOnly.isEdit2'},
             {title: '变更部位', colSpan: '1', rowSpan: '2', field: 'bwmx', hAlign: 0, width: 120, formatter: '@', readOnly: 'readOnly.isEdit'},
             {title: '变更部位', colSpan: '1', rowSpan: '2', field: 'bwmx', hAlign: 0, width: 120, formatter: '@', readOnly: 'readOnly.isEdit'},
             {title: '变更详情', colSpan: '1', rowSpan: '2', field: 'detail', hAlign: 0, width: 120, formatter: '@', readOnly: false},
             {title: '变更详情', colSpan: '1', rowSpan: '2', field: 'detail', hAlign: 0, width: 120, formatter: '@', readOnly: false},
-            {title: '单位', colSpan: '1', rowSpan: '2', field: 'unit', hAlign: 1, width: 60, formatter: '@', readOnly: 'readOnly.isEdit', cellType: 'unit', comboItems: changeUnits, comboEdit: true},
-            {title: '单价', colSpan: '1', rowSpan: '2', field: 'unit_price', hAlign: 2, width: 60, type: 'Number', readOnly: 'readOnly.isEdit', getValue: 'getValue.unit_price'},
+            {title: '单位', colSpan: '1', rowSpan: '2', field: 'unit', hAlign: 1, width: 60, formatter: '@', readOnly: 'readOnly.isEdit2', cellType: 'unit', comboItems: changeUnits, comboEdit: true},
+            {title: '单价', colSpan: '1', rowSpan: '2', field: 'unit_price', hAlign: 2, width: 60, type: 'Number', readOnly: 'readOnly.isEdit2', getValue: 'getValue.unit_price'},
             {title: '原设计|数量', colSpan: '2|1', rowSpan: '1|1', field: 'oamount', hAlign: 2, width: 60, type: 'Number', readOnly: 'readOnly.isEdit', getValue: 'getValue.oamount'},
             {title: '原设计|数量', colSpan: '2|1', rowSpan: '1|1', field: 'oamount', hAlign: 2, width: 60, type: 'Number', readOnly: 'readOnly.isEdit', getValue: 'getValue.oamount'},
             {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'oa_tp', hAlign: 2, width: 80, type: 'Number', readOnly: true, getValue: 'getValue.oa_tp'},
             {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'oa_tp', hAlign: 2, width: 80, type: 'Number', readOnly: true, getValue: 'getValue.oa_tp'},
             {title: '申请变更增(+)减(-)|数量', colSpan: '2|1', rowSpan: '1|1', field: 'camount', hAlign: 2, width: 60, type: 'Number', readOnly: false, getValue: 'getValue.camount'},
             {title: '申请变更增(+)减(-)|数量', colSpan: '2|1', rowSpan: '1|1', field: 'camount', hAlign: 2, width: 60, type: 'Number', readOnly: false, getValue: 'getValue.camount'},
@@ -143,13 +143,16 @@ $(document).ready(() => {
                 return ZhCalc.round(data.camount, findDecimal(data.unit));
                 return ZhCalc.round(data.camount, findDecimal(data.unit));
             },
             },
             del_list: function (data) {
             del_list: function (data) {
-                return '移除';
+                return !_.find(changeUsedData, { id: data.id }) ? '移除' : '';
             }
             }
         },
         },
         readOnly: {
         readOnly: {
             isEdit: function (data) {
             isEdit: function (data) {
                 return !readOnly && data.lid != 0;
                 return !readOnly && data.lid != 0;
             },
             },
+            isEdit2: function (data) {
+                return !readOnly && (data.lid != 0 || (data.lid == 0 && _.find(changeUsedData, { id: data.id })));
+            },
         },
         },
     };
     };
 
 
@@ -197,7 +200,7 @@ $(document).ready(() => {
         del: function () {
         del: function () {
             const select = SpreadJsObj.getSelectObject(changeSpreadSheet);
             const select = SpreadJsObj.getSelectObject(changeSpreadSheet);
             const index = changeList.indexOf(select);
             const index = changeList.indexOf(select);
-            if (index > -1) {
+            if (index > -1 && !_.find(changeUsedData, { id: select.id })) {
                 postData(window.location.pathname + '/save', {type: 'del', id: select.id}, function (result) {
                 postData(window.location.pathname + '/save', {type: 'del', id: select.id}, function (result) {
                     changeList.splice(index, 1);
                     changeList.splice(index, 1);
                     changeSpreadSheet.deleteRows(index, 1);
                     changeSpreadSheet.deleteRows(index, 1);
@@ -226,7 +229,7 @@ $(document).ready(() => {
             const sel = info.sheet.getSelections()[0];
             const sel = info.sheet.getSelections()[0];
             const col = info.sheet.zh_setting.cols[sel.col];
             const col = info.sheet.zh_setting.cols[sel.col];
             const data = SpreadJsObj.getSelectObject(info.sheet);
             const data = SpreadJsObj.getSelectObject(info.sheet);
-            if (col && col.field === 'del_list') {
+            if (col && col.field === 'del_list' && !_.find(changeUsedData, { id: data.id })) {
                 changeSpreadObj.del();
                 changeSpreadObj.del();
             }
             }
             changeSpreadObj.resetXmjSpread(data);
             changeSpreadObj.resetXmjSpread(data);
@@ -265,10 +268,17 @@ $(document).ready(() => {
                     select.camount = ZhCalc.round(select.camount, findDecimal(validText)) || 0;
                     select.camount = ZhCalc.round(select.camount, findDecimal(validText)) || 0;
                     select.oamount = ZhCalc.round(select.oamount, findDecimal(validText)) || 0;
                     select.oamount = ZhCalc.round(select.oamount, findDecimal(validText)) || 0;
                 }
                 }
-                select[col.field] = validText;
                 if(col.field === 'camount') {
                 if(col.field === 'camount') {
-                    select.spamount = ZhCalc.round(select.camount, findDecimal(select.unit)) || 0;
+                    // 判断是否大于等于限制值,否则无法更改
+                    const usedInfo = _.find(changeUsedData, { id: select.id });
+                    if (usedInfo && validText < usedInfo.used_qty) {
+                        toastr.error('清单变更数值必须大于等于已调用值');
+                        SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                        return;
+                    }
+                    select.spamount = ZhCalc.round(validText, findDecimal(select.unit)) || 0;
                 }
                 }
+                select[col.field] = validText;
                 console.log(select);
                 console.log(select);
                 delete select.waitingLoading;
                 delete select.waitingLoading;
 
 
@@ -560,14 +570,14 @@ $(document).ready(() => {
                     name: '删除',
                     name: '删除',
                     icon: 'fa-remove',
                     icon: 'fa-remove',
                     callback: function (key, opt) {
                     callback: function (key, opt) {
-                        changeSpreadObj.del(changeSpreadSheet);
+                        // changeSpreadObj.del(changeSpreadSheet);
                     },
                     },
                     disabled: function (key, opt) {
                     disabled: function (key, opt) {
                         const select = SpreadJsObj.getSelectObject(changeSpreadSheet);
                         const select = SpreadJsObj.getSelectObject(changeSpreadSheet);
                         const sel = changeSpreadSheet.getSelections()[0];
                         const sel = changeSpreadSheet.getSelections()[0];
                         changeSpreadObj.resetXmjSpread(select);
                         changeSpreadObj.resetXmjSpread(select);
                         console.log(select, sel);
                         console.log(select, sel);
-                        if (!readOnly && select && sel.row !== changeSpreadSheet.getRowCount() - 1) {
+                        if (!readOnly && select && sel.row !== changeSpreadSheet.getRowCount() - 1 && !_.find(changeUsedData, { id: select.id })) {
                             return false;
                             return false;
                         } else {
                         } else {
                             return true;
                             return true;
@@ -598,13 +608,15 @@ $(document).ready(() => {
             for (const leaf of gcl.leafXmjs) {
             for (const leaf of gcl.leafXmjs) {
                 const quantity = leaf.quantity !== undefined && leaf.quantity !== null ? leaf.quantity : 0;
                 const quantity = leaf.quantity !== undefined && leaf.quantity !== null ? leaf.quantity : 0;
                 const gcl_id = leaf.gcl_id ? leaf.gcl_id : '';
                 const gcl_id = leaf.gcl_id ? leaf.gcl_id : '';
-                const bwmx = leaf.bwmx !== undefined ? leaf.bwmx : '';
+                const bwmx = leaf.bwmx !== undefined ? leaf.bwmx : (gcl.leafXmjs.length > 1 && gcl.name ? gcl.name : '');
                 const isChecked = data_bwmx.indexOf(
                 const isChecked = data_bwmx.indexOf(
                     leaf.code + '!_!' + (leaf.jldy ? leaf.jldy : '') + '!_!' +
                     leaf.code + '!_!' + (leaf.jldy ? leaf.jldy : '') + '!_!' +
                     (leaf.dwgc ? leaf.dwgc : '') + '!_!' + (leaf.fbgc ? leaf.fbgc : '') + '!_!' + (leaf.fxgc ? leaf.fxgc : '')
                     (leaf.dwgc ? leaf.dwgc : '') + '!_!' + (leaf.fbgc ? leaf.fbgc : '') + '!_!' + (leaf.fxgc ? leaf.fxgc : '')
                     + '!_!' + (leaf.gcl_id ? leaf.gcl_id : '0') + '!_!' +
                     + '!_!' + (leaf.gcl_id ? leaf.gcl_id : '0') + '!_!' +
                     (bwmx !== '' ? bwmx : leaf.jldy ? leaf.jldy : '') + '*;*' + quantity) !== -1 && isCheck ?
                     (bwmx !== '' ? bwmx : leaf.jldy ? leaf.jldy : '') + '*;*' + quantity) !== -1 && isCheck ?
                     'checked' : '';
                     'checked' : '';
+                const isUsed = _.find(changeUsedData, { gcl_id: leaf.gcl_id, bwmx: bwmx, oamount: leaf.quantity });
+                const isDisabled = isUsed ? 'disabled ' : '';
                 codeHtml += '<tr quantity="' + quantity + '" gcl_id="' + gcl_id + '"><td>' + leaf.code + '</td>' +
                 codeHtml += '<tr quantity="' + quantity + '" gcl_id="' + gcl_id + '"><td>' + leaf.code + '</td>' +
                     '<td>' + (leaf.jldy ? leaf.jldy: '') + '</td>' +
                     '<td>' + (leaf.jldy ? leaf.jldy: '') + '</td>' +
                     '<td>' + (leaf.dwgc ? leaf.dwgc : '') + '</td>' +
                     '<td>' + (leaf.dwgc ? leaf.dwgc : '') + '</td>' +
@@ -612,7 +624,7 @@ $(document).ready(() => {
                     '<td>' + (leaf.fxgc ? leaf.fxgc : '') + '</td>' +
                     '<td>' + (leaf.fxgc ? leaf.fxgc : '') + '</td>' +
                     '<td>' + bwmx + '</td>' +
                     '<td>' + bwmx + '</td>' +
                     '<td class="text-right">' + (ZhCalc.round(quantity, findDecimal(gcl.unit)) ? ZhCalc.round(quantity, findDecimal(gcl.unit)) : 0) + '</td>' +
                     '<td class="text-right">' + (ZhCalc.round(quantity, findDecimal(gcl.unit)) ? ZhCalc.round(quantity, findDecimal(gcl.unit)) : 0) + '</td>' +
-                    '<td class="text-center"><input type="checkbox"' + isChecked +
+                    '<td class="text-center"><input type="checkbox" ' + isDisabled + isChecked +
                     '></td></tr>';
                     '></td></tr>';
             }
             }
         } else if (!isDeal && isCheck) {
         } else if (!isDeal && isCheck) {
@@ -686,7 +698,8 @@ $(document).ready(() => {
         const newLedgerList = remakeChangeSpread();
         const newLedgerList = remakeChangeSpread();
         // 更新至服务器
         // 更新至服务器
         postData(window.location.pathname + '/save', { type:'ledger_list', updateData: newLedgerList }, function (result) {
         postData(window.location.pathname + '/save', { type:'ledger_list', updateData: newLedgerList }, function (result) {
-            changeList = result;
+            changeList = result.changeList;
+            changeUsedData = result.usedList;
             SpreadJsObj.loadSheetData(changeSpreadSheet, SpreadJsObj.DataType.Data, changeList);
             SpreadJsObj.loadSheetData(changeSpreadSheet, SpreadJsObj.DataType.Data, changeList);
             changeSpreadObj.makeSjsFooter();
             changeSpreadObj.makeSjsFooter();
             const select = SpreadJsObj.getSelectObject(changeSpreadSheet);
             const select = SpreadJsObj.getSelectObject(changeSpreadSheet);
@@ -898,7 +911,11 @@ function tableDataRemake(changeListData) {
                     let pushbwmx = '0*;*0';
                     let pushbwmx = '0*;*0';
                     if (listinfo.leafXmjs !== undefined) {
                     if (listinfo.leafXmjs !== undefined) {
                         const leafInfo = listinfo.leafXmjs.find(function (item) {
                         const leafInfo = listinfo.leafXmjs.find(function (item) {
-                            return (item.bwmx === undefined || item.bwmx === clinfo.bwmx) && item.code === clinfo.xmj_code && (item.quantity !== null ? item.quantity === parseFloat(clinfo.oamount) : 0 === parseFloat(clinfo.oamount));
+                            const flag = (item.bwmx === undefined || item.bwmx === clinfo.bwmx || item.jldy === clinfo.bwmx) && item.gcl_id === clinfo.gcl_id && (item.quantity !== null ? item.quantity === parseFloat(clinfo.oamount) : 0 === parseFloat(clinfo.oamount));
+                            if (flag && item.code === clinfo.xmj_code) {
+                                return flag && item.code === clinfo.xmj_code;
+                            }
+                            return flag;
                         });
                         });
                         if (leafInfo) {
                         if (leafInfo) {
                             pushbwmx = leafInfo.code + '!_!' + (leafInfo.jldy !== undefined ? leafInfo.jldy : '') + '!_!' +
                             pushbwmx = leafInfo.code + '!_!' + (leafInfo.jldy !== undefined ? leafInfo.jldy : '') + '!_!' +
@@ -906,7 +923,7 @@ function tableDataRemake(changeListData) {
                                 (leafInfo.fbgc ? leafInfo.fbgc : '') + '!_!' +
                                 (leafInfo.fbgc ? leafInfo.fbgc : '') + '!_!' +
                                 (leafInfo.fxgc ? leafInfo.fxgc : '') + '!_!' +
                                 (leafInfo.fxgc ? leafInfo.fxgc : '') + '!_!' +
                                 (leafInfo.gcl_id ? leafInfo.gcl_id : '') + '!_!' +
                                 (leafInfo.gcl_id ? leafInfo.gcl_id : '') + '!_!' +
-                                (leafInfo.bwmx !== undefined ? leafInfo.bwmx : (leafInfo.jldy !== undefined ? leafInfo.jldy : '')) + '*;*' + (leafInfo.quantity !== null ? leafInfo.quantity : 0);
+                                (leafInfo.bwmx !== undefined ? leafInfo.bwmx : (listinfo.leafXmjs.length > 1 && listinfo.name ? listinfo.name : (leafInfo.jldy !== undefined ? leafInfo.jldy : ''))) + '*;*' + (leafInfo.quantity !== null ? leafInfo.quantity : 0);
                         } else {
                         } else {
                             toastr.warning('台账清单列表已不存在'+ clinfo.code +',已更新变更清单列表');
                             toastr.warning('台账清单列表已不存在'+ clinfo.code +',已更新变更清单列表');
                             // changeList.splice(index, 1);
                             // changeList.splice(index, 1);
@@ -929,7 +946,11 @@ function tableDataRemake(changeListData) {
                     let pushbwmx = '0*;*0';
                     let pushbwmx = '0*;*0';
                     if (listinfo.leafXmjs !== undefined) {
                     if (listinfo.leafXmjs !== undefined) {
                         const leafInfo = listinfo.leafXmjs.find(function (item) {
                         const leafInfo = listinfo.leafXmjs.find(function (item) {
-                            return (item.bwmx === undefined || item.bwmx === clinfo.bwmx || item.jldy === clinfo.bwmx) && item.code === clinfo.xmj_code && (item.quantity !== null ? item.quantity === parseFloat(clinfo.oamount) : 0 === parseFloat(clinfo.oamount));
+                            const flag = (item.bwmx === undefined || item.bwmx === clinfo.bwmx || item.jldy === clinfo.bwmx) && item.gcl_id === clinfo.gcl_id && (item.quantity !== null ? item.quantity === parseFloat(clinfo.oamount) : 0 === parseFloat(clinfo.oamount));
+                            if (flag && item.code === clinfo.xmj_code) {
+                                return flag && item.code === clinfo.xmj_code;
+                            }
+                            return flag;
                         });
                         });
                         if (leafInfo) {
                         if (leafInfo) {
                             pushbwmx = leafInfo.code + '!_!' + (leafInfo.jldy !== undefined ? leafInfo.jldy : '') + '!_!' +
                             pushbwmx = leafInfo.code + '!_!' + (leafInfo.jldy !== undefined ? leafInfo.jldy : '') + '!_!' +
@@ -937,7 +958,7 @@ function tableDataRemake(changeListData) {
                                 (leafInfo.fbgc ? leafInfo.fbgc : '') + '!_!' +
                                 (leafInfo.fbgc ? leafInfo.fbgc : '') + '!_!' +
                                 (leafInfo.fxgc ? leafInfo.fxgc : '') + '!_!' +
                                 (leafInfo.fxgc ? leafInfo.fxgc : '') + '!_!' +
                                 (leafInfo.gcl_id ? leafInfo.gcl_id : '') + '!_!' +
                                 (leafInfo.gcl_id ? leafInfo.gcl_id : '') + '!_!' +
-                                (leafInfo.bwmx !== undefined ? leafInfo.bwmx : (leafInfo.jldy !== undefined ? leafInfo.jldy : '')) + '*;*' + (leafInfo.quantity !== null ? leafInfo.quantity : 0);
+                                (leafInfo.bwmx !== undefined ? leafInfo.bwmx : (listinfo.leafXmjs.length > 1 && listinfo.name ? listinfo.name : (leafInfo.jldy !== undefined ? leafInfo.jldy : ''))) + '*;*' + (leafInfo.quantity !== null ? leafInfo.quantity : 0);
                         } else {
                         } else {
                             toastr.warning('台账清单列表已不存在'+ clinfo.code +',已更新变更清单列表');
                             toastr.warning('台账清单列表已不存在'+ clinfo.code +',已更新变更清单列表');
                             // changeList.splice(index, 1);
                             // changeList.splice(index, 1);
@@ -1046,7 +1067,7 @@ function remakeChangeSpread() {
                 gcl_id,
                 gcl_id,
             };
             };
             const radionInfo = changeList.find(function (info) {
             const radionInfo = changeList.find(function (info) {
-                return info.code === code && (info.lid == lid || parseInt(info.lid) === parseInt(lindex)) && gcl_id == info.gcl_id && info.bwmx === bwmx;
+                return info.code === code && (info.lid == lid || parseInt(info.lid) === parseInt(lindex)) && gcl_id == info.gcl_id && (info.bwmx === bwmx || info.bwmx === xmj_jldy) && parseInt(info.oamount) === parseInt(oamount);
             });
             });
             if (radionInfo) {
             if (radionInfo) {
                 trlist.camount = radionInfo.camount;
                 trlist.camount = radionInfo.camount;

+ 2 - 1
app/public/js/change_information_show.js

@@ -203,7 +203,8 @@ $(document).ready(() => {
                 if (response.err === 0) {
                 if (response.err === 0) {
                     codeSuccess(btn);
                     codeSuccess(btn);
                     $("input[name='code']").removeAttr('readonly');
                     $("input[name='code']").removeAttr('readonly');
-                    $("#re-shenpi-btn").removeAttr('disabled');
+                    // $("#re-shenpi-btn").removeAttr('disabled');
+                    $("#re-shenpi-btn2").removeAttr('disabled');
                 } else {
                 } else {
                     toast(response.msg, 'error');
                     toast(response.msg, 'error');
                 }
                 }

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

@@ -107,6 +107,15 @@ $(function(){
             $(this).attr('href', $(this).attr('href') + '?pageSize=' + getLocalCache('account-pageSize'));
             $(this).attr('href', $(this).attr('href') + '?pageSize=' + getLocalCache('account-pageSize'));
         }
         }
     });
     });
+
+    $('.change_sort_link').each(function () {
+        const tender_id = $(this).attr('href').split('/')[2];
+        let orderSetting = getLocalCache('change-'+ tender_id +'-list-order');
+        if(orderSetting) {
+            const orders = orderSetting.split('|');
+            $(this).attr('href', $(this).attr('href') + '?sort=' + orders[0] + '&order=' + orders[1]);
+        }
+    });
 });
 });
 
 
 function checkShowLast (count) {
 function checkShowLast (count) {

+ 6 - 1
app/public/js/ledger.js

@@ -51,7 +51,8 @@ const checkOption = {
             {qty: 'quantity', tp: 'total_price'},
             {qty: 'quantity', tp: 'total_price'},
             {qty: 'deal_qty', tp: 'deal_tp'},
             {qty: 'deal_qty', tp: 'deal_tp'},
         ],
         ],
-    }
+    },
+    same_code: { enable: 1 },
 };
 };
 
 
 $(document).ready(function() {
 $(document).ready(function() {
@@ -1829,6 +1830,10 @@ $(document).ready(function() {
                     toastr.error('父节点不可插入计量单元');
                     toastr.error('父节点不可插入计量单元');
                     SpreadJsObj.reLoadRowData(info.sheet, info.row);
                     SpreadJsObj.reLoadRowData(info.sheet, info.row);
                     return;
                     return;
+                }else if (newText && newText !== '' && (!node.b_code || node.b_code === '')) {
+                    toastr.error('项目节不可插入计量单元');
+                    SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                    return;
                 }
                 }
 
 
                 const data = {};
                 const data = {};

+ 170 - 44
app/public/js/ledger_check.js

@@ -10,15 +10,24 @@
 
 
 
 
 const ledgerCheckType = {
 const ledgerCheckType = {
-    sibling: {value: 1, text: '项目节、清单同层'},
-    empty_code: {value: 2, text: '项目节、清单编号同时为空'},
-    calc: {value: 3, text: '清单数量不等于计量单元之和'},
-    zero: {value: 4, text: '清单数量或单价为0'},
-    tp: {value: 5, text: '清单金额≠数量×单价'},
-    over: {value: 6, text: '超计'},
+    sibling: {value: 1, text: '项目节、清单同层', fun: 'checkSibling', },
+    empty_code: {value: 2, text: '项目节、清单编号同时为空', fun: 'checkCodeEmpty', },
+    calc: {value: 3, text: '清单数量不等于计量单元之和', fun: 'checkCalc', },
+    zero: {value: 4, text: '清单数量或单价为0', fun: 'checkZero', },
+    tp: {value: 5, text: '清单金额≠数量×单价', fun: 'checkTp', },
+    over: {value: 6, text: '超计', fun: 'checkOver', },
+    same_code: {value: 7, text: '重复项目节', fun: 'checkSameCode', },
+    limit3f: {
+        fun: 'check3fLimit', items: [
+            { value: 8, text: '违规计量(工序报验)', key: 'gxbyOver', type: 'gxby', },
+            { value: 9, text: '遗漏计量(工序报验)', key: 'gxbyLost', type: 'gxby', },
+            { value: 10, text: '违规计量(档案管理)', key: 'daglOver', type: 'dagl', },
+            { value: 11, text: '遗漏计量(档案管理)', key: 'daglLost', type: 'dagl', },
+        ]
+    },
 };
 };
 const ledgerCheckUtil = {
 const ledgerCheckUtil = {
-    checkSibling: function (ledgerTree) {
+    checkSibling: function (ledgerTree, ledgerPos, decimal, option) {
         const error = [];
         const error = [];
         for (const node of ledgerTree.nodes) {
         for (const node of ledgerTree.nodes) {
             if (!node.children || node.children.length === 0) continue;
             if (!node.children || node.children.length === 0) continue;
@@ -31,7 +40,7 @@ const ledgerCheckUtil = {
         }
         }
         return error;
         return error;
     },
     },
-    checkCodeEmpty: function (ledgerTree) {
+    checkCodeEmpty: function (ledgerTree, ledgerPos, decimal, option) {
         const error = [];
         const error = [];
         const checkNodeCode = function (node) {
         const checkNodeCode = function (node) {
             if ((!node.code || node.code === '') && (!node.b_code || node.b_code === '')) error.push(node);
             if ((!node.code || node.code === '') && (!node.b_code || node.b_code === '')) error.push(node);
@@ -40,15 +49,15 @@ const ledgerCheckUtil = {
                     checkNodeCode(child);
                     checkNodeCode(child);
                 }
                 }
             }
             }
-        }
+        };
         for (const topLevel of ledgerTree.children) {
         for (const topLevel of ledgerTree.children) {
-            if (topLevel.node_type !== 1) continue;
+            if ([1, 3, 4].indexOf(topLevel.node_type) < 0) continue;
 
 
             checkNodeCode(topLevel);
             checkNodeCode(topLevel);
         }
         }
         return error;
         return error;
     },
     },
-    checkCalc: function (ledgerTree, ledgerPos, option) {
+    checkCalc: function (ledgerTree, ledgerPos, decimal, option) {
         const error = [];
         const error = [];
         for (const node of ledgerTree.nodes) {
         for (const node of ledgerTree.nodes) {
             if (node.children && node.children.length > 0) continue;
             if (node.children && node.children.length > 0) continue;
@@ -70,7 +79,7 @@ const ledgerCheckUtil = {
         }
         }
         return error;
         return error;
     },
     },
-    checkZero: function (ledgerTree) {
+    checkZero: function (ledgerTree, ledgerPos, decimal, option) {
         const error = [];
         const error = [];
         for (const node of ledgerTree.nodes) {
         for (const node of ledgerTree.nodes) {
             if ((!node.b_code || node.b_code === '')) continue;
             if ((!node.b_code || node.b_code === '')) continue;
@@ -82,7 +91,7 @@ const ledgerCheckUtil = {
         }
         }
         return error;
         return error;
     },
     },
-    checkTp: function (ledgerTree, decimal, option) {
+    checkTp: function (ledgerTree, ledgerPos, decimal, option) {
         const error = [];
         const error = [];
         for (const node of ledgerTree.nodes) {
         for (const node of ledgerTree.nodes) {
             if (node.children && node.children.length > 0) continue;
             if (node.children && node.children.length > 0) continue;
@@ -97,7 +106,7 @@ const ledgerCheckUtil = {
         }
         }
         return error;
         return error;
     },
     },
-    checkOver: function(ledgerTree, ledgerPos, option) {
+    checkOver: function(ledgerTree, ledgerPos, decimal, option) {
         const error = [];
         const error = [];
         for (const node of ledgerTree.nodes) {
         for (const node of ledgerTree.nodes) {
             if (node.children && node.children.length > 0) continue;
             if (node.children && node.children.length > 0) continue;
@@ -106,6 +115,131 @@ const ledgerCheckUtil = {
         }
         }
         return error;
         return error;
     },
     },
+    checkSameCode: function (ledgerTree, ledgerPos, decimal, option) {
+        const error = [];
+        let xmj = ledgerTree.nodes.filter(x => { return /^((GD*)|G)?[0-9]+/.test(x.code); });
+        let check = null;
+        while (xmj.length > 0) {
+            [check, xmj] = _.partition(xmj, x => { return x.code === xmj[0].code; });
+            if (check.length > 1) {
+                error.push(...check);
+            }
+        }
+        return error;
+    },
+    check3fLimit: function (ledgerTree, ledgerPos, decimal, option) {
+        const error = {};
+        for (const i of ledgerCheckType.limit3f.items) {
+            error[i.key] = [];
+        }
+        if (option.checkType.length === 0) return error;
+
+        const findPrecision = function (list, unit) {
+            if (unit) {
+                for (const p in list) {
+                    if (list[p].unit && list[p].unit === unit) {
+                        return list[p];
+                    }
+                }
+            }
+            return list.other;
+        };
+        const check3f = function (data, limit, ratio) {
+            if (limit === 0) {
+                if (data.contract_tp || data.pre_contract_tp) return 1; // 违规
+            }
+            if (limit === 1) {
+                if (ratio === 0) {
+                    if (!data.contract_tp && !data.pre_contract_tp) return 2; // 漏计
+                } else {
+                    const tp = ZhCalc.mul(data.total_price, ratio, this.ctx.tender.info.decimal.tp);
+                    const checkTp = ZhCalc.add(data.contract_tp, data.pre_contract_tp);
+                    if (tp > checkTp) return 1; // 违规
+                    if (tp < checkTp) return 2; // 漏计
+                }
+            }
+            return 0; // 合法
+        };
+        const check3fQty = function (data, limit, ratio, unit) {
+            if (limit === 0) {
+                if (data.contract_qty || data.qc_qty || data.pre_contract_qty || data.pre_qc_qty) return 1; // 违规
+            }
+            if (limit === 1) {
+                if (ratio <= 0) {
+                    if (!data.contract_qty && !data.qc_qty && !data.pre_contract_qty && !data.pre_qc_qty) return 2; // 漏计
+                } else {
+                    const precision = findPrecision(tenderInfo.precision, unit);
+                    const checkQty = ZhCalc.mul(data.quantity, ratio, precision.value);
+                    const qty = ZhCalc.add(data.contract_qty, data.pre_contract_qty);
+                    if (qty > checkQty) return 1; // 违规
+                    if (qty < checkQty) return 2; // 漏计
+                }
+            }
+            return 0; // 合法
+        };
+        const checkLeafBills3fLimit = function(bills, checkInfo) {
+            const over = [], lost = [];
+            const posRange = ledgerPos.getLedgerPos(bills.id);
+            if (posRange && posRange.length > 0) {
+                for (const p of posRange) {
+                    const posCheckInfo = _.assign({}, checkInfo);
+                    for (const ct of option.checkType) {
+                        if (p[ct + '_limit'] >= 0) {
+                            posCheckInfo[ct + '_limit'] = p[ct + '_limit'];
+                            posCheckInfo[ct + '_ratio'] = p[ct + '_ratio'];
+                        }
+                    }
+                    for (const ct of option.checkType) {
+                        const checkResult = check3fQty(p, posCheckInfo[ct + '_limit'], posCheckInfo[ct + '_ratio'], bills.unit);
+                        if (checkResult === 1) {
+                            if (over.indexOf(ct) === -1) over.push(ct);
+                        }
+                        if (checkResult === 2) {
+                            if (lost.indexOf(ct) === -1) lost.push(ct);
+                        }
+                    }
+                }
+            } else {
+                for (const ct of option.checkType) {
+                    const checkResult = bills.is_tp
+                        ? check3f(bills, checkInfo[ct + '_limit'], checkInfo[ct + '_ratio'])
+                        : check3fQty(bills, checkInfo[ct + '_limit'], checkInfo[ct + '_ratio'], bills.unit);
+                    if (checkResult === 1) {
+                        if (over.indexOf(ct) === -1) over.push(ct);
+                    }
+                    if (checkResult === 2) {
+                        if (lost.indexOf(ct) === -1) lost.push(ct);
+                    }
+                }
+            }
+
+            if (over.indexOf('gxby') >= 0) error.gxbyOver.push(bills);
+            if (over.indexOf('dagl') >= 0) error.daglOver.push(bills);
+            if (lost.indexOf('gxby') >= 0) error.gxbyLost.push(bills);
+            if (lost.indexOf('dagl') >= 0) error.daglLost.push(bills);
+        };
+        const recursiveCheckBills3fLimit = function (bills, parentCheckInfo) {
+            const checkInfo = _.assign({}, parentCheckInfo);
+            for (const ct of option.checkType) {
+                if (bills[ct + '_limit'] >= 0) {
+                    checkInfo[ct + '_limit'] = bills[ct + '_limit'];
+                    checkInfo[ct + '_ratio'] = bills[ct + '_ratio'];
+                }
+            }
+            if (bills.children && bills.children.length > 0) {
+                for (const c of bills.children) {
+                    recursiveCheckBills3fLimit(c, checkInfo);
+                }
+            } else {
+                checkLeafBills3fLimit(bills, checkInfo);
+            }
+        };
+
+        for (const b of ledgerTree.children) {
+            recursiveCheckBills3fLimit(b, {});
+        }
+        return error;
+    },
 };
 };
 
 
 const ledgerCheck2 = function (setting) {
 const ledgerCheck2 = function (setting) {
@@ -129,35 +263,21 @@ const ledgerCheck2 = function (setting) {
         warning_data: [],
         warning_data: [],
     };
     };
     const progressData = [];
     const progressData = [];
-    if (checkOption.sibling.enable) {
-        const sibling = ledgerCheckUtil.checkSibling(ledger, checkOption.sibling) || [];
-        assignWarningData(sibling, ledgerCheckType.sibling.value, checkData.warning_data);
-        progressData.push({key: 'sibling', caption: ledgerCheckType.sibling.text, error: sibling.length});
-    }
-    if (checkOption.empty_code.enable) {
-        const empty_code = ledgerCheckUtil.checkCodeEmpty(ledger, checkOption.empty_code) || [];
-        assignWarningData(empty_code, ledgerCheckType.empty_code.value, checkData.warning_data);
-        progressData.push({key: 'empty_code', caption: ledgerCheckType.empty_code.text, error: empty_code.length});
-    }
-    if (checkOption.calc.enable) {
-        const calc = ledgerCheckUtil.checkCalc(ledger, ledgerPos, checkOption.calc) || [];
-        assignWarningData(calc, ledgerCheckType.calc.value, checkData.warning_data);
-        progressData.push({key: 'calc', caption: ledgerCheckType.calc.text, error: calc.length});
-    }
-    if (checkOption.zero.enable) {
-        const zero = ledgerCheckUtil.checkZero(ledger, checkOption.zero) || [];
-        assignWarningData(zero, ledgerCheckType.zero.value, checkData.warning_data);
-        progressData.push({key: 'zero', caption: ledgerCheckType.zero.text, error: zero.length});
-    }
-    if (checkOption.tp.enable) {
-        const tp = ledgerCheckUtil.checkTp(ledger, decimal, checkOption.tp) || [];
-        assignWarningData(tp, ledgerCheckType.tp.value, checkData.warning_data);
-        progressData.push({key: 'tp', caption: ledgerCheckType.tp.text, error: tp.length});
-    }
-    if (checkOption.over && checkOption.over.enable) {
-        const over = ledgerCheckUtil.checkOver(ledger, ledgerPos, checkOption.over) || [];
-        assignWarningData(over, ledgerCheckType.over.value, checkData.warning_data);
-        progressData.push({key: 'over', caption: ledgerCheckType.over.text, error: over.length});
+    for (const prop in ledgerCheckType) {
+        if (!checkOption[prop] || !checkOption[prop].enable) continue;
+
+        if (ledgerCheckType[prop].items) {
+            const errors = ledgerCheckUtil[ledgerCheckType[prop].fun](ledger, ledgerPos, decimal, checkOption[prop]) || {};
+            for (const i of ledgerCheckType[prop].items) {
+                if (checkOption[prop].checkType.indexOf(i.type) < 0) continue;
+                assignWarningData(errors[i.key], i.value, checkData.warning_data);
+                progressData.push({key: prop + i.key, caption: i.text, error: errors[i.key].length});
+            }
+        } else {
+            const errors = ledgerCheckUtil[ledgerCheckType[prop].fun](ledger, ledgerPos, decimal, checkOption[prop]) || [];
+            assignWarningData(errors, ledgerCheckType[prop].value, checkData.warning_data);
+            progressData.push({key: prop, caption: ledgerCheckType[prop].text, error: errors.length});
+        }
     }
     }
     setting.checkList.clearCheckData();
     setting.checkList.clearCheckData();
     if (checkData.warning_data.length > 0) {
     if (checkData.warning_data.length > 0) {
@@ -171,7 +291,13 @@ const getCheckType = function (option) {
     const result = {};
     const result = {};
     for (const o in option) {
     for (const o in option) {
         if (option[o].enable) {
         if (option[o].enable) {
-            result[o] = ledgerCheckType[o];
+            if (ledgerCheckType[o].items) {
+                for (const i of ledgerCheckType[o].items) {
+                    result[o + i.key] = i;
+                }
+            } else {
+                result[o] = ledgerCheckType[o];
+            }
         }
         }
     }
     }
     return result;
     return result;

+ 22 - 5
app/public/js/path_tree.js

@@ -1167,12 +1167,14 @@ const createNewPathTree = function (type, setting) {
                 if (p) p.check = p.pwd === pwd;
                 if (p) p.check = p.pwd === pwd;
             }
             }
         }
         }
-        loadPwd(data, cacheKey) {
+        loadPwd(data, cacheKey, confirmList) {
             this.loadingPwd = true;
             this.loadingPwd = true;
             try {
             try {
                 this.pwdCacheKey = cacheKey;
                 this.pwdCacheKey = cacheKey;
+                this.confirmList = confirmList;
                 this.pwd = data;
                 this.pwd = data;
                 this._loadPwdCache();
                 this._loadPwdCache();
+                this._loadOnlineConfirm();
                 for (const p of this.pwd) {
                 for (const p of this.pwd) {
                     p.node = this.getItems(p.ledger_id);
                     p.node = this.getItems(p.ledger_id);
                     this.lockNode(p, !p.check);
                     this.lockNode(p, !p.check);
@@ -1181,6 +1183,21 @@ const createNewPathTree = function (type, setting) {
             this.loadingPwd = false;
             this.loadingPwd = false;
         }
         }
 
 
+        // 确认相关
+        _loadOnlineConfirm() {
+            const cacheArr = this.confirmList;
+            for (const ca of cacheArr) {
+                if (!ca) continue;
+                if (!ca.ledger_id) continue;
+
+                const p = this.pwd.find(x => {return x.ledger_id == ca.ledger_id});
+                if (p) {
+                    p.confirm = true;
+                    p.confirm_time = ca.create_time;
+                }
+            }
+        }
+
         getStageItems(id) {
         getStageItems(id) {
             return this.stageItems[itemsPre + id];
             return this.stageItems[itemsPre + id];
         }
         }
@@ -1600,7 +1617,7 @@ const createNewPathTree = function (type, setting) {
                 this.loadCompareNode(source, c, cur, loadFun);
                 this.loadCompareNode(source, c, cur, loadFun);
             }
             }
         }
         }
-    
+
         generateSortNodes() {
         generateSortNodes() {
             const self = this;
             const self = this;
             const addSortNode = function (node) {
             const addSortNode = function (node) {
@@ -1614,13 +1631,13 @@ const createNewPathTree = function (type, setting) {
                 addSortNode(n);
                 addSortNode(n);
             }
             }
         }
         }
-    
+
         loadCompareTree(data, loadFun) {
         loadCompareTree(data, loadFun) {
             for (const c of data.billsTree.children) {
             for (const c of data.billsTree.children) {
                 this.loadCompareNode(data, c, null, loadFun);
                 this.loadCompareNode(data, c, null, loadFun);
             }
             }
         }
         }
-    
+
         calculateDiffer() {
         calculateDiffer() {
             if (this.setting.calcDiffer) {
             if (this.setting.calcDiffer) {
                 for (const d of this.datas) {
                 for (const d of this.datas) {
@@ -1628,7 +1645,7 @@ const createNewPathTree = function (type, setting) {
                 }
                 }
             }
             }
         }
         }
-    
+
         loadCompareData(data1, data2) {
         loadCompareData(data1, data2) {
             this.loadCompareTree(data1, this.setting.loadInfo1);
             this.loadCompareTree(data1, this.setting.loadInfo1);
             this.loadCompareTree(data2, this.setting.loadInfo2);
             this.loadCompareTree(data2, this.setting.loadInfo2);

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

@@ -296,7 +296,7 @@ $(document).ready(() => {
                     case 'name':
                     case 'name':
                     case 'unit':
                     case 'unit':
                     case 'unit_price':
                     case 'unit_price':
-                        info.cancel = readOnly || node.pre_used;
+                        info.cancel = readOnly;
                         break;
                         break;
                 }
                 }
             },
             },

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

@@ -196,7 +196,7 @@ $(document).ready(() => {
     // 设置页显示数目
     // 设置页显示数目
     $('.nav-tabs .nav-link').each(function () {
     $('.nav-tabs .nav-link').each(function () {
         const pageSize = getLocalCache('account-pageSize') ? getLocalCache('account-pageSize') : '';
         const pageSize = getLocalCache('account-pageSize') ? getLocalCache('account-pageSize') : '';
-        if (getLocalCache('account-pageSize')) {
+        if (getLocalCache('account-pageSize') && $(this).attr('href').indexOf('pageSize') === -1) {
             $(this).attr('href', $(this).attr('href') + '?pageSize=' + getLocalCache('account-pageSize'));
             $(this).attr('href', $(this).attr('href') + '?pageSize=' + getLocalCache('account-pageSize'));
         }
         }
     });
     });

+ 6 - 0
app/public/js/shares/cs_tools.js

@@ -76,6 +76,12 @@ const showSelectTab = function(select, spread, afterShow) {
                                 case 'qty': return '数量';
                                 case 'qty': return '数量';
                                 case 'tp': return '金额';
                                 case 'tp': return '金额';
                                 case 'over': return '超计';
                                 case 'over': return '超计';
+                                case 'sibling': return '项目节清单同层';
+                                case 'same_code': return '重复项目节编号';
+                                case 's2b_over_gxby': return '违规计量(工序报验)';
+                                case 's2b_over_dagl': return '违规计量(档案管理)';
+                                case 's2b_lost_gxby': return '遗漏计量(工序报验)';
+                                case 's2b_lost_dagl': return '遗漏计量(档案管理)';
                                 default: return '';
                                 default: return '';
                             }
                             }
                         }
                         }

+ 19 - 1
app/public/js/shenpi.js

@@ -1106,6 +1106,24 @@ $(document).ready(function () {
         const X = $('#tender-list').find('.result').eq(now-1).offset().top;
         const X = $('#tender-list').find('.result').eq(now-1).offset().top;
         $('#tender-list').scrollTop(X - $('#tender-list').offset().top + $('#tender-list').scrollTop() -30);
         $('#tender-list').scrollTop(X - $('#tender-list').offset().top + $('#tender-list').scrollTop() -30);
     });
     });
+
+    $('body').on('blur', '#coo_table .edit-company', function () {
+        const id = $(this).data('id');
+        const newVal = $(this).val();
+        const cooInfo = _.find(ledger_cooperation_list, { id: id });
+        if(cooInfo && cooInfo.company !== newVal) {
+            const data = {
+                type: 'company',
+                id,
+                company: newVal,
+            };
+            postData('/tender/' + cur_tenderid + '/shenpi/audit/save', data, function (result) {
+                const lcindex = _.findIndex(ledger_cooperation_list, { id: id });
+                cooInfo.company = newVal;
+                ledger_cooperation_list.splice(lcindex, 1, cooInfo);
+            });
+        }
+    })
 });
 });
 
 
 function setRightData(datas, coolist) {
 function setRightData(datas, coolist) {
@@ -1197,7 +1215,7 @@ function setLeftTable(ledgerList, coolist, uid, title) {
         html += `<tr>` +
         html += `<tr>` +
             `<td>${sc.code} ${sc.name}</td>` +
             `<td>${sc.code} ${sc.name}</td>` +
             `<td><p class="mb-0">${sc.pwd}</p><a href="javascript:void(0);" data-lid="${sc.ledger_id}" data-uid="${sc.user_id}" data-pwd="${sc.pwd}" class="edit-pwd">修改</a> <a href="javascript:void(0)" data-lid="${sc.ledger_id}" data-uid="${sc.user_id}" class="del-pwd text-danger">移除</a></td>` +
             `<td><p class="mb-0">${sc.pwd}</p><a href="javascript:void(0);" data-lid="${sc.ledger_id}" data-uid="${sc.user_id}" data-pwd="${sc.pwd}" class="edit-pwd">修改</a> <a href="javascript:void(0)" data-lid="${sc.ledger_id}" data-uid="${sc.user_id}" class="del-pwd text-danger">移除</a></td>` +
-            `<td>${pichtml}</td>` +
+            `<td>${pichtml}</td><td><input type="text" class="form-control form-control-sm edit-company" data-id="${sc.id}" value="${sc.company}" placeholder="输入单位名称或备注"></td>` +
             `</tr>`;
             `</tr>`;
     }
     }
     $('#coo_table').html(html);
     $('#coo_table').html(html);

+ 97 - 5
app/public/js/stage.js

@@ -240,8 +240,13 @@ $(document).ready(() => {
         },
         },
         over: {
         over: {
             enable: 1, isTz: checkTzMeasureType(),
             enable: 1, isTz: checkTzMeasureType(),
+        },
+        limit3f: {
+            enable: 1, checkType: [],
         }
         }
     };
     };
+    if (tender.s2b_gxby_check) checkOption.limit3f.checkType.push('gxby');
+    if (tender.s2b_dagl_check) checkOption.limit3f.checkType.push('dagl');
     // 界面布局
     // 界面布局
     autoFlashHeight();
     autoFlashHeight();
     // 初始化 台账树结构 数据结构
     // 初始化 台账树结构 数据结构
@@ -309,15 +314,45 @@ $(document).ready(() => {
     };
     };
     const stagePos = new StagePosData(stagePosSetting);
     const stagePos = new StagePosData(stagePosSetting);
 
 
+    const setCooperationSelectHtml = function () {
+        const selectHtml = [];
+        selectHtml.push('<option>全部</option>');
+        const cooName = _.uniqWith(_.map(stageTree.pwd, 'company'));
+        for (const i of cooName) {
+            selectHtml.push('<option>', i, '</option>');
+        }
+        $('#cooperationSelect').html(selectHtml.join(''));
+    };
+
     const reloadCooperationHtml = function () {
     const reloadCooperationHtml = function () {
         const html = [];
         const html = [];
-        for (const p of stageTree.pwd) {
+        const select = $('#cooperationSelect').val();
+        const list = select !== '全部' ? _.filter(stageTree.pwd, { company: select }) : stageTree.pwd;
+        for (const p of list) {
             if (!p.node) continue;
             if (!p.node) continue;
-            html.push('<tr>', `<td>${p.node.code}</td>`, `<td>${p.node.name}</td>`);
-            html.push('<td>', p.check ? `已解锁:${p.pwd}` : `<a name="ledger-unlock" lid="${p.ledger_id}" href="javascript: void(0);">解锁</a>`, '</td>');
+            if (p.confirm) {
+                html.push(`<tr class="text-success">`);
+            } else {
+                html.push(`<tr>`);
+            }
+            html.push(`<td>${p.node.code}</td>`, `<td>${p.node.name}</td>`);
+            if(coopwd) {
+                html.push('<td>', p.check ? `已解锁:${p.pwd}` : `<a name="ledger-unlock" lid="${p.ledger_id}" href="javascript: void(0);">解锁</a>`, '</td>');
+            }
+            html.push('<td>', p.company, '</td>');
+            if (p.check && p.confirm) {
+                html.push('<td>', moment(p.confirm_time).format('YYYY-MM-DD HH:mm') + ' ' + (coopwd ? `<a name="ledger-unconfirm" href="javascript: void(0);" lid="${p.ledger_id}">重新审批</a>` : ''), '</td>');
+            } else if (!p.check && p.confirm) {
+                html.push('<td>', moment(p.confirm_time).format('YYYY-MM-DD HH:mm'), '</td>');
+            } else if (p.check && !p.confirm && coopwd) {
+                html.push('<td>', `<a name="ledger-confirm" href="javascript: void(0);" lid="${p.ledger_id}" class="btn btn-sm btn-success">确认</a>`, '</td>');
+            } else {
+                html.push('<td>', '</td>');
+            }
             html.push('</tr>');
             html.push('</tr>');
         }
         }
         $('#cooperationList').html(html.join(''));
         $('#cooperationList').html(html.join(''));
+
     };
     };
 
 
     class Changes {
     class Changes {
@@ -597,6 +632,12 @@ $(document).ready(() => {
     // 初始化 台账 spread
     // 初始化 台账 spread
     const slSpread = SpreadJsObj.createNewSpread($('#stage-ledger')[0]);
     const slSpread = SpreadJsObj.createNewSpread($('#stage-ledger')[0]);
     customizeStageTreeSetting(ledgerSpreadSetting, customColDisplay());
     customizeStageTreeSetting(ledgerSpreadSetting, customColDisplay());
+    ledgerSpreadSetting.cols.push(
+        {title: '工序限制', colSpan: '1', rowSpan: '2', field: 'gxby_limit', hAlign: 0, width: 80, readOnly: true},
+    );
+    posSpreadSetting.cols.push(
+        {title: '工序限制', colSpan: '1', rowSpan: '2', field: 'gxby_limit', hAlign: 0, width: 80, readOnly: true},
+    );
     // 数量变更列,添加按钮
     // 数量变更列,添加按钮
     const qcCol = _.find(ledgerSpreadSetting.cols, {field: 'qc_qty'});
     const qcCol = _.find(ledgerSpreadSetting.cols, {field: 'qc_qty'});
     qcCol.readOnly = true;
     qcCol.readOnly = true;
@@ -1883,6 +1924,7 @@ $(document).ready(() => {
         },
         },
         selectionChanged: function (e, info) {
         selectionChanged: function (e, info) {
             stagePosSpreadObj.loadExprToInput(info.sheet);
             stagePosSpreadObj.loadExprToInput(info.sheet);
+            console.log(SpreadJsObj.getSelectObject(info.sheet));
         },
         },
         addPegs: function (pegs) {
         addPegs: function (pegs) {
             if (!pegs || pegs.length <= 0) return;
             if (!pegs || pegs.length <= 0) return;
@@ -1924,9 +1966,10 @@ $(document).ready(() => {
         treeCalc.calculateAll(stageTree);
         treeCalc.calculateAll(stageTree);
         // 加载解锁相关
         // 加载解锁相关
         if (result.cooperation) {
         if (result.cooperation) {
-            stageTree.loadPwd(result.cooperation, 'bills-p-' + userID + '-' + window.location.pathname.split('/')[2]);
+            stageTree.loadPwd(result.cooperation, 'bills-p-' + userID + '-' + window.location.pathname.split('/')[2], result.cooperationConfirm);
             $('#cooperationCount').html(stageTree.pwd.length || '');
             $('#cooperationCount').html(stageTree.pwd.length || '');
             if (stageTree.pwd.length > 0) $('#cooperationCount').parent().show();
             if (stageTree.pwd.length > 0) $('#cooperationCount').parent().show();
+            setCooperationSelectHtml();
             reloadCooperationHtml();
             reloadCooperationHtml();
         }
         }
         for (const t of result.tags) {
         for (const t of result.tags) {
@@ -3982,6 +4025,13 @@ $(document).ready(() => {
     });
     });
     $('[name=stage-start]').submit(function (e) {
     $('[name=stage-start]').submit(function (e) {
         if (checkAuditorFrom()) {
         if (checkAuditorFrom()) {
+            // 再检查多人协同确认情况
+            const list = stageTree.pwd.find(x => {return !x.confirm });
+            if(list) {
+                toastr.error('请检查多人协同确认情况再上报');
+                $('#hide-all').hide();
+                return false;
+            }
             $(this).parent().parent().parent().modal('hide');
             $(this).parent().parent().parent().modal('hide');
             dataChecker.checkAndPost(this.action, {});
             dataChecker.checkAndPost(this.action, {});
             $('#hide-all').hide();
             $('#hide-all').hide();
@@ -3989,6 +4039,13 @@ $(document).ready(() => {
         return false;
         return false;
     });
     });
     $('#audit-check0').submit(function (e) {
     $('#audit-check0').submit(function (e) {
+        // 再检查多人协同确认情况
+        const list = stageTree.pwd.find(x => {return !x.confirm });
+        if(list) {
+            toastr.error('请检查多人协同确认情况再审批通过');
+            $('#hide-all').hide();
+            return false;
+        }
         const checkType = parseInt($('[name=checkType]').val());
         const checkType = parseInt($('[name=checkType]').val());
         const data = {
         const data = {
             opinion: $(`${'#sp-done'}`).find('[name=opinion]').val().replace(/\r\n/g, '<br/>').replace(/\n/g, '<br/>').replace(/\s/g, ' '),
             opinion: $(`${'#sp-done'}`).find('[name=opinion]').val().replace(/\r\n/g, '<br/>').replace(/\n/g, '<br/>').replace(/\s/g, ' '),
@@ -4073,6 +4130,7 @@ $(document).ready(() => {
 
 
         if (p.pwd === $('#unlock-pwd').val()) {
         if (p.pwd === $('#unlock-pwd').val()) {
             const refresh = stageTree.lockNode(p, false);
             const refresh = stageTree.lockNode(p, false);
+            // 修改线上
             SpreadJsObj.reloadRowsReadonly(slSpread.getActiveSheet(), refresh);
             SpreadJsObj.reloadRowsReadonly(slSpread.getActiveSheet(), refresh);
             stagePosSpreadObj.loadCurPosData();
             stagePosSpreadObj.loadCurPosData();
             $('#unlock').modal('hide');
             $('#unlock').modal('hide');
@@ -4082,5 +4140,39 @@ $(document).ready(() => {
             $('.invalid-feedback', '#unlock').show();
             $('.invalid-feedback', '#unlock').show();
             $('.alert-warning', '#unlock').show();
             $('.alert-warning', '#unlock').show();
         }
         }
-    })
+    });
+    $('#cooperationSelect').change(function () {
+        reloadCooperationHtml();
+    });
+    // 确认
+    $('body').on('click', '[name=ledger-confirm]', function() {
+        const lid = this.getAttribute('lid');
+        if (!lid) return;
+
+        const p = stageTree.pwd.find(x => {return x.ledger_id == lid});
+        if (!p) return;
+        // 修改线上
+        postData(window.location.pathname + '/save/cooperation', { type: 'save-confirm', postData: { ledger_id: lid } }, function (result) {
+            p.confirm = true;
+            p.confirm_time = new Date();
+            stageTree.confirmList = result.cooperationConfirm;
+            reloadCooperationHtml();
+        });
+    });
+
+    // 确认
+    $('body').on('click', '[name=ledger-unconfirm]', function() {
+        const lid = this.getAttribute('lid');
+        if (!lid) return;
+
+        const p = stageTree.pwd.find(x => {return x.ledger_id == lid});
+        if (!p) return;
+        // 修改线上
+        postData(window.location.pathname + '/save/cooperation', { type: 'del-confirm', postData: { ledger_id: lid } }, function (result) {
+            p.confirm = false;
+            p.confirm_time = null;
+            stageTree.confirmList = result.cooperationConfirm;
+            reloadCooperationHtml();
+        });
+    });
 });
 });

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

@@ -244,14 +244,14 @@ $(document).ready(() => {
                 if (payBase.isOld(data)) {
                 if (payBase.isOld(data)) {
                     return payBase.isStarted(data) || !payBase.isYB(data);
                     return payBase.isStarted(data) || !payBase.isYB(data);
                 } else {
                 } else {
-                    return payBase.isWC(data) || payBase.isSF(data) || !(payBase.isOwner(data) || payBase.isYB());
+                    return payBase.isWC(data) || payBase.isSF(data) || payBase.isYF(data) || !(payBase.isOwner(data) || payBase.isYB());
                 }
                 }
             },
             },
             rprice: function (data) {
             rprice: function (data) {
                 if (payBase.isOld(data)) {
                 if (payBase.isOld(data)) {
                     return !payBase.isYB(data);
                     return !payBase.isYB(data);
                 } else {
                 } else {
-                    return payBase.isWC(data) || payBase.isSF(data) && !(payBase.isOwner(data) || payBase.isYB());
+                    return payBase.isWC(data) || payBase.isSF(data) || payBase.isYF(data) || !(payBase.isOwner(data) || payBase.isYB());
                 }
                 }
             },
             },
         },
         },
@@ -366,7 +366,7 @@ $(document).ready(() => {
                     if (num === undefined || num === null || _.isNaN(num))
                     if (num === undefined || num === null || _.isNaN(num))
                         return [false, '输入的表达式非法:' + p.value + '不是一个有效的数字'];
                         return [false, '输入的表达式非法:' + p.value + '不是一个有效的数字'];
                     if (i > 0) {
                     if (i > 0) {
-                        if (param[i - 1].type !== 'calc') {
+                        if (param[i - 1].type !== 'calc' && param[i - 1].type !== 'left') {
                             return [false, '输入的表达式非法:' + p.value + '前应有运算符'];
                             return [false, '输入的表达式非法:' + p.value + '前应有运算符'];
                         } else if (param[i - 1].value === '/' && num === 0) {
                         } else if (param[i - 1].value === '/' && num === 0) {
                             return [false, '输入的表达式非法:请勿除0'];
                             return [false, '输入的表达式非法:请勿除0'];

+ 9 - 5
app/public/js/tender.js

@@ -269,21 +269,22 @@ $(document).ready(function() {
             sheet.options.colHeaderVisible = false;
             sheet.options.colHeaderVisible = false;
             sheet.defaults.rowHeight = 25;
             sheet.defaults.rowHeight = 25;
             sheet.setColumnCount(3);
             sheet.setColumnCount(3);
-            sheet.setRowCount(6);
+            sheet.setRowCount(7);
             sheet.setColumnWidth(0, 1);
             sheet.setColumnWidth(0, 1);
             sheet.setColumnWidth(1, 200);
             sheet.setColumnWidth(1, 200);
             sheet.setColumnWidth(2, 200);
             sheet.setColumnWidth(2, 200);
             sheet.setRowHeight(0, 1);
             sheet.setRowHeight(0, 1);
-            sheet.getRange(1, 1, 5, 1).vAlign(1).backColor('#e4e7ea').locked(true);
-            sheet.getRange(1, 2, 5, 1).vAlign(1).hAlign(2).locked(true);
+            sheet.getRange(1, 1, 6, 1).vAlign(1).backColor('#e4e7ea').locked(true);
+            sheet.getRange(1, 2, 6, 1).vAlign(1).hAlign(2).locked(true);
             sheet.setText(1, 1, '签约合同价');
             sheet.setText(1, 1, '签约合同价');
             sheet.setText(2, 1, '暂列金额');
             sheet.setText(2, 1, '暂列金额');
             sheet.setText(3, 1, '签约合同价(不含暂列金)');
             sheet.setText(3, 1, '签约合同价(不含暂列金)');
             sheet.setText(4, 1, '签约开工预付款');
             sheet.setText(4, 1, '签约开工预付款');
             sheet.setText(5, 1, '签约材料预付款');
             sheet.setText(5, 1, '签约材料预付款');
+            sheet.setText(6, 1, '安全生产费');
             const lineBorder = new spreadNS.LineBorder('#6a696e', spreadNS.LineStyle.thin);
             const lineBorder = new spreadNS.LineBorder('#6a696e', spreadNS.LineStyle.thin);
-            sheet.getRange(0, 0, 6, 3).setBorder(lineBorder, {all: true});
-            sheet.getRange(0, 0, 6, 3).formatter('@');
+            sheet.getRange(0, 0, 7, 3).setBorder(lineBorder, {all: true});
+            sheet.getRange(0, 0, 7, 3).formatter('@');
             sheet.setSelection(1, 2, 1, 1);
             sheet.setSelection(1, 2, 1, 1);
         });
         });
 
 
@@ -347,6 +348,7 @@ $(document).ready(function() {
             sheet.setValue(3, 2, accSub(property.deal_param.zanLiePrice, property.deal_param.contractPrice));
             sheet.setValue(3, 2, accSub(property.deal_param.zanLiePrice, property.deal_param.contractPrice));
             sheet.setValue(4, 2, property.deal_param.startAdvance);
             sheet.setValue(4, 2, property.deal_param.startAdvance);
             sheet.setValue(5, 2, property.deal_param.materialAdvance);
             sheet.setValue(5, 2, property.deal_param.materialAdvance);
+            sheet.setValue(6, 2, property.deal_param.safeAdvance);
 
 
         }
         }
         function setReadOnly (readOnly) {
         function setReadOnly (readOnly) {
@@ -354,6 +356,7 @@ $(document).ready(function() {
             sheet.getCell(2, 2).locked(readOnly);
             sheet.getCell(2, 2).locked(readOnly);
             sheet.getCell(4, 2).locked(readOnly);
             sheet.getCell(4, 2).locked(readOnly);
             sheet.getCell(5, 2).locked(readOnly);
             sheet.getCell(5, 2).locked(readOnly);
+            sheet.getCell(6, 2).locked(readOnly);
         }
         }
         function getNewDealData () {
         function getNewDealData () {
             const result = {};
             const result = {};
@@ -361,6 +364,7 @@ $(document).ready(function() {
             result.zanLiePrice = _.toNumber(sheet.getText(2, 2));
             result.zanLiePrice = _.toNumber(sheet.getText(2, 2));
             result.startAdvance = _.toNumber(sheet.getText(4, 2));
             result.startAdvance = _.toNumber(sheet.getText(4, 2));
             result.materialAdvance = _.toNumber(sheet.getText(5, 2));
             result.materialAdvance = _.toNumber(sheet.getText(5, 2));
+            result.safeAdvance = _.toNumber(sheet.getText(6, 2));
             return result;
             return result;
         }
         }
 
 

+ 237 - 0
app/public/netcasign/example/appPackage.js

@@ -0,0 +1,237 @@
+// 下载文件
+function dataURLtoBlob(dataurl) {
+    var arr = dataurl.split(','), mime = arr[0].match(/:(.*?);/)[1],
+        bstr = atob(arr[1]), n = bstr.length, u8arr = new Uint8Array(n);
+    while (n--) {
+        u8arr[n] = bstr.charCodeAt(n);
+    }
+    return new Blob([u8arr], { type: mime });
+}
+
+function downloadFile(blob, url, name){
+
+    if (window.navigator.msSaveBlob) {
+        try {
+            window.navigator.msSaveBlob(blob, name)
+        } catch (e) {
+            console.log(e);
+        }
+    } else {
+        // 谷歌浏览器 创建a标签 添加download属性下载
+        var a = document.createElement("a");
+        a.setAttribute("href",url);
+        a.setAttribute("download",name);
+        a.setAttribute("target","_blank");
+        var clickEvent = document.createEvent("MouseEvents"); //创建
+        clickEvent.initEvent("click", true, true);//初始化
+        a.dispatchEvent(clickEvent);//触发
+    }
+
+}
+
+function downloadFileByBase64(base64,name){
+    var myBlob = dataURLtoBlob(base64);
+    var myUrl = URL.createObjectURL(myBlob);
+    downloadFile(myBlob, myUrl,name)
+}
+
+function downloadNewFile(){
+    var base64 = NetcaPDFSeal.getSignPDFBytes();
+    var file = 'data:application/pdf;base64,' + base64;
+    var name = new Date();
+    name = name.getTime();
+    downloadFileByBase64(file, name+'.pdf');
+
+}
+
+
+
+
+// 演示功能
+function demonstrationFn(){
+
+    var obj = {
+
+        'netcasave':false,
+        'netcaFirstSeparator':false,
+        'netcaSecondSeparator':false,
+        'netcaThirdSeparaor':false,
+        'netcaSetting':false,
+        'netcaAbout':false
+    };
+    checkObjHasOwnProperty(obj);
+    hideExtraCheckboxesIE(obj);
+    removeExtraCheckboxes();
+    hideExtraCheckboxes();
+
+}
+
+
+/**检查obj是否有指定属性存在*/
+function checkObjHasOwnProperty(obj){
+    if(!obj.hasOwnProperty('openFile')){
+        obj.openFile = true
+    }
+    if(!obj.hasOwnProperty('netcasave')){
+        obj.netcasave = true
+    }
+    if(!obj.hasOwnProperty('viewFind')){
+        obj.viewFind = true
+    }
+    if(!obj.hasOwnProperty('zoomOut')){
+        obj.zoomOut = true
+    }
+    if(!obj.hasOwnProperty('zoomIn')){
+        obj.zoomIn = true
+    }
+    if(!obj.hasOwnProperty('scaleSelectContainer')){
+        obj.scaleSelectContainer = true
+    }
+    if(!obj.hasOwnProperty('netcaFirstSeparator')){
+        obj.netcaFirstSeparator = true
+    }
+    if(!obj.hasOwnProperty('previous')){
+        obj.previous = true
+    }
+    if(!obj.hasOwnProperty('next')){
+        obj.next = true
+    }
+    if(!obj.hasOwnProperty('netcaSecondSeparator')){
+        obj.netcaSecondSeparator = true
+    }
+    if(!obj.hasOwnProperty('verify')){
+        obj.verify = true
+    }
+    if(!obj.hasOwnProperty('netcasign')){
+        obj.netcasign = true
+    }
+    if(!obj.hasOwnProperty('netcaThirdSeparaor')){
+        obj.netcaThirdSeparaor = true
+    }
+    if(!obj.hasOwnProperty('netcaSetting')){
+        obj.netcaSetting = true
+    }
+    if(!obj.hasOwnProperty('netcaAbout')){
+        obj.netcaAbout = true
+    }
+    if(!obj.hasOwnProperty('netcastaff')){
+        obj.netcastaff = true
+    }
+    if(!obj.hasOwnProperty('netcaorganization')){
+        obj.netcaorganization = true
+    }
+    if(!obj.hasOwnProperty('netcalegalperson')){
+        obj.netcalegalperson = true
+    }
+    if(!obj.hasOwnProperty('print')){
+        obj.print = true
+    }
+    if(!obj.hasOwnProperty('closeFile')){
+        obj.closeFile = true
+    }
+}
+/**
+ * 非IE浏览器下,移除多余的checkbox
+ * */
+function removeExtraCheckboxes(){
+    var arr = [2,7,11,14,15,16];
+    if(!NetcaPDFSeal._isIE()){
+        for(var i=0;i<arr.length;i++){
+            (function(i){
+                $('input[name="checkbox'+arr[i]+'"]').parent().remove()
+            })(i);
+        }
+    }
+}
+/**
+ * 根据toolbarButton的true/false,隐藏多余的checkbox
+ * */
+function hideExtraCheckboxes(){
+    var arr1 = [4,5,6,8,9,10,12,13,20,21,22,23,24,28];
+    for(var j=0;j<arr1.length;j++){
+        (function(j){
+            if($('.netca_btn'+arr1[j]+'').css('display') === 'none'){
+                $('input[name="checkbox'+arr1[j]+'"]').parent().remove()
+            }
+        })(j);
+    }
+}
+/** IE 隐藏多余的checkbox
+ * */
+function hideExtraCheckboxesIE(obj) {
+    for (var k in obj) {
+        if(k === 'print' && obj[k] === false){
+            $('#checkbox20').parent().remove();
+        }
+        if(k === 'zoomOut' &&  obj[k] === false){
+            $('#checkbox4').parent().remove();
+        }
+        if(k === 'zoomIn' &&  obj[k] === false){
+            $('#checkbox5').parent().remove();
+        }
+        if(k === 'scaleSelectContainer' && obj[k] === false){
+            $('#checkbox6').parent().remove();
+        }
+        if(k === 'previous' && obj[k] === false){
+            $('#checkbox8').parent().remove();
+        }
+        if(k === 'next' && obj[k] === false){
+            $('#checkbox9').parent().remove();
+        }
+        if(k === 'netcapage' && obj[k] === false){
+            $('#checkbox10').parent().remove();
+        }
+        if(k === 'openFile' && obj[k] === false){
+            $('#checkbox1').parent().remove();
+        }
+        if(k === 'netcasave' && obj[k] === false){
+            $('#checkbox2').parent().remove();
+        }
+        if(k === 'viewFind' && obj[k] === false){
+            $('#checkbox3').parent().remove();
+        }
+        if(k === 'netcaFirstSeparator' && obj[k] === false){
+            $('#checkbox7').parent().remove();
+        }
+        if(k === 'netcaSecondSeparator' && obj[k] === false){
+            $('#checkbox11').parent().remove();
+        }
+        if(k === 'netcaThirdSeparaor' && obj[k] === false){
+            $('#checkbox14').parent().remove();
+        }
+        if(k === 'verfity' && obj[k] === false){
+            $('#checkbox13').parent().remove();
+        }
+        if(k === 'netcaSetting' && obj[k] === false){
+            $('#checkbox15').parent().remove();
+        }
+        if(k === 'netcaAbout' && obj[k] === false){
+            $('#checkbox16').parent().remove();
+        }
+        if(k === 'netcasign' && obj[k] === false){
+            $('#checkbox21').parent().remove();
+        }
+        if(k === 'netcastaff' && obj[k] === false){
+            $('#checkbox22').parent().remove();
+        }
+        if(k === 'netcaorganization' && obj[k] === false){
+            $('#checkbox23').parent().remove();
+        }
+        if(k === 'netcalegalperson' && obj[k] === false){
+            $('#checkbox24').parent().remove();
+        }
+        if(k === 'closeFile' && obj[k] === false){
+            $('#checkbox28').parent().remove();
+        }
+    }
+}
+
+function getSignCallBack(res){
+    console.log("签名信息", JSON.parse(res))
+}
+function getVerifyCallBack(res){
+    console.log("验证信息", JSON.parse(res))
+}
+// NetcaPDFSeal.setSignCallbackEvent(getSignCallBack);
+// NetcaPDFSeal.setVerifyCallbackEvent(getVerifyCallBack);
+

文件差异内容过多而无法显示
+ 1 - 0
app/public/netcasign/js/base64.min.js


+ 615 - 0
app/public/netcasign/js/debugger.js

@@ -0,0 +1,615 @@
+/* Copyright 2012 Mozilla Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/* eslint-disable no-var */
+
+'use strict';
+
+var FontInspector = (function FontInspectorClosure() {
+  var fonts, createObjectURL;
+  var active = false;
+  var fontAttribute = 'data-font-name';
+  function removeSelection() {
+    let divs = document.querySelectorAll(`span[${fontAttribute}]`);
+    for (let div of divs) {
+      div.className = '';
+    }
+  }
+  function resetSelection() {
+    let divs = document.querySelectorAll(`span[${fontAttribute}]`);
+    for (let div of divs) {
+      div.className = 'debuggerHideText';
+    }
+  }
+  function selectFont(fontName, show) {
+    let divs = document.querySelectorAll(`span[${fontAttribute}=${fontName}]`);
+    for (let div of divs) {
+      div.className = show ? 'debuggerShowText' : 'debuggerHideText';
+    }
+  }
+  function textLayerClick(e) {
+    if (!e.target.dataset.fontName ||
+        e.target.tagName.toUpperCase() !== 'SPAN') {
+      return;
+    }
+    var fontName = e.target.dataset.fontName;
+    var selects = document.getElementsByTagName('input');
+    for (var i = 0; i < selects.length; ++i) {
+      var select = selects[i];
+      if (select.dataset.fontName !== fontName) {
+        continue;
+      }
+      select.checked = !select.checked;
+      selectFont(fontName, select.checked);
+      select.scrollIntoView();
+    }
+  }
+  return {
+    // Properties/functions needed by PDFBug.
+    id: 'FontInspector',
+    name: 'Font Inspector',
+    panel: null,
+    manager: null,
+    init: function init(pdfjsLib) {
+      var panel = this.panel;
+      panel.setAttribute('style', 'padding: 5px;');
+      var tmp = document.createElement('button');
+      tmp.addEventListener('click', resetSelection);
+      tmp.textContent = 'Refresh';
+      panel.appendChild(tmp);
+
+      fonts = document.createElement('div');
+      panel.appendChild(fonts);
+
+      createObjectURL = pdfjsLib.createObjectURL;
+    },
+    cleanup: function cleanup() {
+      fonts.textContent = '';
+    },
+    enabled: false,
+    get active() {
+      return active;
+    },
+    set active(value) {
+      active = value;
+      if (active) {
+        document.body.addEventListener('click', textLayerClick, true);
+        resetSelection();
+      } else {
+        document.body.removeEventListener('click', textLayerClick, true);
+        removeSelection();
+      }
+    },
+    // FontInspector specific functions.
+    fontAdded: function fontAdded(fontObj, url) {
+      function properties(obj, list) {
+        var moreInfo = document.createElement('table');
+        for (var i = 0; i < list.length; i++) {
+          var tr = document.createElement('tr');
+          var td1 = document.createElement('td');
+          td1.textContent = list[i];
+          tr.appendChild(td1);
+          var td2 = document.createElement('td');
+          td2.textContent = obj[list[i]].toString();
+          tr.appendChild(td2);
+          moreInfo.appendChild(tr);
+        }
+        return moreInfo;
+      }
+      var moreInfo = properties(fontObj, ['name', 'type']);
+      var fontName = fontObj.loadedName;
+      var font = document.createElement('div');
+      var name = document.createElement('span');
+      name.textContent = fontName;
+      var download = document.createElement('a');
+      if (url) {
+        url = /url\(['"]?([^\)"']+)/.exec(url);
+        download.href = url[1];
+      } else if (fontObj.data) {
+        download.href = createObjectURL(fontObj.data, fontObj.mimeType);
+      }
+      download.textContent = 'Download';
+      var logIt = document.createElement('a');
+      logIt.href = '';
+      logIt.textContent = 'Log';
+      logIt.addEventListener('click', function(event) {
+        event.preventDefault();
+        console.log(fontObj);
+      });
+      var select = document.createElement('input');
+      select.setAttribute('type', 'checkbox');
+      select.dataset.fontName = fontName;
+      select.addEventListener('click', (function(select, fontName) {
+        return (function() {
+           selectFont(fontName, select.checked);
+        });
+      })(select, fontName));
+      font.appendChild(select);
+      font.appendChild(name);
+      font.appendChild(document.createTextNode(' '));
+      font.appendChild(download);
+      font.appendChild(document.createTextNode(' '));
+      font.appendChild(logIt);
+      font.appendChild(moreInfo);
+      fonts.appendChild(font);
+      // Somewhat of a hack, should probably add a hook for when the text layer
+      // is done rendering.
+      setTimeout(() => {
+        if (this.active) {
+          resetSelection();
+        }
+      }, 2000);
+    },
+  };
+})();
+
+var opMap;
+
+// Manages all the page steppers.
+var StepperManager = (function StepperManagerClosure() {
+  var steppers = [];
+  var stepperDiv = null;
+  var stepperControls = null;
+  var stepperChooser = null;
+  var breakPoints = Object.create(null);
+  return {
+    // Properties/functions needed by PDFBug.
+    id: 'Stepper',
+    name: 'Stepper',
+    panel: null,
+    manager: null,
+    init: function init(pdfjsLib) {
+      var self = this;
+      this.panel.setAttribute('style', 'padding: 5px;');
+      stepperControls = document.createElement('div');
+      stepperChooser = document.createElement('select');
+      stepperChooser.addEventListener('change', function(event) {
+        self.selectStepper(this.value);
+      });
+      stepperControls.appendChild(stepperChooser);
+      stepperDiv = document.createElement('div');
+      this.panel.appendChild(stepperControls);
+      this.panel.appendChild(stepperDiv);
+      if (sessionStorage.getItem('pdfjsBreakPoints')) {
+        breakPoints = JSON.parse(sessionStorage.getItem('pdfjsBreakPoints'));
+      }
+
+      opMap = Object.create(null);
+      for (var key in pdfjsLib.OPS) {
+        opMap[pdfjsLib.OPS[key]] = key;
+      }
+    },
+    cleanup: function cleanup() {
+      stepperChooser.textContent = '';
+      stepperDiv.textContent = '';
+      steppers = [];
+    },
+    enabled: false,
+    active: false,
+    // Stepper specific functions.
+    create: function create(pageIndex) {
+      var debug = document.createElement('div');
+      debug.id = 'stepper' + pageIndex;
+      debug.setAttribute('hidden', true);
+      debug.className = 'stepper';
+      stepperDiv.appendChild(debug);
+      var b = document.createElement('option');
+      b.textContent = 'Page ' + (pageIndex + 1);
+      b.value = pageIndex;
+      stepperChooser.appendChild(b);
+      var initBreakPoints = breakPoints[pageIndex] || [];
+      var stepper = new Stepper(debug, pageIndex, initBreakPoints);
+      steppers.push(stepper);
+      if (steppers.length === 1) {
+        this.selectStepper(pageIndex, false);
+      }
+      return stepper;
+    },
+    selectStepper: function selectStepper(pageIndex, selectPanel) {
+      var i;
+      pageIndex = pageIndex | 0;
+      if (selectPanel) {
+        this.manager.selectPanel(this);
+      }
+      for (i = 0; i < steppers.length; ++i) {
+        var stepper = steppers[i];
+        if (stepper.pageIndex === pageIndex) {
+          stepper.panel.removeAttribute('hidden');
+        } else {
+          stepper.panel.setAttribute('hidden', true);
+        }
+      }
+      var options = stepperChooser.options;
+      for (i = 0; i < options.length; ++i) {
+        var option = options[i];
+        option.selected = (option.value | 0) === pageIndex;
+      }
+    },
+    saveBreakPoints: function saveBreakPoints(pageIndex, bps) {
+      breakPoints[pageIndex] = bps;
+      sessionStorage.setItem('pdfjsBreakPoints', JSON.stringify(breakPoints));
+    },
+  };
+})();
+
+// The stepper for each page's IRQueue.
+var Stepper = (function StepperClosure() {
+  // Shorter way to create element and optionally set textContent.
+  function c(tag, textContent) {
+    var d = document.createElement(tag);
+    if (textContent) {
+      d.textContent = textContent;
+    }
+    return d;
+  }
+
+  function simplifyArgs(args) {
+    if (typeof args === 'string') {
+      var MAX_STRING_LENGTH = 75;
+      return args.length <= MAX_STRING_LENGTH ? args :
+        args.substring(0, MAX_STRING_LENGTH) + '...';
+    }
+    if (typeof args !== 'object' || args === null) {
+      return args;
+    }
+    if ('length' in args) { // array
+      var simpleArgs = [], i, ii;
+      var MAX_ITEMS = 10;
+      for (i = 0, ii = Math.min(MAX_ITEMS, args.length); i < ii; i++) {
+        simpleArgs.push(simplifyArgs(args[i]));
+      }
+      if (i < args.length) {
+        simpleArgs.push('...');
+      }
+      return simpleArgs;
+    }
+    var simpleObj = {};
+    for (var key in args) {
+      simpleObj[key] = simplifyArgs(args[key]);
+    }
+    return simpleObj;
+  }
+
+  function Stepper(panel, pageIndex, initialBreakPoints) {
+    this.panel = panel;
+    this.breakPoint = 0;
+    this.nextBreakPoint = null;
+    this.pageIndex = pageIndex;
+    this.breakPoints = initialBreakPoints;
+    this.currentIdx = -1;
+    this.operatorListIdx = 0;
+  }
+  Stepper.prototype = {
+    init: function init(operatorList) {
+      var panel = this.panel;
+      var content = c('div', 'c=continue, s=step');
+      var table = c('table');
+      content.appendChild(table);
+      table.cellSpacing = 0;
+      var headerRow = c('tr');
+      table.appendChild(headerRow);
+      headerRow.appendChild(c('th', 'Break'));
+      headerRow.appendChild(c('th', 'Idx'));
+      headerRow.appendChild(c('th', 'fn'));
+      headerRow.appendChild(c('th', 'args'));
+      panel.appendChild(content);
+      this.table = table;
+      this.updateOperatorList(operatorList);
+    },
+    updateOperatorList: function updateOperatorList(operatorList) {
+      var self = this;
+
+      function cboxOnClick() {
+        var x = +this.dataset.idx;
+        if (this.checked) {
+          self.breakPoints.push(x);
+        } else {
+          self.breakPoints.splice(self.breakPoints.indexOf(x), 1);
+        }
+        StepperManager.saveBreakPoints(self.pageIndex, self.breakPoints);
+      }
+
+      var MAX_OPERATORS_COUNT = 15000;
+      if (this.operatorListIdx > MAX_OPERATORS_COUNT) {
+        return;
+      }
+
+      var chunk = document.createDocumentFragment();
+      var operatorsToDisplay = Math.min(MAX_OPERATORS_COUNT,
+                                        operatorList.fnArray.length);
+      for (var i = this.operatorListIdx; i < operatorsToDisplay; i++) {
+        var line = c('tr');
+        line.className = 'line';
+        line.dataset.idx = i;
+        chunk.appendChild(line);
+        var checked = this.breakPoints.includes(i);
+        var args = operatorList.argsArray[i] || [];
+
+        var breakCell = c('td');
+        var cbox = c('input');
+        cbox.type = 'checkbox';
+        cbox.className = 'points';
+        cbox.checked = checked;
+        cbox.dataset.idx = i;
+        cbox.onclick = cboxOnClick;
+
+        breakCell.appendChild(cbox);
+        line.appendChild(breakCell);
+        line.appendChild(c('td', i.toString()));
+        var fn = opMap[operatorList.fnArray[i]];
+        var decArgs = args;
+        if (fn === 'showText') {
+          var glyphs = args[0];
+          var newArgs = [];
+          var str = [];
+          for (var j = 0; j < glyphs.length; j++) {
+            var glyph = glyphs[j];
+            if (typeof glyph === 'object' && glyph !== null) {
+              str.push(glyph.fontChar);
+            } else {
+              if (str.length > 0) {
+                newArgs.push(str.join(''));
+                str = [];
+              }
+              newArgs.push(glyph); // null or number
+            }
+          }
+          if (str.length > 0) {
+            newArgs.push(str.join(''));
+          }
+          decArgs = [newArgs];
+        }
+        line.appendChild(c('td', fn));
+        line.appendChild(c('td', JSON.stringify(simplifyArgs(decArgs))));
+      }
+      if (operatorsToDisplay < operatorList.fnArray.length) {
+        line = c('tr');
+        var lastCell = c('td', '...');
+        lastCell.colspan = 4;
+        chunk.appendChild(lastCell);
+      }
+      this.operatorListIdx = operatorList.fnArray.length;
+      this.table.appendChild(chunk);
+    },
+    getNextBreakPoint: function getNextBreakPoint() {
+      this.breakPoints.sort(function(a, b) {
+        return a - b;
+      });
+      for (var i = 0; i < this.breakPoints.length; i++) {
+        if (this.breakPoints[i] > this.currentIdx) {
+          return this.breakPoints[i];
+        }
+      }
+      return null;
+    },
+    breakIt: function breakIt(idx, callback) {
+      StepperManager.selectStepper(this.pageIndex, true);
+      var self = this;
+      var dom = document;
+      self.currentIdx = idx;
+      var listener = function(e) {
+        switch (e.keyCode) {
+          case 83: // step
+            dom.removeEventListener('keydown', listener);
+            self.nextBreakPoint = self.currentIdx + 1;
+            self.goTo(-1);
+            callback();
+            break;
+          case 67: // continue
+            dom.removeEventListener('keydown', listener);
+            var breakPoint = self.getNextBreakPoint();
+            self.nextBreakPoint = breakPoint;
+            self.goTo(-1);
+            callback();
+            break;
+        }
+      };
+      dom.addEventListener('keydown', listener);
+      self.goTo(idx);
+    },
+    goTo: function goTo(idx) {
+      var allRows = this.panel.getElementsByClassName('line');
+      for (var x = 0, xx = allRows.length; x < xx; ++x) {
+        var row = allRows[x];
+        if ((row.dataset.idx | 0) === idx) {
+          row.style.backgroundColor = 'rgb(251,250,207)';
+          row.scrollIntoView();
+        } else {
+          row.style.backgroundColor = null;
+        }
+      }
+    },
+  };
+  return Stepper;
+})();
+
+var Stats = (function Stats() {
+  var stats = [];
+  function clear(node) {
+    while (node.hasChildNodes()) {
+      node.removeChild(node.lastChild);
+    }
+  }
+  function getStatIndex(pageNumber) {
+    for (var i = 0, ii = stats.length; i < ii; ++i) {
+      if (stats[i].pageNumber === pageNumber) {
+        return i;
+      }
+    }
+    return false;
+  }
+  return {
+    // Properties/functions needed by PDFBug.
+    id: 'Stats',
+    name: 'Stats',
+    panel: null,
+    manager: null,
+    init(pdfjsLib) {
+      this.panel.setAttribute('style', 'padding: 5px;');
+    },
+    enabled: false,
+    active: false,
+    // Stats specific functions.
+    add(pageNumber, stat) {
+      if (!stat) {
+        return;
+      }
+      var statsIndex = getStatIndex(pageNumber);
+      if (statsIndex !== false) {
+        var b = stats[statsIndex];
+        this.panel.removeChild(b.div);
+        stats.splice(statsIndex, 1);
+      }
+      var wrapper = document.createElement('div');
+      wrapper.className = 'stats';
+      var title = document.createElement('div');
+      title.className = 'title';
+      title.textContent = 'Page: ' + pageNumber;
+      var statsDiv = document.createElement('div');
+      statsDiv.textContent = stat.toString();
+      wrapper.appendChild(title);
+      wrapper.appendChild(statsDiv);
+      stats.push({ pageNumber, div: wrapper, });
+      stats.sort(function(a, b) {
+        return a.pageNumber - b.pageNumber;
+      });
+      clear(this.panel);
+      for (var i = 0, ii = stats.length; i < ii; ++i) {
+        this.panel.appendChild(stats[i].div);
+      }
+    },
+    cleanup() {
+      stats = [];
+      clear(this.panel);
+    },
+  };
+})();
+
+// Manages all the debugging tools.
+window.PDFBug = (function PDFBugClosure() {
+  var panelWidth = 300;
+  var buttons = [];
+  var activePanel = null;
+
+  return {
+    tools: [
+      FontInspector,
+      StepperManager,
+      Stats
+    ],
+    enable(ids) {
+      var all = false, tools = this.tools;
+      if (ids.length === 1 && ids[0] === 'all') {
+        all = true;
+      }
+      for (var i = 0; i < tools.length; ++i) {
+        var tool = tools[i];
+        if (all || ids.includes(tool.id)) {
+          tool.enabled = true;
+        }
+      }
+      if (!all) {
+        // Sort the tools by the order they are enabled.
+        tools.sort(function(a, b) {
+          var indexA = ids.indexOf(a.id);
+          indexA = indexA < 0 ? tools.length : indexA;
+          var indexB = ids.indexOf(b.id);
+          indexB = indexB < 0 ? tools.length : indexB;
+          return indexA - indexB;
+        });
+      }
+    },
+    init(pdfjsLib, container) {
+      /*
+       * Basic Layout:
+       * PDFBug
+       *  Controls
+       *  Panels
+       *    Panel
+       *    Panel
+       *    ...
+       */
+      var ui = document.createElement('div');
+      ui.id = 'PDFBug';
+
+      var controls = document.createElement('div');
+      controls.setAttribute('class', 'controls');
+      ui.appendChild(controls);
+
+      var panels = document.createElement('div');
+      panels.setAttribute('class', 'panels');
+      ui.appendChild(panels);
+
+      container.appendChild(ui);
+      container.style.right = panelWidth + 'px';
+
+      // Initialize all the debugging tools.
+      var tools = this.tools;
+      var self = this;
+      for (var i = 0; i < tools.length; ++i) {
+        var tool = tools[i];
+        var panel = document.createElement('div');
+        var panelButton = document.createElement('button');
+        panelButton.textContent = tool.name;
+        panelButton.addEventListener('click', (function(selected) {
+          return function(event) {
+            event.preventDefault();
+            self.selectPanel(selected);
+          };
+        })(i));
+        controls.appendChild(panelButton);
+        panels.appendChild(panel);
+        tool.panel = panel;
+        tool.manager = this;
+        if (tool.enabled) {
+          tool.init(pdfjsLib);
+        } else {
+          panel.textContent = tool.name + ' is disabled. To enable add ' +
+                              ' "' + tool.id + '" to the pdfBug parameter ' +
+                              'and refresh (separate multiple by commas).';
+        }
+        buttons.push(panelButton);
+      }
+      this.selectPanel(0);
+    },
+    cleanup() {
+      for (var i = 0, ii = this.tools.length; i < ii; i++) {
+        if (this.tools[i].enabled) {
+          this.tools[i].cleanup();
+        }
+      }
+    },
+    selectPanel(index) {
+      if (typeof index !== 'number') {
+        index = this.tools.indexOf(index);
+      }
+      if (index === activePanel) {
+        return;
+      }
+      activePanel = index;
+      var tools = this.tools;
+      for (var j = 0; j < tools.length; ++j) {
+        if (j === index) {
+          buttons[j].setAttribute('class', 'active');
+          tools[j].active = true;
+          tools[j].panel.removeAttribute('hidden');
+        } else {
+          buttons[j].setAttribute('class', '');
+          tools[j].active = false;
+          tools[j].panel.setAttribute('hidden', 'true');
+        }
+      }
+    },
+  };
+})();

+ 506 - 0
app/public/netcasign/js/json2.js

@@ -0,0 +1,506 @@
+//  json2.js
+//  2016-10-28
+//  Public Domain.
+//  NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
+//  See http://www.JSON.org/js.html
+//  This code should be minified before deployment.
+//  See http://javascript.crockford.com/jsmin.html
+
+//  USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
+//  NOT CONTROL.
+
+//  This file creates a global JSON object containing two methods: stringify
+//  and parse. This file provides the ES5 JSON capability to ES3 systems.
+//  If a project might run on IE8 or earlier, then this file should be included.
+//  This file does nothing on ES5 systems.
+
+//      JSON.stringify(value, replacer, space)
+//          value       any JavaScript value, usually an object or array.
+//          replacer    an optional parameter that determines how object
+//                      values are stringified for objects. It can be a
+//                      function or an array of strings.
+//          space       an optional parameter that specifies the indentation
+//                      of nested structures. If it is omitted, the text will
+//                      be packed without extra whitespace. If it is a number,
+//                      it will specify the number of spaces to indent at each
+//                      level. If it is a string (such as "\t" or "&nbsp;"),
+//                      it contains the characters used to indent at each level.
+//          This method produces a JSON text from a JavaScript value.
+//          When an object value is found, if the object contains a toJSON
+//          method, its toJSON method will be called and the result will be
+//          stringified. A toJSON method does not serialize: it returns the
+//          value represented by the name/value pair that should be serialized,
+//          or undefined if nothing should be serialized. The toJSON method
+//          will be passed the key associated with the value, and this will be
+//          bound to the value.
+
+//          For example, this would serialize Dates as ISO strings.
+
+//              Date.prototype.toJSON = function (key) {
+//                  function f(n) {
+//                      // Format integers to have at least two digits.
+//                      return (n < 10)
+//                          ? "0" + n
+//                          : n;
+//                  }
+//                  return this.getUTCFullYear()   + "-" +
+//                       f(this.getUTCMonth() + 1) + "-" +
+//                       f(this.getUTCDate())      + "T" +
+//                       f(this.getUTCHours())     + ":" +
+//                       f(this.getUTCMinutes())   + ":" +
+//                       f(this.getUTCSeconds())   + "Z";
+//              };
+
+//          You can provide an optional replacer method. It will be passed the
+//          key and value of each member, with this bound to the containing
+//          object. The value that is returned from your method will be
+//          serialized. If your method returns undefined, then the member will
+//          be excluded from the serialization.
+
+//          If the replacer parameter is an array of strings, then it will be
+//          used to select the members to be serialized. It filters the results
+//          such that only members with keys listed in the replacer array are
+//          stringified.
+
+//          Values that do not have JSON representations, such as undefined or
+//          functions, will not be serialized. Such values in objects will be
+//          dropped; in arrays they will be replaced with null. You can use
+//          a replacer function to replace those with JSON values.
+
+//          JSON.stringify(undefined) returns undefined.
+
+//          The optional space parameter produces a stringification of the
+//          value that is filled with line breaks and indentation to make it
+//          easier to read.
+
+//          If the space parameter is a non-empty string, then that string will
+//          be used for indentation. If the space parameter is a number, then
+//          the indentation will be that many spaces.
+
+//          Example:
+
+//          text = JSON.stringify(["e", {pluribus: "unum"}]);
+//          // text is '["e",{"pluribus":"unum"}]'
+
+//          text = JSON.stringify(["e", {pluribus: "unum"}], null, "\t");
+//          // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]'
+
+//          text = JSON.stringify([new Date()], function (key, value) {
+//              return this[key] instanceof Date
+//                  ? "Date(" + this[key] + ")"
+//                  : value;
+//          });
+//          // text is '["Date(---current time---)"]'
+
+//      JSON.parse(text, reviver)
+//          This method parses a JSON text to produce an object or array.
+//          It can throw a SyntaxError exception.
+
+//          The optional reviver parameter is a function that can filter and
+//          transform the results. It receives each of the keys and values,
+//          and its return value is used instead of the original value.
+//          If it returns what it received, then the structure is not modified.
+//          If it returns undefined then the member is deleted.
+
+//          Example:
+
+//          // Parse the text. Values that look like ISO date strings will
+//          // be converted to Date objects.
+
+//          myData = JSON.parse(text, function (key, value) {
+//              var a;
+//              if (typeof value === "string") {
+//                  a =
+//   /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
+//                  if (a) {
+//                      return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4],
+//                          +a[5], +a[6]));
+//                  }
+//              }
+//              return value;
+//          });
+
+//          myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) {
+//              var d;
+//              if (typeof value === "string" &&
+//                      value.slice(0, 5) === "Date(" &&
+//                      value.slice(-1) === ")") {
+//                  d = new Date(value.slice(5, -1));
+//                  if (d) {
+//                      return d;
+//                  }
+//              }
+//              return value;
+//          });
+
+//  This is a reference implementation. You are free to copy, modify, or
+//  redistribute.
+
+/*jslint
+    eval, for, this
+*/
+
+/*property
+    JSON, apply, call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours,
+    getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join,
+    lastIndex, length, parse, prototype, push, replace, slice, stringify,
+    test, toJSON, toString, valueOf
+*/
+
+
+// Create a JSON object only if one does not already exist. We create the
+// methods in a closure to avoid creating global variables.
+
+if (typeof JSON !== "object") {
+    JSON = {};
+}
+
+(function () {
+    "use strict";
+
+    var rx_one = /^[\],:{}\s]*$/;
+    var rx_two = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g;
+    var rx_three = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g;
+    var rx_four = /(?:^|:|,)(?:\s*\[)+/g;
+    var rx_escapable = /[\\"\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
+    var rx_dangerous = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
+
+    function f(n) {
+        // Format integers to have at least two digits.
+        return n < 10
+            ? "0" + n
+            : n;
+    }
+
+    function this_value() {
+        return this.valueOf();
+    }
+
+    if (typeof Date.prototype.toJSON !== "function") {
+
+        Date.prototype.toJSON = function () {
+
+            return isFinite(this.valueOf())
+                ? this.getUTCFullYear() + "-" +
+                        f(this.getUTCMonth() + 1) + "-" +
+                        f(this.getUTCDate()) + "T" +
+                        f(this.getUTCHours()) + ":" +
+                        f(this.getUTCMinutes()) + ":" +
+                        f(this.getUTCSeconds()) + "Z"
+                : null;
+        };
+
+        Boolean.prototype.toJSON = this_value;
+        Number.prototype.toJSON = this_value;
+        String.prototype.toJSON = this_value;
+    }
+
+    var gap;
+    var indent;
+    var meta;
+    var rep;
+
+
+    function quote(string) {
+
+// If the string contains no control characters, no quote characters, and no
+// backslash characters, then we can safely slap some quotes around it.
+// Otherwise we must also replace the offending characters with safe escape
+// sequences.
+
+        rx_escapable.lastIndex = 0;
+        return rx_escapable.test(string)
+            ? "\"" + string.replace(rx_escapable, function (a) {
+                var c = meta[a];
+                return typeof c === "string"
+                    ? c
+                    : "\\u" + ("0000" + a.charCodeAt(0).toString(16)).slice(-4);
+            }) + "\""
+            : "\"" + string + "\"";
+    }
+
+
+    function str(key, holder) {
+
+// Produce a string from holder[key].
+
+        var i;          // The loop counter.
+        var k;          // The member key.
+        var v;          // The member value.
+        var length;
+        var mind = gap;
+        var partial;
+        var value = holder[key];
+
+// If the value has a toJSON method, call it to obtain a replacement value.
+
+        if (value && typeof value === "object" &&
+                typeof value.toJSON === "function") {
+            value = value.toJSON(key);
+        }
+
+// If we were called with a replacer function, then call the replacer to
+// obtain a replacement value.
+
+        if (typeof rep === "function") {
+            value = rep.call(holder, key, value);
+        }
+
+// What happens next depends on the value's type.
+
+        switch (typeof value) {
+        case "string":
+            return quote(value);
+
+        case "number":
+
+// JSON numbers must be finite. Encode non-finite numbers as null.
+
+            return isFinite(value)
+                ? String(value)
+                : "null";
+
+        case "boolean":
+        case "null":
+
+// If the value is a boolean or null, convert it to a string. Note:
+// typeof null does not produce "null". The case is included here in
+// the remote chance that this gets fixed someday.
+
+            return String(value);
+
+// If the type is "object", we might be dealing with an object or an array or
+// null.
+
+        case "object":
+
+// Due to a specification blunder in ECMAScript, typeof null is "object",
+// so watch out for that case.
+
+            if (!value) {
+                return "null";
+            }
+
+// Make an array to hold the partial results of stringifying this object value.
+
+            gap += indent;
+            partial = [];
+
+// Is the value an array?
+
+            if (Object.prototype.toString.apply(value) === "[object Array]") {
+
+// The value is an array. Stringify every element. Use null as a placeholder
+// for non-JSON values.
+
+                length = value.length;
+                for (i = 0; i < length; i += 1) {
+                    partial[i] = str(i, value) || "null";
+                }
+
+// Join all of the elements together, separated with commas, and wrap them in
+// brackets.
+
+                v = partial.length === 0
+                    ? "[]"
+                    : gap
+                        ? "[\n" + gap + partial.join(",\n" + gap) + "\n" + mind + "]"
+                        : "[" + partial.join(",") + "]";
+                gap = mind;
+                return v;
+            }
+
+// If the replacer is an array, use it to select the members to be stringified.
+
+            if (rep && typeof rep === "object") {
+                length = rep.length;
+                for (i = 0; i < length; i += 1) {
+                    if (typeof rep[i] === "string") {
+                        k = rep[i];
+                        v = str(k, value);
+                        if (v) {
+                            partial.push(quote(k) + (
+                                gap
+                                    ? ": "
+                                    : ":"
+                            ) + v);
+                        }
+                    }
+                }
+            } else {
+
+// Otherwise, iterate through all of the keys in the object.
+
+                for (k in value) {
+                    if (Object.prototype.hasOwnProperty.call(value, k)) {
+                        v = str(k, value);
+                        if (v) {
+                            partial.push(quote(k) + (
+                                gap
+                                    ? ": "
+                                    : ":"
+                            ) + v);
+                        }
+                    }
+                }
+            }
+
+// Join all of the member texts together, separated with commas,
+// and wrap them in braces.
+
+            v = partial.length === 0
+                ? "{}"
+                : gap
+                    ? "{\n" + gap + partial.join(",\n" + gap) + "\n" + mind + "}"
+                    : "{" + partial.join(",") + "}";
+            gap = mind;
+            return v;
+        }
+    }
+
+// If the JSON object does not yet have a stringify method, give it one.
+
+    if (typeof JSON.stringify !== "function") {
+        meta = {    // table of character substitutions
+            "\b": "\\b",
+            "\t": "\\t",
+            "\n": "\\n",
+            "\f": "\\f",
+            "\r": "\\r",
+            "\"": "\\\"",
+            "\\": "\\\\"
+        };
+        JSON.stringify = function (value, replacer, space) {
+
+// The stringify method takes a value and an optional replacer, and an optional
+// space parameter, and returns a JSON text. The replacer can be a function
+// that can replace values, or an array of strings that will select the keys.
+// A default replacer method can be provided. Use of the space parameter can
+// produce text that is more easily readable.
+
+            var i;
+            gap = "";
+            indent = "";
+
+// If the space parameter is a number, make an indent string containing that
+// many spaces.
+
+            if (typeof space === "number") {
+                for (i = 0; i < space; i += 1) {
+                    indent += " ";
+                }
+
+// If the space parameter is a string, it will be used as the indent string.
+
+            } else if (typeof space === "string") {
+                indent = space;
+            }
+
+// If there is a replacer, it must be a function or an array.
+// Otherwise, throw an error.
+
+            rep = replacer;
+            if (replacer && typeof replacer !== "function" &&
+                    (typeof replacer !== "object" ||
+                    typeof replacer.length !== "number")) {
+                throw new Error("JSON.stringify");
+            }
+
+// Make a fake root object containing our value under the key of "".
+// Return the result of stringifying the value.
+
+            return str("", {"": value});
+        };
+    }
+
+
+// If the JSON object does not yet have a parse method, give it one.
+
+    if (typeof JSON.parse !== "function") {
+        JSON.parse = function (text, reviver) {
+
+// The parse method takes a text and an optional reviver function, and returns
+// a JavaScript value if the text is a valid JSON text.
+
+            var j;
+
+            function walk(holder, key) {
+
+// The walk method is used to recursively walk the resulting structure so
+// that modifications can be made.
+
+                var k;
+                var v;
+                var value = holder[key];
+                if (value && typeof value === "object") {
+                    for (k in value) {
+                        if (Object.prototype.hasOwnProperty.call(value, k)) {
+                            v = walk(value, k);
+                            if (v !== undefined) {
+                                value[k] = v;
+                            } else {
+                                delete value[k];
+                            }
+                        }
+                    }
+                }
+                return reviver.call(holder, key, value);
+            }
+
+
+// Parsing happens in four stages. In the first stage, we replace certain
+// Unicode characters with escape sequences. JavaScript handles many characters
+// incorrectly, either silently deleting them, or treating them as line endings.
+
+            text = String(text);
+            rx_dangerous.lastIndex = 0;
+            if (rx_dangerous.test(text)) {
+                text = text.replace(rx_dangerous, function (a) {
+                    return "\\u" +
+                            ("0000" + a.charCodeAt(0).toString(16)).slice(-4);
+                });
+            }
+
+// In the second stage, we run the text against regular expressions that look
+// for non-JSON patterns. We are especially concerned with "()" and "new"
+// because they can cause invocation, and "=" because it can cause mutation.
+// But just to be safe, we want to reject all unexpected forms.
+
+// We split the second stage into 4 regexp operations in order to work around
+// crippling inefficiencies in IE's and Safari's regexp engines. First we
+// replace the JSON backslash pairs with "@" (a non-JSON character). Second, we
+// replace all simple value tokens with "]" characters. Third, we delete all
+// open brackets that follow a colon or comma or that begin the text. Finally,
+// we look to see that the remaining characters are only whitespace or "]" or
+// "," or ":" or "{" or "}". If that is so, then the text is safe for eval.
+
+            if (
+                rx_one.test(
+                    text
+                        .replace(rx_two, "@")
+                        .replace(rx_three, "]")
+                        .replace(rx_four, "")
+                )
+            ) {
+
+// In the third stage we use the eval function to compile the text into a
+// JavaScript structure. The "{" operator is subject to a syntactic ambiguity
+// in JavaScript: it can begin a block or an object literal. We wrap the text
+// in parens to eliminate the ambiguity.
+
+                j = eval("(" + text + ")");
+
+// In the optional fourth stage, we recursively walk the new structure, passing
+// each name/value pair to a reviver function for possible transformation.
+
+                return (typeof reviver === "function")
+                    ? walk({"": j}, "")
+                    : j;
+            }
+
+// If the text is not JSON parseable, then a SyntaxError is thrown.
+
+            throw new SyntaxError("JSON.parse");
+        };
+    }
+}());

文件差异内容过多而无法显示
+ 11012 - 0
app/public/netcasign/js/netca-jquery-client.js


+ 191 - 0
app/public/netcasign/js/netcaseal.js

@@ -0,0 +1,191 @@
+
+/* 
+    Netca电子签章模块(V1.3.0)
+    版本 V1.0.0
+        提供基础的电子签章接口    
+
+    版本 V1.2.0  2019-10-07
+        提供关键字的印章接口
+
+    版本 V1.3.0  2020-03-03
+        提供获取签名域信息接口
+*/
+
+NetcaPKI.getSealClientVersion = function (params) {
+    var requestQueryParams = {};
+    requestQueryParams["appName"] = "SignatureCreator";
+    requestQueryParams["function"] = "GetSealClientVersion";
+    requestQueryParams["param"] = params;
+    return NetcaPKI.SendNetcaCryptoJsonRpcMessage(requestQueryParams);
+}
+
+NetcaPKI.getNetcaSealImage=function(params){
+    var requestQueryParams = {};
+    requestQueryParams["function"] ="GetNetcaSealImage";
+    requestQueryParams["param"] = params;
+    return NetcaPKI.SendNetcaCryptoJsonRpcMessage(requestQueryParams);
+}
+
+NetcaPKI.GetNetcaSealImage=function(params){
+    return NetcaPKI.getNetcaSealImage(params);
+}
+
+NetcaPKI.SignatureCreatorPdfSignSealFieldOrPosition = function(params)
+{
+    var requestQueryParams = {};
+    requestQueryParams["appName"] = "SignatureCreator";
+    requestQueryParams["function"] = "SignatureCreatorSignSeal";
+    requestQueryParams["param"] = params;
+    return NetcaPKI.SendNetcaCryptoJsonRpcMessage(requestQueryParams);
+}
+
+NetcaPKI.signatureCreatorSignSeal = function(params)
+{
+    var requestQueryParams = {};
+    requestQueryParams["appName"] = "SignatureCreator";
+    requestQueryParams["function"] = "SignatureCreatorSignSeal";
+    requestQueryParams["param"] = params;
+    return NetcaPKI.SendNetcaCryptoJsonRpcMessage(requestQueryParams);
+}
+
+NetcaPKI.SignatureCreatorPdfSignSealFieldOrPositionEx = function(params)
+{
+    var requestQueryParams = {};
+    requestQueryParams["appName"] = "SignatureCreator";
+    requestQueryParams["function"] = "SignatureCreatorSignSealEx";
+    requestQueryParams["param"] = params;
+    return NetcaPKI.SendNetcaCryptoJsonRpcMessage(requestQueryParams);
+}
+
+NetcaPKI.signatureCreatorSignSealEx = function(params)
+{
+    var requestQueryParams = {};
+    requestQueryParams["appName"] = "SignatureCreator";
+    requestQueryParams["function"] = "SignatureCreatorSignSealEx";
+    requestQueryParams["param"] = params;
+    return NetcaPKI.SendNetcaCryptoJsonRpcMessage(requestQueryParams);
+}
+
+NetcaPKI.SignatureCreatorSignSealEx = function(params)
+{
+    return NetcaPKI.signatureCreatorSignSealEx(params)
+}
+
+NetcaPKI.signatureVerifierVerifyPDF = function (params) {
+    var requestQueryParams = {};
+    requestQueryParams["appName"] = "SignatureCreator";
+    requestQueryParams["function"] = "SignatureVerifierVerifyPDF";
+    requestQueryParams["param"] = params;
+    return NetcaPKI.SendNetcaCryptoJsonRpcMessage(requestQueryParams);
+}
+
+NetcaPKI.signatureVerifierUndoPDF = function (params) {
+    var requestQueryParams = {};
+    requestQueryParams["appName"] = "SignatureCreator";
+    requestQueryParams["function"] = "SignatureVerifierUndoPDF";
+    requestQueryParams["param"] = params;
+    return NetcaPKI.SendNetcaCryptoJsonRpcMessage(requestQueryParams);
+}
+
+NetcaPKI.Custom_PdfSignAndUpload=function(params){
+	var requestQueryParams = {};
+    requestQueryParams["function"] ="Custom_PdfSignAndUploadByBytes";
+    requestQueryParams["param"] = params;
+    return NetcaPKI.SendNetcaCryptoJsonRpcMessage(requestQueryParams);
+}
+
+NetcaPKI.custom_PdfSignAndUploadByBytes=function(params){
+	var requestQueryParams = {};
+    requestQueryParams["function"] ="Custom_PdfSignAndUploadByBytes";
+    requestQueryParams["param"] = params;
+    return NetcaPKI.SendNetcaCryptoJsonRpcMessage(requestQueryParams);
+}
+
+NetcaPKI.Custom_PdfSignAndUploadByURL=function(params){
+	var requestQueryParams = {};
+    requestQueryParams["function"] ="Custom_PdfSignAndUploadByURL";
+    requestQueryParams["param"] = params;
+    return NetcaPKI.SendNetcaCryptoJsonRpcMessage(requestQueryParams);
+}
+
+NetcaPKI.getSignatureFieldInfo=function(params){
+	var requestQueryParams = {};
+    requestQueryParams["appName"] = "SignatureCreator";
+    requestQueryParams["function"] ="GetSignatureFieldInfo";
+    requestQueryParams["param"] = params;
+    return NetcaPKI.SendNetcaCryptoJsonRpcMessage(requestQueryParams);
+}
+NetcaPKI.SelectSealSigntureInfoWithDispaly=function(params){
+    var requestQueryParams = {};
+    requestQueryParams["appName"] = "SignatureCreator";
+    requestQueryParams["function"] ="SelectSealSigntureInfoWithDispaly";
+    requestQueryParams["param"] = params;
+    return NetcaPKI.SendNetcaCryptoJsonRpcMessage(requestQueryParams);
+}
+NetcaPKI.getSealConfigInfo=function(params){
+    var requestQueryParams = {};
+    requestQueryParams["appName"] = "SignatureCreator";
+    requestQueryParams["function"] ="GetSealConfigInfo";
+    requestQueryParams["param"] = params;
+    return NetcaPKI.SendNetcaCryptoJsonRpcMessage(requestQueryParams);
+}
+NetcaPKI.printPDF=function(params){
+    var requestQueryParams = {};
+    requestQueryParams["appName"] = "SignatureCreator";
+    requestQueryParams["function"] ="PrintPDF";
+    requestQueryParams["param"] = params;
+    return NetcaPKI.SendNetcaCryptoJsonRpcMessage(requestQueryParams);
+}
+NetcaPKI.signatureCreatorBatchSeal=function(params){
+    var requestQueryParams = {};
+    requestQueryParams["appName"] = "SignatureCreator";
+    requestQueryParams["function"] ="SignatureCreatorBatchSeal";
+    requestQueryParams["param"] = params;
+    return NetcaPKI.SendNetcaCryptoJsonRpcMessage(requestQueryParams);
+}
+NetcaPKI.signatureCreatorAcrossPageSeal=function(params){
+    var requestQueryParams = {};
+    requestQueryParams["appName"] = "SignatureCreator";
+    requestQueryParams["function"] ="SignatureCreatorAcrossPageSeal";
+    requestQueryParams["param"] = params;
+    return NetcaPKI.SendNetcaCryptoJsonRpcMessage(requestQueryParams);
+}
+NetcaPKI.getPdfPageInfo=function(params){
+    var requestQueryParams = {};
+    requestQueryParams["appName"] = "SignatureCreator";
+    requestQueryParams["function"] ="GetPdfPageInfo";
+    requestQueryParams["param"] = params;
+    return NetcaPKI.SendNetcaCryptoJsonRpcMessage(requestQueryParams);
+}
+
+NetcaPKI.createStream=function(params){
+    var requestQueryParams = {};
+    requestQueryParams["appName"] = "SignatureCreator";
+    requestQueryParams["function"] ="CreateStream";
+    requestQueryParams["param"] = params;
+    return NetcaPKI.SendNetcaCryptoJsonRpcMessage(requestQueryParams);
+}
+
+NetcaPKI.writeStream=function(params){
+    var requestQueryParams = {};
+    requestQueryParams["appName"] = "SignatureCreator";
+    requestQueryParams["function"] ="WriteStream";
+    requestQueryParams["param"] = params;
+    return NetcaPKI.SendNetcaCryptoJsonRpcMessage(requestQueryParams);
+}
+
+NetcaPKI.destoryStream=function(params){
+    var requestQueryParams = {};
+    requestQueryParams["appName"] = "SignatureCreator";
+    requestQueryParams["function"] ="DestoryStream";
+    requestQueryParams["param"] = params;
+    return NetcaPKI.SendNetcaCryptoJsonRpcMessage(requestQueryParams);
+}
+
+NetcaPKI.readStream=function(params){
+    var requestQueryParams = {};
+    requestQueryParams["appName"] = "SignatureCreator";
+    requestQueryParams["function"] ="ReadStream";
+    requestQueryParams["param"] = params;
+    return NetcaPKI.SendNetcaCryptoJsonRpcMessage(requestQueryParams);
+}

文件差异内容过多而无法显示
+ 2048 - 0
app/public/netcasign/js/netcasealpdf.js


文件差异内容过多而无法显示
+ 2185 - 0
app/public/netcasign/js/netcawebsocket.js


文件差异内容过多而无法显示
+ 23599 - 0
app/public/netcasign/js/pdf.js


文件差异内容过多而无法显示
+ 1 - 0
app/public/netcasign/js/pdf.js.map


文件差异内容过多而无法显示
+ 55370 - 0
app/public/netcasign/js/pdf.worker.js


文件差异内容过多而无法显示
+ 1 - 0
app/public/netcasign/js/pdf.worker.js.map


文件差异内容过多而无法显示
+ 15580 - 0
app/public/netcasign/js/viewer.js


文件差异内容过多而无法显示
+ 1 - 0
app/public/netcasign/js/viewer.js.map


二进制
app/public/netcasign/ui/cmaps/78-EUC-H.bcmap


二进制
app/public/netcasign/ui/cmaps/78-EUC-V.bcmap


二进制
app/public/netcasign/ui/cmaps/78-H.bcmap


二进制
app/public/netcasign/ui/cmaps/78-RKSJ-H.bcmap


二进制
app/public/netcasign/ui/cmaps/78-RKSJ-V.bcmap


二进制
app/public/netcasign/ui/cmaps/78-V.bcmap


二进制
app/public/netcasign/ui/cmaps/78ms-RKSJ-H.bcmap


二进制
app/public/netcasign/ui/cmaps/78ms-RKSJ-V.bcmap


二进制
app/public/netcasign/ui/cmaps/83pv-RKSJ-H.bcmap


二进制
app/public/netcasign/ui/cmaps/90ms-RKSJ-H.bcmap


二进制
app/public/netcasign/ui/cmaps/90ms-RKSJ-V.bcmap


二进制
app/public/netcasign/ui/cmaps/90msp-RKSJ-H.bcmap


二进制
app/public/netcasign/ui/cmaps/90msp-RKSJ-V.bcmap


二进制
app/public/netcasign/ui/cmaps/90pv-RKSJ-H.bcmap


二进制
app/public/netcasign/ui/cmaps/90pv-RKSJ-V.bcmap


二进制
app/public/netcasign/ui/cmaps/Add-H.bcmap


二进制
app/public/netcasign/ui/cmaps/Add-RKSJ-H.bcmap


二进制
app/public/netcasign/ui/cmaps/Add-RKSJ-V.bcmap


二进制
app/public/netcasign/ui/cmaps/Add-V.bcmap


二进制
app/public/netcasign/ui/cmaps/Adobe-CNS1-0.bcmap


二进制
app/public/netcasign/ui/cmaps/Adobe-CNS1-1.bcmap


二进制
app/public/netcasign/ui/cmaps/Adobe-CNS1-2.bcmap


二进制
app/public/netcasign/ui/cmaps/Adobe-CNS1-3.bcmap


二进制
app/public/netcasign/ui/cmaps/Adobe-CNS1-4.bcmap


二进制
app/public/netcasign/ui/cmaps/Adobe-CNS1-5.bcmap


二进制
app/public/netcasign/ui/cmaps/Adobe-CNS1-6.bcmap


二进制
app/public/netcasign/ui/cmaps/Adobe-CNS1-UCS2.bcmap


二进制
app/public/netcasign/ui/cmaps/Adobe-GB1-0.bcmap


二进制
app/public/netcasign/ui/cmaps/Adobe-GB1-1.bcmap


二进制
app/public/netcasign/ui/cmaps/Adobe-GB1-2.bcmap


二进制
app/public/netcasign/ui/cmaps/Adobe-GB1-3.bcmap


二进制
app/public/netcasign/ui/cmaps/Adobe-GB1-4.bcmap


二进制
app/public/netcasign/ui/cmaps/Adobe-GB1-5.bcmap


二进制
app/public/netcasign/ui/cmaps/Adobe-GB1-UCS2.bcmap


二进制
app/public/netcasign/ui/cmaps/Adobe-Japan1-0.bcmap


二进制
app/public/netcasign/ui/cmaps/Adobe-Japan1-1.bcmap


二进制
app/public/netcasign/ui/cmaps/Adobe-Japan1-2.bcmap


二进制
app/public/netcasign/ui/cmaps/Adobe-Japan1-3.bcmap


二进制
app/public/netcasign/ui/cmaps/Adobe-Japan1-4.bcmap


二进制
app/public/netcasign/ui/cmaps/Adobe-Japan1-5.bcmap


二进制
app/public/netcasign/ui/cmaps/Adobe-Japan1-6.bcmap


二进制
app/public/netcasign/ui/cmaps/Adobe-Japan1-UCS2.bcmap


+ 0 - 0
app/public/netcasign/ui/cmaps/Adobe-Korea1-0.bcmap


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