Selaa lähdekoodia

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

Tony Kang 8 kuukautta sitten
vanhempi
commit
7d94256117
69 muutettua tiedostoa jossa 3523 lisäystä ja 1151 poistoa
  1. 25 3
      app/const/audit.js
  2. 12 6
      app/const/spread.js
  3. 1 0
      app/controller/change_controller.js
  4. 102 1
      app/controller/dashboard_controller.js
  5. 30 0
      app/controller/ledger_controller.js
  6. 1 1
      app/controller/login_controller.js
  7. 61 46
      app/controller/material_controller.js
  8. 152 0
      app/controller/pay_controller.js
  9. 4 0
      app/controller/revise_controller.js
  10. 6 0
      app/controller/stage_controller.js
  11. 17 3
      app/controller/tender_controller.js
  12. 45 0
      app/controller/wap_controller.js
  13. 20 0
      app/lib/rm/budget.js
  14. 22 11
      app/lib/ybp_tree.js
  15. 43 52
      app/middleware/material_check.js
  16. 55 0
      app/middleware/phase_pay_check.js
  17. 2 2
      app/middleware/stage_check.js
  18. 4 0
      app/public/css/main.css
  19. BIN
      app/public/images/dsk.png
  20. BIN
      app/public/images/ybpx.png
  21. 18 1
      app/public/js/dashboard.js
  22. 4 2
      app/public/js/file_detail.js
  23. 11 1
      app/public/js/filing_template.js
  24. 38 0
      app/public/js/ledger.js
  25. 10 1
      app/public/js/material.js
  26. 249 33
      app/public/js/material_audit.js
  27. 6 1
      app/public/js/measure_compare.js
  28. 123 142
      app/public/js/measure_material.js
  29. 30 2
      app/public/js/path_tree.js
  30. 290 0
      app/public/js/shares/dsk.js
  31. 32 13
      app/public/js/stage_gather.js
  32. 1 1
      app/public/js/tender_list_info.js
  33. 9 0
      app/router.js
  34. 1 1
      app/service/file.js
  35. 1 1
      app/service/filing.js
  36. 2 2
      app/service/filing_template.js
  37. 17 10
      app/service/ledger.js
  38. 48 3
      app/service/material.js
  39. 649 328
      app/service/material_audit.js
  40. 65 20
      app/service/message.js
  41. 64 0
      app/service/message_att.js
  42. 165 0
      app/service/phase_pay.js
  43. 2 1
      app/service/project_account.js
  44. 2 0
      app/service/report_memory.js
  45. 26 4
      app/service/stage_audit.js
  46. 2 2
      app/service/sub_project.js
  47. 2 2
      app/view/dashboard/index.ejs
  48. 3 1
      app/view/dashboard/modal.ejs
  49. 12 0
      app/view/dashboard/msg.ejs
  50. 109 1
      app/view/dashboard/msg_add.ejs
  51. 1 0
      app/view/dashboard/msg_modal.ejs
  52. 1 1
      app/view/file/template_modal.ejs
  53. 2 1
      app/view/ledger/explode.ejs
  54. 45 0
      app/view/ledger/explode_modal.ejs
  55. 2 2
      app/view/material/audit_btn.ejs
  56. 565 417
      app/view/material/audit_modal.ejs
  57. 17 11
      app/view/material/index.ejs
  58. 0 2
      app/view/material/modal.ejs
  59. 6 1
      app/view/measure/stage.ejs
  60. 107 0
      app/view/phase_pay/index.ejs
  61. 23 0
      app/view/shares/dsk_modal.ejs
  62. 2 2
      app/view/shares/select_rela_tender_modal.ejs
  63. 11 9
      app/view/tender/detail.ejs
  64. 3 3
      app/view/tender/detail_modal.ejs
  65. 5 0
      app/view/tender/tender_sub_menu.ejs
  66. 11 1
      app/view/wap/msg.ejs
  67. 17 0
      config/web.js
  68. 81 0
      db_script/material_audit_order.js
  69. 31 3
      sql/update.sql

+ 25 - 3
app/const/audit.js

@@ -11,8 +11,8 @@
 const auditType = (function () {
 const auditType = (function () {
     const types = [
     const types = [
         { key: 'common', name: '个人', value: 1, short: '', long: '', class: '', },
         { key: 'common', name: '个人', value: 1, short: '', long: '', class: '', },
-        { key: 'and', name: '会签', value: 2, short: '会', long: '多人会签', class: 'primary', valid: ['ledger', 'revise', 'stage', 'change', 'financial'] },
-        { key: 'or', name: '或签', value: 3, short: '或', long: '多人或签', class: 'success', valid: ['ledger', 'revise', 'stage', 'change', 'financial'] },
+        { key: 'and', name: '会签', value: 2, short: '会', long: '多人会签', class: 'primary', valid: ['ledger', 'revise', 'stage', 'change', 'material', 'financial'] },
+        { key: 'or', name: '或签', value: 3, short: '或', long: '多人或签', class: 'success', valid: ['ledger', 'revise', 'stage', 'change', 'material', 'financial'] },
         { key: 'union', name: '协同', value: 4, short: '协', long: '多人协同', class: 'warning', valid: ['stage']},
         { key: 'union', name: '协同', value: 4, short: '协', long: '多人协同', class: 'warning', valid: ['stage']},
     ];
     ];
     const key = {};
     const key = {};
@@ -543,6 +543,7 @@ const material = (function() {
         checkNo: 4, // 审批退回原报
         checkNo: 4, // 审批退回原报
         checkNoPre: 5, // 审批退回上一人
         checkNoPre: 5, // 审批退回上一人
         checkAgain: 6, // 终审退回  --该状态仅可用于,终审退回时,修改原终审的审批状态,并同时新增一条新的终审审批中记录
         checkAgain: 6, // 终审退回  --该状态仅可用于,终审退回时,修改原终审的审批状态,并同时新增一条新的终审审批中记录
+        checkSkip: 8, // 跳过
     };
     };
     // 流程状态提示
     // 流程状态提示
     const statusString = [];
     const statusString = [];
@@ -577,6 +578,27 @@ const material = (function() {
     statusButtonClass[status.checkNo] = 'btn-warning';
     statusButtonClass[status.checkNo] = 'btn-warning';
 
 
     // 描述文本
     // 描述文本
+    const auditString = [];
+    auditString[status.uncheck] = '';
+    auditString[status.checking] = '审批中';
+    auditString[status.checked] = '审批通过';
+    auditString[status.checkNo] = '审批退回';
+    auditString[status.checkNoPre] = '审批退回';
+    auditString[status.checkAgain] = '重新审批';
+    auditString[status.checkCancel] = '撤回';
+    auditString[status.checkSkip] = '审批通过';
+    // 文字样式
+    const auditStringClass = [];
+    auditStringClass[status.uncheck] = '';
+    auditStringClass[status.checking] = 'text-warning';
+    auditStringClass[status.checked] = 'text-success';
+    auditStringClass[status.checkNo] = 'text-warning';
+    auditStringClass[status.checkNoPre] = 'text-warning';
+    auditStringClass[status.checkAgain] = 'text-warning';
+    auditStringClass[status.checkCancel] = 'text-warning';
+    auditStringClass[status.checkSkip] = 'text-success';
+
+    // 描述文本
     const auditProgress = [];
     const auditProgress = [];
     auditProgress[status.uncheck] = '待上报';
     auditProgress[status.uncheck] = '待上报';
     auditProgress[status.checking] = '审批中';
     auditProgress[status.checking] = '审批中';
@@ -588,7 +610,7 @@ const material = (function() {
     auditProgressClass[status.checking] = 'text-warning';
     auditProgressClass[status.checking] = 'text-warning';
     auditProgressClass[status.checked] = 'text-success';
     auditProgressClass[status.checked] = 'text-success';
     auditProgressClass[status.checkNo] = 'text-warning';
     auditProgressClass[status.checkNo] = 'text-warning';
-    return { status, statusString, statusClass, statusButton, statusButtonClass, auditProgress, auditProgressClass };
+    return { status, statusString, statusClass, statusButton, statusButtonClass, auditString, auditStringClass, auditProgress, auditProgressClass };
 })();
 })();
 
 
 // 预付款审批流程
 // 预付款审批流程

+ 12 - 6
app/const/spread.js

@@ -1234,16 +1234,18 @@ const stageGather = {
             {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'total_price', hAlign: 2, width: 60, type: 'Number'},
             {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'total_price', hAlign: 2, width: 60, type: 'Number'},
             {title: '本期合同计量|数量', colSpan: '2|1', rowSpan: '1|1', field: 'contract_qty', hAlign: 2, width: 60, type: 'Number'},
             {title: '本期合同计量|数量', colSpan: '2|1', rowSpan: '1|1', field: 'contract_qty', hAlign: 2, width: 60, type: 'Number'},
             {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'contract_tp', hAlign: 2, width: 60, type: 'Number'},
             {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'contract_tp', hAlign: 2, width: 60, type: 'Number'},
-            {title: '本期数量变更|数量', colSpan: '2|1', rowSpan: '1|1', field: 'qc_qty', hAlign: 2, width: 60, type: 'Number'},
+            {title: '本期数量变更|数量', colSpan: '3|1', rowSpan: '1|1', field: 'qc_qty', hAlign: 2, width: 60, type: 'Number'},
             {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'qc_tp', hAlign: 2, width: 60, type: 'Number'},
             {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'qc_tp', hAlign: 2, width: 60, type: 'Number'},
+            {title: '|不计价', colSpan: '|1', rowSpan: '|1', field: 'qc_minus_qty', hAlign: 2, width: 60, type: 'Number'},
             { title: '本期补差|原单价', colSpan: '2|1', rowSpan: '1|1', field: 'org_price', hAlign: 2, width: 60, type: 'Number' },
             { title: '本期补差|原单价', colSpan: '2|1', rowSpan: '1|1', field: 'org_price', hAlign: 2, width: 60, type: 'Number' },
             { title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'pc_tp', hAlign: 2, width: 60, type: 'Number' },
             { title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'pc_tp', hAlign: 2, width: 60, type: 'Number' },
             {title: '本期完成计量|数量', colSpan: '2|1', rowSpan: '1|1', field: 'gather_qty', hAlign: 2, width: 60, type: 'Number'},
             {title: '本期完成计量|数量', colSpan: '2|1', rowSpan: '1|1', field: 'gather_qty', hAlign: 2, width: 60, type: 'Number'},
             {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'gather_tp', hAlign: 2, width: 60, type: 'Number'},
             {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'gather_tp', hAlign: 2, width: 60, type: 'Number'},
             {title: '截止本期合同计量|数量', colSpan: '2|1', rowSpan: '1|1', field: 'end_contract_qty', hAlign: 2, width: 60, type: 'Number'},
             {title: '截止本期合同计量|数量', colSpan: '2|1', rowSpan: '1|1', field: 'end_contract_qty', hAlign: 2, width: 60, type: 'Number'},
             {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'end_contract_tp', hAlign: 2, width: 60, type: 'Number'},
             {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'end_contract_tp', hAlign: 2, width: 60, type: 'Number'},
-            {title: '截止本期数量变更|数量', colSpan: '2|1', rowSpan: '1|1', field: 'end_qc_qty', hAlign: 2, width: 60, type: 'Number'},
+            {title: '截止本期数量变更|数量', colSpan: '3|1', rowSpan: '1|1', field: 'end_qc_qty', hAlign: 2, width: 60, type: 'Number'},
             {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'end_qc_tp', hAlign: 2, width: 60, type: 'Number'},
             {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'end_qc_tp', hAlign: 2, width: 60, type: 'Number'},
+            {title: '|不计价', colSpan: '|1', rowSpan: '|1', field: 'end_qc_minus_qty', hAlign: 2, width: 60, type: 'Number'},
             {title: '截止本期完成计量|数量', colSpan: '3|1', rowSpan: '1|1', field: 'end_gather_qty', hAlign: 2, width: 60, type: 'Number'},
             {title: '截止本期完成计量|数量', colSpan: '3|1', rowSpan: '1|1', field: 'end_gather_qty', hAlign: 2, width: 60, type: 'Number'},
             {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'end_gather_tp', hAlign: 2, width: 60, type: 'Number'},
             {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'end_gather_tp', hAlign: 2, width: 60, type: 'Number'},
             {title: '|完成率(%)', colSpan: '|1', rowSpan: '|1', field: 'end_final_1_percent', hAlign: 2, width: 80, type: 'Number'},
             {title: '|完成率(%)', colSpan: '|1', rowSpan: '|1', field: 'end_final_1_percent', hAlign: 2, width: 80, type: 'Number'},
@@ -1264,11 +1266,13 @@ const stageGather = {
         cols: [
         cols: [
             {title: '项目节编号', colSpan: '1', rowSpan: '2', field: 'code', hAlign: 0, width: 100, formatter: '@'},
             {title: '项目节编号', colSpan: '1', rowSpan: '2', field: 'code', hAlign: 0, width: 100, formatter: '@'},
             {title: '台账数量', colSpan: '1', rowSpan: '2', field: 'quantity', hAlign: 2, width: 60, type: 'Number'},
             {title: '台账数量', colSpan: '1', rowSpan: '2', field: 'quantity', hAlign: 2, width: 60, type: 'Number'},
-            {title: '本期计量数量|合同', colSpan: '3|1', rowSpan: '1|1', field: 'contract_qty', hAlign: 2, width: 60, type: 'Number'},
+            {title: '本期计量数量|合同', colSpan: '4|1', rowSpan: '1|1', field: 'contract_qty', hAlign: 2, width: 60, type: 'Number'},
             {title: '|数量变更', colSpan: '|1', rowSpan: '|1', field: 'qc_qty', hAlign: 2, width: 60, type: 'Number'},
             {title: '|数量变更', colSpan: '|1', rowSpan: '|1', field: 'qc_qty', hAlign: 2, width: 60, type: 'Number'},
+            {title: '|不计价', colSpan: '|1', rowSpan: '|1', field: 'qc_minus_qty', hAlign: 2, width: 60, type: 'Number'},
             {title: '|完成', colSpan: '|1', rowSpan: '1|1', field: 'gather_qty', hAlign: 2, width: 60, type: 'Number'},
             {title: '|完成', colSpan: '|1', rowSpan: '1|1', field: 'gather_qty', hAlign: 2, width: 60, type: 'Number'},
-            {title: '截止本期计量数量|合同', colSpan: '4|1', rowSpan: '1|1', field: 'end_contract_qty', hAlign: 2, width: 60, type: 'Number'},
+            {title: '截止本期计量数量|合同', colSpan: '5|1', rowSpan: '1|1', field: 'end_contract_qty', hAlign: 2, width: 60, type: 'Number'},
             {title: '|数量变更', colSpan: '|1', rowSpan: '|1', field: 'end_qc_qty', hAlign: 2, width: 60, type: 'Number'},
             {title: '|数量变更', colSpan: '|1', rowSpan: '|1', field: 'end_qc_qty', hAlign: 2, width: 60, type: 'Number'},
+            {title: '|不计价', colSpan: '|1', rowSpan: '|1', field: 'end_qc_minus_qty', hAlign: 2, width: 60, type: 'Number'},
             {title: '|完成', colSpan: '|1', rowSpan: '|1', field: 'end_gather_qty', hAlign: 2, width: 60, type: 'Number'},
             {title: '|完成', colSpan: '|1', rowSpan: '|1', field: 'end_gather_qty', hAlign: 2, width: 60, type: 'Number'},
             {title: '|完成率(%)', colSpan: '1', rowSpan: '|1', field: 'end_final_1_percent', hAlign: 2, width: 80, type: 'Number', visible: false},
             {title: '|完成率(%)', colSpan: '1', rowSpan: '|1', field: 'end_final_1_percent', hAlign: 2, width: 80, type: 'Number', visible: false},
             {title: '单位工程', colSpan: '1', rowSpan: '2', field: 'dwgc', hAlign: 0, width: 80, formatter: '@'},
             {title: '单位工程', colSpan: '1', rowSpan: '2', field: 'dwgc', hAlign: 0, width: 80, formatter: '@'},
@@ -1295,11 +1299,13 @@ const stageGather = {
             {title: '细目', colSpan: '1', rowSpan: '2', field: 'jldy', hAlign: 0, width: 80, formatter: '@', visible: false},
             {title: '细目', colSpan: '1', rowSpan: '2', field: 'jldy', hAlign: 0, width: 80, formatter: '@', visible: false},
             {title: '计量单元', colSpan: '1', rowSpan: '2', field: 'bwmx', hAlign: 0, width: 80, formatter: '@'},
             {title: '计量单元', colSpan: '1', rowSpan: '2', field: 'bwmx', hAlign: 0, width: 80, formatter: '@'},
             {title: '台账数量', colSpan: '1', rowSpan: '2', field: 'quantity', hAlign: 2, width: 60, type: 'Number'},
             {title: '台账数量', colSpan: '1', rowSpan: '2', field: 'quantity', hAlign: 2, width: 60, type: 'Number'},
-            {title: '本期计量数量|合同', colSpan: '3|1', rowSpan: '1|1', field: 'contract_qty', hAlign: 2, width: 60, type: 'Number'},
+            {title: '本期计量数量|合同', colSpan: '4|1', rowSpan: '1|1', field: 'contract_qty', hAlign: 2, width: 60, type: 'Number'},
             {title: '|数量变更', colSpan: '|1', rowSpan: '|1', field: 'qc_qty', hAlign: 2, width: 60, type: 'Number'},
             {title: '|数量变更', colSpan: '|1', rowSpan: '|1', field: 'qc_qty', hAlign: 2, width: 60, type: 'Number'},
+            {title: '|不计价', colSpan: '|1', rowSpan: '|1', field: 'qc_minus_qty', hAlign: 2, width: 60, type: 'Number'},
             {title: '|完成', colSpan: '|1', rowSpan: '1|1', field: 'gather_qty', hAlign: 2, width: 60, type: 'Number'},
             {title: '|完成', colSpan: '|1', rowSpan: '1|1', field: 'gather_qty', hAlign: 2, width: 60, type: 'Number'},
-            {title: '截止本期计量数量|合同', colSpan: '3|1', rowSpan: '1|1', field: 'end_contract_qty', hAlign: 2, width: 60, type: 'Number'},
+            {title: '截止本期计量数量|合同', colSpan: '4|1', rowSpan: '1|1', field: 'end_contract_qty', hAlign: 2, width: 60, type: 'Number'},
             {title: '|数量变更', colSpan: '|1', rowSpan: '|1', field: 'end_qc_qty', hAlign: 2, width: 60, type: 'Number'},
             {title: '|数量变更', colSpan: '|1', rowSpan: '|1', field: 'end_qc_qty', hAlign: 2, width: 60, type: 'Number'},
+            {title: '|不计价', colSpan: '|1', rowSpan: '|1', field: 'end_qc_minus_qty', hAlign: 2, width: 60, type: 'Number'},
             {title: '|完成', colSpan: '|1', rowSpan: '|1', field: 'end_gather_qty', hAlign: 2, width: 60, type: 'Number'},
             {title: '|完成', colSpan: '|1', rowSpan: '|1', field: 'end_gather_qty', hAlign: 2, width: 60, type: 'Number'},
         ],
         ],
         emptyRows: 0,
         emptyRows: 0,

+ 1 - 0
app/controller/change_controller.js

@@ -1564,6 +1564,7 @@ module.exports = app => {
                 switch (data.type) {
                 switch (data.type) {
                     case 'gather':
                     case 'gather':
                         responseData.data = await ctx.service.changeAuditList.gatherBgBills(ctx.tender.id);
                         responseData.data = await ctx.service.changeAuditList.gatherBgBills(ctx.tender.id);
+                        responseData.data.sort((a, b) => { return ctx.helper.compareCode(a.code, b.code) });
                         break;
                         break;
                     default:
                     default:
                         throw '查询的数据不存在';
                         throw '查询的数据不存在';

+ 102 - 1
app/controller/dashboard_controller.js

@@ -13,6 +13,9 @@ const officeList = require('../const/cld_office').list;
 const maintainConst = require('../const/maintain');
 const maintainConst = require('../const/maintain');
 const typeColMap = require('../const/advance').typeColMap;
 const typeColMap = require('../const/advance').typeColMap;
 const moment = require('moment');
 const moment = require('moment');
+const fs = require('fs');
+const path = require('path');
+const sendToWormhole = require('stream-wormhole');
 
 
 module.exports = app => {
 module.exports = app => {
 
 
@@ -150,6 +153,10 @@ module.exports = app => {
                 if (msgInfo && msgInfo.type === 1 && msgInfo.project_id !== ctx.session.sessionProject.id) {
                 if (msgInfo && msgInfo.type === 1 && msgInfo.project_id !== ctx.session.sessionProject.id) {
                     throw '非该项目通知无权查看';
                     throw '非该项目通知无权查看';
                 }
                 }
+                if (msgInfo) {
+                    msgInfo.content = JSON.parse(JSON.stringify(msgInfo.content).replace(/\\r\\n/g, '<br>').replace(/\\"/g, '&#34;').replace(/'/g, '&#39;').replace(/\\t/g, '&#9;'));
+                    msgInfo.files = await this.ctx.service.messageAtt.getAtt(msgInfo.id);
+                }
 
 
                 const total = type === 1 ?
                 const total = type === 1 ?
                     await ctx.service.message.count({ project_id: ctx.session.sessionProject.id, type }) :
                     await ctx.service.message.count({ project_id: ctx.session.sessionProject.id, type }) :
@@ -206,12 +213,17 @@ module.exports = app => {
                 const rule = ctx.service.message.rule();
                 const rule = ctx.service.message.rule();
                 const jsValidator = await this.jsValidator.convert(rule).build();
                 const jsValidator = await this.jsValidator.convert(rule).build();
                 const msgInfo = id === 0 ? {} : await ctx.service.message.getDataById(id);
                 const msgInfo = id === 0 ? {} : await ctx.service.message.getDataById(id);
+                const files = await ctx.service.messageAtt.getAtt(id);
                 const renderData = {
                 const renderData = {
                     jsValidator,
                     jsValidator,
                     msgInfo,
                     msgInfo,
+                    files,
+                    whiteList: ctx.app.config.multipart.whitelist,
+                    moment,
                 };
                 };
                 await this.layout('dashboard/msg_add.ejs', renderData, 'dashboard/msg_modal.ejs');
                 await this.layout('dashboard/msg_add.ejs', renderData, 'dashboard/msg_modal.ejs');
             } catch (error) {
             } catch (error) {
+                console.log(error);
                 // this.setMessage(error.toString(), this.messageType.ERROR);
                 // this.setMessage(error.toString(), this.messageType.ERROR);
                 ctx.redirect(ctx.request.header.referer);
                 ctx.redirect(ctx.request.header.referer);
             }
             }
@@ -266,6 +278,7 @@ module.exports = app => {
                     ctx.redirect('/dashboard/msg');
                     ctx.redirect('/dashboard/msg');
                 }
                 }
             } catch (error) {
             } catch (error) {
+                console.log(error);
                 ctx.redirect(ctx.request.header.referer);
                 ctx.redirect(ctx.request.header.referer);
             }
             }
         }
         }
@@ -287,11 +300,12 @@ module.exports = app => {
                 if (!msgInfo || msgInfo.create_uid !== ctx.session.sessionUser.accountId) {
                 if (!msgInfo || msgInfo.create_uid !== ctx.session.sessionUser.accountId) {
                     throw '通知不存在或无权限操作';
                     throw '通知不存在或无权限操作';
                 }
                 }
-                const result = await ctx.service.message.deleteById(msgInfo.id);
+                const result = await ctx.service.message.deleteMsg(msgInfo.id);
                 if (result) {
                 if (result) {
                     ctx.redirect('/dashboard/msg');
                     ctx.redirect('/dashboard/msg');
                 }
                 }
             } catch (error) {
             } catch (error) {
+                console.log(error);
                 ctx.redirect(ctx.request.header.referer);
                 ctx.redirect(ctx.request.header.referer);
             }
             }
         }
         }
@@ -310,6 +324,93 @@ module.exports = app => {
                 ctx.body = { err: 1, msg: err.toString(), data: null };
                 ctx.body = { err: 1, msg: err.toString(), data: null };
             }
             }
         }
         }
+
+        /**
+         * 上传附件
+         * @param {*} ctx 上下文
+         */
+        async msgUploadFile(ctx) {
+            let stream;
+            try {
+                const responseData = { err: 0, msg: '', data: {} };
+                const mid = ctx.params.id || 0;
+                if (!mid) throw '参数有误';
+                const parts = this.ctx.multipart({
+                    autoFields: true,
+                });
+                const files = [];
+                const create_time = Date.parse(new Date()) / 1000;
+                let idx = 0;
+                while ((stream = await parts()) !== undefined) {
+                    if (!stream.filename) {
+                        // 如果没有传入直接返回
+                        return;
+                    }
+                    const fileInfo = path.parse(stream.filename);
+                    const filepath = `app/public/upload/message/fujian_${create_time + idx.toString() + fileInfo.ext}`;
+                    await ctx.app.fujianOss.put(ctx.app.config.fujianOssFolder + filepath, stream);
+                    files.push({ filepath, name: stream.filename, ext: fileInfo.ext });
+                    ++idx;
+                    stream && (await sendToWormhole(stream));
+                }
+                const in_time = new Date();
+                const payload = files.map(file => {
+                    let idx;
+                    if (Array.isArray(parts.field.name)) {
+                        idx = parts.field.name.findIndex(name => name === file.name);
+                    } else {
+                        idx = 'isString';
+                    }
+                    const newFile = {
+                        project_id: ctx.session.sessionProject.id,
+                        mid,
+                        uid: ctx.session.sessionUser.accountId,
+                        filename: file.name,
+                        fileext: file.ext,
+                        filesize: ctx.helper.bytesToSize(idx === 'isString' ? parts.field.size : parts.field.size[idx]),
+                        filepath: file.filepath,
+                        upload_time: in_time,
+                    };
+                    return newFile;
+                });
+                // 执行文件信息写入数据库
+                await ctx.service.messageAtt.saveFileMsgToDb(payload);
+                // 将最新的当前标段的所有文件信息返回
+                responseData.data = await ctx.service.messageAtt.getAtt(mid);
+                ctx.body = responseData;
+            } catch (err) {
+                stream && (await sendToWormhole(stream));
+                this.log(err);
+                ctx.body = { err: 1, msg: err.toString(), data: null };
+            }
+        }
+
+        /**
+         * 删除附件
+         * @param {Ojbect} ctx 上下文
+         */
+        async msgDeleteFile(ctx) {
+            try {
+                const mid = ctx.params.id || 0;
+                const responseData = { err: 0, msg: '', data: {} };
+                const data = JSON.parse(ctx.request.body.data);
+                const fileInfo = await ctx.service.messageAtt.getDataById(data.id);
+                if (fileInfo) {
+                    // 先删除文件
+                    // await fs.unlinkSync(path.resolve(this.app.baseDir, './app', fileInfo.filepath));
+                    await ctx.app.fujianOss.delete(ctx.app.config.fujianOssFolder + fileInfo.filepath);
+                    // 再删除数据库
+                    await ctx.service.messageAtt.delete(data.id);
+                } else {
+                    throw '不存在该文件';
+                }
+                responseData.data = await ctx.service.messageAtt.getAtt(mid);
+                ctx.body = responseData;
+            } catch (err) {
+                this.log(err);
+                ctx.body = { err: 1, msg: err.toString(), data: null };
+            }
+        }
     }
     }
 
 
     return DashboardController;
     return DashboardController;

+ 30 - 0
app/controller/ledger_controller.js

@@ -25,6 +25,7 @@ const sendToWormhole = require('stream-wormhole');
 const spreadSetting = require('../lib/spread_setting');
 const spreadSetting = require('../lib/spread_setting');
 const PermissionCheck = require('../const/account_permission').PermissionCheck;
 const PermissionCheck = require('../const/account_permission').PermissionCheck;
 const streamToArray = require('stream-to-array');
 const streamToArray = require('stream-to-array');
+const DSK = require('../lib/dsk');
 
 
 module.exports = app => {
 module.exports = app => {
 
 
@@ -559,6 +560,35 @@ module.exports = app => {
             } catch (err) {
             } catch (err) {
                 await sendToWormhole(stream);
                 await sendToWormhole(stream);
                 ctx.log(err);
                 ctx.log(err);
+                ctx.ajaxErrorBody(err, '导入计价文件出错');
+            }
+        }
+
+        async importDsk(ctx) {
+            try {
+                const data = JSON.parse(ctx.request.body.data);
+                if (!data.subjects || data.subjects.length === 0) throw '无导入计价数据';
+
+                // 拼凑ybp数据
+                const ybpData = { header: {}, subjects: [] };
+                const dsk = new DSK(ctx);
+                for (const s of data.subjects) {
+                    const subject = { project: { name: '' }, rations: [], };
+                    subject.bills = await dsk.getProjectBills(s.compilationId, s.subjectId);
+                    // 拼凑fees.marketCommon.totalFee,fees.marketLabour.totalFee防止出错
+                    subject.bills.forEach(b => {
+                        b.fees = {
+                            marketCommon: { unitPrice: b.unitPrice, totalFee: b.totalPrice },
+                            marketLabour: { unitPrice: 0, totalFee: 0 },
+                        };
+                    });
+                    ybpData.subjects.push(subject);
+                }
+                const bills = await ctx.service.ledger.importYbpData(ctx.tender, ybpData);
+                ctx.body = { err: 0, msg: '', data: { bills, pos: [] }};
+            } catch (err) {
+                ctx.log(err);
+                ctx.ajaxErrorBody(err, '导入计价分段出错');
             }
             }
         }
         }
         // async uploadExcel(ctx) {
         // async uploadExcel(ctx) {

+ 1 - 1
app/controller/login_controller.js

@@ -274,7 +274,7 @@ module.exports = app => {
                         throw '登录账号还没有认证手机,请联系项目管理员。';
                         throw '登录账号还没有认证手机,请联系项目管理员。';
                     }
                     }
                     // 重置密码并发短信
                     // 重置密码并发短信
-                    const newpwd = ctx.helper.generateRandomString(6, 2);
+                    const newpwd = ctx.helper.generateRandomString(6);
                     console.log(newpwd);
                     console.log(newpwd);
                     const result = await ctx.service.projectAccount.resetPassword(pa.id, newpwd);
                     const result = await ctx.service.projectAccount.resetPassword(pa.id, newpwd);
                     if (!result) {
                     if (!result) {

+ 61 - 46
app/controller/material_controller.js

@@ -10,6 +10,7 @@
 
 
 const moment = require('moment');
 const moment = require('moment');
 const auditConst = require('../const/audit').material;
 const auditConst = require('../const/audit').material;
+const auditType = require('../const/audit').auditType;
 const auditStageConst = require('../const/audit').stage;
 const auditStageConst = require('../const/audit').stage;
 const tenderConst = require('../const/tender');
 const tenderConst = require('../const/tender');
 const measureType = tenderConst.measureType;
 const measureType = tenderConst.measureType;
@@ -60,6 +61,8 @@ module.exports = app => {
                     materialColShow: material_col_show,
                     materialColShow: material_col_show,
                     qtySourceConst: materialConst.qty_source,
                     qtySourceConst: materialConst.qty_source,
                     qtySourceValueConst: materialConst.qty_source_value,
                     qtySourceValueConst: materialConst.qty_source_value,
+                    auditType,
+                    jsFiles: this.app.jsFiles.common.concat(this.app.jsFiles.material.index),
                 };
                 };
                 let openMaterialTax = ctx.session.sessionProject.page_show.openMaterialTax;
                 let openMaterialTax = ctx.session.sessionProject.page_show.openMaterialTax;
                 let allMaterialTax = true;
                 let allMaterialTax = true;
@@ -81,7 +84,17 @@ module.exports = app => {
                 for (const s of renderData.materials) {
                 for (const s of renderData.materials) {
                     // s.curAuditor = null;
                     // s.curAuditor = null;
                     // 根据期状态返回展示用户
                     // 根据期状态返回展示用户
-                    s.curAuditor = await ctx.service.materialAudit.getAuditorByStatus(s.id, s.status, s.times);
+                    // s.curAuditor = await ctx.service.materialAudit.getAuditorByStatus(s.id, s.status, s.times);
+                    if (!s.final_auditor_str || s.status !== auditConst.status.checked) {
+                        // 根据期状态返回展示用户
+                        s.curAuditors = await ctx.service.materialAudit.getAuditorsByStatus(s.id, s.status, s.times);
+                        if (s.status === auditConst.status.checked && s.curAuditors.length > 0) {
+                            const final_auditor_str = (s.curAuditors[0].audit_type === auditType.key.common)
+                                ? `${s.curAuditors[0].name}${(s.curAuditors[0].role ? '-' + s.curAuditors[0].role : '')}`
+                                : ctx.helper.transFormToChinese(s.curAuditors[0].audit_order) + '审';
+                            await ctx.service.material.defaultUpdate({ id: s.id, final_auditor_str });
+                        }
+                    }
                     if (!ctx.session.sessionProject.page_show.openMaterialStageRepeat) {
                     if (!ctx.session.sessionProject.page_show.openMaterialStageRepeat) {
                         const materialStageList = s.stage_id.split(',');
                         const materialStageList = s.stage_id.split(',');
                         for (const ms of materialStageList) {
                         for (const ms of materialStageList) {
@@ -133,29 +146,12 @@ module.exports = app => {
                 if (!ctx.session.sessionProject.page_show.openMaterial) {
                 if (!ctx.session.sessionProject.page_show.openMaterial) {
                     throw '该功能已关闭';
                     throw '该功能已关闭';
                 }
                 }
-                const responseData = {
-                    err: 0, msg: '', data: {},
-                };
                 const order = JSON.parse(ctx.request.body.data).order;
                 const order = JSON.parse(ctx.request.body.data).order;
                 const tenderId = ctx.params.id;
                 const tenderId = ctx.params.id;
                 const materialInfo = await ctx.service.material.getDataByCondition({ tid: tenderId, order });
                 const materialInfo = await ctx.service.material.getDataByCondition({ tid: tenderId, order });
-                // 获取审批流程中右边列表
-                const auditHistory = [];
-                const times = materialInfo.status === auditConst.status.checkNo ? materialInfo.times - 1 : materialInfo.times;
-                if (times >= 1) {
-                    for (let i = 1; i <= times; i++) {
-                        auditHistory.push(await ctx.service.materialAudit.getAuditors(materialInfo.id, i));
-                    }
-                }
-                responseData.data.auditHistory = auditHistory;
-                // 获取审批流程中左边列表
-                responseData.data.auditors = await ctx.service.materialAudit.getAuditorsWithOwner(materialInfo.id, times);
-
-                responseData.data.user = await ctx.service.projectAccount.getAccountInfoById(materialInfo.user_id);
-                // 获取原报信息
-                // const materialAuditor = await ctx.service.projectAccount.getAccountInfoById(materialInfo.user_id);
-                // responseData.data.materialAuditor = materialAuditor;
-                ctx.body = responseData;
+                await ctx.service.material.loadMaterialUser(materialInfo);
+                await ctx.service.material.loadMaterialAuditViewData(materialInfo);
+                ctx.body = { err: 0, msg: '', data: materialInfo };
             } catch (error) {
             } catch (error) {
                 this.log(error);
                 this.log(error);
                 ctx.body = { err: 1, msg: error.toString(), data: null };
                 ctx.body = { err: 1, msg: error.toString(), data: null };
@@ -310,8 +306,9 @@ module.exports = app => {
                 shenpiConst,
                 shenpiConst,
                 qtySourceConst: materialConst.qty_source,
                 qtySourceConst: materialConst.qty_source,
                 qtySourceValueConst: materialConst.qty_source_value,
                 qtySourceValueConst: materialConst.qty_source_value,
+                auditType,
             };
             };
-            if ((ctx.material.status === auditConst.status.uncheck || ctx.material.status === auditConst.status.checkNo) && (ctx.session.sessionUser.accountId === ctx.material.user_id || ctx.tender.isTourist)) {
+            if ((ctx.material.status === auditConst.status.uncheck || ctx.material.status === auditConst.status.checkNo) && (ctx.session.sessionUser.accountId === ctx.material.user_id || ctx.tender.isTourist) || ctx.session.sessionUser.is_admin) {
                 // data.accountGroup = accountGroup;
                 // data.accountGroup = accountGroup;
                 // 获取所有项目参与者
                 // 获取所有项目参与者
                 const accountList = await ctx.service.projectAccount.getAllDataByCondition({
                 const accountList = await ctx.service.projectAccount.getAllDataByCondition({
@@ -340,21 +337,22 @@ module.exports = app => {
          * @private
          * @private
          */
          */
         async _getMaterialAuditViewData(ctx) {
         async _getMaterialAuditViewData(ctx) {
-            const times = ctx.material.status === auditConst.status.checkNo ? ctx.material.times - 1 : ctx.material.times;
-            ctx.material.user = await ctx.service.projectAccount.getAccountInfoById(ctx.material.user_id);
-            ctx.material.auditHistory = [];
-            if (times >= 1) {
-                for (let i = 1; i <= times; i++) {
-                    ctx.material.auditHistory.push(await ctx.service.materialAudit.getAuditors(ctx.material.id, i));
-                }
-            }
-            // 获取审批流程中左边列表
-            ctx.material.auditors2 = ctx.material.status === auditConst.status.checkNo && ctx.material.user_id !== ctx.session.sessionUser.accountId ?
-                await ctx.service.materialAudit.getAuditorsWithOwner(ctx.material.id, times) :
-                await ctx.service.materialAudit.getAuditorsWithOwner(ctx.material.id, ctx.material.times);
-            if (ctx.material.status === auditConst.status.uncheck || ctx.material.status === auditConst.status.checkNo) {
-                ctx.material.auditorList = await ctx.service.materialAudit.getAuditors(ctx.material.id, ctx.material.times);
-            }
+            // const times = ctx.material.status === auditConst.status.checkNo ? ctx.material.times - 1 : ctx.material.times;
+            // ctx.material.user = await ctx.service.projectAccount.getAccountInfoById(ctx.material.user_id);
+            // ctx.material.auditHistory = [];
+            // if (times >= 1) {
+            //     for (let i = 1; i <= times; i++) {
+            //         ctx.material.auditHistory.push(await ctx.service.materialAudit.getAuditors(ctx.material.id, i));
+            //     }
+            // }
+            // // 获取审批流程中左边列表
+            // ctx.material.auditors2 = ctx.material.status === auditConst.status.checkNo && ctx.material.user_id !== ctx.session.sessionUser.accountId ?
+            //     await ctx.service.materialAudit.getAuditorsWithOwner(ctx.material.id, times) :
+            //     await ctx.service.materialAudit.getAuditorsWithOwner(ctx.material.id, ctx.material.times);
+            // if (ctx.material.status === auditConst.status.uncheck || ctx.material.status === auditConst.status.checkNo) {
+            //     ctx.material.auditorList = await ctx.service.materialAudit.getAuditors(ctx.material.id, ctx.material.times);
+            // }
+            await ctx.service.material.loadMaterialAuditViewData(ctx.material);
 
 
             // 是否已验证手机短信
             // 是否已验证手机短信
             const pa = await ctx.service.projectAccount.getDataById(ctx.session.sessionUser.accountId);
             const pa = await ctx.service.projectAccount.getDataById(ctx.session.sessionUser.accountId);
@@ -1232,7 +1230,7 @@ module.exports = app => {
 
 
                 ctx.material.auditorList = await ctx.service.materialAudit.getAuditors(ctx.material.id, ctx.material.times);
                 ctx.material.auditorList = await ctx.service.materialAudit.getAuditors(ctx.material.id, ctx.material.times);
                 // 检查审核人是否已存在
                 // 检查审核人是否已存在
-                const exist = this.app._.find(ctx.material.auditorList, { aid: id });
+                const exist = this.app._.find(ctx.material.auditorList, { aid: id, is_old: 0 });
                 if (exist) {
                 if (exist) {
                     throw '该审核人已存在,请勿重复添加';
                     throw '该审核人已存在,请勿重复添加';
                 }
                 }
@@ -1243,7 +1241,7 @@ module.exports = app => {
                     throw '添加审核人失败';
                     throw '添加审核人失败';
                 }
                 }
 
 
-                const auditors = await ctx.service.materialAudit.getAuditorsWithOwner(ctx.material.id, ctx.material.times);
+                const auditors = await ctx.service.materialAudit.getUserGroup(ctx.material.id, ctx.material.times);
                 ctx.body = { err: 0, msg: '', data: auditors };
                 ctx.body = { err: 0, msg: '', data: auditors };
             } catch (err) {
             } catch (err) {
                 this.log(err);
                 this.log(err);
@@ -1308,10 +1306,10 @@ module.exports = app => {
          */
          */
         async checkAudit(ctx) {
         async checkAudit(ctx) {
             try {
             try {
-                if (!this.ctx.material || this.ctx.material.status !== auditConst.status.checking) {
+                if (!ctx.material || (ctx.material.status !== auditConst.status.checking && ctx.material.status !== auditConst.status.checkNoPre)) {
                     throw '当前材料调差期数据有误';
                     throw '当前材料调差期数据有误';
                 }
                 }
-                if (!this.ctx.material.curAuditor || this.ctx.material.curAuditor.aid !== ctx.session.sessionUser.accountId) {
+                if (ctx.material.curAuditorIds.indexOf(ctx.session.sessionUser.accountId) < 0) {
                     throw '您无权进行该操作';
                     throw '您无权进行该操作';
                 }
                 }
                 const data = {
                 const data = {
@@ -1321,11 +1319,11 @@ module.exports = app => {
                 if (!data.checkType || isNaN(data.checkType)) {
                 if (!data.checkType || isNaN(data.checkType)) {
                     throw '提交数据错误';
                     throw '提交数据错误';
                 }
                 }
-                if (data.checkType === auditConst.status.checkNo) {
-                    if (!data.checkType || isNaN(data.checkType)) {
-                        throw '提交数据错误';
-                    }
-                }
+                // if (data.checkType === auditConst.status.checkNo) {
+                //     if (!data.checkType || isNaN(data.checkType)) {
+                //         throw '提交数据错误';
+                //     }
+                // }
 
 
                 await ctx.service.materialAudit.check(ctx.material.id, data, ctx.material.times);
                 await ctx.service.materialAudit.check(ctx.material.id, data, ctx.material.times);
 
 
@@ -1745,6 +1743,23 @@ module.exports = app => {
                 };
                 };
             }
             }
         }
         }
+
+        async saveAudit(ctx) {
+            try {
+                if (!ctx.session.sessionUser.is_admin || ctx.material.status === auditConst.status.checked) {
+                    throw '您无权进行该操作';
+                }
+
+                const data = JSON.parse(ctx.request.body.data);
+                await ctx.service.materialAudit.saveAudit(ctx.material.id, ctx.material.times, 0, data);
+
+                const auditors = await ctx.service.materialAudit.getUniqUserGroup(ctx.material.id, ctx.material.times);
+                ctx.body = { err: 0, msg: '', data: auditors };
+            } catch (err) {
+                this.log(err);
+                ctx.body = this.ajaxErrorBody(err, '保存审批人数据失败');
+            }
+        }
     }
     }
 
 
     return MaterialController;
     return MaterialController;

+ 152 - 0
app/controller/pay_controller.js

@@ -0,0 +1,152 @@
+'use strict';
+
+/**
+ * 合同支付
+ *
+ * @author Mai
+ * @date
+ * @version
+ */
+
+const audit = require('../const/audit');
+
+module.exports = app => {
+
+    class PayController extends app.BaseController {
+        /**
+         * 构造函数
+         *
+         * @param {Object} ctx - egg全局变量
+         * @return {void}
+         */
+        constructor(ctx) {
+            super(ctx);
+        }
+
+        async index(ctx) {
+            try {
+                const phasePays = await this.ctx.service.phasePay.getAllPhasePay(ctx.tender.id, 'DESC');
+                this.ctx.service.phasePay.calculatePhasePay(phasePays);
+                const renderData = {
+                    phasePays,
+                    auditConst: audit.common,
+                };
+                await this.layout('phase_pay/index.ejs', renderData);
+            } catch (err) {
+                ctx.helper.log(err);
+            }
+        }
+
+        async pay(ctx) {
+            try {
+                const pays = await this.ctx.service.phasePay.getAllPhasePay(ctx.tender.id, 'DESC');
+                const renderData = {
+                    pays,
+                    jsFiles: this.app.jsFiles.common.concat(this.app.jsFiles.pay.list)
+                };
+                await this.layout('pay/pay.ejs', renderData);
+            } catch (err) {
+                ctx.helper.log(err);
+                ctx.postError(err, '读取合同支付数据错误');
+                ctx.redirect(this.request.headers.referer);
+            }
+        }
+
+        async uploadFile(ctx) {
+            let stream;
+            try {
+                const parts = ctx.multipart({ autoFields: true });
+                let index = 0;
+                const create_time = Date.parse(new Date()) / 1000;
+                let stream = await parts();
+                const bonus = await ctx.service.stageBonus.getStageDataById(parts.field.bonus_id);
+                //if (!bonus || bonus.sid !== ctx.stage.id) throw '该奖罚金,当前不允许上传附件';
+                while (stream !== undefined) {
+                    if (!stream.filename) {
+                        throw '未发现上传文件!';
+                    }
+
+                    const fileInfo = path.parse(stream.filename);
+                    const dirName = 'app/public/upload/extra/' + moment().format('YYYYMMDD');
+                    const fileName = create_time + '_' + index + fileInfo.ext;
+
+                    // 保存文件
+                    await ctx.helper.saveStreamFile(stream, path.join(this.app.baseDir, dirName, fileName));
+                    await sendToWormhole(stream);
+
+                    // 插入到stage_pay对应的附件列表中
+                    bonus.proof_file.push({
+                        filename: fileInfo.name,
+                        fileext: fileInfo.ext,
+                        filesize: Array.isArray(parts.field.size) ? parts.field.size[index] : parts.field.size,
+                        filepath: path.join(dirName, fileName),
+                        uid: ctx.session.sessionUser.accountId,
+                        in_time: moment(create_time * 1000).format('YYYY-MM-DD'),
+                        renew: bonus.sid === ctx.stage.id ? ctx.stage.status === auditConst.status.checked : true,
+                    });
+                    ++index;
+                    if (Array.isArray(parts.field.size) && index < parts.field.size.length) {
+                        stream = await parts();
+                    } else {
+                        stream = undefined;
+                    }
+                }
+                const result = await ctx.service.stageBonus.updateDatas({
+                    update: [
+                        { id: bonus.id, proof_file: bonus.proof_file },
+                    ]
+                });
+                for (const pf of bonus.proof_file) {
+                    pf.username = (await ctx.service.projectAccount.getAccountInfoById(pf.uid)).name;
+                    if (ctx.helper.canPreview(pf.fileext)){
+                        pf.viewpath = pf.filepath.substr(3, pf.filepath.length - 3);
+                    }
+                    delete pf.filepath;
+                }
+                ctx.body = {err: 0, msg: '', data: bonus.proof_file};
+            } catch (error) {
+                ctx.helper.log(error);
+                // 失败需要消耗掉stream 以防卡死
+                if (stream) {
+                    await sendToWormhole(stream);
+                }
+                ctx.body = this.ajaxErrorBody(error, '上传附件失败,请重试');
+            }
+        }
+
+        async deleteFile(ctx) {
+            try {
+                const data = JSON.parse(ctx.request.body.data);
+
+                const bonus = await ctx.service.stageBonus.getStageDataById(data.b_id);
+                if (!bonus || !bonus.proof_file || !bonus.proof_file[data.index]) throw '删除的文件不存在';
+
+                const fileInfo = bonus.proof_file[data.index];
+                if (fileInfo.uid !== ctx.session.sessionUser.accountId) throw '您无权删除该文件';
+
+                const deleteFilePermission = PermissionCheck.delFile(this.ctx.session.sessionUser.permission);
+                if (ctx.stage.status === auditConst.status.checked && !fileInfo.renew && !deleteFilePermission) throw '不可删除该文件';
+
+                // 先删除文件
+                await fs.unlinkSync(path.join(this.app.baseDir, fileInfo.filepath));
+                // 再删除数据库
+                bonus.proof_file.splice(data.index, 1);
+                const result = await ctx.service.stageBonus.updateDatas({
+                    update: [
+                        { id: bonus.id, proof_file: bonus.proof_file },
+                    ]
+                });
+                for (const pf of bonus.proof_file) {
+                    delete pf.filepath;
+                    pf.username = (await ctx.service.projectAccount.getAccountInfoById(pf.uid)).name;
+                }
+                ctx.body = {err: 0, msg: '', data: bonus.proof_file};
+            } catch (err) {
+                this.log(err);
+                this.ctx.ajaxErrorBody(err, '删除文件失败');
+            }
+        }
+    }
+
+    return PayController;
+};

+ 4 - 0
app/controller/revise_controller.js

@@ -922,6 +922,10 @@ module.exports = app => {
                 if (!c) return;
                 if (!c) return;
                 c.bills.push(cb);
                 c.bills.push(cb);
             });
             });
+            const helper = ctx.helper;
+            change.forEach(x => {
+                x.bills.sort((a, b) => { return helper.compareCode(a.code, b.code)});
+            });
             return change;
             return change;
         }
         }
 
 

+ 6 - 0
app/controller/stage_controller.js

@@ -349,6 +349,10 @@ module.exports = app => {
                 if (!c) return;
                 if (!c) return;
                 c.bills.push(cb);
                 c.bills.push(cb);
             });
             });
+            const helper = ctx.helper;
+            change.forEach(x => {
+                x.bills.sort((a, b) => { return helper.compareCode(a.code, b.code)});
+            });
             return change;
             return change;
         }
         }
 
 
@@ -1188,8 +1192,10 @@ module.exports = app => {
          */
          */
         async _getChangeDetailData(tid, sid, cid, isImport) {
         async _getChangeDetailData(tid, sid, cid, isImport) {
             const data = {};
             const data = {};
+            const helper = this.ctx.helper;
             data.attachments = await this.ctx.service.changeAtt.getChangeAttachment(cid);
             data.attachments = await this.ctx.service.changeAtt.getChangeAttachment(cid);
             data.bills = await this.ctx.service.changeAuditList.getAllDataByCondition({ where: { cid } });
             data.bills = await this.ctx.service.changeAuditList.getAllDataByCondition({ where: { cid } });
+            data.bills.sort((a, b) => { return helper.compareCode(a.code, b.code)});
             if (isImport) {
             if (isImport) {
                 data.endUsedBills = await this.ctx.service.stageImportChange.getChangeEndUsedData(tid, cid);
                 data.endUsedBills = await this.ctx.service.stageImportChange.getChangeEndUsedData(tid, cid);
                 data.curUsedBills = await this.ctx.service.stageImportChange.getChangeUsedData(sid, cid);
                 data.curUsedBills = await this.ctx.service.stageImportChange.getChangeUsedData(sid, cid);

+ 17 - 3
app/controller/tender_controller.js

@@ -519,9 +519,9 @@ module.exports = app => {
                 let allMaterialTax = true;
                 let allMaterialTax = true;
                 if (materials && materials.length > 0) {
                 if (materials && materials.length > 0) {
                     materialData = materials[0];
                     materialData = materials[0];
-                    materialData.curAuditor = await ctx.service.materialAudit.getAuditorByStatus(materialData.id, materialData.status, materialData.times);
-                    const times = materialData.status === auditConst.material.status.checkNo ? materialData.times - 1 : materialData.times;
-                    materialData.auditors = materialData.status === auditConst.material.status.uncheck ? [] : await ctx.service.materialAudit.getFinalAuditGroup(materialData.id, times);
+                    // materialData.curAuditor = await ctx.service.materialAudit.getAuditorByStatus(materialData.id, materialData.status, materialData.times);
+                    // const times = materialData.status === auditConst.material.status.checkNo ? materialData.times - 1 : materialData.times;
+                    // materialData.auditors = materialData.status === auditConst.material.status.uncheck ? [] : await ctx.service.materialAudit.getFinalAuditGroup(materialData.id, times);
                     for (const s of materials) {
                     for (const s of materials) {
                         if (allMaterialTax && s.material_tax === 0) {
                         if (allMaterialTax && s.material_tax === 0) {
                             allMaterialTax = false;
                             allMaterialTax = false;
@@ -531,6 +531,20 @@ module.exports = app => {
                         }
                         }
                     }
                     }
                     materialData.decimal = materialData.decimal ? JSON.parse(materialData.decimal) : materialConst.decimal;
                     materialData.decimal = materialData.decimal ? JSON.parse(materialData.decimal) : materialConst.decimal;
+                    materialData.status = materialData.status === auditConst.material.status.checkNoPre ? auditConst.material.status.checking : materialData.status;
+                    const times = materialData.status === auditConst.material.status.checkNo ? materialData.times - 1 : materialData.times;
+                    let cur;
+                    if (materialData.status === auditConst.material.status.checked) {
+                        cur = await this.ctx.service.materialAudit.getLastestAuditors(materialData.id, materialData.times, auditConst.material.status.checked);
+                    } else if (materialData.status === auditConst.material.status.checking) {
+                        cur = await this.ctx.service.materialAudit.getCurAuditors(materialData.id, materialData.times);
+                    } else {
+                        cur = await this.ctx.service.materialAudit.getAuditorsByStatus(materialData.id, materialData.status, materialData.times);
+                    }
+                    // const status_name = await this.ctx.service.stageAudit.getAuditorByStatus(lastStage.id, lastStage.status, lastStage.times);
+                    materialData.status_users = cur.length > 0 ? (cur[0].audit_type === auditConst.auditType.key.common ? cur[0].name : ctx.helper.transFormToChinese(cur[0].audit_order) + '审') : '';
+                    const history = await ctx.service.materialAudit.getAuditorHistory(materialData.id, times);
+                    materialData.auditors = history[history.length - 1];
                 }
                 }
                 // 修订完成数目
                 // 修订完成数目
                 // const reviseNum = await ctx.service.ledgerRevise.count({ tid: tender.id, status: auditConst.revise.status.checked });
                 // const reviseNum = await ctx.service.ledgerRevise.count({ tid: tender.id, status: auditConst.revise.status.checked });

+ 45 - 0
app/controller/wap_controller.js

@@ -904,6 +904,46 @@ module.exports = app => {
          * @param {Object} ctx - egg全局变量
          * @param {Object} ctx - egg全局变量
          * @return {void}
          * @return {void}
          */
          */
+        async messageDownloadFile(ctx) {
+            const id = ctx.params.fid;
+            if (id) {
+                try {
+                    const fileInfo = await ctx.service.messageAtt.getDataById(id);
+                    if (fileInfo !== undefined && fileInfo !== '') {
+                        // const fileName = path.join(this.app.baseDir, fileInfo.filepath);
+                        // 解决中文无法下载问题
+                        const userAgent = (ctx.request.header['user-agent'] || '').toLowerCase();
+                        let disposition = '';
+                        if (userAgent.indexOf('msie') >= 0 || userAgent.indexOf('chrome') >= 0) {
+                            disposition = 'attachment; filename=' + encodeURIComponent(fileInfo.filename + fileInfo.fileext);
+                        } else if (userAgent.indexOf('firefox') >= 0) {
+                            disposition = 'attachment; filename*="utf8\'\'' + encodeURIComponent(fileInfo.filename + fileInfo.fileext) + '"';
+                        } else {
+                            /* safari等其他非主流浏览器只能自求多福了 */
+                            disposition = 'attachment; filename=' + new Buffer(fileInfo.filename + fileInfo.fileext).toString('binary');
+                        }
+                        ctx.response.set({
+                            'Content-Type': 'application/octet-stream',
+                            'Content-Disposition': disposition,
+                            'Content-Length': fileInfo.filesize,
+                        });
+                        // ctx.body = await fs.createReadStream(fileName);
+                        ctx.body = await ctx.helper.ossFileGet(fileInfo.filepath);
+                    } else {
+                        throw '不存在该文件';
+                    }
+                } catch (err) {
+                    this.log(err);
+                    this.setMessage(err.toString(), this.messageType.ERROR);
+                }
+            }
+        }
+
+        /**
+         * 下载附件
+         * @param {Object} ctx - egg全局变量
+         * @return {void}
+         */
         async shoufangDownloadFile(ctx) {
         async shoufangDownloadFile(ctx) {
             const id = ctx.params.fid;
             const id = ctx.params.fid;
             if (id) {
             if (id) {
@@ -946,15 +986,20 @@ module.exports = app => {
                     throw '参数有误';
                     throw '参数有误';
                 }
                 }
                 const msgInfo = await ctx.service.message.getDataById(msgId);
                 const msgInfo = await ctx.service.message.getDataById(msgId);
+                const files = await ctx.service.messageAtt.getAtt(msgId);
                 if (!msgInfo) {
                 if (!msgInfo) {
                     throw '项目通知不存在';
                     throw '项目通知不存在';
                 }
                 }
                 if (msgInfo && msgInfo.type === 1 && msgInfo.project_id !== ctx.session.sessionProject.id) {
                 if (msgInfo && msgInfo.type === 1 && msgInfo.project_id !== ctx.session.sessionProject.id) {
                     throw '非该项目通知无权查看';
                     throw '非该项目通知无权查看';
                 }
                 }
+                if (msgInfo) {
+                    msgInfo.content = JSON.parse(JSON.stringify(msgInfo.content).replace(/\\r\\n/g, '<br>').replace(/\\"/g, '&#34;').replace(/'/g, '&#39;').replace(/\\t/g, '&#9;'));
+                }
                 const renderData = {
                 const renderData = {
                     msgInfo,
                     msgInfo,
                     moment,
                     moment,
+                    files,
                 };
                 };
                 await ctx.render('wap/msg.ejs', renderData);
                 await ctx.render('wap/msg.ejs', renderData);
             } catch (error) {
             } catch (error) {

+ 20 - 0
app/lib/rm/budget.js

@@ -33,6 +33,18 @@ class rptMemPaymentSafe extends RptMemBase {
             this.ctx.subProject = await this.ctx.service.subProject.getDataByCondition({ budget_id: id });
             this.ctx.subProject = await this.ctx.service.subProject.getDataByCondition({ budget_id: id });
             this.ctx.subProject.info = await this.ctx.service.subProjInfo.getInfo4Report(this.ctx.subProject.id);
             this.ctx.subProject.info = await this.ctx.service.subProjInfo.getInfo4Report(this.ctx.subProject.id);
         }
         }
+        if (budget.final_id) {
+            const finalInfo = await this.ctx.service.budgetFinalList.getDataById(budget.final_id);
+            this.ctx.budget.final_uid = finalInfo.uid;
+            this.ctx.budget.final_u_name = finalInfo.u_name;
+            this.ctx.budget.final_u_role = finalInfo.u_role;
+            this.ctx.budget.final_u_company = finalInfo.u_company;
+            this.ctx.budget.final_u_mobile = finalInfo.u_mobile;
+            this.ctx.budget.final_u_login = finalInfo.u_login;
+            this.ctx.budget.final_create_time = finalInfo.create_time;
+            this.ctx.budget.final_update_time = finalInfo.update_time;
+            this.ctx.budget.final_tender_info = finalInfo.tender_info ? JSON.parse(finalInfo.tender_info) : [];
+        }
     }
     }
 
 
     async doBeforeLoadReport(params) {
     async doBeforeLoadReport(params) {
@@ -72,6 +84,12 @@ class rptMemPaymentSafe extends RptMemBase {
         // return showLevel ? tree.getDefaultDatasByLevel(this.ctx.tender.rpt_show_level) : tree.getDefaultDatas();
         // return showLevel ? tree.getDefaultDatasByLevel(this.ctx.tender.rpt_show_level) : tree.getDefaultDatas();
         return tree.getDefaultDatas();
         return tree.getDefaultDatas();
     }
     }
+    async budgetFinalInfo(bid) {
+        const budget = this.ctx.budget && this.ctx.budget.id === bid
+            ? this.ctx.budget
+            : await this.ctx.service.budget.getDataById(bid);
+        return [finalInfo];
+    }
     async budgetFinal(bid, showLevel = false) {
     async budgetFinal(bid, showLevel = false) {
         const budget = this.ctx.budget && this.ctx.budget.id === bid
         const budget = this.ctx.budget && this.ctx.budget.id === bid
             ? this.ctx.budget
             ? this.ctx.budget
@@ -90,6 +108,8 @@ class rptMemPaymentSafe extends RptMemBase {
                 return [this.ctx.subProject.info];
                 return [this.ctx.subProject.info];
             case 'mem_qty_info':
             case 'mem_qty_info':
                 return [...this.ctx.subProject.info.main_quantity, ...this.ctx.subProject.info.gcl_quantity];
                 return [...this.ctx.subProject.info.main_quantity, ...this.ctx.subProject.info.gcl_quantity];
+            case 'budget':
+                return [this.ctx.budget];
             case 'mem_budget_gu':
             case 'mem_budget_gu':
                 return this.budgetGu(this.ctx.budget.id);
                 return this.budgetGu(this.ctx.budget.id);
             case 'mem_budget_gai':
             case 'mem_budget_gai':

+ 22 - 11
app/lib/ybp_tree.js

@@ -7,9 +7,9 @@ const YbpNodeKind = {
     bx: 5, // 补项
     bx: 5, // 补项
     cs: 6, // 分类
     cs: 6, // 分类
     dt: 7, // 费用明细
     dt: 7, // 费用明细
-    xmj: 8,
+    xmj: 8, // 项目节
 };
 };
-const defaultMerge = [ YbpNodeKind.dxfy, YbpNodeKind.fb, YbpNodeKind.cs, YbpNodeKind.xmj ];
+const defaultMerge = [ YbpNodeKind.dxfy, YbpNodeKind.fb, YbpNodeKind.cs, YbpNodeKind.dt, YbpNodeKind.xmj ];
 const YbpImportType = { flow: 0, merge: 1 };
 const YbpImportType = { flow: 0, merge: 1 };
 
 
 class YbpTree {
 class YbpTree {
@@ -37,7 +37,7 @@ class YbpTree {
 
 
     sortChildren(children, recursive) {
     sortChildren(children, recursive) {
         const setting = this.setting;
         const setting = this.setting;
-        children.sort((x, y) => { return x[setting.order] > y[setting.order]; });
+        children.sort((x, y) => { return x[setting.order] - y[setting.order]; });
 
 
         if (!recursive) return;
         if (!recursive) return;
         for (const c of children) {
         for (const c of children) {
@@ -167,8 +167,9 @@ class YbpImportTree {
         const setting = this.setting;
         const setting = this.setting;
         const compareData = { kind: node.kind };
         const compareData = { kind: node.kind };
         const hasUp = (!node.children || node.children.length === 0) && defaultMerge.indexOf(compareData.kind) < 0;
         const hasUp = (!node.children || node.children.length === 0) && defaultMerge.indexOf(compareData.kind) < 0;
-        compareData.code = defaultMerge.indexOf(compareData.kind) < 0 ? '' : node.code || '';
-        compareData.b_code = defaultMerge.indexOf(compareData.kind) < 0 ? node.code || '' : '';
+        const isXmj = defaultMerge.indexOf(compareData.kind) >= 0;
+        compareData.code = isXmj ? node.code || '' : '';
+        compareData.b_code = isXmj ? '' : node.code || '';
         compareData.name = node.name || '';
         compareData.name = node.name || '';
         compareData.unit = node.unit || '';
         compareData.unit = node.unit || '';
         compareData.unit_price = hasUp
         compareData.unit_price = hasUp
@@ -192,11 +193,21 @@ class YbpImportTree {
                 this.children.push(cur);
                 this.children.push(cur);
             }
             }
             this.datas.push(cur);
             this.datas.push(cur);
-            cur.quantity = node.quantity || 0;
+            cur.dgn_qty1 = isXmj ? node.quantity || 0 : 0;
+            cur.dgn_qty2 = isXmj ? node.quantity2 || 0 : 0;
+            cur.quantity = isXmj ? 0 : node.quantity || 0;
             cur.total_fee = node.fees ? node.fees.marketCommon.totalFee : 0;
             cur.total_fee = node.fees ? node.fees.marketCommon.totalFee : 0;
             cur.labour_fee = node.fees && node.fees.marketLabour ? node.fees.marketLabour.totalFee : 0;
             cur.labour_fee = node.fees && node.fees.marketLabour ? node.fees.marketLabour.totalFee : 0;
         } else {
         } else {
-            cur.quantity = this.helper.add(cur.quantity, node.quantity);
+            cur.dgn_qty1 = isXmj ? node.quantity || 0 : 0;
+            cur.dgn_qty2 = isXmj ? node.quantity2 || 0 : 0;
+            cur.quantity = isXmj ? 0 : node.quantity || 0;
+            if (isXmj) {
+                cur.dgn_qty1 = this.helper.add(cur.dgn_qty1, node.quantity || 0);
+                cur.dgn_qty2 = this.helper.add(cur.dgn_qty2, node.quantity2 || 0);
+            } else {
+                cur.quantity = this.helper.add(cur.quantity, node.quantity || 0);
+            }
             cur.total_fee = this.helper.add(cur.total_fee, node.fees ? node.fees.marketCommon.totalFee : 0);
             cur.total_fee = this.helper.add(cur.total_fee, node.fees ? node.fees.marketCommon.totalFee : 0);
             cur.labour_fee = this.helper.add(cur.labour_fee, node.fees && node.fees.marketLabour ? node.fees.marketLabour.totalFee : 0);
             cur.labour_fee = this.helper.add(cur.labour_fee, node.fees && node.fees.marketLabour ? node.fees.marketLabour.totalFee : 0);
         }
         }
@@ -213,8 +224,8 @@ class YbpImportTree {
         }
         }
     }
     }
 
 
-    sortChildren(children, recursive) {
-        children.sort((x, y) => {
+    sortChildren(children, recursive, resort) {
+        resort && children.sort((x, y) => {
             return x.kind === YbpNodeKind.bill
             return x.kind === YbpNodeKind.bill
                 ? (x.b_code ? (y.b_code ? x.b_code.localeCompare(y.b_code) : 1) : -1)
                 ? (x.b_code ? (y.b_code ? x.b_code.localeCompare(y.b_code) : 1) : -1)
                 : (x.code ? (y.code ? x.code.localeCompare(y.code) : -1) : 1);
                 : (x.code ? (y.code ? x.code.localeCompare(y.code) : -1) : 1);
@@ -238,8 +249,8 @@ class YbpImportTree {
             _loadNode(child);
             _loadNode(child);
         }
         }
     }
     }
-    sort() {
-        this.sortChildren(this.children, true);
+    sort(sortChildren) {
+        this.sortChildren(this.children, true, sortChildren);
         this.generateSortNodes();
         this.generateSortNodes();
     }
     }
     _mapTreeNode() {
     _mapTreeNode() {

+ 43 - 52
app/middleware/material_check.js

@@ -48,17 +48,20 @@ module.exports = options => {
             if (!material) {
             if (!material) {
                 throw '材料调差期数据错误';
                 throw '材料调差期数据错误';
             }
             }
+
+            // 读取原报、审核人数据
+            yield this.service.material.loadMaterialUser(material);
             const openMaterialTax = this.session.sessionProject.page_show.openMaterialTax;
             const openMaterialTax = this.session.sessionProject.page_show.openMaterialTax;
             if ((material.status === status.uncheck || material.status === status.checkNo) && material.material_tax !== openMaterialTax) {
             if ((material.status === status.uncheck || material.status === status.checkNo) && material.material_tax !== openMaterialTax) {
                 yield this.service.material.updateMaterialTax(material.id, openMaterialTax);
                 yield this.service.material.updateMaterialTax(material.id, openMaterialTax);
                 material.material_tax = openMaterialTax;
                 material.material_tax = openMaterialTax;
             }
             }
             // 读取原报、审核人数据
             // 读取原报、审核人数据
-            material.auditors = yield this.service.materialAudit.getAuditors(material.id, material.times);
+            // material.auditors = yield this.service.materialAudit.getAuditors(material.id, material.times);
             // if (material.status > 1) {
             // if (material.status > 1) {
             //     material.auditors2 === 2 ? material.auditors : (yield this.service.materialAudit.getAuditors(material.id, material.times - 1));
             //     material.auditors2 === 2 ? material.auditors : (yield this.service.materialAudit.getAuditors(material.id, material.times - 1));
             // }
             // }
-            material.curAuditor = yield this.service.materialAudit.getCurAuditor(material.id, material.times);
+            // material.curAuditor = yield this.service.materialAudit.getCurAuditor(material.id, material.times);
 
 
             // 权限相关
             // 权限相关
             // todo 校验权限 (标段参与人、分享)
             // todo 校验权限 (标段参与人、分享)
@@ -66,64 +69,21 @@ module.exports = options => {
                 auditorIds = _.map(material.auditors, 'aid'),
                 auditorIds = _.map(material.auditors, 'aid'),
                 shareIds = [];
                 shareIds = [];
             if (accountId === material.user_id) { // 原报
             if (accountId === material.user_id) { // 原报
-                // if (material.curAuditor) {
-                //     material.readOnly = material.status === status.checking && material.curAuditor.user_id === accountId;
-                // } else {
-                //     material.readOnly = material.status !== status.uncheck && material.status !== status.checkNo;
-                // }
-                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.aid === accountId ? material.curAuditor.order : material.curAuditor.order - 1;
-                }
                 material.filePermission = true;
                 material.filePermission = true;
             } else if (this.tender.isTourist) {
             } 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;
-                }
                 material.filePermission = this.tender.touristPermission.file || auditorIds.indexOf(accountId) !== -1;
                 material.filePermission = this.tender.touristPermission.file || auditorIds.indexOf(accountId) !== -1;
             } else if (auditorIds.indexOf(accountId) !== -1) { // 审批人
             } else if (auditorIds.indexOf(accountId) !== -1) { // 审批人
                 if (material.status === status.uncheck) {
                 if (material.status === status.uncheck) {
                     throw '您无权查看该数据';
                     throw '您无权查看该数据';
                 }
                 }
                 // material.readOnly = material.status !== status.checking || accountId !== material.curAuditor.aid;
                 // material.readOnly = material.status !== status.checking || accountId !== material.curAuditor.aid;
-                material.curTimes = material.status === status.checkNo ? material.times - 1 : material.times;
-                if (material.status === status.checked) {
-                    material.curOrder = _.max(_.map(material.auditors, 'order'));
-                } else if (material.status === status.checkNo) {
-                    const audit = this.service.materialAudit.getDataByCondition({
-                        mid: material.id, times: material.times, status: status.checkNo,
-                    });
-                    material.curOrder = audit.order;
-                } else {
-                    material.curOrder = accountId === material.curAuditor.aid ? material.curAuditor.order : material.curAuditor.order - 1;
-                }
                 material.filePermission = true;
                 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 '您无权查看该数据';
                 }
                 }
-                // material.readOnly = true;
-                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.filePermission = false;
                 material.filePermission = false;
             } else if (this.session.sessionUser.is_admin) {
             } else if (this.session.sessionUser.is_admin) {
-                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;
-                }
                 material.filePermission = true;
                 material.filePermission = true;
             } else { // 其他不可见
             } else { // 其他不可见
                 throw '您无权查看该数据';
                 throw '您无权查看该数据';
@@ -135,33 +95,64 @@ module.exports = options => {
             });
             });
             // 调差的readOnly 指表格和页面只能看不能改,和审批无关
             // 调差的readOnly 指表格和页面只能看不能改,和审批无关
             material.readOnly = !(((material.status === status.uncheck || material.status === status.checkNo) && accountId === material.user_id)
             material.readOnly = !(((material.status === status.uncheck || material.status === status.checkNo) && accountId === material.user_id)
-                || (this.session.sessionProject.page_show.openMaterialEditForAudit && (material.status === status.checking || material.status === status.checkNoPre) && material.curAuditor.aid === accountId));
-            material.editForAudit = this.session.sessionProject.page_show.openMaterialEditForAudit && (material.status === status.checking || material.status === status.checkNoPre) && material.curAuditor.aid === accountId;
+                || (this.session.sessionProject.page_show.openMaterialEditForAudit && (material.status === status.checking || material.status === status.checkNoPre) && material.curAuditorIds.indexOf(accountId) !== -1));
+            material.editForAudit = this.session.sessionProject.page_show.openMaterialEditForAudit && (material.status === status.checking || material.status === status.checkNoPre) && material.curAuditorIds.indexOf(accountId) !== -1;
             material.decimal = material.decimal ? JSON.parse(material.decimal) : materialConst.decimal;
             material.decimal = material.decimal ? JSON.parse(material.decimal) : materialConst.decimal;
+            // 判断stage流程可否撤回,是哪一种撤回
+            // yield this.service.material.doCheckMaterialCanCancel(material);
             this.material = material;
             this.material = material;
             // 根据状态判断是否需要更新审批人列表
             // 根据状态判断是否需要更新审批人列表
             if ((material.status === status.uncheck || material.status === status.checkNo) && this.tender.info.shenpi.material !== shenpiConst.sp_status.sqspr) {
             if ((material.status === status.uncheck || material.status === status.checkNo) && this.tender.info.shenpi.material !== shenpiConst.sp_status.sqspr) {
                 const shenpi_status = this.tender.info.shenpi.material;
                 const shenpi_status = this.tender.info.shenpi.material;
                 // 进一步比较审批流是否与审批流程设置的相同,不同则替换为固定审批流或固定的终审
                 // 进一步比较审批流是否与审批流程设置的相同,不同则替换为固定审批流或固定的终审
                 const auditList = yield this.service.materialAudit.getAllDataByCondition({ where: { mid: material.id, times: material.times }, orders: [['order', 'asc']] });
                 const auditList = yield this.service.materialAudit.getAllDataByCondition({ where: { mid: material.id, times: material.times }, orders: [['order', 'asc']] });
-                const auditIdList = _.map(auditList, 'aid');
                 if (shenpi_status === shenpiConst.sp_status.gdspl) {
                 if (shenpi_status === shenpiConst.sp_status.gdspl) {
                     const shenpiList = yield this.service.shenpiAudit.getAllDataByCondition({ where: { tid: material.tid, sp_type: shenpiConst.sp_type.material, sp_status: shenpi_status } });
                     const shenpiList = yield this.service.shenpiAudit.getAllDataByCondition({ where: { tid: material.tid, sp_type: shenpiConst.sp_type.material, sp_status: shenpi_status } });
-                    const shenpiIdList = _.map(shenpiList, 'audit_id');
                     // 判断2个id数组是否相同,不同则删除原审批流,切换成固定的审批流
                     // 判断2个id数组是否相同,不同则删除原审批流,切换成固定的审批流
-                    if (!_.isEqual(auditIdList, shenpiIdList)) {
-                        yield this.service.materialAudit.updateNewAuditList(material, shenpiIdList);
+                    let sameAudit = auditList.length === shenpiList.length;
+                    if (sameAudit) {
+                        for (const audit of auditList) {
+                            const shenpi = shenpiList.find(x => { return x.audit_id === audit.aid; });
+                            if (!shenpi || shenpi.audit_order !== audit.audit_order || shenpi.audit_type !== audit.audit_type) {
+                                sameAudit = false;
+                                break;
+                            }
+                        }
+                    }
+                    if (!sameAudit) {
+                        yield this.service.materialAudit.updateNewAuditList(material, shenpiList);
+                        yield this.service.material.loadMaterialUser(material);
                     }
                     }
                 } else if (shenpi_status === shenpiConst.sp_status.gdzs) {
                 } else if (shenpi_status === shenpiConst.sp_status.gdzs) {
                     const shenpiInfo = yield this.service.shenpiAudit.getDataByCondition({ tid: material.tid, sp_type: shenpiConst.sp_type.material, sp_status: shenpi_status });
                     const shenpiInfo = yield this.service.shenpiAudit.getDataByCondition({ tid: material.tid, sp_type: shenpiConst.sp_type.material, sp_status: shenpi_status });
                     // 判断最后一个id是否与固定终审id相同,不同则删除原审批流中如果存在的id和添加终审
                     // 判断最后一个id是否与固定终审id相同,不同则删除原审批流中如果存在的id和添加终审
-                    if (shenpiInfo && shenpiInfo.audit_id !== _.last(auditIdList)) {
+                    const lastAuditors = auditList.filter(x => { x.order === auditList[auditList.length - 1].order; });
+                    if (shenpiInfo && (lastAuditors.length === 0 || (lastAuditors.length > 1 || shenpiInfo.audit_id !== lastAuditors[0].aid))) {
                         yield this.service.materialAudit.updateLastAudit(material, auditList, shenpiInfo.audit_id);
                         yield this.service.materialAudit.updateLastAudit(material, auditList, shenpiInfo.audit_id);
+                        yield this.service.material.loadMaterialUser(material);
                     } else if (!shenpiInfo) {
                     } else if (!shenpiInfo) {
                         // 不存在终审人的状态下这里恢复为授权审批人
                         // 不存在终审人的状态下这里恢复为授权审批人
                         this.tender.info.shenpi.material = shenpiConst.sp_status.sqspr;
                         this.tender.info.shenpi.material = shenpiConst.sp_status.sqspr;
                     }
                     }
                 }
                 }
+                // const auditIdList = _.map(auditList, 'aid');
+                // if (shenpi_status === shenpiConst.sp_status.gdspl) {
+                //     const shenpiList = yield this.service.shenpiAudit.getAllDataByCondition({ where: { tid: material.tid, sp_type: shenpiConst.sp_type.material, sp_status: shenpi_status } });
+                //     const shenpiIdList = _.map(shenpiList, 'audit_id');
+                //     // 判断2个id数组是否相同,不同则删除原审批流,切换成固定的审批流
+                //     if (!_.isEqual(auditIdList, shenpiIdList)) {
+                //         yield this.service.materialAudit.updateNewAuditList(material, shenpiIdList);
+                //     }
+                // } else if (shenpi_status === shenpiConst.sp_status.gdzs) {
+                //     const shenpiInfo = yield this.service.shenpiAudit.getDataByCondition({ tid: material.tid, sp_type: shenpiConst.sp_type.material, sp_status: shenpi_status });
+                //     // 判断最后一个id是否与固定终审id相同,不同则删除原审批流中如果存在的id和添加终审
+                //     if (shenpiInfo && shenpiInfo.audit_id !== _.last(auditIdList)) {
+                //         yield this.service.materialAudit.updateLastAudit(material, auditList, shenpiInfo.audit_id);
+                //     } else if (!shenpiInfo) {
+                //         // 不存在终审人的状态下这里恢复为授权审批人
+                //         this.tender.info.shenpi.material = shenpiConst.sp_status.sqspr;
+                //     }
+                // }
             }
             }
             yield next;
             yield next;
         } catch (err) {
         } catch (err) {

+ 55 - 0
app/middleware/phase_pay_check.js

@@ -0,0 +1,55 @@
+'use strict';
+
+/**
+ *
+ *
+ * @author Mai
+ * @date
+ * @version
+ */
+
+module.exports = options => {
+    /**
+     * 期校验 中间件
+     * 1. 读取期数据
+     * 2. 检验用户是否参与期(不校验具体权限)
+     *
+     * 写入ctx.phasePay数据
+     * 其中:
+     * phasePay.user: 创建人
+     * phasePay.auditors: 审批人列表(退回原报时,加载上一流程)
+     * phasePay.curAuditors: 当前审批人(未上报为空,审批通过 or 退回原报时,为空)
+     *
+     * phasePay.readOnly: 登录人,是否可操作
+     * phasePay.curTimes: 当前登录人,操作、查阅数据times
+     * phasePay.curOrder: 当前登录人,操作、查阅数据order
+     *
+     * 该方法为通用方法,如需phasePay其他数据,请在controller中查询
+     *
+     * @param {function} next - 中间件继续执行的方法
+     * @return {void}
+     */
+    return function* phasePayCheck(next) {
+        try {
+            // 读取标段数据
+            const phaseOrder = parseInt(this.params.order);
+            if (phaseOrder <= 0) throw '您访问的合同支付不存在';
+
+            const phasePay = yield this.service.phasePay.getPhasePayByOrder(this.tender.id, phaseOrder);
+            if (!phasePay) throw '合同支付数据错误';
+
+            // 读取原报、审核人数据
+            yield this.service.phasePay.loadUser(phasePay);
+            this.phasePay = phasePay;
+            yield next;
+        } catch (err) {
+            this.log(err);
+            if (this.helper.isAjax(this.request)) {
+                this.ajaxErrorBody(err, '读取合同支付错误');
+            } else {
+                this.postError(err, '读取合同支付错误');
+                this.redirect(this.request.headers.referer);
+            }
+        }
+    };
+};

+ 2 - 2
app/middleware/stage_check.js

@@ -101,7 +101,7 @@ module.exports = options => {
                     stage.assist = ass;
                     stage.assist = ass;
                     stage.curOrder = stage.curAuditors[0].order;
                     stage.curOrder = stage.curAuditors[0].order;
                 } else {
                 } else {
-                    stage.curOrder = stage.curAuditors[0].order - 1
+                    stage.curOrder = stage.curAuditors[0].order - 1;
                 }
                 }
                 if (!stage.readOnly) {
                 if (!stage.readOnly) {
                     if (stage.curAuditors[0].audit_type === auditType.key.and) {
                     if (stage.curAuditors[0].audit_type === auditType.key.and) {
@@ -133,7 +133,7 @@ module.exports = options => {
                 }
                 }
             }
             }
             // 判断stage流程可否撤回,是哪一种撤回
             // 判断stage流程可否撤回,是哪一种撤回
-            yield this.service.stage.doCheckStageCanCancel(stage);
+            // yield this.service.stage.doCheckStageCanCancel(stage);
 
 
             // 是否台账修订中
             // 是否台账修订中
             const lastRevise = yield this.service.ledgerRevise.getLastestRevise(this.tender.id);
             const lastRevise = yield this.service.ledgerRevise.getLastestRevise(this.tender.id);

+ 4 - 0
app/public/css/main.css

@@ -833,6 +833,10 @@ input.nospin[type="number"]{-moz-appearance:textfield;}
 .datepickers-container {
 .datepickers-container {
   z-index: 9999
   z-index: 9999
 }
 }
+.modal-height-600{
+    height:600px;
+    overflow: hidden
+}
 .modal-height-500{
 .modal-height-500{
   height:450px;
   height:450px;
   overflow: hidden
   overflow: hidden

BIN
app/public/images/dsk.png


BIN
app/public/images/ybpx.png


+ 18 - 1
app/public/js/dashboard.js

@@ -43,6 +43,7 @@ $(document).ready(() => {
             $('#creator').html('');
             $('#creator').html('');
             $('#view-msg .modal-title').text('系统通知');
             $('#view-msg .modal-title').text('系统通知');
         }
         }
+        setFiles(msgInfo.files);
     });
     });
 
 
     $('.system-msg').on('click', function () {
     $('.system-msg').on('click', function () {
@@ -54,7 +55,23 @@ $(document).ready(() => {
         $('#content').html(msgInfo.content);
         $('#content').html(msgInfo.content);
         $('#view-msg .modal-title').text('系统通知');
         $('#view-msg .modal-title').text('系统通知');
         $('#user_permission').html('');
         $('#user_permission').html('');
-    })
+        setFiles(msgInfo.files);
+    });
+
+    function setFiles(files) {
+        $('#files-list').html('');
+        if (files && files.length > 0) {
+            let html = '<h5>附件</h5>';
+            for (const file of files) {
+                html += `<a href="${file.viewpath ? file.viewpath : '/wap/message/download/file/' + file.id}" target="_blank">
+                                        <div class="card my-1" style="cursor: pointer">
+                                            <div class="card-body"><i class="fa fa-file"></i> ${file.filename}<span class="pull-right text-secondary">${file.filesize}</span></div>
+                                        </div>
+                                    </a>`;
+            }
+            $('#files-list').html(html);
+        }
+    }
 
 
     // $('#notice').on('click', 'li a', function() {
     // $('#notice').on('click', 'li a', function() {
     //     const id = $(this).data('id')
     //     const id = $(this).data('id')

+ 4 - 2
app/public/js/file_detail.js

@@ -70,6 +70,7 @@ $(document).ready(function() {
                     tree_pid: x.tree_pid,
                     tree_pid: x.tree_pid,
                     name: x.name + (x.total_file_count > 0 ? `(${x.total_file_count})` : ''),
                     name: x.name + (x.total_file_count > 0 ? `(${x.total_file_count})` : ''),
                     spid: x.spid,
                     spid: x.spid,
+                    tips: x.tips || '',
                 };
                 };
                 if (x.is_fixed) result.isParent = true;
                 if (x.is_fixed) result.isParent = true;
                 if (x.is_folder || x.is_fixed) result.open = self.expandCache.indexOf(result.id) >= 0;
                 if (x.is_folder || x.is_fixed) result.open = self.expandCache.indexOf(result.id) >= 0;
@@ -476,7 +477,7 @@ $(document).ready(function() {
         treeId: 'filing',
         treeId: 'filing',
         view: {
         view: {
             selectedMulti: false,
             selectedMulti: false,
-            showTitle: false,
+            showTitle: true,
         },
         },
         data: {
         data: {
             simpleData: {
             simpleData: {
@@ -484,7 +485,8 @@ $(document).ready(function() {
                 pIdKey: 'tree_pid',
                 pIdKey: 'tree_pid',
                 rootPId: '-1',
                 rootPId: '-1',
                 enable: true,
                 enable: true,
-            }
+            },
+            key: { title: 'tips' },
         },
         },
         edit: {
         edit: {
             enable: true,
             enable: true,

+ 11 - 1
app/public/js/filing_template.js

@@ -279,6 +279,7 @@ $(document).ready(function() {
                 cols: [
                 cols: [
                     { title: '名称', colSpan: '1', rowSpan: '1', field: 'name', hAlign: 0, width: 250, formatter: '@', readOnly: true, cellType: 'tree' },
                     { title: '名称', colSpan: '1', rowSpan: '1', field: 'name', hAlign: 0, width: 250, formatter: '@', readOnly: true, cellType: 'tree' },
                     { title: '固定', colSpan: '1', rowSpan: '1', field: 'is_fixed', hAlign: 1, width: 50, cellType: 'checkbox' },
                     { title: '固定', colSpan: '1', rowSpan: '1', field: 'is_fixed', hAlign: 1, width: 50, cellType: 'checkbox' },
+                    { title: '提示', colSpan: '1', rowSpan: '1', field: 'tips', hAlign: 0, width: 280, formatter: '@', wordWrap: 1 },
                 ],
                 ],
                 emptyRows: 0,
                 emptyRows: 0,
                 headRows: 1,
                 headRows: 1,
@@ -343,6 +344,13 @@ $(document).ready(function() {
                 }
                 }
                 SpreadJsObj.reLoadRowsData(info.sheet, row);
                 SpreadJsObj.reLoadRowsData(info.sheet, row);
             });
             });
+            this.spread.bind(spreadNS.Events.EditEnded, function(e, info) {
+                if (!info.sheet.zh_setting) return;
+                const col = info.sheet.zh_setting.cols[info.col];
+                const node = SpreadJsObj.getSelectObject(info.sheet);
+                node[col.field] = trimInvalidChar(info.editingText);
+                SpreadJsObj.reLoadRowData(info.sheet, info.row)
+            });
             $(`#${setting.modal}-ok`).click(function() {
             $(`#${setting.modal}-ok`).click(function() {
                 try {
                 try {
                     const data = self.getMultiUpdateData();
                     const data = self.getMultiUpdateData();
@@ -369,7 +377,7 @@ $(document).ready(function() {
                         if (!fixedParent) throw `【${node.name}】查询不到固定信息`;
                         if (!fixedParent) throw `【${node.name}】查询不到固定信息`;
                         filing_type = fixedParent.source_filing_type;
                         filing_type = fixedParent.source_filing_type;
                     }
                     }
-                    data.push({ id: node.id, is_fixed: node.is_fixed, filing_type, tree_order: i + 1 });
+                    data.push({ id: node.id, is_fixed: node.is_fixed, filing_type, tree_order: i + 1, tips: node.tips || '' });
                     if (node.children) getUpdateData(node.children);
                     if (node.children) getUpdateData(node.children);
                 }
                 }
             };
             };
@@ -392,6 +400,7 @@ $(document).ready(function() {
                     name: node.name,
                     name: node.name,
                     is_fixed: node.is_fixed,
                     is_fixed: node.is_fixed,
                     filing_type: node.filing_type,
                     filing_type: node.filing_type,
+                    tips: node.tips,
                 });
                 });
             }
             }
             return data;
             return data;
@@ -412,6 +421,7 @@ $(document).ready(function() {
                 name: node.name,
                 name: node.name,
                 is_fixed: node.is_fixed,
                 is_fixed: node.is_fixed,
                 filing_type: node.filing_type,
                 filing_type: node.filing_type,
+                tips: node.tips,
             }});
             }});
             const blob = new Blob([JSON.stringify(exportData, '', '')], { type: 'application/text'});
             const blob = new Blob([JSON.stringify(exportData, '', '')], { type: 'application/text'});
             const template = templateList.find(x => { return x.id === sourceTree.nodes[0].temp_id });
             const template = templateList.find(x => { return x.id === sourceTree.nodes[0].temp_id });

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

@@ -4962,6 +4962,7 @@ $(document).ready(function() {
           $('#showAttachment').attr('file-id', '');
           $('#showAttachment').attr('file-id', '');
       });
       });
   });
   });
+
     $('#upload-ybp-file').click(function () {
     $('#upload-ybp-file').click(function () {
         const file = $('#ybp-file')[0];
         const file = $('#ybp-file')[0];
         const formData = new FormData();
         const formData = new FormData();
@@ -4978,6 +4979,43 @@ $(document).ready(function() {
             $('#upload-ybp').modal('hide');
             $('#upload-ybp').modal('hide');
         });
         });
     });
     });
+    $('#id-upload-ybp').click(function() {
+        $('#import-dsk').modal('hide');
+        $('#upload-ybp').modal('show');
+    });
+    $('#id-post-dsk').click(async function() {
+        if (dsk.checkBind()) {
+            $('#import-dsk').modal('hide');
+            dsk.chooseSubject({
+                validCheck: function (node, tree) {
+                    const checkType = node.type !== dsk.projectTypeKey.folder;
+                    if (!checkType) {
+                        toastr.warning('请勿选择文件夹');
+                        return false;
+                    }
+                    const choosed = tree.datas.filter(x => { return !!x.selected; });
+                    if (choosed.length === 0) return true;
+                    if (node.project_id !== choosed[0].project_id) {
+                        toastr.warning('请勿跨建设项目选择');
+                        return false;
+                    }
+                    return true;
+                },
+                callback: async function (subjects) {
+                    postData('ledger/dsk', {subjects}, function (result) {
+                        ledgerTree.loadDatas(result.bills);
+                        treeCalc.calculateAll(ledgerTree);
+                        SpreadJsObj.loadSheetData(ledgerSpread.getActiveSheet(), 'tree', ledgerTree);
+                        pos.loadDatas(result.pos);
+                        posOperationObj.loadCurPosData();
+                        checkShowLast(result.bills.length);
+                    });
+                }
+            });
+        } else {
+            window.open('/profile/sms');
+        }
+    });
 });
 });
 // 生成当前节点列表
 // 生成当前节点列表
 function getNodeList(node) {
 function getNodeList(node) {

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

@@ -199,7 +199,7 @@ $(document).ready(() => {
         {title: '上涨 幅度(%)', colSpan: '1', rowSpan: '2', field: 'm_up_risk', hAlign: 2, width: 50, type: 'Number', readOnly: 'readOnly.isEdit'},
         {title: '上涨 幅度(%)', colSpan: '1', rowSpan: '2', field: 'm_up_risk', hAlign: 2, width: 50, type: 'Number', readOnly: 'readOnly.isEdit'},
         {title: '下跌 幅度(%)', colSpan: '1', rowSpan: '2', field: 'm_down_risk', hAlign: 2, width: 50, type: 'Number', readOnly: 'readOnly.isEdit'},
         {title: '下跌 幅度(%)', colSpan: '1', rowSpan: '2', field: 'm_down_risk', hAlign: 2, width: 50, type: 'Number', readOnly: 'readOnly.isEdit'},
         {title: '基准价', colSpan: '1', rowSpan: '2', field: 'basic_price', hAlign: 2, width: 60, type: 'Number', readOnly: 'readOnly.isEdit'},
         {title: '基准价', colSpan: '1', rowSpan: '2', field: 'basic_price', hAlign: 2, width: 60, type: 'Number', readOnly: 'readOnly.isEdit'},
-        {title: '基准时间', colSpan: '1', rowSpan: '2', field: 'basic_times', hAlign: 0, width: 70, formatter: '@', readOnly: 'readOnly.isEdit'},
+        {title: '基准时间', colSpan: '1', rowSpan: '2', field: 'basic_times', hAlign: 0, width: 70, formatter: '@', readOnly: 'readOnly.remark'},
         {title: '本期信息价|单价', colSpan: '3|1', rowSpan: '1|1', field: 'msg_tp', hAlign: 2, width: 60, type: 'Number', readOnly: 'readOnly.msg_tp'},
         {title: '本期信息价|单价', colSpan: '3|1', rowSpan: '1|1', field: 'msg_tp', hAlign: 2, width: 60, type: 'Number', readOnly: 'readOnly.msg_tp'},
         {title: '|时间', colSpan: '|1', rowSpan: '|1', field: 'msg_times', hAlign: 0, width: 80, formatter: '@', readOnly: 'readOnly.remark'},
         {title: '|时间', colSpan: '|1', rowSpan: '|1', field: 'msg_times', hAlign: 0, width: 80, formatter: '@', readOnly: 'readOnly.remark'},
         {title: '|单价差', colSpan: '|1', rowSpan: '|1', field: 'msg_spread', hAlign: 2, width: 60, type: 'Number', readOnly: true, getValue: 'getValue.msg_spread'},]
         {title: '|单价差', colSpan: '|1', rowSpan: '|1', field: 'msg_spread', hAlign: 2, width: 60, type: 'Number', readOnly: true, getValue: 'getValue.msg_spread'},]
@@ -212,6 +212,8 @@ $(document).ready(() => {
             {title: '|调差金额(材料税)', colSpan: '|1', rowSpan: '|1', field: 'm_tax_tp', hAlign: 2, width: 100, type: 'Number', readOnly: true, getValue: 'getValue.m_tax_tp'},
             {title: '|调差金额(材料税)', colSpan: '|1', rowSpan: '|1', field: 'm_tax_tp', hAlign: 2, width: 100, type: 'Number', readOnly: true, getValue: 'getValue.m_tax_tp'},
             {title: '截止上期|调差金额', colSpan: '2|1', rowSpan: '1|1', field: 'pre_tp', hAlign: 2, width: 100, type: 'Number', readOnly: true},
             {title: '截止上期|调差金额', colSpan: '2|1', rowSpan: '1|1', field: 'pre_tp', hAlign: 2, width: 100, type: 'Number', readOnly: true},
             {title: '|调差金额(材料税)', colSpan: '|1', rowSpan: '|1', field: 'tax_pre_tp', hAlign: 2, width: 100, type: 'Number', readOnly: true},
             {title: '|调差金额(材料税)', colSpan: '|1', rowSpan: '|1', field: 'tax_pre_tp', hAlign: 2, width: 100, type: 'Number', readOnly: true},
+            {title: '截止本期|调差金额', colSpan: '2|1', rowSpan: '1|1', field: 'end_tp', hAlign: 2, width: 100, type: 'Number', readOnly: true, getValue: 'getValue.end_tp'},
+            {title: '|调差金额(材料税)', colSpan: '|1', rowSpan: '|1', field: 'tax_end_tp', hAlign: 2, width: 100, type: 'Number', readOnly: true, getValue: 'getValue.tax_end_tp'},
             {title: '备注', colSpan: '1', rowSpan: '2', field: 'remark', hAlign: 0, width: 50, formatter: '@', cellType: 'ellipsisAutoTip', readOnly: 'readOnly.remark'},
             {title: '备注', colSpan: '1', rowSpan: '2', field: 'remark', hAlign: 0, width: 50, formatter: '@', cellType: 'ellipsisAutoTip', readOnly: 'readOnly.remark'},
             {title: '是否汇总', colSpan: '1', rowSpan: '2', field: 'is_summary', hAlign: 1, width: 60, cellType: 'checkbox', readOnly: 'readOnly.isEdit'}
             {title: '是否汇总', colSpan: '1', rowSpan: '2', field: 'is_summary', hAlign: 1, width: 60, cellType: 'checkbox', readOnly: 'readOnly.isEdit'}
         ])
         ])
@@ -221,6 +223,7 @@ $(document).ready(() => {
             {title: '|本期应耗数量', colSpan: '|1', rowSpan: '|1', field: 'quantity', hAlign: 2, width: 90, type: 'Number', readOnly: true},
             {title: '|本期应耗数量', colSpan: '|1', rowSpan: '|1', field: 'quantity', hAlign: 2, width: 90, type: 'Number', readOnly: true},
             {title: '|调差金额', colSpan: '|1', rowSpan: '|1', field: 'm_tp', hAlign: 2, width: 100, type: 'Number', readOnly: true, getValue: 'getValue.m_tp'},
             {title: '|调差金额', colSpan: '|1', rowSpan: '|1', field: 'm_tp', hAlign: 2, width: 100, type: 'Number', readOnly: true, getValue: 'getValue.m_tp'},
             {title: '截止上期|调差金额', colSpan: '1|1', rowSpan: '1|1', field: 'pre_tp', hAlign: 2, width: 100, type: 'Number', readOnly: true},
             {title: '截止上期|调差金额', colSpan: '1|1', rowSpan: '1|1', field: 'pre_tp', hAlign: 2, width: 100, type: 'Number', readOnly: true},
+            {title: '截止本期|调差金额', colSpan: '1|1', rowSpan: '1|1', field: 'end_tp', hAlign: 2, width: 100, type: 'Number', readOnly: true, getValue: 'getValue.end_tp'},
             {title: '备注', colSpan: '1', rowSpan: '2', field: 'remark', hAlign: 0, width: 50, formatter: '@', cellType: 'ellipsisAutoTip', readOnly: 'readOnly.remark'},
             {title: '备注', colSpan: '1', rowSpan: '2', field: 'remark', hAlign: 0, width: 50, formatter: '@', cellType: 'ellipsisAutoTip', readOnly: 'readOnly.remark'},
             {title: '是否汇总', colSpan: '1', rowSpan: '2', field: 'is_summary', hAlign: 1, width: 60, cellType: 'checkbox', readOnly: 'readOnly.isEdit'}
             {title: '是否汇总', colSpan: '1', rowSpan: '2', field: 'is_summary', hAlign: 1, width: 60, cellType: 'checkbox', readOnly: 'readOnly.isEdit'}
         ])
         ])
@@ -293,6 +296,12 @@ $(document).ready(() => {
             m_tax_tp: function (data) {
             m_tax_tp: function (data) {
                 const m_tp = ZhCalc.round(ZhCalc.mul(materialCol.getValue.m_spread(data), data.quantity), materialDecimal.tp);
                 const m_tp = ZhCalc.round(ZhCalc.mul(materialCol.getValue.m_spread(data), data.quantity), materialDecimal.tp);
                 return data.m_tax ? ZhCalc.round(ZhCalc.mul(m_tp, (1+ZhCalc.div(data.m_tax, 100))), materialDecimal.tp) : m_tp;
                 return data.m_tax ? ZhCalc.round(ZhCalc.mul(m_tp, (1+ZhCalc.div(data.m_tax, 100))), materialDecimal.tp) : m_tp;
+            },
+            end_tp: function (data) {
+                return ZhCalc.add(data.m_tp, data.pre_tp) || '';
+            },
+            tax_end_tp: function (data) {
+                return ZhCalc.add(data.m_tax_tp, data.tax_pre_tp) || '';
             }
             }
         },
         },
         readOnly: {
         readOnly: {

+ 249 - 33
app/public/js/material_audit.js

@@ -59,7 +59,7 @@ $(document).ready(function () {
     })
     })
 
 
     // 添加审批流程按钮逻辑
     // 添加审批流程按钮逻辑
-    $('.book-list').on('click', 'dt', function () {
+    $('.book-list').on('click', '.book-list dt', function () {
         const idx = $(this).find('.acc-btn').attr('data-groupid')
         const idx = $(this).find('.acc-btn').attr('data-groupid')
         const type = $(this).find('.acc-btn').attr('data-type')
         const type = $(this).find('.acc-btn').attr('data-type')
         if (type === 'hide') {
         if (type === 'hide') {
@@ -78,7 +78,7 @@ $(document).ready(function () {
     })
     })
 
 
     // 添加到审批流程中
     // 添加到审批流程中
-    $('dl').on('click', 'dd', function () {
+    $('#book-list').on('click', 'dd', function () {
         const id = parseInt($(this).data('id'));
         const id = parseInt($(this).data('id'));
         if (id) {
         if (id) {
             postData(getUrlPre() + '/audit/add', { auditorId: id }, (datas) => {
             postData(getUrlPre() + '/audit/add', { auditorId: id }, (datas) => {
@@ -87,19 +87,23 @@ $(document).ready(function () {
                 const auditorshtml = [];
                 const auditorshtml = [];
                 for (const [index,data] of datas.entries()) {
                 for (const [index,data] of datas.entries()) {
                     if (index !== 0) {
                     if (index !== 0) {
-                        html.push('<li class="list-group-item" auditorId="'+ data.aid +'">');
+                        html.push('<li class="list-group-item d-flex" auditorId="'+ data[0].aid +'">');
+                        html.push(`<div class="col-auto">${index}</div>`);
+                        html.push('<div class="col">');
+                        for (const auditor of data) {
+                            html.push(`<div class="d-inline-block mx-1"><i class="fa fa-user text-muted"></i> ${auditor.name} <small class="text-muted">${auditor.role}</small></div>`);
+                        }
+                        html.push('</div>');
+                        html.push('<div class="col-auto">');
+                        if (data[0].audit_type !== auditType.key.common) {
+                            html.push(`<span class="badge badge-pill badge-${auditType.info[data[0].audit_type].class} badge-bg-small"><small>${auditType.info[data[0].audit_type].long}</small></span>`);
+                        }
                         if (shenpi_status === shenpiConst.sp_status.sqspr || (shenpi_status === shenpiConst.sp_status.gdzs && index+1 !== datas.length)) {
                         if (shenpi_status === shenpiConst.sp_status.sqspr || (shenpi_status === shenpiConst.sp_status.gdzs && index+1 !== datas.length)) {
-                            html.push('<a href="javascript: void(0)" class="text-danger pull-right">移除</a>');
+                            html.push('<a href="javascript: void(0)" class="text-danger pull-right ml-1">移除</a>');
                         }
                         }
-                        html.push('<span>');
-                        html.push(data.order + ' ');
-                        html.push(data.name + ' ');
-                        html.push('</span>');
-                        html.push('<small class="text-muted">');
-                        html.push(data.role);
-                        html.push('</small></li>');
+                        html.push('</div>');
+                        html.push('</li>');
                     }
                     }
-                    // 添加新审批人流程修改
                     auditorshtml.push('<li class="list-group-item" data-auditorid="' + data.aid + '">');
                     auditorshtml.push('<li class="list-group-item" data-auditorid="' + data.aid + '">');
                     auditorshtml.push('<i class="fa ' + (index+1 === datas.length ? 'fa-stop-circle' : 'fa-chevron-circle-down') + '"></i> ');
                     auditorshtml.push('<i class="fa ' + (index+1 === datas.length ? 'fa-stop-circle' : 'fa-chevron-circle-down') + '"></i> ');
                     auditorshtml.push(data.name + ' <small class="text-muted">' + data.role + '</small>');
                     auditorshtml.push(data.name + ' <small class="text-muted">' + data.role + '</small>');
@@ -113,27 +117,7 @@ $(document).ready(function () {
                     auditorshtml.push('</li>');
                     auditorshtml.push('</li>');
                 }
                 }
                 $('#auditors').html(html.join(''));
                 $('#auditors').html(html.join(''));
-
-
-                // 重新上报时。令其它的审批人流程图标转换
-                // $('#auditors-list li i').removeClass('fa-stop-circle').addClass('fa-chevron-circle-down');
-                // for (let i = 0; i < $('#auditors-list li').length; i++) {
-                //     $('#auditors-list li').eq(i).find('.pull-right').text(transFormToChinese(i+1) + '审');
-                //     $('#auditors-list2 li').eq(i).find('.pull-right').text(transFormToChinese(i+1) + '审');
-                // }
-
                 $('#auditors-list').html(auditorshtml.join(''));
                 $('#auditors-list').html(auditorshtml.join(''));
-
-                // const auditorshtml2 = [];
-                // // 重新上报时。令其它的审批人流程图标转换
-                // $('#auditors-list2 li i').removeClass('fa-stop-circle').addClass('fa-chevron-circle-down');
-                // // 添加新审批人
-                // auditorshtml2.push('<li class="list-group-item" data-auditid="' + data.aid + '">');
-                // auditorshtml2.push('<h5 class="card-title"><i class="fa fa-stop-circle"></i> ');
-                // auditorshtml2.push(data.name + ' <small class="text-muted">' + data.role + '</small>');
-                // auditorshtml2.push('<span class="pull-right">终审</span>');
-                // auditorshtml2.push('</h5></li>');
-                // $('#auditors-list2').append(auditorshtml2.join(''));
             });
             });
         }
         }
     });
     });
@@ -147,7 +131,7 @@ $(document).ready(function () {
             li.remove();
             li.remove();
             for (const rst of result) {
             for (const rst of result) {
                 const aLi = $('li[auditorId=' + rst.aid + ']');
                 const aLi = $('li[auditorId=' + rst.aid + ']');
-                $('span', aLi).text(rst.order + ' ' + rst.name + ' ');
+                $('div:first', aLi).text(rst.order);
             }
             }
 
 
             // 如果是重新上报
             // 如果是重新上报
@@ -213,6 +197,238 @@ $(document).ready(function () {
             }
             }
         });
         });
     });
     });
+
+    // 管理员更改审批流程js部分
+    let timer2 = null;
+    let oldSearchVal2 = null;
+    $('body').on('input propertychange', '.gr-search', function(e) {
+        oldSearchVal2 = e.target.value;
+        timer2 && clearTimeout(timer2);
+        timer2 = setTimeout(() => {
+            const newVal = $(this).val();
+            const code = $(this).attr('data-code');
+            let html = '';
+            if (newVal && newVal === oldSearchVal2) {
+                accountList.filter(item => item && item.id !== material_uid && (item.name.indexOf(newVal) !== -1 || (item.mobile && item.mobile.indexOf(newVal) !== -1))).forEach(item => {
+                    html += `<dd class="border-bottom p-2 mb-0 " data-id="${item.id}" >
+                        <p class="mb-0 d-flex"><span class="text-primary">${item.name}</span><span
+                                class="ml-auto">${item.mobile || ''}</span></p>
+                        <span class="text-muted">${item.role || ''}</span>
+                    </dd>`
+                });
+                $('#' + code + '_dropdownMenu .book-list').empty();
+                $('#' + code + '_dropdownMenu .book-list').append(html);
+            } else {
+                if (!$('#' + code + '_dropdownMenu .acc-btn').length) {
+                    accountGroup.forEach((group, idx) => {
+                        if (!group) return;
+                        html += `<dt><a href="javascript: void(0);" class="acc-btn" data-groupid="${idx}" data-type="hide"><i class="fa fa-plus-square"></i>
+                        </a> ${group.groupName}</dt>
+                        <div class="dd-content" data-toggleid="${idx}">`;
+                        group.groupList.forEach(item => {
+                            if (item.id !== material_uid) {
+                                html += `<dd class="border-bottom p-2 mb-0 " data-id="${item.id}" >
+                                    <p class="mb-0 d-flex"><span class="text-primary">${item.name}</span><span
+                                            class="ml-auto">${item.mobile || ''}</span></p>
+                                    <span class="text-muted">${item.role || ''}</span>
+                                </dd>`;
+                            }
+                        });
+                        html += '</div>';
+                    });
+                    $('#' + code + '_dropdownMenu .book-list').empty();
+                    $('#' + code + '_dropdownMenu .book-list').append(html);
+                }
+            }
+        }, 400);
+    });
+
+    const getAdminEditShenpiListHtml = function(auditGroup) {
+        const html = [];
+        for (const [i, group] of auditGroup.entries()) {
+            if (i === 0) continue;
+            for (const [j, auditor] of group.entries()) {
+                html.push('<tr>');
+                if (j === 0) {
+                    const auditTypeHtml = auditor.type === auditType.key.common ? '' : `<span class="ml-2 badge badge-pill badge-${auditType.info[auditor.audit_type].class} p-1"><small>${auditType.info[auditor.audit_type].short}</small></span>`;
+                    html.push(`<td class="text-left d-flex">${i + '审'}${auditTypeHtml}</td>`);
+                } else {
+                    html.push(`<td class="text-left d-flex"></td>`);
+                }
+                html.push(`<td></span> ${auditor.name} <small class="text-muted">${auditor.role}</small></td>`);
+                html.push(`<td style="text-align: center"><span class="${sam_auditConst.auditStringClass[auditor.status]}">${ auditor.status !== sam_auditConst.status.uncheck ? sam_auditConst.auditString[auditor.status] : '待审批' }</span></td>`);
+                html.push('<td style="text-align: center">');
+                if (auditor.status === sam_auditConst.status.checking && j === group.length - 1) {
+                    html.push('<span class="dropdown mr-2">',
+                        `<a href="javascript: void(0)" class="add-audit" id="${auditor.aid}_add_dropdownMenuButton" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">增加</a>`,
+                        makeSelectAudit(auditor.aid+'_add', auditor.aid, 'add'),'</div>', '</span>');
+                }
+                if (auditor.status === sam_auditConst.status.uncheck) {
+                    if (j === group.length - 1) {
+                        html.push('<span class="dropdown mr-2">',
+                            `<a href="javascript: void(0)" class="add-audit" id="${auditor.aid}_add_dropdownMenuButton" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">增加</a>`,
+                            makeSelectAudit(auditor.aid+'_add', auditor.aid, 'add'),'</div>', '</span>');
+                        if (auditor.audit_type !== auditType.key.common) {
+                            html.push('<span class="dropdown mr-2">',
+                                `<a href="javascript: void(0)" class="add-audit" id="${auditor.aid}_add_dropdownMenuButton" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">平级</a>`,
+                                makeSelectAudit(auditor.aid+'_add-sibling', auditor.aid, 'add-sibling'),'</div>', '</span>');
+                        }
+                    }
+                    html.push('<span class="dropdown mr-2">',
+                        `<a href="javascript: void(0)" class="add-audit" id="${auditor.aid}_add_dropdownMenuButton" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">更换</a>`,
+                        makeSelectAudit(auditor.aid+'_change', auditor.aid, 'change'),'</div>', '</span>');
+                    html.push(`<span class="dropdown">
+                                    <a href="javascript: void(0)" class="text-danger" title="移除" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">移除</a>
+                                    <div class="dropdown-menu">
+                                        <span class="dropdown-item" href="javascript:void(0);">确认移除审批人?</span>
+                                        <div class="dropdown-divider"></div>
+                                        <div class="px-2 py-1 text-center">
+                                            <button class="remove-audit btn btn-sm btn-danger" data-id="${auditor.aid}">移除</button>
+                                            <button class="btn btn-sm btn-secondary">取消</button>
+                                        </div>
+                                    </div>
+                                    </span>`);
+                }
+                html.push('</td>');
+                html.push('</tr>');
+            }
+        }
+        return html.join('');
+    };
+
+    $('body').on('click', '#admin-edit-shenpi dl dd', function () {
+        const id = parseInt($(this).attr('data-id'));
+        if (!id) return;
+
+        let this_aid = parseInt($(this).parents('.book-list').attr('data-aid'));
+        let this_operate = $(this).parents('.book-list').attr('data-operate');
+        const user = _.find(accountList, function (item) {
+            return item.id === id;
+        });
+        const auditorIndex = _.findIndex(auditorList, { aid: id });
+        if (auditorIndex !== -1) {
+            toastr.warning('该审核人已存在,请勿重复添加');
+            return;
+        }
+        const prop = {
+            operate: this_operate,
+            old_aid: this_aid,
+            new_aid: user.id,
+        };
+        postData(getUrlPre() + '/audit/save', prop, (datas) => {
+            $('#admin-edit-shenpi-list').html(getAdminEditShenpiListHtml(datas));
+            changeLiucheng(datas);
+        });
+    });
+
+    // 移除审批人
+    $('body').on('click', '.remove-audit', function () {
+        const id = parseInt($(this).attr('data-id'));
+        const prop = {
+            operate: 'del',
+            old_aid: id,
+        };
+        postData(getUrlPre() + '/audit/save', prop, (datas) => {
+            $('#admin-edit-shenpi-list').html(getAdminEditShenpiListHtml(datas));
+            changeLiucheng(datas);
+        });
+    });
+
+    const getAuditTypeText = function (type) {
+        if (type === auditType.key.common) return '';
+        return `<span class="text-${auditType.info[type].class}">${auditType.info[type].long}</span>`;
+    };
+
+    function changeLiucheng(datas) {
+        const auditorshtml = [];
+        let lastAuditorHtml = [];
+        for (const [index,data] of datas.entries()) {
+            auditorshtml.push('<li class="list-group-item d-flex justify-content-between align-items-center">');
+            if (index === 0) {
+                auditorshtml.push('<span class="mr-1"><i class="fa fa fa-play-circle fa-rotate-90"></i></span>');
+            } else if (index+1 === datas.length) {
+                auditorshtml.push('<span class="mr-1"><i class="fa fa fa-stop-circle"></i></span>');
+            } else {
+                auditorshtml.push('<span class="mr-1"><i class="fa fa-chevron-circle-down"></i></span>');
+            }
+            auditorshtml.push('<span class="text-muted">');
+            for (const u of data) {
+                auditorshtml.push(`<small class="d-inline-block text-dark mx-1" title="${u.role}" data-auditorId="${u.aid}">${u.name}</small>`);
+            }
+            auditorshtml.push('</span>');
+            auditorshtml.push('<div class="d-flex ml-auto">');
+            if (data[0].audit_type !== auditType.key.common) auditorshtml.push(`<span class="badge badge-pill badge-${auditType.info[data[0].audit_type].class} p-1"><small>${auditType.info[data[0].audit_type].short}</small></span>`);
+            if (index === 0) {
+                auditorshtml.push('<span class="badge badge-light badge-pill ml-auto"><small>原报</small></span>');
+            } else if (index+1 === datas.length) {
+                auditorshtml.push('<span class="badge badge-light badge-pill"><small>终审</small></span>');
+            } else {
+                auditorshtml.push('<span class="badge badge-light badge-pill"><small>'+ transFormToChinese(index) +'审</small></span>');
+            }
+            auditorshtml.push('</div>');
+            auditorshtml.push('</li>');
+            if (data[0].status === sam_auditConst.status.uncheck) {
+                lastAuditorHtml.push('<li class="timeline-list-item pb-2 is_uncheck">');
+                if (index < datas.length - 1) {
+                    lastAuditorHtml.push('<div class="timeline-item-tail"></div>');
+                }
+                lastAuditorHtml.push('<div class="timeline-item-icon bg-secondary text-light"></div>');
+
+                lastAuditorHtml.push('<div class="timeline-item-content">');
+                lastAuditorHtml.push(`<div class="py-1">
+                        <span class="text-black-50">
+                        ${ index !== datas.length - 1 ? data[0].audit_order + '' : '终' }审 ${getAuditTypeText(data[0].audit_type)}
+                        </span>
+                    </div>`);
+                lastAuditorHtml.push('<div class="card"><div class="card-body px-3 py-0">');
+                for (const [i, auditor] of data.entries()) {
+                    lastAuditorHtml.push(`<div class="card-text p-2 py-3 row ${ ( i > 0 ? 'border-top' : '') }">`);
+                    lastAuditorHtml.push(`<div class="col"><span class="h6">${auditor.name}</span><span class="text-muted ml-1">${auditor.role}</span></div>`);
+                    lastAuditorHtml.push('<div class="col">');
+                    lastAuditorHtml.push('</div>');
+                    lastAuditorHtml.push('</div>');
+                }
+                lastAuditorHtml.push('</div></div>');
+                lastAuditorHtml.push('</div>');
+                lastAuditorHtml.push('</li>');
+            }
+        }
+        $('.last-auditor-list .is_uncheck').remove();
+        $('.last-auditor-list').append(lastAuditorHtml.join(''));
+        $('.auditors-list').html(auditorshtml.join(''));
+
+    }
+
+    // 审批流程-选择审批人html 生成
+    function makeSelectAudit(code, aid, status) {
+        let divhtml = '';
+        accountGroup.forEach((group, idx) => {
+            let didivhtml = '';
+            if(group) {
+                group.groupList.forEach(item => {
+                    didivhtml += item.id !== material_uid ? '<dd class="border-bottom p-2 mb-0 " data-id="' + item.id + '" >\n' +
+                        '<p class="mb-0 d-flex"><span class="text-primary">' + item.name + '</span><span\n' +
+                        '                                                                                class="ml-auto">' + item.mobile + '</span></p>\n' +
+                        '                                                                    <span class="text-muted">' + item.role + '</span>\n' +
+                        '                                                                    </dd>\n' : '';
+                });
+                divhtml += '<dt><a href="javascript: void(0);" class="acc-btn" data-groupid="' + idx + '" data-type="hide"><i class="fa fa-plus-square"></i></a> ' + group.groupName + '</dt>\n' +
+                    '                                                                <div class="dd-content" data-toggleid="' + idx + '">\n' + didivhtml +
+                    '                                                                </div>\n';
+            }
+        });
+        let html = '<div class="dropdown-menu dropdown-menu-right" id="' + code + '_dropdownMenu" aria-labelledby="' + code + '_dropdownMenuButton" style="width:220px">\n' +
+            '                                                        <div class="mb-2 p-2"><input class="form-control form-control-sm gr-search"\n' +
+            '                                                                                     placeholder="姓名/手机 检索" autocomplete="off" data-code="' + code + '"></div>\n' +
+            '                                                        <dl class="list-unstyled book-list" data-aid="'+ aid +'" data-operate="'+ status +'">\n' + divhtml +
+            '                                                        </dl>\n' +
+            '                                                    </div>\n' +
+            '                                                </div>\n' +
+            '                                            </span>\n' +
+            '                                        </span>\n' +
+            '                                        </li>';
+        return html;
+    }
 });
 });
 // 检查上报情况
 // 检查上报情况
 function checkAuditorFrom () {
 function checkAuditorFrom () {

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

@@ -434,14 +434,19 @@ $(document).ready(() => {
                 for (const aData of result.stages) {
                 for (const aData of result.stages) {
                     calculateStageLedgerData(aData.bills);
                     calculateStageLedgerData(aData.bills);
                     cTree.loadMinorData(aData.bills, aData.order + '', ['contract_qty', 'contract_tp', 'qc_qty', 'qc_tp', 'gather_qty', 'gather_tp'], ['contract_tp', 'qc_tp', 'gather_tp']);
                     cTree.loadMinorData(aData.bills, aData.order + '', ['contract_qty', 'contract_tp', 'qc_qty', 'qc_tp', 'gather_qty', 'gather_tp'], ['contract_tp', 'qc_tp', 'gather_tp']);
-                    treeCalc.calculateAll(cTree);
                     calculateStagePosData(aData.pos);
                     calculateStagePosData(aData.pos);
                     cPos.loadMinorData(aData.pos, aData.order + '', ['contract_qty', 'qc_qty', 'gather_qty']);
                     cPos.loadMinorData(aData.pos, aData.order + '', ['contract_qty', 'qc_qty', 'gather_qty']);
                 }
                 }
+                cTree.reCalcSumData(['contract_qty', 'contract_tp', 'qc_qty', 'qc_tp', 'gather_qty', 'gather_tp'], showData);
+                treeCalc.calculateAll(cTree);
+                cPos.reCalcSumData(['contract_qty', 'qc_qty', 'gather_qty'], showData);
                 refreshView();
                 refreshView();
                 $('#select-qi').modal('hide');
                 $('#select-qi').modal('hide');
             }, null, true);
             }, null, true);
         } else {
         } else {
+            cTree.reCalcSumData(['contract_qty', 'contract_tp', 'qc_qty', 'qc_tp', 'gather_qty', 'gather_tp'], showData);
+            treeCalc.calculateAll(cTree);
+            cPos.reCalcSumData(['contract_qty', 'qc_qty', 'gather_qty'], showData);
             refreshView();
             refreshView();
             $('#select-qi').modal('hide');
             $('#select-qi').modal('hide');
         }
         }

+ 123 - 142
app/public/js/measure_material.js

@@ -8,166 +8,147 @@
  * @version
  * @version
  */
  */
 $(function () {
 $(function () {
+    const getGroupAuditHtml = function (group) {
+        return group.map(u => { return `<small class="d-inline-block text-dark mx-1" title="${u.role}" data-auditorId="${u.aid}">${u.name}</small>`; }).join('');
+    };
+
+    const getAuditTypeHtml = function (type) {
+        if (type === auditType.key.common) return '';
+        return `<div class="li-subscript"><span class="badge badge-pill badge-${auditType.info[type].class} p-1 badge-bg-small"><small>${auditType.info[type].short}</small></span></div>`;
+    };
+
+    const getAuditTypeText = function (type) {
+        if (type === auditType.key.common) return '';
+        return `<span class="text-${auditType.info[type].class}">${auditType.info[type].long}</span>`;
+    };
     // 获取审批流程
     // 获取审批流程
     $('a[data-target="#sp-list" ]').on('click', function () {
     $('a[data-target="#sp-list" ]').on('click', function () {
         const data = {
         const data = {
             order: $(this).attr('m-order'),
             order: $(this).attr('m-order'),
         };
         };
         postData('/tender/' + tenderId + '/measure/material/auditors', data, function (result) {
         postData('/tender/' + tenderId + '/measure/material/auditors', data, function (result) {
-            const { auditHistory, auditors, user } = result
-            let auditorsHTML = ''
-            let historyHTML = ''
-            auditors.forEach((auditor, idx) => {
+            const { auditHistory, auditors2, user } = result
+            let auditorsHTML = [];
+            auditors2.forEach((group, idx) => {
                 if (idx === 0) {
                 if (idx === 0) {
-                    auditorsHTML += `<li class="list-group-item">
-                        <i class="fa fa fa-play-circle fa-rotate-90"></i> ${auditor.name}
-                        <small class="text-muted">${auditor.role}</small>
-                        <span class="pull-right">原报</span>
-                    </li>`
-                } else if(idx === auditors.length -1 && idx !== 0) {
-                    auditorsHTML += `<li class="list-group-item">
-                        <i class="fa fa fa-stop-circle"></i> ${auditor.name}
-                        <small class="text-muted">${auditor.role}</small>
-                        <span class="pull-right">终审</span>
-                    </li>`
+                    auditorsHTML.push(`<li class="list-group-item d-flex justify-content-between align-items-center">
+                    <span class="mr-1"><i class="fa fa fa-play-circle fa-rotate-90"></i></span>
+                <span class="text-muted">${getGroupAuditHtml(group)}</span>
+                <span class="badge badge-light badge-pill ml-auto"><small>原报</small></span>
+                </li>`);
+                } else if(idx === auditors2.length -1 && idx !== 0) {
+                    auditorsHTML.push(`<li class="list-group-item d-flex justify-content-between align-items-center">
+                    <span class="mr-1"><i class="fa fa fa-stop-circle fa-rotate-90"></i></span>
+                <span class="text-muted">${getGroupAuditHtml(group)}</span>
+                <div class="d-flex ml-auto">
+                ${getAuditTypeHtml(group[0].audit_type)}
+                <span class="badge badge-light badge-pill ml-auto"><small>终审</small></span>
+                </div>
+                </li>`);
                 } else {
                 } else {
-                    auditorsHTML += `<li class="list-group-item">
-                        <i class="fa fa-chevron-circle-down"></i> ${auditor.name}
-                        <small class="text-muted">${auditor.role}</small>
-                        <span class="pull-right">${transFormToChinese(idx)}审</span>
-                    </li>`
+                    auditorsHTML.push(`<li class="list-group-item d-flex justify-content-between align-items-center">
+                    <span class="mr-1"><i class="fa fa fa-chevron-circle-down"></i></span>
+                <span class="text-muted">${getGroupAuditHtml(group)}</span>
+                <div class="d-flex ml-auto">
+                ${getAuditTypeHtml(group[0].audit_type)}
+                <span class="badge badge-light badge-pill"><small>${transFormToChinese(idx)}审</small></span>
+                </div>
+                </li>`);
                 }
                 }
-            })
+            });
             $('#auditor-list').empty()
             $('#auditor-list').empty()
-            $('#auditor-list').append(auditorsHTML)
-            const leftAuditors = auditors;
-            auditHistory.forEach((auditors, idx) => {
-                if(idx === auditHistory.length - 1 && auditHistory.length !== 1) {
-                    historyHTML += `<div class="text-right"><a href="javascript: void(0);" id="fold-btn" data-target="show"
-                    >展开历史审批流程</a></div>`
+            $('#auditor-list').append(auditorsHTML.join(''));
+
+            let historyHTML = [];
+            auditHistory.forEach((his, idx) => {
+                if (idx === auditHistory.length - 1 && auditHistory.length !== 1) {
+                    historyHTML.push(`<div class="text-right"><a href="javascript: void(0);" id="fold-btn" data-target="show">展开历史审批流程</a></div>`);
                 }
                 }
-                historyHTML += `<div class="${idx < auditHistory.length - 1 ? 'fold-card' : ''}">
-                <div class="text-center text-muted">${idx + 1}#</div>
-                <ul class="timeline-list list-unstyled mt-2">`
-                auditors.forEach((auditor, index) => {
+                historyHTML.push(`<div class="${idx < auditHistory.length - 1 ? 'fold-card' : ''}">`);
+                historyHTML.push(`<div class="text-center text-muted">${idx+1}#</div>`);
+                historyHTML.push(`<ul class="timeline-list list-unstyled mt-2 ${ idx === auditHistory.length - 1 && auditHistory.length !== 1 ? 'last-auditor-list' : '' }">`);
+                his.forEach((group, index) => {
                     if (index === 0) {
                     if (index === 0) {
-                        historyHTML += `<li class="timeline-list-item pb-2">
-                            <div class="timeline-item-date">
-                                ${formatDate(auditor.begin_time)}
-                            </div>
-                            <div class="timeline-item-tail"></div>
-                            <div class="timeline-item-icon bg-success text-light">
-                                <i class="fa fa-caret-down"></i>
-                            </div>
-                            <div class="timeline-item-content">
-                                <div class="card">
-                                    <div class="card-body p-3">
-                                        <div class="card-text">
-                                            <p class="mb-1"><span
-                                                    class="h5">${user.name}</span><span
-                                                    class="pull-right text-success">${idx !== 0 ? '重新' : ''}上报审批</span>
-                                            </p>
-                                            <p class="text-muted mb-0">${user.role}</p>
-                                        </div>
-                                    </div>
-                                </div>
-                            </div>
-                        </li>
-                        <li class="timeline-list-item pb-2">
-                            <div class="timeline-item-date">
-                                ${formatDate(auditor.end_time)}
-                            </div>`
-
-                            if(index < auditors.length - 1) {
-                                historyHTML += `<div class="timeline-item-tail"></div>`
-                            }
-                            if(auditor.status === auditConst.status.checked) {
-                                historyHTML += `<div class="timeline-item-icon bg-success text-light">
-                                    <i class="fa fa-check"></i>
-                                </div>`
-
-                            } else if(auditor.status === auditConst.status.checkNo || auditor.status === auditConst.status.checkNoPre) {
-                                historyHTML += `<div class="timeline-item-icon bg-warning text-light">
-                                    <i class="fa fa-level-up"></i>
-                                </div>`
-                            } else if(auditor.status === auditConst.status.checking) {
-                                historyHTML += `<div class="timeline-item-icon bg-warning text-light">
-                                    <i class="fa fa-ellipsis-h"></i>
-                                </div>`
-                            } else {
-                                historyHTML += `<div class="timeline-item-icon bg-secondary text-light"></div>`
-
-                            }
-                            historyHTML += `<div class="timeline-item-content">
-                                <div class="card">
-                                    <div class="card-body p-3">
-                                        <div class="card-text">
-                                            <p class="mb-1"><span class="h5">${auditor.name}</span><span
-                                                    class="pull-right ${auditConst.statusClass[auditor.status]}">${auditConst.statusString[auditor.status]}</span>
-                                            </p>
-                                            <p class="text-muted mb-0">${auditor.role}</p>
-                                        </div>
-                                    </div>`
-                            if (auditor.opinion) {
-                            historyHTML += `<div class="card-body p-3 border-top">
-                                    <p style="margin: 0;">${auditor.opinion}</p>
-                                </div>`
-                            }
-                            historyHTML += `</div></div></li>`
+                        historyHTML.push(`<li class="timeline-list-item pb-2">
+                                            <div class="timeline-item-date">
+                                                ${group.beginYear}
+                                                <span>${group.beginDate}</span>
+                                                <span>${group.beginTime}</span>
+                                            </div>
+                                            <div class="timeline-item-tail"></div>
+                                            <div class="timeline-item-icon bg-success text-light"><i class="fa fa-caret-down"></i></div>
+                                            <div class="timeline-item-content">
+                                                <div class="py-1">
+                                                    <span class="text-black-50">原报</span>
+                                                    <span class="pull-right text-success">${idx !== 0 ? '重新' : '' }上报审批</span>
+                                                </div>
+                                                <div class="card">
+                                                    <div class="card-body px-3 py-0">
+                                                        <div class="card-text p-2 py-3 row">
+                                                            <div class="col">
+                                                                <span class="h6">${user.name}</span>
+                                                                <span class="text-muted ml-1">${user.role}</span>
+                                                            </div>
+                                                            <div class="col">
+                                                                <span class="pull-right text-success"><i class="fa fa-check-circle"></i></span>
+                                                            </div>
+                                                        </div>
+                                                    </div>
+                                                </div>
+                                            </div>
+                                        </li>`);
+                    }
+                    historyHTML.push(`<li class="timeline-list-item pb-2 ${ group.status === auditConst.status.uncheck && idx === auditHistory.length - 1 && auditHistory.length !== 1 ? 'is_uncheck' : ''}">`);
+                    if (group.endYear) {
+                        historyHTML.push(`<div class="timeline-item-date">${group.endYear}<span>${group.endDate}</span><span>${group.endTime}</span></div>`);
+                    }
+                    if (index < his.length - 1) {
+                        historyHTML.push('<div class="timeline-item-tail"></div>');
+                    }
+                    if (group.status === auditConst.status.checked) {
+                        historyHTML.push('<div class="timeline-item-icon bg-success text-light"><i class="fa fa-check"></i></div>');
+                    } else if (group.status === auditConst.status.checkNo || group.status === auditConst.status.checkNoPre || group.status === auditConst.status.checkCancel) {
+                        historyHTML.push('<div class="timeline-item-icon bg-warning text-light"><i class="fa fa-level-up"></i></div>');
+                    } else if (group.status === auditConst.status.checking) {
+                        historyHTML.push('<div class="timeline-item-icon bg-warning text-light"><i class="fa fa-ellipsis-h"></i></div>');
                     } else {
                     } else {
-                        historyHTML += `<li class="timeline-list-item pb-2">
-                        <div class="timeline-item-date">
-                            ${formatDate(auditor.end_time)}
-                        </div>`
-
-                        if(index < auditors.length - 1) {
-                            historyHTML += `<div class="timeline-item-tail"></div>`
-                        }
-                        if(auditor.status === auditConst.status.checked) {
-                            historyHTML += `<div class="timeline-item-icon bg-success text-light">
-                                <i class="fa fa-check"></i>
-                            </div>`
-                        } else if(auditor.status === auditConst.status.checkNo || auditor.status === auditConst.status.checkNoPre) {
-                            historyHTML += `<div class="timeline-item-icon bg-warning text-light">
-                                <i class="fa fa-level-up"></i>
-                            </div>`
+                        historyHTML.push('<div class="timeline-item-icon bg-secondary text-light"></div>');
+                    }
 
 
-                        } else if(auditor.status === auditConst.status.checking) {
-                            historyHTML += `<div class="timeline-item-icon bg-warning text-light">
-                                <i class="fa fa-ellipsis-h"></i>
-                            </div>`
-                        } else {
-                            historyHTML += `<div class="timeline-item-icon bg-secondary text-light"></div>`
+                    historyHTML.push('<div class="timeline-item-content">');
+                    const statuStr = group.status !== auditConst.status.uncheck ?
+                        `<span class="pull-right ${auditConst.statusClass[group.status]}">${auditConst.statusString[group.status]}</span>` : '';
+                    historyHTML.push(`<div class="py-1">
+                        <span class="text-black-50">
+                        ${ !group.is_final ? group.audit_order + '' : '终' }审 ${getAuditTypeText(group.audit_type)}
+                        </span>
+                        ${statuStr}
+                    </div>`);
+                    historyHTML.push('<div class="card"><div class="card-body px-3 py-0">');
+                    for (const [i, auditor] of group.auditors.entries()) {
+                        historyHTML.push(`<div class="card-text p-2 py-3 row ${ ( i > 0 ? 'border-top' : '') }">`);
+                        historyHTML.push(`<div class="col"><span class="h6">${auditor.name}</span><span class="text-muted ml-1">${auditor.role}</span></div>`);
+                        historyHTML.push('<div class="col">');
+                        if (auditor.status === auditConst.status.checked) {
+                            historyHTML.push('<span class="pull-right text-success"><i class="fa fa-check-circle"></i></span>');
+                        } if (auditor.status === auditConst.status.checkNo || auditor.status === auditConst.status.checkNoPre || auditor.status === auditConst.status.checkCancel) {
+                            historyHTML.push('<span class="pull-right text-warning"><i class="fa fa-share-square fa-rotate-270"></i></span>');
                         }
                         }
-                        historyHTML += `<div class="timeline-item-content">
-                        <div class="card">
-                            <div class="card-body p-3">
-                                <div class="card-text">
-                                    <p class="mb-1"><span class="h5">${auditor.name}</span>
-                                        <span
-                                            class="pull-right
-                                                            ${auditConst.statusClass[auditor.status]}">${auditor.status !== auditConst.status.uncheck ? auditConst.statusString[auditor.status] : ''}
-                                            ${auditor.status === auditConst.status.checkNo ? user.name : ''}
-                                            ${auditor.status === auditConst.status.checkNoPre ? (leftAuditors.find(item => item.order === auditor.sort-1) ? leftAuditors.find(item => item.order === auditor.sort-1).name : '') : ''}
-                                        </span>
-                                    </p>
-                                    <p class="text-muted mb-0">${auditor.role}</p>
-                                </div>
-                            </div>`
-
+                        historyHTML.push('</div>');
                         if (auditor.opinion) {
                         if (auditor.opinion) {
-                        historyHTML += `<div class="card-body p-3 border-top">
-                            <p style="margin: 0;">${auditor.opinion} </p>
-                        </div>`
+                            historyHTML.push(`<div class="col-12 py-1 bg-light"><i class="fa fa-commenting-o mr-1"></i>${auditor.opinion}</div>`);
                         }
                         }
-                        historyHTML += `</div></div></li>`
+                        historyHTML.push('</div>');
                     }
                     }
-                })
-                historyHTML += '</ul></div>'
-
-            })
-            $('#audit-list').empty()
-            $('#audit-list').append(historyHTML)
+                    historyHTML.push('</div></div>');
+                    historyHTML.push('</div>');
+                    historyHTML.push('</li>');
+                });
+                historyHTML.push('</div>');
+                historyHTML.push('</ul>');
+            });
+            $('#audit-list').empty();
+            $('#audit-list').append(historyHTML.join(''));
         });
         });
     });
     });
 
 

+ 30 - 2
app/public/js/path_tree.js

@@ -259,7 +259,6 @@ class MasterPosData extends PosData {
                 for (const prop of fields) {
                 for (const prop of fields) {
                     if (data[prop] !== undefined) {
                     if (data[prop] !== undefined) {
                         node[prop + fieldSuf] = data[prop];
                         node[prop + fieldSuf] = data[prop];
-                        node['sum_' + prop] = ZhCalc.add(node['sum_' + prop], data[prop]);
                     }
                     }
                 }
                 }
                 loadedData.push(node);
                 loadedData.push(node);
@@ -267,6 +266,21 @@ class MasterPosData extends PosData {
         }
         }
         return loadedData;
         return loadedData;
     }
     }
+
+    reCalcSumData(fields, fieldSufs) {
+        this.datas.forEach(d => {
+            for (const prop of fields) {
+                delete d['sum_' + prop];
+            }
+            for (const prop of fields) {
+                for (const s of fieldSufs) {
+                    if (d[prop + s] !== undefined) {
+                        d['sum_'+ prop] = ZhCalc.add(d['sum_' + prop], d[prop + s]);
+                    }
+                }
+            }
+        });
+    }
 }
 }
 
 
 const createNewPathTree = function (type, setting) {
 const createNewPathTree = function (type, setting) {
@@ -1636,7 +1650,6 @@ const createNewPathTree = function (type, setting) {
                     for (const prop of fields) {
                     for (const prop of fields) {
                         if (data[prop] !== undefined) {
                         if (data[prop] !== undefined) {
                             node[prop + fieldSuf] = data[prop];
                             node[prop + fieldSuf] = data[prop];
-                            node['sum_' + prop] = ZhCalc.add(node['sum_' + prop], data[prop]);
                         }
                         }
                     }
                     }
                     loadedData.push(node);
                     loadedData.push(node);
@@ -1644,6 +1657,21 @@ const createNewPathTree = function (type, setting) {
             }
             }
             return loadedData;
             return loadedData;
         }
         }
+
+        reCalcSumData(fields, fieldSufs) {
+            this.datas.forEach(d => {
+                for (const prop of fields) {
+                    delete d['sum_' + prop];
+                }
+                for (const prop of fields) {
+                    for (const s of fieldSufs) {
+                        if (d[prop + s] !== undefined) {
+                            d['sum_'+ prop] = ZhCalc.add(d['sum_' + prop], d[prop + s]);
+                        }
+                    }
+                }
+            });
+        }
     }
     }
 
 
     class FilterTree extends BaseTree {
     class FilterTree extends BaseTree {

+ 290 - 0
app/public/js/shares/dsk.js

@@ -0,0 +1,290 @@
+'use strict';
+
+const dsk = (function () {
+    const projectType = [
+        { key: 'folder', value: 1, name: '文件夹' },
+        { key: 'project', value: 2, name: '建设项目' },
+        { key: 'item', value: 3, name: '单项工程' },
+        { key: 'unit', value: 4, name: '单位工程' },
+    ];
+    const projectTypeKey = (function(arr) {
+        const result = {};
+        for (const a of arr) {
+            result[a.key] = a.value;
+        }
+        return result;
+    })(projectType);
+    const checkBind = async function() {
+        const data = await postDataAsync('/profile/dsk/api', {type: 'hadbind'});
+        return data !== 1 && data !== 2;
+    };
+    const loadCompilation = async function () {
+        const data = await postDataAsync('/profile/dsk/api', {type: 'compilation'});
+        return data.compilation;
+    };
+    const loadProject = async function(compilationId, showWaiting = true) {
+        return await postDataAsync('/profile/dsk/api', {type: 'project', compilationId}, showWaiting);
+    };
+    const loadProjectTree = async function (compilationId, projectId, showWaiting = true) {
+        return await postDataAsync('/profile/dsk/api', {type: 'project_tree', compilationId, projectId}, showWaiting)
+    };
+    const loadBills = async function(compilationId, subjectId, showWaiting = true) {
+        return await postDataAsync('/profile/dsk/api', {type: 'project_bills', compilationId, treeId: subjectId}, showWaiting)
+    };
+
+    class DskTree {
+        /**
+         *
+         * @param setting - {Object}配置
+         * setting中必须设置id,pid,order,rootId(id, 父id, 同层排序, 根节点id)
+         * e.g.{id: 'ID', pid: 'parentID', order: 'seq'}
+         * 目前仅用于载入建筑ybp文件中的清单部分,生成树结构以便汇总导入
+         */
+        constructor(setting) {
+            this.setting = JSON.parse(JSON.stringify(setting));
+            this.clearDatas();
+        }
+        clearDatas() {
+            // 数据集合
+            this.datas = [];
+            // id索引
+            this.items = {};
+            // 排序索引
+            this.nodes = [];
+            // 首层节点
+            this.children = [];
+        }
+
+        sortChildren(children, recursive) {
+            const setting = this.setting;
+            children.sort((x, y) => { return x[setting.order] > y[setting.order]; });
+
+            if (!recursive) return;
+            for (const c of children) {
+                this.sortChildren(c.children);
+            }
+        }
+        sort() {
+            this.sortChildren(this.children, true);
+            const self = this;
+            const _loadNode = function(node) {
+                self.nodes.push(node);
+                for (const c of node.children) {
+                    _loadNode(c);
+                }
+            };
+            for (const child of this.children) {
+                _loadNode(child);
+            }
+        }
+        loadDatas(datas) {
+            this.clearDatas();
+            const setting = this.setting;
+            const self = this;
+            const _loadData = function(d) {
+                if (d[setting.pid] === setting.rootId) {
+                    self.children.push(d);
+                } else {
+                    let parent = self.items[d[setting.pid]];
+                    if (!parent) {
+                        parent = datas.find(x => { return x[setting.id] === d[setting.pid]; });
+                        if (!parent) {
+                            // console.log(d[setting.pid]);
+                            return null;
+                        }
+                        parent = _loadData(parent);
+                    }
+                    if (!parent) return null;
+                    parent.children.push(d);
+                }
+                d.children = [];
+                self.datas.push(d);
+                self.items[d[setting.id]] = d;
+                return d;
+            };
+            for (const d of datas) {
+                if (this.items[d[setting.id]]) continue;
+                _loadData(d);
+            }
+            this.sort();
+        }
+    }
+
+    const chooseSubjectObj = {
+        obj: null,
+        compilationObj: null,
+        subjectSpread: null,
+        subjectSheet: null,
+        compliation: null,
+        loadCompilation: async function() {
+            if (this.compliation) return;
+
+            this.compliation = await loadCompilation();
+            for (const c of this.compliation) {
+                c.project = await loadProject(c.ID);
+            }
+            const html = [];
+            for (const c of this.compliation) {
+                html.push(`<option value="${c.ID}">${c.name}</option>`);
+            }
+            this.compilationObj.html(html.join(''));
+        },
+        analysisSubjectTree(compilation) {
+            const subjectTree = createNewPathTree('gather', {
+                id: 'id',
+                pid: 'pid',
+                order: 'sort',
+                level: 'level',
+                rootId: -1,
+                fullPath: 'full_path',
+            });
+            const tempTree = new DskTree({ id: 'ID', pid: 'parentID', order: 'seq', rootId: '-1' });
+            tempTree.loadDatas(compilation.project);
+            const projectTree = new DskTree({ id: 'ID', pid: 'parentID', order: 'seq', rootId: '-1' });
+
+            const loadProjectTreeNode = function (data, parent, project) {
+                let cur;
+                if (data.type === projectTypeKey.project) {
+                    cur = parent;
+                } else {
+                    const type = projectType.find(x => { return x.value === data.type; });
+                    const node = { type: type.value, type_str: type.name, dsk_id: data.ID, name: data.name, project_id: project.ID, compilation_id: compilation.ID };
+                    cur = subjectTree.addNode(node, parent);
+                }
+                if (data.children) {
+                    data.children.forEach(c => {
+                        loadProjectTreeNode(c, cur, project);
+                    })
+                }
+            };
+            const loadTempTreeNode = function (data, parent) {
+                const type = projectType.find(x => { return x.value === data.type; });
+                const node = { type: type.value, type_str: type.name, dsk_id: data.ID, name: data.name, compilation_id: compilation.ID };
+                const cur = subjectTree.addNode(node, parent);
+                if (data.children) {
+                    data.children.forEach(c => {
+                        loadTempTreeNode(c, cur);
+                    })
+                }
+                if (data.type === projectTypeKey.project) {
+                    const top = data.subjects.find(x => { return x.ID === data.ID; });
+                    top.parentID = "-1";
+                    projectTree.loadDatas(data.subjects);
+                    cur.project_id = data.ID;
+                    projectTree.children.forEach(c => {
+                        loadProjectTreeNode(c, cur, data);
+                    });
+                }
+            };
+            tempTree.children.forEach(c => {
+                loadTempTreeNode(c, null);
+            });
+            subjectTree.sortTreeNode(true);
+            compilation.subjectTree = subjectTree;
+        },
+        loadSubjects: async function (compilationId) {
+            showWaitingView();
+            const compilation = compilationId ? this.compliation.find(x => { return x.ID === compilationId}) : this.compliation[0];
+            if (!compilation) return;
+            if (!compilation.subjectTree) {
+                for (const p of compilation.project) {
+                    if (p.type === projectTypeKey.project) p.subjects = await loadProjectTree(compilation.ID, p.ID, false);
+                    console.log(p.subjects);
+                }
+                this.analysisSubjectTree(compilation);
+            }
+            SpreadJsObj.loadSheetData(this.subjectSheet, SpreadJsObj.DataType.Tree, compilation.subjectTree);
+            closeWaitingView();
+        },
+        getSelectSubjects: function () {
+            const tree = this.subjectSheet.zh_tree;
+            if (!tree) return [];
+
+            return tree.datas.filter(x => { return x.type === projectTypeKey.unit && x.selected; }).map(x => { return { compilationId: x.compilation_id, subjectId: x.dsk_id }; });
+        },
+        csButtonClicked: function(e, info) {
+            if (!info.sheet.zh_setting) return;
+
+            const col = info.sheet.zh_setting.cols[info.col];
+            if (col.field !== 'selected') return;
+
+            const tree = info.sheet.zh_tree;
+            const node = SpreadJsObj.getSelectObject(info.sheet);
+            if (!node.selected) {
+                const canChoose = chooseSubjectObj.setting.validCheck ? chooseSubjectObj.setting.validCheck(node, tree) : true;
+                if (!canChoose) return;
+            }
+
+            node.selected = !node.selected;
+            if (node.children && node.children.length > 0) {
+                const posterity = tree.getPosterity(node);
+                for (const p of posterity) {
+                    p.selected = node.selected;
+                }
+                SpreadJsObj.reLoadRowData(info.sheet, info.row, posterity.length + 1);
+            } else {
+                SpreadJsObj.reLoadRowData(info.sheet, info.row, 1);
+            }
+        },
+        init() {
+            if (this.subjectSpread) return;
+
+            this.obj = $('#choose-dsk-subject');
+            this.compilationObj = $('#cdsks-compilation');
+            this.subjectSpread = SpreadJsObj.createNewSpread($('#cdsks-spread')[0]);
+            this.subjectSheet = this.subjectSpread.getActiveSheet();
+            SpreadJsObj.initSheet(this.subjectSheet, {
+                cols: [
+                    {title: '选择', field: 'selected', hAlign: 1, width: 60, formatter: '@', cellType: 'checkbox'},
+                    {title: '项目/分段名称', field: 'name', hAlign: 0, width: 740, formatter: '@', cellType: 'tree'},
+                    {title: '类型', field: 'type_str', hAlign: 0, width: 80, formatter: '@' },
+                ],
+                emptyRows: 0,
+                headRows: 1,
+                headRowHeight: [32],
+                defaultRowHeight: 21,
+                headerFont: '12px 微软雅黑',
+                font: '12px 微软雅黑',
+                selectedBackColor: '#fffacd',
+                readOnly: true,
+            });
+            this.subjectSpread.bind(spreadNS.Events.ButtonClicked, this.csButtonClicked);
+
+            $('#choose-dsk-subject-ok').click(() => {
+                if (chooseSubjectObj.setting.callback) {
+                    const subjects = chooseSubjectObj.getSelectSubjects();
+                    if (subjects.length === 0) {
+                        toastr.warning('请选择导入的计价分段');
+                        return;
+                    }
+                    chooseSubjectObj.setting.callback(subjects);
+                    this.obj.modal('hide');
+                }
+            });
+            $('#choose-dsk-subject').on('shown.bs.modal', function() {
+                chooseSubjectObj.subjectSpread.refresh();
+            });
+            $('#cdsks-compilation').click(function() {
+                chooseSubjectObj.loadSubjects(this.value);
+            });
+        },
+        async show (setting) {
+            this.init();
+            this.setting = setting;
+            this.obj.modal('show');
+            await this.loadCompilation();
+            await this.loadSubjects();
+        }
+    };
+
+    const chooseSubject = function (setting) {
+        chooseSubjectObj.show(setting);
+    };
+
+    return {
+        projectType, projectTypeKey,
+        checkBind, loadCompilation, loadProject, loadProjectTree, loadBills,
+        chooseSubject
+    };
+})();
+

+ 32 - 13
app/public/js/stage_gather.js

@@ -68,10 +68,18 @@ $(document).ready(function () {
     // 初始化所属项目节
     // 初始化所属项目节
     const leafXmjSpread = SpreadJsObj.createNewSpread($('#leaf-xmj-spread')[0]);
     const leafXmjSpread = SpreadJsObj.createNewSpread($('#leaf-xmj-spread')[0]);
     if (thousandth) sjsSettingObj.setTpThousandthFormat(leafXmjSpreadSetting);
     if (thousandth) sjsSettingObj.setTpThousandthFormat(leafXmjSpreadSetting);
+    leafXmjSpreadSetting.getColor = function (sheet, data, row, col, defaultColor) {
+        if (!data) return defaultColor;
+        return data.overRange && hintOver ? spreadColor.stage.over : defaultColor;
+    };
     SpreadJsObj.initSheet(leafXmjSpread.getActiveSheet(), leafXmjSpreadSetting);
     SpreadJsObj.initSheet(leafXmjSpread.getActiveSheet(), leafXmjSpreadSetting);
 
 
     const gatherLeafXmjSpread = SpreadJsObj.createNewSpread($('#leaf-xmj-gather-spread')[0]);
     const gatherLeafXmjSpread = SpreadJsObj.createNewSpread($('#leaf-xmj-gather-spread')[0]);
     if (thousandth) sjsSettingObj.setTpThousandthFormat(gatherLeafXmjSpreadSetting);
     if (thousandth) sjsSettingObj.setTpThousandthFormat(gatherLeafXmjSpreadSetting);
+    gatherLeafXmjSpreadSetting.getColor = function (sheet, data, row, col, defaultColor) {
+        if (!data) return defaultColor;
+        return data.overRange && hintOver ? spreadColor.stage.over : defaultColor;
+    };
     const gatherLeafXmjSheet = gatherLeafXmjSpread.getActiveSheet();
     const gatherLeafXmjSheet = gatherLeafXmjSpread.getActiveSheet();
     SpreadJsObj.initSheet(gatherLeafXmjSheet, gatherLeafXmjSpreadSetting);
     SpreadJsObj.initSheet(gatherLeafXmjSheet, gatherLeafXmjSpreadSetting);
 
 
@@ -174,24 +182,32 @@ $(document).ready(function () {
             }
             }
         }
         }
     }
     }
+    function checkXmjOverRange(data, checkXmjList) {
+        const xmjGatherOver = function (data, qtyField, per) {
+            if (data.end_contract_qty) {
+                if (!data[qtyField]) return true;
+                return data[qtyField] > 0
+                    ? data.end_contract_qty > ZhCalc.mul(data[qtyField], per)
+                    : data.end_contract_qty < ZhCalc.mul(data[qtyField], per) || data.end_contract_qty > 0;
+            } else {
+                return false;
+            }
+        };
+        const nPercent = checkOverInfo.coe;
+        for (const node of data) {
+            if (!node || !node[checkXmjList] || node[checkXmjList].length === 0) continue;
+            for (const xmj of node[checkXmjList]) {
+                const bOverRangeTz = checkOverInfo.checkTz ? xmjGatherOver(xmj, 'final_1_qty', nPercent) : false;
+                const bOverRangeDeal = false; // checkOverInfo.checkDeal ? xmjGatherOver(xmj, 'deal_final_1_qty', nPercent) : false;
+                xmj.overRange = bOverRangeDeal || bOverRangeTz;
+            }
+        }
+    }
     // 切换清单行,读取所属项目节数据
     // 切换清单行,读取所属项目节数据
     gclSpread.getActiveSheet().bind(spreadNS.Events.SelectionChanged, function (e, info) {
     gclSpread.getActiveSheet().bind(spreadNS.Events.SelectionChanged, function (e, info) {
         const iOldRow = info.oldSelections[0].row, iNewRow = info.newSelections[0].row;
         const iOldRow = info.oldSelections[0].row, iNewRow = info.newSelections[0].row;
         if (iNewRow !== iOldRow) loadRelaData(iNewRow);
         if (iNewRow !== iOldRow) loadRelaData(iNewRow);
     });
     });
-    // $('.custom-radio').click(() => {
-    //     setLocalCache('StageGatherOverType', $('input[name=customRadio]:checked').attr('id'));
-    //     checkOverRange(gclGatherData);
-    //     SpreadJsObj.reLoadSheetData(gclSpread.getActiveSheet());
-    //     // 收起菜单
-    //     $('.dropdown-menu').click();
-    // });
-    // $('#over-percent').change(function () {
-    //     this.value = this.value >= 50 ? (this.value <= 100 ? ZhCalc.round(this.value, 2) : 100) : 50;
-    //     setLocalCache('StageGatherOverPercent', this.value);
-    //     checkOverRange(gclGatherData);
-    //     SpreadJsObj.reLoadSheetData(gclSpread.getActiveSheet());
-    // });
 
 
     postData(preUrl + '/load', { filter: 'ledger;pos;dealBills;spec;change;stageChange;preStageChange' }, function (result) {
     postData(preUrl + '/load', { filter: 'ledger;pos;dealBills;spec;change;stageChange;preStageChange' }, function (result) {
         // 解析清单汇总数据
         // 解析清单汇总数据
@@ -205,6 +221,8 @@ $(document).ready(function () {
         gclGatherModel.checkDiffer(gclGatherData);
         gclGatherModel.checkDiffer(gclGatherData);
         gclGatherModel.reGatherLeafXmj();
         gclGatherModel.reGatherLeafXmj();
         checkOverRange(gclGatherData);
         checkOverRange(gclGatherData);
+        checkXmjOverRange(gclGatherData, 'leafXmjs');
+        checkXmjOverRange(gclGatherData, 'gatherLeafXmjs');
         // 加载清单数据
         // 加载清单数据
         SpreadJsObj.loadSheetData(gclSpread.getActiveSheet(), SpreadJsObj.DataType.Data, gclGatherData);
         SpreadJsObj.loadSheetData(gclSpread.getActiveSheet(), SpreadJsObj.DataType.Data, gclGatherData);
         loadRelaData(0);
         loadRelaData(0);
@@ -289,6 +307,7 @@ $(document).ready(function () {
             col.visible = col.type === 'Number' || fields.indexOf(col.field) >= 0;
             col.visible = col.type === 'Number' || fields.indexOf(col.field) >= 0;
         }
         }
         gclGatherModel.reGatherLeafXmj(fields);
         gclGatherModel.reGatherLeafXmj(fields);
+        checkXmjOverRange(gclGatherData, 'gatherLeafXmjs');
         SpreadJsObj.refreshColumnVisible(gatherLeafXmjSheet);
         SpreadJsObj.refreshColumnVisible(gatherLeafXmjSheet);
         loadGatherLeafXmjData();
         loadGatherLeafXmjData();
     });
     });

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

@@ -152,7 +152,7 @@ const tenderListSpec = (function(){
         // 本期实付
         // 本期实付
         if (colSetCache.sf_tp.show) {
         if (colSetCache.sf_tp.show) {
             html.push('<td style="width: 100px" class="text-right">');
             html.push('<td style="width: 100px" class="text-right">');
-            html.push(node.yf_tp || '');
+            html.push(node.sf_tp || '');
             html.push('</td>');
             html.push('</td>');
         }
         }
         // 截止本期实付
         // 截止本期实付

+ 9 - 0
app/router.js

@@ -20,6 +20,7 @@ module.exports = app => {
     // 期读取中间件
     // 期读取中间件
     const stageCheck = app.middlewares.stageCheck();
     const stageCheck = app.middlewares.stageCheck();
     const settleCheck = app.middlewares.settleCheck();
     const settleCheck = app.middlewares.settleCheck();
+    const phasePayCheck = app.middlewares.phasePayCheck();
     // 材料调差读取中间件
     // 材料调差读取中间件
     const materialCheck = app.middlewares.materialCheck();
     const materialCheck = app.middlewares.materialCheck();
     // 第三方接口认证判断中间件
     // 第三方接口认证判断中间件
@@ -90,6 +91,9 @@ module.exports = app => {
     app.get('/dashboard/msg/add/:id', sessionAuth, 'dashboardController.msgAdd');
     app.get('/dashboard/msg/add/:id', sessionAuth, 'dashboardController.msgAdd');
     app.post('/dashboard/msg/set/:id', sessionAuth, datetimeFill, 'dashboardController.msgSet');
     app.post('/dashboard/msg/set/:id', sessionAuth, datetimeFill, 'dashboardController.msgSet');
     app.get('/dashboard/msg/del/:id', sessionAuth, 'dashboardController.msgDelete');
     app.get('/dashboard/msg/del/:id', sessionAuth, 'dashboardController.msgDelete');
+    app.post('/dashboard/msg/:id/file/upload', sessionAuth, 'dashboardController.msgUploadFile');
+    app.post('/dashboard/msg/:id/file/del', sessionAuth, 'dashboardController.msgDeleteFile');
+    app.get('/wap/message/download/file/:fid', 'wapController.messageDownloadFile');
 
 
     // 推送相关
     // 推送相关
     // app.post('/dashboard/push', sessionAuth, 'dashboardController.pushSet');
     // app.post('/dashboard/push', sessionAuth, 'dashboardController.pushSet');
@@ -242,6 +246,7 @@ module.exports = app => {
     app.post('/tender/:id/ledger/update', sessionAuth, tenderCheck, uncheckTenderCheck, 'ledgerController.update');
     app.post('/tender/:id/ledger/update', sessionAuth, tenderCheck, uncheckTenderCheck, 'ledgerController.update');
     app.post('/tender/:id/ledger/upload-excel/:ueType', sessionAuth, tenderCheck, uncheckTenderCheck, 'ledgerController.uploadExcel');
     app.post('/tender/:id/ledger/upload-excel/:ueType', sessionAuth, tenderCheck, uncheckTenderCheck, 'ledgerController.uploadExcel');
     app.post('/tender/:id/ledger/ybp', sessionAuth, tenderCheck, uncheckTenderCheck, 'ledgerController.uploadYbp');
     app.post('/tender/:id/ledger/ybp', sessionAuth, tenderCheck, uncheckTenderCheck, 'ledgerController.uploadYbp');
+    app.post('/tender/:id/ledger/dsk', sessionAuth, tenderCheck, uncheckTenderCheck, 'ledgerController.importDsk');
     app.get('/tender/:id/ledger/download/:file', sessionAuth, tenderCheck, uncheckTenderCheck, 'ledgerController.download');
     app.get('/tender/:id/ledger/download/:file', sessionAuth, tenderCheck, uncheckTenderCheck, 'ledgerController.download');
     app.post('/tender/:id/anc-gcl/update', sessionAuth, tenderCheck, uncheckTenderCheck, 'ledgerController.ancGclUpdate');
     app.post('/tender/:id/anc-gcl/update', sessionAuth, tenderCheck, uncheckTenderCheck, 'ledgerController.ancGclUpdate');
     app.post('/tender/:id/pos/update', sessionAuth, tenderCheck, uncheckTenderCheck, 'ledgerController.posUpdate');
     app.post('/tender/:id/pos/update', sessionAuth, tenderCheck, uncheckTenderCheck, 'ledgerController.posUpdate');
@@ -387,6 +392,9 @@ module.exports = app => {
     app.get('/tender/:id/measure/stage/:order/pay/download/file/:fid', sessionAuth, 'stageController.payDownloadFile');
     app.get('/tender/:id/measure/stage/:order/pay/download/file/:fid', sessionAuth, 'stageController.payDownloadFile');
     app.post('/tender/:id/measure/stage/:order/pay/delete/file', sessionAuth, tenderCheck, uncheckTenderCheck, stageCheck, 'stageController.payDeleteFile');
     app.post('/tender/:id/measure/stage/:order/pay/delete/file', sessionAuth, tenderCheck, uncheckTenderCheck, stageCheck, 'stageController.payDeleteFile');
 
 
+    app.get('/tender/:id/pay', sessionAuth, tenderCheck, uncheckTenderCheck, 'payController.index');
+    app.get('/tender/:id/pay/:order', sessionAuth, tenderCheck, uncheckTenderCheck, phasePayCheck, 'payController.pay');
+
     // 变更概况
     // 变更概况
     app.get('/tender/:id/measure/stage/:order/change', sessionAuth, tenderCheck, uncheckTenderCheck, stageCheck, 'stageController.change');
     app.get('/tender/:id/measure/stage/:order/change', sessionAuth, tenderCheck, uncheckTenderCheck, stageCheck, 'stageController.change');
     app.post('/tender/:id/measure/stage/:order/change/data', sessionAuth, tenderCheck, uncheckTenderCheck, stageCheck, 'stageController.getChangeData');
     app.post('/tender/:id/measure/stage/:order/change/data', sessionAuth, tenderCheck, uncheckTenderCheck, stageCheck, 'stageController.getChangeData');
@@ -672,6 +680,7 @@ module.exports = app => {
     app.post('/tender/:id/measure/material/:order/audit/start', sessionAuth, tenderCheck, uncheckTenderCheck, materialCheck, tenderBuildCheck, 'materialController.startAudit');
     app.post('/tender/:id/measure/material/:order/audit/start', sessionAuth, tenderCheck, uncheckTenderCheck, materialCheck, tenderBuildCheck, 'materialController.startAudit');
     app.post('/tender/:id/measure/material/:order/audit/check', sessionAuth, tenderCheck, uncheckTenderCheck, materialCheck, tenderBuildCheck, 'materialController.checkAudit');
     app.post('/tender/:id/measure/material/:order/audit/check', sessionAuth, tenderCheck, uncheckTenderCheck, materialCheck, tenderBuildCheck, 'materialController.checkAudit');
     app.get('/tender/:id/measure/material/:order/audit/check/again', sessionAuth, tenderCheck, uncheckTenderCheck, materialCheck, 'materialController.checkAuditAgain');
     app.get('/tender/:id/measure/material/:order/audit/check/again', sessionAuth, tenderCheck, uncheckTenderCheck, materialCheck, 'materialController.checkAuditAgain');
+    app.post('/tender/:id/measure/material/:order/audit/save', sessionAuth, tenderCheck, uncheckTenderCheck, materialCheck, 'materialController.saveAudit');
     // 调差工料
     // 调差工料
     app.get('/tender/:id/measure/material/:order', sessionAuth, tenderCheck, uncheckTenderCheck, materialCheck, 'materialController.info');
     app.get('/tender/:id/measure/material/:order', sessionAuth, tenderCheck, uncheckTenderCheck, materialCheck, 'materialController.info');
     app.post('/tender/:id/measure/material/:order/save', sessionAuth, tenderCheck, uncheckTenderCheck, materialCheck, 'materialController.saveBillsData');
     app.post('/tender/:id/measure/material/:order/save', sessionAuth, tenderCheck, uncheckTenderCheck, materialCheck, 'materialController.saveBillsData');

+ 1 - 1
app/service/file.js

@@ -107,7 +107,7 @@ module.exports = app => {
                         id: this.uuid.v4(), spid: filing.spid, filing_id: filing.id, filing_type: filing.filing_type,
                         id: this.uuid.v4(), spid: filing.spid, filing_id: filing.id, filing_type: filing.filing_type,
                         user_id: user.id, user_name: user.name, user_company: user.company, user_role: user.role,
                         user_id: user.id, user_name: user.name, user_company: user.company, user_role: user.role,
                         filename: x.filename, fileext: x.fileext, filesize: x.filesize, filepath: x.filepath,
                         filename: x.filename, fileext: x.fileext, filesize: x.filesize, filepath: x.filepath,
-                        is_rela: 1, rela_info: x.rela_info,
+                        is_rela: 1, rela_info: JSON.stringify(x.rela_info),
                     };
                     };
                 });
                 });
                 await conn.insert(this.tableName, insertData);
                 await conn.insert(this.tableName, insertData);

+ 1 - 1
app/service/filing.js

@@ -93,7 +93,7 @@ module.exports = app => {
                 const newData = {
                 const newData = {
                     id: f.newId, tree_pid : parent ? parent.newId : rootId, tree_level: f.tree_level, tree_order: f.tree_order,
                     id: f.newId, tree_pid : parent ? parent.newId : rootId, tree_level: f.tree_level, tree_order: f.tree_order,
                     spid, add_user_id: this.ctx.session.sessionUser.accountId, is_fixed: f.is_fixed,
                     spid, add_user_id: this.ctx.session.sessionUser.accountId, is_fixed: f.is_fixed,
-                    filing_type: f.filing_type, name: f.name,
+                    filing_type: f.filing_type, name: f.name, tips: f.tips,
                 };
                 };
                 insertData.push(newData);
                 insertData.push(newData);
             }
             }

+ 2 - 2
app/service/filing_template.js

@@ -233,7 +233,7 @@ module.exports = app => {
                 insertData.push({
                 insertData.push({
                     id: this.uuid.v4(), temp_id: templateId, add_user_id: this.ctx.session.sessionUser.accountId,
                     id: this.uuid.v4(), temp_id: templateId, add_user_id: this.ctx.session.sessionUser.accountId,
                     tree_pid: parent ? parent.id : rootId, tree_level: parent ? parent.tree_level + 1 : 1, tree_order: d.tree_order,
                     tree_pid: parent ? parent.id : rootId, tree_level: parent ? parent.tree_level + 1 : 1, tree_order: d.tree_order,
-                    name: d.name, filing_type: d.filing_type, is_fixed: parent ? d.is_fixed : 1, org_id: d.id,
+                    name: d.name, tips: d.tips, filing_type: d.filing_type, is_fixed: parent ? d.is_fixed : 1, org_id: d.id,
                 });
                 });
             }
             }
             insertData.forEach(x => { delete x.org_id; });
             insertData.forEach(x => { delete x.org_id; });
@@ -255,7 +255,7 @@ module.exports = app => {
 
 
             const sourceData = await this.getData(templateId);
             const sourceData = await this.getData(templateId);
 
 
-            const validFields = ['id', 'is_fixed', 'name', 'filing_type', 'tree_order'];
+            const validFields = ['id', 'is_fixed', 'name', 'filing_type', 'tree_order', 'tips'];
             const updateData = [];
             const updateData = [];
             for (const d of data) {
             for (const d of data) {
                 if (!d.id) throw '提交数据格式错误';
                 if (!d.id) throw '提交数据格式错误';

+ 17 - 10
app/service/ledger.js

@@ -34,7 +34,6 @@ const SumLoad = require('../lib/sum_load');
 module.exports = app => {
 module.exports = app => {
 
 
     class Ledger extends app.BaseBillsService {
     class Ledger extends app.BaseBillsService {
-
         /**
         /**
          * 构造函数
          * 构造函数
          *
          *
@@ -707,8 +706,7 @@ module.exports = app => {
             }
             }
         }
         }
 
 
-        async importYbp(tender, ybp) {
-            const YBP = require('../lib/ybp');
+        async importYbpData(tender, ybpData) {
             const YbpTrees = require('../lib/ybp_tree');
             const YbpTrees = require('../lib/ybp_tree');
             const gatherTreeSetting = {
             const gatherTreeSetting = {
                 id: 'ledger_id', pid: 'ledger_pid', order: 'order', full_path: 'full_path', level: 'level', rootId: -1,
                 id: 'ledger_id', pid: 'ledger_pid', order: 'order', full_path: 'full_path', level: 'level', rootId: -1,
@@ -722,8 +720,6 @@ module.exports = app => {
             const ybpTreeSetting = { id: 'ID', pid: 'parentID', order: 'seq', rootId: '-1' };
             const ybpTreeSetting = { id: 'ID', pid: 'parentID', order: 'seq', rootId: '-1' };
             const helper = this.ctx.helper;
             const helper = this.ctx.helper;
 
 
-            const ybpAnalysis = new YBP(this.ctx);
-            const ybpData = ybpAnalysis.decryptBuffer(ybp);
             const gatherTree = new YbpTrees.YbpImportTree(gatherTreeSetting, YbpTrees.YbpImportType.flow, helper, tender.info.decimal);
             const gatherTree = new YbpTrees.YbpImportTree(gatherTreeSetting, YbpTrees.YbpImportType.flow, helper, tender.info.decimal);
             for (const subject of ybpData.subjects) {
             for (const subject of ybpData.subjects) {
                 if (!subject.bills || subject.bills.length === 0) continue;
                 if (!subject.bills || subject.bills.length === 0) continue;
@@ -755,12 +751,13 @@ module.exports = app => {
                 };
                 };
                 gatherTree.importTree(ybpTree, subject.project.name, loadRelaFun);
                 gatherTree.importTree(ybpTree, subject.project.name, loadRelaFun);
             }
             }
-            gatherTree.sort();
+            gatherTree.sort(false);
             gatherTree.calculateAll();
             gatherTree.calculateAll();
 
 
             const bills = [], glj = [];
             const bills = [], glj = [];
             for (const n of gatherTree.nodes) {
             for (const n of gatherTree.nodes) {
                 const billsId = this.uuid.v4();
                 const billsId = this.uuid.v4();
+                const isLeaf = !n.children || n.children.length === 0;
                 bills.push({
                 bills.push({
                     id: billsId,
                     id: billsId,
                     tender_id: tender.id,
                     tender_id: tender.id,
@@ -769,16 +766,18 @@ module.exports = app => {
                     level: n.level,
                     level: n.level,
                     order: n.order,
                     order: n.order,
                     full_path: n.full_path,
                     full_path: n.full_path,
-                    is_leaf: !n.children || n.children.length === 0,
+                    is_leaf: isLeaf,
                     code: n.code,
                     code: n.code,
                     b_code: n.b_code,
                     b_code: n.b_code,
                     name: n.name,
                     name: n.name,
                     unit: n.unit,
                     unit: n.unit,
-                    unit_price: n.unit_price || 0,
+                    unit_price: isLeaf ? n.unit_price || 0 : 0,
                     sgfh_qty: n.quantity || 0,
                     sgfh_qty: n.quantity || 0,
-                    sgfh_tp: n.total_price || 0,
+                    sgfh_tp: isLeaf ? n.total_price || 0 : 0,
                     quantity: n.quantity || 0,
                     quantity: n.quantity || 0,
-                    total_price: n.total_price || 0,
+                    total_price: isLeaf ? n.total_price || 0 : 0,
+                    dgn_qty1: n.dgn_qty1 || 0,
+                    dgn_qty2: n.dgn_qty2 || 0,
                     memo: n.source.join(';'),
                     memo: n.source.join(';'),
                 });
                 });
                 if (!n.glj) continue;
                 if (!n.glj) continue;
@@ -808,6 +807,14 @@ module.exports = app => {
             }
             }
         }
         }
 
 
+        async importYbp(tender, ybp) {
+            const YBP = require('../lib/ybp');
+            const ybpAnalysis = new YBP(this.ctx);
+            const ybpData = ybpAnalysis.decryptBuffer(ybp);
+
+            return await this.importYbpData(tender, ybpData);
+        }
+
         async sumLoad(lid, tenders) {
         async sumLoad(lid, tenders) {
             const maxId = await this._getMaxLid(this.ctx.tender.id);
             const maxId = await this._getMaxLid(this.ctx.tender.id);
             const select = await this.getDataById(lid);
             const select = await this.getDataById(lid);

+ 48 - 3
app/service/material.js

@@ -9,6 +9,7 @@
  */
  */
 
 
 const auditConst = require('../const/audit').material;
 const auditConst = require('../const/audit').material;
+const auditType = require('../const/audit').auditType;
 const projectLogConst = require('../const/project_log');
 const projectLogConst = require('../const/project_log');
 const materialConst = require('../const/material');
 const materialConst = require('../const/material');
 module.exports = app => {
 module.exports = app => {
@@ -24,6 +25,50 @@ module.exports = app => {
             this.tableName = 'material';
             this.tableName = 'material';
         }
         }
 
 
+        async loadMaterialUser(material) {
+            const status = auditConst.status;
+            const accountId = this.ctx.session.sessionUser.accountId;
+
+            material.user = await this.ctx.service.projectAccount.getAccountInfoById(material.user_id);
+            material.auditors = await this.ctx.service.materialAudit.getAuditors(material.id, material.times); // 全部参与的审批人
+            material.auditorIds = this._.map(material.auditors, 'aid');
+            material.curAuditors = material.auditors.filter(x => { return x.status === status.checking; }); // 当前流程中审批中的审批人
+            material.curAuditorIds = this._.map(material.curAuditors, 'aid');
+            material.flowAuditors = material.curAuditors.length > 0 ? material.auditors.filter(x => { return x.order === material.curAuditors[0].order; }) : []; // 当前流程中参与的审批人(包含会签时,审批通过的人)
+            material.flowAuditorIds = this._.map(material.flowAuditors, 'aid');
+            material.nextAuditors = material.curAuditors.length > 0 ? material.auditors.filter(x => { return x.order === material.curAuditors[0].order + 1; }) : [];
+            material.nextAuditorIds = this._.map(material.nextAuditors, 'aid');
+            material.auditorGroups = this.ctx.helper.groupAuditors(material.auditors);
+            material.userGroups = this.ctx.helper.groupAuditorsUniq(material.auditorGroups);
+            material.userGroups.unshift([{
+                aid: material.user.id, order: 0, times: material.times, audit_order: 0, audit_type: auditType.key.common,
+                name: material.user.name, role: material.user.role, company: material.user.company,
+            }]);
+            material.finalAuditorIds = material.userGroups[material.userGroups.length - 1].map(x => { return x.aid; });
+        }
+
+        async loadMaterialAuditViewData(material) {
+            const times = material.status === auditConst.status.checkNo ? material.times - 1 : material.times;
+
+            if (!material.user) material.user = await this.ctx.service.projectAccount.getAccountInfoById(material.user_id);
+            material.auditHistory = await this.ctx.service.materialAudit.getAuditorHistory(material.id, times);
+            // 获取审批流程中左边列表
+            if (material.status === auditConst.status.checkNo && material.user_id !== this.ctx.session.sessionUser.accountId) {
+                const auditors = await this.ctx.service.materialAudit.getAuditors(material.id, times); // 全部参与的审批人
+                const auditorGroups = this.ctx.helper.groupAuditors(auditors);
+                material.auditors2 = this.ctx.helper.groupAuditorsUniq(auditorGroups);
+                material.auditors2.unshift([{
+                    aid: material.user.id, order: 0, times: material.times - 1, audit_order: 0, audit_type: auditType.key.common,
+                    name: material.user.name, role: material.user.role, company: material.user.company,
+                }]);
+            } else {
+                material.auditors2 = material.userGroups;
+            }
+            if (material.status === auditConst.status.uncheck || material.status === auditConst.status.checkNo) {
+                material.auditorList = await this.ctx.service.materialAudit.getAuditors(material.id, material.times);
+            }
+        }
+
         /**
         /**
          * 获取 最新一期 材料调差期计量
          * 获取 最新一期 材料调差期计量
          * @param tenderId
          * @param tenderId
@@ -115,11 +160,11 @@ module.exports = app => {
             // 最新一期计量(未审批完成),当前操作人的期详细数据,应实时计算
             // 最新一期计量(未审批完成),当前操作人的期详细数据,应实时计算
             if (materials.length > 0 && materials[0].status !== auditConst.status.checked) {
             if (materials.length > 0 && materials[0].status !== auditConst.status.checked) {
                 const material = materials[0];
                 const material = materials[0];
-                const curAuditor = await this.ctx.service.materialAudit.getCurAuditor(material.id, material.times);
-                const isActive = curAuditor ? curAuditor.aid === this.ctx.session.sessionUser.accountId : material.user_id === this.ctx.session.sessionUser.accountId;
+                const curAuditors = await this.ctx.service.materialAudit.getCurAuditors(material.id, material.times);
+                const isActive = curAuditors && curAuditors.length > 0 ? this._.findIndex(curAuditors, { aid: this.ctx.session.sessionUser.accountId }) !== -1 : material.user_id === this.ctx.session.sessionUser.accountId;
                 if (isActive) {
                 if (isActive) {
                     material.curTimes = material.times;
                     material.curTimes = material.times;
-                    material.curOrder = curAuditor ? curAuditor.order : 0;
+                    material.curOrder = curAuditors && curAuditors.length > 0 ? curAuditors[0].order : 0;
                 }
                 }
             }
             }
             return materials;
             return materials;

Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 649 - 328
app/service/material_audit.js


+ 65 - 20
app/service/message.js

@@ -99,25 +99,45 @@ module.exports = app => {
             if (data._csrf_j !== undefined) {
             if (data._csrf_j !== undefined) {
                 delete data._csrf_j;
                 delete data._csrf_j;
             }
             }
-            if (id > 0) {
-                // 修改操作时
-                data.id = id;
-                const msgInfo = await this.getDataById(id);
-                data.istop = parseFloat(msgInfo.istop) === 0 && parseInt(data.istop) === 1 ? data.create_time : data.istop === undefined ? 0 : msgInfo.istop;
-                delete data.create_time;
-            } else {
-                data.release_time = data.create_time;
-                data.project_id = projectId;
-                data.create_uid = user.accountId;
-                data.creator = user.name;
-                data.istop = parseInt(data.istop) === 1 ? data.create_time : 0;
+            const transaction = await this.db.beginTransaction();
+            try {
+                if (id > 0) {
+                    // 修改操作时
+                    data.id = id;
+                    const msgInfo = await this.getDataById(id);
+                    data.istop = parseFloat(msgInfo.istop) === 0 && parseInt(data.istop) === 1 ? data.create_time : data.istop === undefined ? 0 : msgInfo.istop;
+                    delete data.create_time;
+                } else {
+                    data.release_time = data.create_time;
+                    data.project_id = projectId;
+                    data.create_uid = user.accountId;
+                    data.creator = user.name;
+                    data.istop = parseInt(data.istop) === 1 ? data.create_time : 0;
+                }
+                const operate = id === 0 ? await transaction.insert(this.tableName, data) :
+                    await transaction.update(this.tableName, data);
+
+                const result = operate.affectedRows > 0 ? operate.insertId : false;
+                if (id === 0) {
+                    const attList = await this.ctx.service.messageAtt.getAllDataByCondition({ where: { mid: 0, uid: user.accountId } });
+                    if (attList.length > 0) {
+                        const fileUpdateDatas = [];
+                        for (const att of attList) {
+                            fileUpdateDatas.push({
+                                id: att.id,
+                                mid: result,
+                            });
+                        }
+                        await transaction.updateRows(this.ctx.service.messageAtt.tableName, fileUpdateDatas);
+                    }
+                }
+                await transaction.commit();
+                return result;
+            } catch (error) {
+                console.log(error);
+                await transaction.rollback();
+                return false;
             }
             }
-            const operate = id === 0 ? await this.db.insert(this.tableName, data) :
-                await this.db.update(this.tableName, data);
-
-            const result = operate.affectedRows > 0 ? operate.insertId : false;
-
-            return result;
         }
         }
 
 
         /**
         /**
@@ -130,12 +150,37 @@ module.exports = app => {
             if (type === 1) {
             if (type === 1) {
                 const sql = 'SELECT * FROM ?? WHERE `project_id` = ? AND `type` = ? ORDER BY CONCAT(`istop`,`release_time`) DESC LIMIT ?,?';
                 const sql = 'SELECT * FROM ?? WHERE `project_id` = ? AND `type` = ? ORDER BY CONCAT(`istop`,`release_time`) DESC LIMIT ?,?';
                 const sqlParam = [this.tableName, projectId, type, offset, limit];
                 const sqlParam = [this.tableName, projectId, type, offset, limit];
-                return await this.db.query(sql, sqlParam);
+                const result = await this.db.query(sql, sqlParam);
+                for (const r of result) {
+                    r.files = await this.ctx.service.messageAtt.getAtt(r.id);
+                }
+                return result;
             }
             }
 
 
             const sql = 'SELECT * FROM ?? WHERE `type` = ? AND `status` = ? ORDER BY `release_time` DESC LIMIT ?,?';
             const sql = 'SELECT * FROM ?? WHERE `type` = ? AND `status` = ? ORDER BY `release_time` DESC LIMIT ?,?';
             const sqlParam = [this.tableName, type, 1, offset, limit];
             const sqlParam = [this.tableName, type, 1, offset, limit];
-            return await this.db.query(sql, sqlParam);
+            const result = await this.db.query(sql, sqlParam);
+            for (const r of result) {
+                r.files = await this.ctx.service.messageAtt.getAtt(r.id);
+            }
+            return result;
+        }
+
+        async deleteMsg(id) {
+            const transaction = await this.db.beginTransaction();
+            try {
+                await transaction.delete(this.tableName, { id });
+                // 删除附件
+                const attList = await this.ctx.service.messageAtt.getAllDataByCondition({ where: { mid: id } });
+                await this.ctx.helper.delFiles(attList);
+                await transaction.delete(this.ctx.service.messageAtt.tableName, { mid: id });
+                await transaction.commit();
+                return true;
+            } catch (error) {
+                console.log(error);
+                await transaction.rollback();
+                return false;
+            }
         }
         }
     }
     }
 
 

+ 64 - 0
app/service/message_att.js

@@ -0,0 +1,64 @@
+'use strict';
+
+/**
+ * Created by EllisRan on 2020/3/3.
+ */
+
+const BaseService = require('../base/base_service');
+
+module.exports = app => {
+
+    class MessageAtt extends BaseService {
+
+        /**
+         * 构造函数
+         *
+         * @param {Object} ctx - egg全局变量
+         * @return {void}
+         */
+        constructor(ctx) {
+            super(ctx);
+            this.tableName = 'message_attachment';
+            this.dataId = 'id';
+        }
+
+        async getAtt(mid = 0) {
+            let userSql = '';
+            if (mid === 0) {
+                userSql += ' AND a.`uid` = ' + this.ctx.session.sessionUser.accountId;
+            }
+            const sql = 'SELECT a.*, b.name as username FROM ?? as a LEFT JOIN ?? as b ON a.`uid` = b.`id` WHERE a.`mid` = ?' + userSql + ' ORDER BY `upload_time` DESC';
+            const sqlParam = [this.tableName, this.ctx.service.projectAccount.tableName, mid];
+            const result = await this.db.query(sql, sqlParam);
+            return result.map(item => {
+                item.orginpath = this.ctx.app.config.fujianOssPath + item.filepath;
+                if (!this.ctx.helper.canPreview(item.fileext)) {
+                    item.filepath = `/wap/message/download/file/file/${item.id}/download`;
+                } else {
+                    item.filepath = this.ctx.app.config.fujianOssPath + item.filepath;
+                    item.viewpath = item.filepath;
+                }
+                return item;
+            });
+        }
+
+        /**
+         * 存储上传的文件信息至数据库
+         * @param {Array} payload 载荷
+         * @return {Promise<void>} 数据库插入执行实例
+         */
+        async saveFileMsgToDb(payload) {
+            return await this.db.insert(this.tableName, payload);
+        }
+
+        /**
+         * 删除附件
+         * @param {Number} id - 附件id
+         * @return {void}
+         */
+        async delete(id) {
+            return await this.deleteById(id);
+        }
+    }
+    return MessageAtt;
+};

+ 165 - 0
app/service/phase_pay.js

@@ -0,0 +1,165 @@
+'use strict';
+
+/**
+ *
+ *
+ * @author Mai
+ * @date
+ * @version
+ */
+
+const audit = require('../const/audit').common;
+
+module.exports = app => {
+    class PhasePay extends app.BaseService {
+
+        /**
+         * 构造函数
+         *
+         * @param {Object} ctx - egg全局变量
+         * @return {void}
+         */
+        constructor(ctx) {
+            super(ctx);
+            this.tableName = 'phase_pay';
+        }
+
+        analysisPhasePay(data) {
+            const datas = data instanceof Array ? data : [data];
+            datas.forEach(x => {
+                x.rela_stage = x.rela_stage ? JSON.parse(x.rela_stage) : [];
+                x.calc_base = x.calc_base ? JSON.parse(x.calc_base) : [];
+            });
+        }
+
+        calculatePhasePay(data) {
+            const helper = this.ctx.helper;
+            const datas = data instanceof Array ? data : [data];
+            const formatNum = this.ctx.tender.info.display.thousandth ? this.ctx.helper.formatNum : function(num) { return num ? num + '' : ''; };
+            datas.forEach(x => {
+                x.end_calc_tp = helper.add(x.calc_tp, x.pre_calc_tp);
+                x.end_pay_tp = helper.add(x.pay_tp, x.pre_pay_tp);
+                x.end_cut_tp = helper.add(x.cut_tp, x.pre_cut_tp);
+                x.end_sf_tp = helper.add(x.sf_tp, x.pre_sf_tp);
+                x.end_yf_tp = helper.add(x.yf_tp, x.pre_yf_tp);
+                if (thousandth)
+                for (const prop in x) {
+                    if (prop.indexOf('_tp') > 0) {
+                        x['display_' + prop] = formatNum(x[prop]);
+                    }
+                }
+            });
+        }
+
+        /**
+         * 获取全部修订
+         * @param tid
+         * @returns {Promise<*>}
+         */
+        async getAllPhasePay (tid, sort = 'ASC') {
+            const result = await this.getAllDataByCondition({
+                where: {tid: tid},
+                orders: [['phase_order', sort]],
+            });
+            this.analysisPhasePay(result);
+            return result;
+        }
+
+        async getPhasePay(id) {
+            const result = await this.getDataById(id);
+            this.analysisPhasePay(result);
+            return result;
+        }
+
+        async getPhasePayByOrder(tid, phaseOrder) {
+            const result = await this.getDataByCondition({ tid, phase_order: phaseOrder });
+            this.analysisPhasePay(result);
+            return result;
+        }
+
+        async loadUser(phasePay) {
+            // todo 加载审批人
+        }
+
+        async getNewOrder(tid) {
+            const sql = 'SELECT Max(`phase_order`) As max_order FROM ' + this.tableName + ' Where `tid` = ?';
+            const sqlParam = [tid];
+            const result = await this.db.queryOne(sql, sqlParam);
+            return result.max_order || 0;
+        }
+
+        async _checkRelaStageConflict(relaStage, phasePay) {
+            const pays = this.getAllPhasePay(phasePay.tid);
+            for (const p of pays) {
+                if (p.id === phasePay.id) continue;
+                for (const s of relaStage) {
+                    if (p.rela_stage.find(x => { return x.id === s.id; })) return true;
+                }
+            }
+            return false;
+        }
+
+        async getCalcBase(relaStage) {
+            // todo 获取计算基数
+        }
+
+        async add(tid, relaStage, phaseDate, memo) {
+            if (!tid) throw '数据错误';
+            const user_id = this.ctx.session.sessionUser.accountId;
+
+            const maxOrder = await this.getNewOrder(tid);
+            const data = {
+                id: this.uuid.v4(), tid: tid, create_user_id: user_id, update_user_id: user_id,
+                phase_order: maxOrder + 1, phase_date: phaseDate, memo,
+                audit_times: 1, audit_status: audit.status.uncheck,
+                rela_stage: JSON.stringify(relaStage.map(s => { return {stage_id: s.id, stage_order: s.order}; })),
+            };
+            if (await this._checkRelaStageConflict(relaStage, data)) throw '选择的计量期,已被调用,请刷新页面后选择计量期新增合同支付';
+
+            const calcBase = await this.getCalcBase(relaStage);
+            data.calc_base = JSON.stringify(calcBase);
+            const transaction = await this.db.beginTransaction();
+            try {
+                const result = await transaction.insert(this.tableName, data);
+                if (result.affectedRows !== 1) throw '新增合同支付失败';
+
+                await this.ctx.service.phasePayDetail.initPhaseData(data);
+                await transaction.commit();
+                return data;
+            } catch(err) {
+                await transaction.rollback();
+                throw err;
+            }
+        }
+
+        async refreshCalcBase(id) {
+            const curPay = this.getPhasePay(id);
+            if (!curPay) throw '合同支付不存在, 请刷新页面重试';
+
+            const calcBase = await this.getCalcBase(relaStage);
+            await this.defaultUpdate({
+                id, update_user_id: this.ctx.session.sessionUser.accountId,
+                calc_base: JSON.stringify(calcBase),
+                rela_stage: JSON.stringify(relaStage.map(s => { return {stage_id: s.id, stage_order: s.order}; })),
+                calc_base_time: new Date()
+            });
+        }
+
+        async resetRelaStageId(id, relaStage) {
+            const curPay = this.getPhasePay(id);
+            if (!curPay) throw '合同支付不存在, 请刷新页面重试';
+
+            if (await this._checkRelaStageConflict(relaStage, curPay)) throw '选择的计量期,已被调用,请刷新页面后选择计量期新增合同支付';
+            const calcBase = await this.getCalcBase(relaStage);
+            await this.defaultUpdate({
+                id, update_user_id: this.ctx.session.sessionUser.accountId,
+                calc_base: JSON.stringify(calcBase),
+                rela_stage: JSON.stringify(relaStage.map(s => { return {stage_id: s.id, stage_order: s.order}; })),
+                calc_base_time: new Date()
+            });
+        }
+
+    }
+
+    return PhasePay;
+};

+ 2 - 1
app/service/project_account.js

@@ -235,6 +235,7 @@ module.exports = app => {
                         sessionToken,
                         sessionToken,
                         loginType,
                         loginType,
                         loginStatus,
                         loginStatus,
+                        dskAccountData: accountData.dsk_account ? JSON.parse(accountData.dsk_account) : null,
                         // permission,
                         // permission,
                         // cooperation,
                         // cooperation,
                     };
                     };
@@ -689,7 +690,7 @@ module.exports = app => {
 
 
                 // 判断是否更改了账号
                 // 判断是否更改了账号
                 if (accountData.account !== account) {
                 if (accountData.account !== account) {
-                    await this.syncAccount(projectData.code, accountData.account, account);
+                    this.syncAccount(projectData.code, accountData.account, account);
                 }
                 }
 
 
                 this.transaction.commit();
                 this.transaction.commit();

+ 2 - 0
app/service/report_memory.js

@@ -1507,9 +1507,11 @@ module.exports = app => {
 
 
         async getChangeInfoBills(tid, sid) {
         async getChangeInfoBills(tid, sid) {
             if (!this.changeInfo) await this.getChangeInfo(tid, sid);
             if (!this.changeInfo) await this.getChangeInfo(tid, sid);
+            const helper = this.ctx.helper;
             this.changeInfoBills = [];
             this.changeInfoBills = [];
             for (const c of this.changeInfo) {
             for (const c of this.changeInfo) {
                 const cb = await this.ctx.service.changeAuditList.getAllDataByCondition({ where: { cid: c.cid } });
                 const cb = await this.ctx.service.changeAuditList.getAllDataByCondition({ where: { cid: c.cid } });
+                cb.sort((a, b) => { return helper.compareCode(a.code, b.code)});
                 const cbu = await this.ctx.service.stageChange.getUsedData(tid, c.cid);
                 const cbu = await this.ctx.service.stageChange.getUsedData(tid, c.cid);
                 const curUsedBills = await this.ctx.service.stageChange.getStageUsedData(sid, c.cid);
                 const curUsedBills = await this.ctx.service.stageChange.getStageUsedData(sid, c.cid);
                 for (const b of cb) {
                 for (const b of cb) {

+ 26 - 4
app/service/stage_audit.js

@@ -1570,8 +1570,7 @@ module.exports = app => {
                 '  FROM ?? AS sa ' +
                 '  FROM ?? AS sa ' +
                 '    Left Join ?? AS s On sa.`sid` = s.`id` ' +
                 '    Left Join ?? AS s On sa.`sid` = s.`id` ' +
                 '    Left Join ?? As t ON sa.`tid` = t.`id`' +
                 '    Left Join ?? As t ON sa.`tid` = t.`id`' +
-                '  WHERE ((sa.`aid` = ? and sa.`status` = ?) OR (s.`user_id` = ? and sa.`status` = ? and s.`status` = ? and sa.`times` = (s.`times`-1))' +
-                '  OR (s.`user_id` = ? and s.`status` = ? and sa.`times` = s.`times`))' +
+                '  WHERE (sa.`aid` = ? and sa.`status` = ?) OR (s.`user_id` = ? and sa.`status` = ? and s.`status` = ? and sa.`times` = (s.`times`-1))' +
                 '  ORDER BY sa.`begin_time` DESC';
                 '  ORDER BY sa.`begin_time` DESC';
             const sqlParam = [
             const sqlParam = [
                 this.tableName,
                 this.tableName,
@@ -1582,19 +1581,42 @@ module.exports = app => {
                 auditorId,
                 auditorId,
                 auditConst.status.checkNo,
                 auditConst.status.checkNo,
                 auditConst.status.checkNo,
                 auditConst.status.checkNo,
-                auditorId,
-                auditConst.status.uncheck,
             ];
             ];
             const result = await this.db.query(sql, sqlParam);
             const result = await this.db.query(sql, sqlParam);
             // 过滤result中存在重复sid的值, 保留最新的一条
             // 过滤result中存在重复sid的值, 保留最新的一条
+            const sql1 = 'SELECT s.`order` As `sorder`, s.`status` AS `sstatus`, s.`in_time` AS `begin_time`,' +
+                ' t.`id` AS `tid`, t.`name`, t.`project_id`, t.`type`, t.`user_id`' +
+                ' FROM ?? AS s' +
+                ' Left Join ?? As t ON s.`tid` = t.`id`' +
+                ' WHERE s.`user_id` = ? and s.`status` != ?' +
+                ' ORDER BY s.`in_time` ASC';
+            const sqlParam1 = [this.ctx.service.stage.tableName, this.ctx.service.tender.tableName, auditorId, auditConst.status.checked];
+            const result1 = await this.db.query(sql1, sqlParam1);
+            const filterResult1 = [];
+            const tidArr = [];
+            for (const res of result1) {
+                if (tidArr.indexOf(res.tid) === -1) {
+                    tidArr.push(res.tid);
+                    if (res.sstatus === auditConst.status.uncheck) {
+                        filterResult1.push(res);
+                    }
+                }
+            }
             const filterResult = [];
             const filterResult = [];
             const sidArr = [];
             const sidArr = [];
             for (const r of result) {
             for (const r of result) {
                 if (sidArr.indexOf(r.sid) === -1) {
                 if (sidArr.indexOf(r.sid) === -1) {
                     filterResult.push(r);
                     filterResult.push(r);
                     sidArr.push(r.sid);
                     sidArr.push(r.sid);
+                    if (tidArr.indexOf(r.tid) !== -1) {
+                        const index = filterResult1.findIndex(item => item.tid === r.tid);
+                        if (index !== -1) filterResult1.splice(index, 1);
+                    }
                 }
                 }
             }
             }
+            // 合并并按begin_time排序
+            filterResult.push(...filterResult1);
+            filterResult.sort((a, b) => b.begin_time - a.begin_time);
             return filterResult;
             return filterResult;
         }
         }
 
 

+ 2 - 2
app/service/sub_project.js

@@ -357,8 +357,8 @@ module.exports = app => {
                 const relaTenderId = data.rela_tender.split(',');
                 const relaTenderId = data.rela_tender.split(',');
                 const removeTenderId = orgRelaTenderId.filter(x => { return relaTenderId.indexOf(x) < 0});
                 const removeTenderId = orgRelaTenderId.filter(x => { return relaTenderId.indexOf(x) < 0});
                 const addTenderId = relaTenderId.filter(x => { return orgRelaTenderId.indexOf(x) < 0});
                 const addTenderId = relaTenderId.filter(x => { return orgRelaTenderId.indexOf(x) < 0});
-                if (removeTenderId.length > 0) await conn.update(this.ctx.service.tender.tableName, { id: removeTenderId, spid: '' });
-                if (addTenderId.length > 0) await conn.update(this.ctx.service.tender.tableName, { id: addTenderId, spid: data.id });
+                if (removeTenderId.length > 0) await conn.update(this.ctx.service.tender.tableName, { spid: '' }, { where: { id: removeTenderId }});
+                if (addTenderId.length > 0) await conn.update(this.ctx.service.tender.tableName, { spid: data.id }, { where: { id: addTenderId }});
                 await conn.commit();
                 await conn.commit();
                 return data;
                 return data;
             } catch (error) {
             } catch (error) {

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

@@ -141,7 +141,7 @@
                                                     <td><span class="bg-new-stage text-new-stage badge text-width">计量审批</span></td>
                                                     <td><span class="bg-new-stage text-new-stage badge text-width">计量审批</span></td>
                                                     <td><a href="/tender/<%- audit.tid %>"><%- audit.name %></a> <a href="/tender/<%- audit.tid %>/measure/stage/<%- audit.sorder %>">第<%- audit.sorder %>期</a></td>
                                                     <td><a href="/tender/<%- audit.tid %>"><%- audit.name %></a> <a href="/tender/<%- audit.tid %>/measure/stage/<%- audit.sorder %>">第<%- audit.sorder %>期</a></td>
                                                     <td>第<%- audit.sorder %>期</td>
                                                     <td>第<%- audit.sorder %>期</td>
-                                                    <td><%- ctx.moment(audit.in_time).format('YYYY/MM/DD HH:mm') %></td>
+                                                    <td><%- ctx.moment(audit.begin_time).format('YYYY/MM/DD HH:mm') %></td>
                                                     <td><a href="/tender/<%- audit.tid %>/measure/stage/<%- audit.sorder %>" class="btn btn-outline-primary btn-sm btn-table" role="button">上报</a></td>
                                                     <td><a href="/tender/<%- audit.tid %>/measure/stage/<%- audit.sorder %>" class="btn btn-outline-primary btn-sm btn-table" role="button">上报</a></td>
                                                 </tr>
                                                 </tr>
                                             <% } else { %>
                                             <% } else { %>
@@ -355,7 +355,7 @@
                         </div>
                         </div>
                         <div class="card-body p-0">
                         <div class="card-body p-0">
                             <div class="contant-height-three">
                             <div class="contant-height-three">
-                                <ul class="list-group list-group-flush">
+                                <ul class="list-group list-group-flush msg-height-list">
                                     <% if (msgList.length === 0) { %>
                                     <% if (msgList.length === 0) { %>
                                         <!--没有通知-->
                                         <!--没有通知-->
                                         <li class="list-group-item text-muted text-center p-5">
                                         <li class="list-group-item text-muted text-center p-5">

+ 3 - 1
app/view/dashboard/modal.ejs

@@ -46,9 +46,11 @@
                                 </span>
                                 </span>
                             </p>
                             </p>
                             <!--内容开始-->
                             <!--内容开始-->
-                            <div class="msg-content border-top-1 pt-3" id="content">
+                            <div class="msg-content border-top-1 pt-3 mb-2" id="content">
 
 
                             </div>
                             </div>
+                            <div class="mt-3" id="files-list">
+                            </div>
                         </div>
                         </div>
                     </div>
                     </div>
                 </div>
                 </div>

+ 12 - 0
app/view/dashboard/msg.ejs

@@ -42,6 +42,18 @@
                                 <div class="msg-content border-top-1 pt-3" id="content">
                                 <div class="msg-content border-top-1 pt-3" id="content">
                                     <%- msgInfo.content %>
                                     <%- msgInfo.content %>
                                 </div>
                                 </div>
+                                <div class="mt-3" id="files-list">
+                                    <% if (msgInfo.files && msgInfo.files.length > 0) { %>
+                                    <h5>附件</h5>
+                                    <% for (const file of msgInfo.files) { %>
+                                    <a href="<% if (file.viewpath) { %><%- file.viewpath %><% } else { %>/wap/message/download/file/<%- file.id %><% } %>" target="_blank">
+                                        <div class="card my-1" style="cursor: pointer">
+                                            <div class="card-body"><i class="fa fa-file"></i> <%- file.filename %><span class="pull-right text-secondary"><%- file.filesize %></span></div>
+                                        </div>
+                                    </a>
+                                    <% } %>
+                                    <% } %>
+                                </div>
                                 <% } %>
                                 <% } %>
                             </div>
                             </div>
                         </div>
                         </div>

+ 109 - 1
app/view/dashboard/msg_add.ejs

@@ -21,6 +21,26 @@
                                         <textarea id="content" rows="15" name="content" class="form-control form-control-sm"><%- msgInfo.content %></textarea>
                                         <textarea id="content" rows="15" name="content" class="form-control form-control-sm"><%- msgInfo.content %></textarea>
                                     </div>
                                     </div>
                                 </div>
                                 </div>
+                                <div class="form-group" id="msg-file">
+                                    <label>附件</label>
+                                    <input type="hidden" value="<%= msgInfo.id === undefined ? 0 : msgInfo.id %>" id="mid">
+                                    <div class="form-group upload-permission">
+                                        <label for="formGroupExampleInput">单个文件大小限制:30MB,支持<span data-toggle="tooltip" data-placement="bottom" title="doc,docx,xls,xlsx,ppt,pptx,pdf">office等文档格式</span>、<span data-toggle="tooltip" data-placement="bottom" title="jpg,png,bmp">图片格式</span>、<span data-toggle="tooltip" data-placement="bottom" title="rar,zip">压缩包格式</span></label>
+                                        <br>
+                                        <input type="file" class="" multiple>
+                                    </div>
+                                    <div class="sp-wrap" style="overflow: auto">
+                                        <table class="table table-sm table-bordered" id="file-table">
+                                            <thead>
+                                            <tr class="text-center">
+                                                <th width="5%">序号</th><th>名称</th><th width="15%">上传人</th><th width="15%">上传时间</th><th width="15%">操作</th>
+                                            </tr>
+                                            </thead>
+                                            <tbody>
+                                            </tbody>
+                                        </table>
+                                    </div>
+                                </div>
                             </div>
                             </div>
                         </div>
                         </div>
                     </div>
                     </div>
@@ -43,13 +63,101 @@
     </div>
     </div>
 </div>
 </div>
 <%- jsValidator %>
 <%- jsValidator %>
+<script src="/public/js/moment/moment.min.js"></script>
 <script type="text/javascript">
 <script type="text/javascript">
     new Vue({
     new Vue({
         el: '#save-form',
         el: '#save-form',
     });
     });
+    const user_id = <%- ctx.session.sessionUser.accountId %>;
+    const files = JSON.parse(unescape('<%- escape(JSON.stringify(files)) %>'));
+    const whiteList = JSON.parse('<%- JSON.stringify(whiteList) %>');
     $(function () {
     $(function () {
         $('#btn_click').click(function () {
         $('#btn_click').click(function () {
             $('#btn_type').val(2);
             $('#btn_type').val(2);
-        })
+        });
+
+        setFiles(files);
+
+        function setFiles(files, _this = '#file-table tbody') {
+            let filesHtml = '';
+            const newFiles = files.map(file => {
+                let showDel = false;
+                if (file.uid === user_id) {
+                    showDel = true
+                }
+                return {...file, showDel}
+            })
+            newFiles.forEach((file, idx) => {
+                filesHtml += `<tr class="text-center">
+                                        <td>${idx + 1}</td><td class="text-left"><a href="${file.filepath}" target="_blank">${file.filename}</a></td><td>${file.username}</td><td>${moment(file.upload_time).format('YYYY-MM-DD HH:mm:ss')}</td>
+                                        <td>
+                                            <div class="btn-group-table">
+                                                ${file.viewpath ? `<a href="${file.viewpath}" target="_blank" class="mr-1"><i class="fa fa-eye fa-fw"></i></a>` : ''}
+                                                <a href="/wap/message/download/file/${file.id}" class="mr-1"><i class="fa fa-download fa-fw"></i></a>
+                                                ${file.showDel ? `<a href="javascript: void(0);" class="text-danger file-del mr-1" data-id="${file.id}"><i class="fa fa-trash-o fa-fw text-danger"></i></a>` : ''}
+                                            </div>
+                                        </td>
+                                    </tr>`;
+            });
+            $(_this).html(filesHtml);
+        }
+
+        $('#msg-file input[type="file"]').change(function () {
+            const files = Array.from(this.files);
+            console.log(files);
+            const valiData = files.map(v => {
+                const ext = v.name.substring(v.name.lastIndexOf('.') + 1)
+                return {
+                    size: v.size,
+                    ext
+                }
+            })
+            const mid = parseInt($('#mid').val());
+            if (validateFiles(valiData)) {
+                if (files.length) {
+                    const formData = new FormData()
+                    files.forEach(file => {
+                        formData.append('name', file.name)
+                        formData.append('size', file.size)
+                        formData.append('file', file)
+                    })
+                    postDataWithFile(`/dashboard/msg/${mid}/file/upload`, formData, function (result) {
+                        setFiles(result);
+                    });
+                }
+            }
+            $('#msg-file input[type="file"]').val('');
+        });
+
+        $('body').on('click', '#msg-file .file-del', function () {
+            const fid = $(this).data('id');
+            const mid = parseInt($('#mid').val());
+            deleteAfterHint(function () {
+                postData(`/dashboard/msg/${mid}/file/del`, { id: fid }, function (result) {
+                    setFiles(result);
+                });
+            }, '确认删除该文件?');
+        });
     })
     })
+    /**
+     * 校验文件大小、格式
+     * @param {Array} files 文件数组
+     */
+    function validateFiles(files) {
+        if (files.length > 10) {
+            toastr.error('至多同时上传10个文件');
+            return false
+        }
+        return files.every(file => {
+            if (file.size > 1024 * 1024 * 30) {
+                toastr.error('文件大小限制为30MB');
+                return false
+            }
+            if (whiteList.indexOf('.' + file.ext.toLowerCase()) === -1) {
+                toastr.error('请上传正确的格式文件');
+                return false
+            }
+            return true
+        })
+    }
 </script>
 </script>

+ 1 - 0
app/view/dashboard/msg_modal.ejs

@@ -1,3 +1,4 @@
+<% include ../shares/delete_hint_modal.ejs %>
 <div class="modal fade" tabindex="-1" role="dialog" id="del-modal">
 <div class="modal fade" tabindex="-1" role="dialog" id="del-modal">
     <div class="modal-dialog" role="document">
     <div class="modal-dialog" role="document">
         <div class="modal-content">
         <div class="modal-content">

+ 1 - 1
app/view/file/template_modal.ejs

@@ -1,6 +1,6 @@
 <% include ../shares/select_file_modal.ejs %>
 <% include ../shares/select_file_modal.ejs %>
 <div class="modal fade" id="multi-set" data-backdrop="static">
 <div class="modal fade" id="multi-set" data-backdrop="static">
-    <div class="modal-dialog" role="document">
+    <div class="modal-dialog modal-lg" role="document">
         <div class="modal-content">
         <div class="modal-content">
             <div class="modal-header">
             <div class="modal-header">
                 <h5 class="modal-title" id="tender-select-title">更多配置</h5>
                 <h5 class="modal-title" id="tender-select-title">更多配置</h5>

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

@@ -43,7 +43,7 @@
                     <a id="exportLedger" class="btn btn-primary btn-sm" href="javascript: void(0)">导出台账Excel</a>
                     <a id="exportLedger" class="btn btn-primary btn-sm" href="javascript: void(0)">导出台账Excel</a>
                 </div>
                 </div>
                 <div class="d-inline-block ml-1">
                 <div class="d-inline-block ml-1">
-                    <a href="#upload-ybp" data-toggle="modal" data-target="#upload-ybp" class="btn btn-sm btn-primary">导入计价文件</a>
+                    <a href="#import-dsk" data-toggle="modal" data-target="#import-dsk" class="btn btn-sm btn-primary">导入计价文件</a>
                 </div>
                 </div>
                 <% if (syncLedgerUrl) { %>
                 <% if (syncLedgerUrl) { %>
                 <div class="d-inline-block ml-1">
                 <div class="d-inline-block ml-1">
@@ -357,6 +357,7 @@
 </div>
 </div>
 <script src="/public/js/moment/moment.min.js"></script>
 <script src="/public/js/moment/moment.min.js"></script>
 <script type="text/javascript">
 <script type="text/javascript">
+    const dskAccountData = JSON.parse('<%- JSON.stringify(ctx.session.sessionUser.dskAccountData) %>');
     const readOnly = <%- ctx.tender.ledgerReadOnly %>;
     const readOnly = <%- ctx.tender.ledgerReadOnly %>;
     const contractExpr = <%- !!ctx.session.sessionProject.page_show.openContractExpr && tender.ledger_status === auditConst.status.checked %>;
     const contractExpr = <%- !!ctx.session.sessionProject.page_show.openContractExpr && tender.ledger_status === auditConst.status.checked %>;
     const deleteFilePermission = <%- deleteFilePermission %>;
     const deleteFilePermission = <%- deleteFilePermission %>;

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

@@ -56,6 +56,50 @@
     </div>
     </div>
 </div>
 </div>
 <!--上传计价文件-->
 <!--上传计价文件-->
+<div class="modal fade" id="import-dsk" data-backdrop="static" aria-modal="true">
+    <div class="modal-dialog" role="document" style="width:400px">
+        <div class="modal-content">
+            <div class="modal-header">
+                <h5 class="modal-title">选择计价文件</h5>
+                <div class="btn-group ml-auto">
+                    <a href="" data-toggle="dropdown" title="文件类型说明"><i class="fa fa-question-circle"></i></a>
+                    <div class="dropdown-menu bg-dark">
+                        <a class="dropdown-item text-light" href="#">1、yup/ybpx文件:纵横在线云计价大司空导出的项目或者标段文件</a>
+                        <a class="dropdown-item text-light in-3" href="#">yup为一个分段对应一个台账。</a>
+                        <a class="dropdown-item text-light in-3" href="#">ybpx为合并所有分段为一个台账。</a>
+                        <a class="dropdown-item text-light" href="#">2、在线计价文件:绑定纵横在线云计价大司空账号,获取账号下的项目文件。</a>
+                    </div>
+                </div>
+            </div>
+            <div class="modal-body">
+                <p>请选择合适的方式导入文件</p>
+                <div class="d-flex justify-content-between">
+                    <div></div>
+                    <div class="card card-upload-width p-2" id="id-upload-ybp">
+                        <input type="file" id="btn_file" style="display:none">
+                        <div class="card-body p-0">
+                            <div class="m-2"><img src="/public/images/ybpx.png" class="rounded m-auto d-block"></div>
+                            <div class="text-center my-3">上传yup/ybpx文件</div>
+                        </div>
+                    </div>
+                    <div class="card card-upload-width p-2" data-toggle="modal" data-target="#binddskuser" id="id-post-dsk">
+                        <div class="card-body p-0">
+                            <div class="m-2"><img src="/public/images/dsk.png" class="rounded m-auto d-block"></div>
+                            <div class="text-center mt-3 ">获取在线计价文件</div>
+                        </div>
+                    </div>
+                    <div></div>
+                </div>
+            </div>
+            <div class="modal-footer">
+                <div class="ml-auto">
+                    <button type="button" class="btn btn-sm btn-secondary" data-dismiss="modal">取消</button>
+                    <button type="button" class="btn btn-sm btn-primary">确定</button>
+                </div>
+            </div>
+        </div>
+    </div>
+</div>
 <div class="modal fade" id="upload-ybp" data-backdrop="static" enctype="multipart/form-data">
 <div class="modal fade" id="upload-ybp" data-backdrop="static" enctype="multipart/form-data">
     <div class="modal-dialog" role="document">
     <div class="modal-dialog" role="document">
         <div class="modal-content">
         <div class="modal-content">
@@ -535,3 +579,4 @@
 <% include ./audit_modal.ejs %>
 <% include ./audit_modal.ejs %>
 <% include ../shares/hint_modal.ejs %>
 <% include ../shares/hint_modal.ejs %>
 <% include ../shares/batch_replace_modal.ejs %>
 <% include ../shares/batch_replace_modal.ejs %>
+<% include ../shares/dsk_modal.ejs %>

+ 2 - 2
app/view/material/audit_btn.ejs

@@ -6,7 +6,7 @@
             <a id="sub-sp-btn" href="javascript: void(0);" data-toggle="modal" data-target="#sub-sp" class="btn btn-outline-secondary btn-sm btn-block">上报中</a>
             <a id="sub-sp-btn" href="javascript: void(0);" data-toggle="modal" data-target="#sub-sp" class="btn btn-outline-secondary btn-sm btn-block">上报中</a>
         <% } %>
         <% } %>
     <% } else if (ctx.material.status === auditConst.status.checking) { %>
     <% } else if (ctx.material.status === auditConst.status.checking) { %>
-        <% if (ctx.material.curAuditor && ctx.material.curAuditor.aid === ctx.session.sessionUser.accountId) { %>
+        <% if (ctx.material.curAuditorIds.indexOf(ctx.session.sessionUser.accountId) >= 0) { %>
             <a id="sp-done-btn" href="javascript: void(0);" data-toggle="modal" data-target="#sp-done" class="btn btn-success btn-sm btn-block">审批通过</a>
             <a id="sp-done-btn" href="javascript: void(0);" data-toggle="modal" data-target="#sp-done" class="btn btn-success btn-sm btn-block">审批通过</a>
             <a href="#sp-back" data-toggle="modal" data-target="#sp-back" class="btn btn-warning btn-sm btn-block">审批退回</a>
             <a href="#sp-back" data-toggle="modal" data-target="#sp-back" class="btn btn-warning btn-sm btn-block">审批退回</a>
         <% } else { %>
         <% } else { %>
@@ -20,7 +20,7 @@
             <a href="#sp-list" data-type="show" data-toggle="modal" data-target="#sp-list"  class="btn btn-primary btn-sm btn-block sp-list-btn">重新上报</a>
             <a href="#sp-list" data-type="show" data-toggle="modal" data-target="#sp-list"  class="btn btn-primary btn-sm btn-block sp-list-btn">重新上报</a>
         <% } %>
         <% } %>
     <% } %>
     <% } %>
-    <% if (ctx.material.auditors !== undefined && ctx.material.auditors.length !== 0 && ctx.material.auditors[ctx.material.auditors.length-1].aid === ctx.session.sessionUser.accountId && ctx.material.status === auditConst.status.checked && ctx.material.order === ctx.material.highOrder) { %>
+    <% if (ctx.material.auditors !== undefined && ctx.material.finalAuditorIds.indexOf(ctx.session.sessionUser.accountId) >= 0 && ctx.material.status === auditConst.status.checked && ctx.material.order === ctx.material.highOrder) { %>
         <a href="javascript: void(0);" data-toggle="modal" data-target="#sp-down-back" class="btn btn-warning btn-sm btn-block">重新审批</a>
         <a href="javascript: void(0);" data-toggle="modal" data-target="#sp-down-back" class="btn btn-warning btn-sm btn-block">重新审批</a>
     <% } %>
     <% } %>
 </div>
 </div>

Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 565 - 417
app/view/material/audit_modal.ejs


+ 17 - 11
app/view/material/index.ejs

@@ -93,8 +93,16 @@
                             <td class="text-right"><% if (m.material_tax) { %><%= m.ex_tp !== null ? ctx.helper.round(ctx.helper.mul(m.ex_tp, 1+m.exponent_rate/100), m.decimal.tp) : null %><% } else { %><%= ctx.helper.add(ctx.helper.round(ctx.helper.mul(m.ex_tp, 1+m.exponent_rate/100), m.decimal.tp), m.rate_tp) %><% } %></td>
                             <td class="text-right"><% if (m.material_tax) { %><%= m.ex_tp !== null ? ctx.helper.round(ctx.helper.mul(m.ex_tp, 1+m.exponent_rate/100), m.decimal.tp) : null %><% } else { %><%= ctx.helper.add(ctx.helper.round(ctx.helper.mul(m.ex_tp, 1+m.exponent_rate/100), m.decimal.tp), m.rate_tp) %><% } %></td>
                             <% } %>
                             <% } %>
                             <td class="<%- auditConst.auditProgressClass[m.status] %>">
                             <td class="<%- auditConst.auditProgressClass[m.status] %>">
-                                <% if (m.curAuditor) { %>
-                                    <a href="#sp-list" data-toggle="modal" data-target="#sp-list" m-order="<%- m.order %>"><%- m.curAuditor.name %><%if (m.curAuditor.role !== '' && m.curAuditor.role !== null) { %>-<%- m.curAuditor.role %><% } %></a>
+                                <% if (m.status === auditConst.status.checked && m.final_auditor_str) { %>
+                                    <a href="#sp-list" data-toggle="modal" data-target="#sp-list" m-order="<%- m.order %>"><%- m.final_auditor_str %></a>
+                                <% } else { %>
+                                    <% if (m.curAuditors && m.curAuditors.length > 0) { %>
+                                        <% if (m.curAuditors[0].audit_type === auditType.key.common) { %>
+                                            <a href="#sp-list" data-toggle="modal" data-target="#sp-list" m-order="<%- m.order %>"><%- m.curAuditors[0].name %><%if (m.curAuditors[0].role !== '' && m.curAuditors[0].role !== null) { %>-<%- m.curAuditors[0].role %><% } %></a>
+                                        <% } else { %>
+                                            <a href="#sp-list" data-toggle="modal" data-target="#sp-list" m-order="<%- m.order %>"><%- ctx.helper.transFormToChinese(m.curAuditors[0].audit_order) + '审' %></a>
+                                        <% } %>
+                                    <% } %>
                                 <% } %>
                                 <% } %>
                                 <%- auditConst.auditProgress[m.status] %>
                                 <%- auditConst.auditProgress[m.status] %>
                             </td>
                             </td>
@@ -103,11 +111,16 @@
                                     <a href="<%- '/tender/' + ctx.tender.id + '/measure/material/' + m.order %>" class="btn <%- auditConst.statusButtonClass[m.status] %> btn-sm"><%- auditConst.statusButton[m.status] %></a>
                                     <a href="<%- '/tender/' + ctx.tender.id + '/measure/material/' + m.order %>" class="btn <%- auditConst.statusButtonClass[m.status] %> btn-sm"><%- auditConst.statusButton[m.status] %></a>
                                 <% } else if (m.status === auditConst.status.checkNo && m.curAuditor && m.user_id === ctx.session.sessionUser.accountId) { %>
                                 <% } else if (m.status === auditConst.status.checkNo && m.curAuditor && m.user_id === ctx.session.sessionUser.accountId) { %>
                                     <a href="<%- '/tender/' + ctx.tender.id + '/measure/material/' + m.order %>" class="btn <%- auditConst.statusButtonClass[m.status] %> btn-sm"><%- auditConst.statusButton[m.status] %></a>
                                     <a href="<%- '/tender/' + ctx.tender.id + '/measure/material/' + m.order %>" class="btn <%- auditConst.statusButtonClass[m.status] %> btn-sm"><%- auditConst.statusButton[m.status] %></a>
-                                <% } else if (m.status === auditConst.status.checking && m.curAuditor && m.curAuditor.aid === ctx.session.sessionUser.accountId) { %>
+                                <% } else if ((m.status === auditConst.status.checking || m.status === auditConst.status.checkNoPre) && m.curAuditors && m.curAuditors.findIndex(x => { return x.aid === ctx.session.sessionUser.accountId; }) >= 0) { %>
+                                <% const curAudit = m.curAuditors.find(x => { return x.aid === ctx.session.sessionUser.accountId; }); %>
+                                <% if (curAudit.status === auditConst.status.checking) { %>
                                     <a href="<%- '/tender/' + ctx.tender.id + '/measure/material/' + m.order %>" class="btn <%- auditConst.statusButtonClass[m.status] %> btn-sm"><%- auditConst.statusButton[m.status] %></a>
                                     <a href="<%- '/tender/' + ctx.tender.id + '/measure/material/' + m.order %>" class="btn <%- auditConst.statusButtonClass[m.status] %> btn-sm"><%- auditConst.statusButton[m.status] %></a>
                                 <% } else { %>
                                 <% } else { %>
                                     <span class="<%- auditConst.auditProgressClass[m.status] %>"><%- auditConst.auditProgress[m.status] %></span>
                                     <span class="<%- auditConst.auditProgressClass[m.status] %>"><%- auditConst.auditProgress[m.status] %></span>
                                 <% } %>
                                 <% } %>
+                                <% } else { %>
+                                    <span class="<%- auditConst.auditProgressClass[m.status] %>"><%- auditConst.auditProgress[m.status] %></span>
+                                <% } %>
                                 <% if ((ctx.session.sessionUser.is_admin || ((m.status === auditConst.status.uncheck || m.status === auditConst.status.checkNo) && m.user_id === ctx.session.sessionUser.accountId)) && m.order === materials.length) { %>
                                 <% if ((ctx.session.sessionUser.is_admin || ((m.status === auditConst.status.uncheck || m.status === auditConst.status.checkNo) && m.user_id === ctx.session.sessionUser.accountId)) && m.order === materials.length) { %>
                                     <a href="#del-qi" class="btn btn-outline-danger btn-sm ml-1" data-toggle="modal" data-target="#del-qi">删除</a>
                                     <a href="#del-qi" class="btn btn-outline-danger btn-sm ml-1" data-toggle="modal" data-target="#del-qi">删除</a>
                                 <% } %>
                                 <% } %>
@@ -120,20 +133,13 @@
         </div>
         </div>
     </div>
     </div>
 </div>
 </div>
-<script src="/public/js/sub_menu.js"></script>
-<script src="/public/js/decimal.min.js"></script>
-<script src="/public/js/zh_calc.js"></script>
-<script src="/public/js/path_tree.js"></script>
-<script src="/public/js/gcl_gather.js"></script>
-<script src="/public/js/datepicker/datepicker.min.js"></script>
-<script src="/public/js/datepicker/datepicker.zh.js"></script>
-
 <script>
 <script>
     const tenderId = '<%- ctx.tender.id %>';
     const tenderId = '<%- ctx.tender.id %>';
     const auditConst = JSON.parse('<%- auditConst2 %>');
     const auditConst = JSON.parse('<%- auditConst2 %>');
     const preUrl = JSON.parse(unescape('<%- escape(JSON.stringify(preUrl)) %>'));
     const preUrl = JSON.parse(unescape('<%- escape(JSON.stringify(preUrl)) %>'));
     const lastMaterialListNum = parseInt('<%- lastMaterialList ? lastMaterialList.length : 0 %>');
     const lastMaterialListNum = parseInt('<%- lastMaterialList ? lastMaterialList.length : 0 %>');
     const materialColShow = JSON.parse(unescape('<%- escape(JSON.stringify(materialColShow)) %>'));
     const materialColShow = JSON.parse(unescape('<%- escape(JSON.stringify(materialColShow)) %>'));
+    const auditType = JSON.parse('<%- JSON.stringify(auditType) %>');
     $.subMenu({
     $.subMenu({
         menu: '#sub-menu', miniMenu: '#sub-mini-menu', miniMenuList: '#mini-menu-list',
         menu: '#sub-menu', miniMenu: '#sub-mini-menu', miniMenuList: '#mini-menu-list',
         toMenu: '#to-menu', toMiniMenu: '#to-mini-menu',
         toMenu: '#to-menu', toMiniMenu: '#to-mini-menu',

+ 0 - 2
app/view/material/modal.ejs

@@ -228,5 +228,3 @@
         </div>
         </div>
     </div>
     </div>
 </div>
 </div>
-<script src="/public/js/moment/moment.min.js"></script>
-<script src="/public/js/measure_material.js?202308281"></script>

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

@@ -110,7 +110,12 @@
                             <% } else if (s.status === auditConst.status.checkNo && s.user_id === ctx.session.sessionUser.accountId) { %>
                             <% } else if (s.status === auditConst.status.checkNo && s.user_id === ctx.session.sessionUser.accountId) { %>
                             <a href="<%- '/tender/' + ctx.tender.id + '/measure/stage/' + s.order %>" target="_blank" class="btn <%- auditConst.statusButtonClass[s.status] %> btn-sm"><%- auditConst.statusButton[s.status] %></a>
                             <a href="<%- '/tender/' + ctx.tender.id + '/measure/stage/' + s.order %>" target="_blank" class="btn <%- auditConst.statusButtonClass[s.status] %> btn-sm"><%- auditConst.statusButton[s.status] %></a>
                             <% } else if ((s.status === auditConst.status.checking || s.status === auditConst.status.checkNoPre) && s.curAuditors && s.curAuditors.findIndex(x => { return x.aid === ctx.session.sessionUser.accountId; }) >= 0) { %>
                             <% } else if ((s.status === auditConst.status.checking || s.status === auditConst.status.checkNoPre) && s.curAuditors && s.curAuditors.findIndex(x => { return x.aid === ctx.session.sessionUser.accountId; }) >= 0) { %>
-                            <a href="<%- '/tender/' + ctx.tender.id + '/measure/stage/' + s.order %>" target="_blank" class="btn <%- auditConst.statusButtonClass[s.status] %> btn-sm"><%- auditConst.statusButton[s.status] %></a>
+                            <% const curAudit = s.curAuditors.find(x => { return x.aid === ctx.session.sessionUser.accountId; }); %>
+                            <% if (curAudit.status === auditConst.status.checking) { %>
+                                <a href="<%- '/tender/' + ctx.tender.id + '/measure/stage/' + s.order %>" target="_blank" class="btn <%- auditConst.statusButtonClass[s.status] %> btn-sm"><%- auditConst.statusButton[s.status] %></a>
+                            <% } else { %>
+                                <span class="<%- auditConst.auditStringClass[s.status] %>"><%- auditConst.auditString[s.status] %></span>
+                            <% } %>
                             <% } else { %>
                             <% } else { %>
                             <span class="<%- auditConst.auditStringClass[s.status] %>"><%- auditConst.auditString[s.status] %></span>
                             <span class="<%- auditConst.auditStringClass[s.status] %>"><%- auditConst.auditString[s.status] %></span>
                             <% } %>
                             <% } %>

+ 107 - 0
app/view/phase_pay/index.ejs

@@ -0,0 +1,107 @@
+<% include ../tender/tender_sub_menu.ejs %>
+<div class="panel-content">
+    <div class="panel-title">
+        <div class="title-main d-flex">
+            <% include ../tender/tender_sub_mini_menu.ejs %>
+            <h2>
+                合同支付列表
+            </h2>
+            <div class="ml-auto">
+                <a href="#add-qi" data-toggle="modal" data-target="#add-qi" class="btn btn-primary btn-sm">开始新一期</a>
+            </div>
+        </div>
+    </div>
+    <div class="content-wrap">
+        <div class="c-body">
+            <div class="sjs-height-0">
+                <table class="table table-bordered table-hover">
+                    <thead>
+                    <tr>
+                        <th class="text-center" width="80px">支付期数</th>
+                        <th class="text-center" width="70px">支付月份</th>
+                        <th class="text-center" width="70px">计量期</th>
+                        <th class="text-center" width="100px">本期付款</th>
+                        <th class="text-center" width="100px">本期扣款</th>
+                        <th class="text-center" width="100px">本期应付</th>
+                        <th class="text-center" width="100px">本期实付</th>
+                        <th class="text-center" width="100px">截止本期应付</th>
+                        <th class="text-center" width="100px">截止本期实付</th>
+                        <th class="text-center" width="200px">审批进度</th>
+                        <th class="text-center" width="90px">操作</th>
+                        <th class="text-center" width="200px">备注</th>
+                    </tr>
+                    </thead>
+                    <tbody>
+                    <% for (const pay of phasePays) { %>
+                    <tr>
+                        <td>
+                            <a href="<%- '/tender/' + pay.tid + '/pay/' + pay.phase_order %>" target="_blank">第 <%- pay.phase_order %> 期</a>
+                            <% if (pay.audit_status !== auditConst.status.checked && pay.create_user_id === ctx.session.sessionUser.accountId) { %>
+                            <a href="#edit" class="edit-pay" data-id="<%- pay.id %>" data-toggle="modal" data-target="#edit"><i class="fa fa-pencil-square-o "></i></a>
+                            <% } %>
+                        </td>
+                        <td class="text-center"><%- s.phase_date %></td>
+                        <td class="text-center">
+                            <% for (const s of pay.rela_stage) { %>
+                            <a href="<%- '/tender/' + pay.tid + '/measure/stage/' + s.sorder %>" target="_blank">第 <%- s.sorder %> 期</a>
+                            <% } %>
+                        </td>
+                        <td class="text-right"><%- s.display_pay_tp %></td>
+                        <td class="text-right"><%- s.display_cut_tp %></td>
+                        <td class="text-right"><%- s.display_yf_tp %></td>
+                        <td class="text-right"><%- s.display_sf_tp %></td>
+                        <td class="text-right"><%- s.display_end_yf_tp %></td>
+                        <td class="text-right"><%- s.display_end_sf_tp %></td>
+                        <td class="<%- auditConst.auditProgressClass[s.status] %>">
+                            <% if (s.status === auditConst.status.checked && s.final_auditor_str) { %>
+                            <a href="#sp-list" data-toggle="modal" data-target="#sp-list" s-order="<%- s.order %>"><%- s.final_auditor_str %></a>
+                            <% } else { %>
+                            <% if (s.curAuditors.length > 0) { %>
+                            <% if (s.curAuditors[0].audit_type === auditType.key.common) { %>
+                            <a href="#sp-list" data-toggle="modal" data-target="#sp-list" s-order="<%- s.order %>"><%- s.curAuditors[0].name %><%if (s.curAuditors[0].role !== '' && s.curAuditors[0].role !== null) { %>-<%- s.curAuditors[0].role %><% } %></a>
+                            <% } else { %>
+                            <a href="#sp-list" data-toggle="modal" data-target="#sp-list" s-order="<%- s.order %>"><%- ctx.helper.transFormToChinese(s.curAuditors[0].audit_order) + '审' %></a>
+                            <% } %>
+                            <% } %>
+                            <% } %>
+                            <%- auditConst.auditProgress[s.status] %>
+                        </td>
+                        <td class="text-center">
+                            <% if (s.status === auditConst.status.uncheck && s.user_id === ctx.session.sessionUser.accountId) { %>
+                            <a href="<%- '/tender/' + ctx.tender.id + '/measure/stage/' + s.order %>" target="_blank" class="btn <%- auditConst.statusButtonClass[s.status] %> btn-sm"><%- auditConst.statusButton[s.status] %></a>
+                            <% } else if (s.status === auditConst.status.checkNo && s.user_id === ctx.session.sessionUser.accountId) { %>
+                            <a href="<%- '/tender/' + ctx.tender.id + '/measure/stage/' + s.order %>" target="_blank" class="btn <%- auditConst.statusButtonClass[s.status] %> btn-sm"><%- auditConst.statusButton[s.status] %></a>
+                            <% } else if ((s.status === auditConst.status.checking || s.status === auditConst.status.checkNoPre) && s.curAuditors && s.curAuditors.findIndex(x => { return x.aid === ctx.session.sessionUser.accountId; }) >= 0) { %>
+                            <a href="<%- '/tender/' + ctx.tender.id + '/measure/stage/' + s.order %>" target="_blank" class="btn <%- auditConst.statusButtonClass[s.status] %> btn-sm"><%- auditConst.statusButton[s.status] %></a>
+                            <% } else { %>
+                            <span class="<%- auditConst.auditStringClass[s.status] %>"><%- auditConst.auditString[s.status] %></span>
+                            <% } %>
+                        </td>
+                        <td> <%- pay.memo %></td>
+                    </tr>
+                    <% } %>
+                    </tbody>
+                </table>
+            </div>
+        </div>
+    </div>
+</div>
+<script src="/public/js/sub_menu.js"></script>
+<script>
+    $.subMenu({
+        menu: '#sub-menu', miniMenu: '#sub-mini-menu', miniMenuList: '#mini-menu-list',
+        toMenu: '#to-menu', toMiniMenu: '#to-mini-menu',
+        key: 'menu.1.0.0',
+        miniHint: '#sub-mini-hint', hintKey: 'menu.hint.1.0.1',
+        callback: function (info) {
+            if (info.mini) {
+                $('.panel-title').addClass('fluid');
+                $('#sub-menu').removeClass('panel-sidebar');
+            } else {
+                $('.panel-title').removeClass('fluid');
+                $('#sub-menu').addClass('panel-sidebar');
+            }
+            autoFlashHeight();
+        }
+    });
+</script>

+ 23 - 0
app/view/shares/dsk_modal.ejs

@@ -0,0 +1,23 @@
+<div class="modal fade" id="choose-dsk-subject" data-backdrop="static">
+    <div class="modal-dialog modal-lgx" role="document" >
+        <div class="modal-content">
+            <div class="modal-header">
+                <h5 class="modal-title">选择计价分段</h5>
+            </div>
+            <div class="modal-body">
+                <div class="form-inline mb-2">
+                    <label for="inputPassword2" class="">编制办法:</label>
+                    <select class="form-control form-control-sm" style="width:300px" id="cdsks-compilation">
+                    </select>
+                </div>
+                <div class="modal-height-500" id="cdsks-spread">
+                    <div ></div>
+                </div>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-sm btn-secondary" data-dismiss="modal">取消</button>
+                <button type="button" class="btn btn-sm btn-primary" id="choose-dsk-subject-ok">确定</button>
+            </div>
+        </div>
+    </div>
+</div>

+ 2 - 2
app/view/shares/select_rela_tender_modal.ejs

@@ -9,11 +9,11 @@
                 <div class="row">
                 <div class="row">
                     <div class="col-7">
                     <div class="col-7">
                         <h5>可选标段 </h5>
                         <h5>可选标段 </h5>
-                        <div class="modal-height-300" id="sr-source-spread"></div>
+                        <div class="modal-height-600" id="sr-source-spread"></div>
                     </div>
                     </div>
                     <div class="col-5">
                     <div class="col-5">
                         <h5>已选标段 </h5>
                         <h5>已选标段 </h5>
-                        <div class="modal-height-300" id="sr-result-spread"></div>
+                        <div class="modal-height-600" id="sr-result-spread"></div>
                     </div>
                     </div>
                 </div>
                 </div>
             </div>
             </div>

Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 11 - 9
app/view/tender/detail.ejs


+ 3 - 3
app/view/tender/detail_modal.ejs

@@ -1913,7 +1913,7 @@
                         <label class="form-check-label" for="oc-field-tz">台账</label>
                         <label class="form-check-label" for="oc-field-tz">台账</label>
                     </div>
                     </div>
                     <div class="form-check form-check-inline">
                     <div class="form-check form-check-inline">
-                        <input class="form-check-input" name="oc-field" type="radio" id="oc-field-bothy" value="both">
+                        <input class="form-check-input" name="oc-field" type="radio" id="oc-field-both" value="both">
                         <label class="form-check-label" for="oc-field-both">签约或台账</label>
                         <label class="form-check-label" for="oc-field-both">签约或台账</label>
                     </div>
                     </div>
                 </div>
                 </div>
@@ -2197,8 +2197,8 @@
         const prop = {
         const prop = {
             over_range_check: {
             over_range_check: {
                 field: $('[name=oc-field]:checked').val(),
                 field: $('[name=oc-field]:checked').val(),
-                billsWithPos: percent,
-                percent: $('[name=oc-bp]:checked').val(),
+                billsWithPos: $('[name=oc-bp]:checked').val(),
+                percent: percent,
             }
             }
         }
         }
         const tenderId = window.location.pathname.split('/')[2];
         const tenderId = window.location.pathname.split('/')[2];

+ 5 - 0
app/view/tender/tender_sub_menu.ejs

@@ -33,6 +33,11 @@
             </ul>
             </ul>
         </div>
         </div>
         <div class="nav-box">
         <div class="nav-box">
+            <ul class="nav-list list-unstyled">
+                <li <% if (ctx.url.indexOf('/tender/' + ctx.tender.id + '/pay') !== -1) { %>class="active"<% } %>><a href="/tender/<%- ctx.tender.id %>/pay" class="h3"><i class="fa fa-pie-chart fa-fw"></i> <span>合同支付</span></a></li>
+            </ul>
+        </div>
+        <div class="nav-box">
             <% if (!ctx.session.sessionProject.page_show.openChangeProject && !ctx.session.sessionProject.page_show.openChangeApply && !ctx.session.sessionProject.page_show.openChangePlan) { %>
             <% if (!ctx.session.sessionProject.page_show.openChangeProject && !ctx.session.sessionProject.page_show.openChangeApply && !ctx.session.sessionProject.page_show.openChangePlan) { %>
             <ul class="nav-list list-unstyled">
             <ul class="nav-list list-unstyled">
                 <li <% if (ctx.url.indexOf('/tender/' + ctx.tender.id + '/change') !== -1) { %>class="active"<% } %>><a class="change_sort_link h3" href="/tender/<%- ctx.tender.id %>/change"><i class="fa fa-retweet fa-fw"></i> <span>工程变更</span></a></li>
                 <li <% if (ctx.url.indexOf('/tender/' + ctx.tender.id + '/change') !== -1) { %>class="active"<% } %>><a class="change_sort_link h3" href="/tender/<%- ctx.tender.id %>/change"><i class="fa fa-retweet fa-fw"></i> <span>工程变更</span></a></li>

+ 11 - 1
app/view/wap/msg.ejs

@@ -43,9 +43,19 @@
                 <span id="creator"><%- msgInfo.type === 1 ? msgInfo.creator : '' %></span> 发布于 <span id="release_time"><%= moment(msgInfo.release_time*1000).format('YYYY-MM-DD') %></span>
                 <span id="creator"><%- msgInfo.type === 1 ? msgInfo.creator : '' %></span> 发布于 <span id="release_time"><%= moment(msgInfo.release_time*1000).format('YYYY-MM-DD') %></span>
             </p>
             </p>
             <!--内容开始-->
             <!--内容开始-->
-            <div class="msg-content border-top-1 pt-3" id="content">
+            <div class="msg-content border-top-1 pt-3 mb-2" id="content">
                 <%- msgInfo.content %>
                 <%- msgInfo.content %>
             </div>
             </div>
+            <div class="mt-3">
+                <b>附件</b>
+                <% for (const file of files) { %>
+                <a href="<% if (file.viewpath) { %><%- file.viewpath %><% } else { %>/wap/message/download/file/<%- file.id %><% } %>" target="_blank">
+                    <div class="card my-1" style="cursor: pointer">
+                        <div class="card-body"><i class="fa fa-file"></i> <%- file.filename %><small class="pull-right text-secondary"><%- file.filesize %></small></div>
+                    </div>
+                </a>
+                <% } %>
+            </div>
         </div>
         </div>
     </div>
     </div>
     <!--底栏菜单-->
     <!--底栏菜单-->

+ 17 - 0
config/web.js

@@ -189,6 +189,7 @@ const JsFiles = {
                     '/public/js/file-saver/FileSaver.js',
                     '/public/js/file-saver/FileSaver.js',
                 ],
                 ],
                 mergeFiles: [
                 mergeFiles: [
+                    '/public/js/shares/dsk.js',
                     '/public/js/sub_menu.js',
                     '/public/js/sub_menu.js',
                     '/public/js/div_resizer.js',
                     '/public/js/div_resizer.js',
                     '/public/js/spreadjs_rela/spreadjs_zh.js',
                     '/public/js/spreadjs_rela/spreadjs_zh.js',
@@ -678,6 +679,9 @@ const JsFiles = {
                 mergeFile: 'sr_detail',
                 mergeFile: 'sr_detail',
             },
             },
         },
         },
+        phasePay: {
+
+        },
         measure: {
         measure: {
             compare: {
             compare: {
                 files: [
                 files: [
@@ -701,6 +705,19 @@ const JsFiles = {
             },
             },
         },
         },
         material: {
         material: {
+            index: {
+                files: ['/public/js/moment/moment.min.js', '/public/js/decimal.min.js'],
+                mergeFiles: [
+                    '/public/js/sub_menu.js',
+                    '/public/js/zh_calc.js',
+                    '/public/js/path_tree.js',
+                    '/public/js/gcl_gather.js',
+                    '/public/js/datepicker/datepicker.min.js',
+                    '/public/js/datepicker/datepicker.zh.js',
+                    '/public/js/measure_material.js',
+                ],
+                mergeFile: 'material_index',
+            },
             info: {
             info: {
                 files: ['/public/js/spreadjs/sheets/v11/gc.spread.sheets.all.11.2.2.min.js', '/public/js/decimal.min.js',
                 files: ['/public/js/spreadjs/sheets/v11/gc.spread.sheets.all.11.2.2.min.js', '/public/js/decimal.min.js',
                     '/public/js/js-xlsx/xlsx.full.min.js',
                     '/public/js/js-xlsx/xlsx.full.min.js',

+ 81 - 0
db_script/material_audit_order.js

@@ -0,0 +1,81 @@
+const BaseUtil = require('./baseUtils');
+const querySql = BaseUtil.querySql;
+const errorStageTimes = [];
+const updateMaterialTimes = async function(material, times) {
+    const auditData = await querySql('SELECT * FROM zh_material_audit WHERE mid = ? AND times = ? ORDER BY `order`', [material.id, times]);
+    if (auditData.length === 0) return;
+
+    const auditIds = [], filterIds = [];
+    for (const ad of auditData) {
+        if (ad.audit_order) {
+            if (filterIds.indexOf(ad.aid) < 0) filterIds.push(ad.aid);
+        } else {
+            const auditId = auditIds.find(x => { return x.aid === ad.aid});
+            if (auditId) {
+                auditId.ids.push(ad.id);
+            } else {
+                auditIds.push({ aid: ad.aid, ids: [ad.id] });
+            }
+        }
+    }
+    if (auditIds.length === 0) return;
+    if (filterIds.length > 0 && auditIds.length > 0) errorStageTimes.push({ tid: material.tid, mid: material.id, times });
+    for (const [i, aid] of auditIds.entries()) {
+        for (const id of aid.ids) {
+            await querySql('UPDATE zh_material_audit SET audit_order = ? WHERE id = ?', [i+1, id]);
+        }
+    }
+};
+
+const updateMaterial = async function (material) {
+    for (let i = 1; i <= material.times; i++) {
+        await updateMaterialTimes(material, i);
+    }
+};
+
+const doCompleteTest = async function(id) {
+    try {
+        const tender = await querySql('Select * From zh_tender where id > ?', [id]);
+        for (const t of tender) {
+            console.log(`Update Tender ${t.name}(${t.id}):`);
+            const materials = await querySql('Select * From zh_material where tid = ?', [t.id]);
+            for (const s of materials) {
+                await updateMaterial(s);
+            }
+        }
+    } catch (err) {
+        console.log(err);
+    }
+    BaseUtil.closePool();
+    if (errorStageTimes.length > 0) {
+        console.log('ERROR material info: ');
+        console.log(errorStageTimes);
+    }
+};
+
+const doComplete = async function() {
+    try {
+        const tenders = await querySql('Select * From zh_tender');
+        for (const t of tenders) {
+            console.log(`Update Tender ${t.name}(${t.id}):`);
+            const materials = await querySql('Select * From zh_material where tid = ?', [t.id]);
+            for (const s of materials) {
+                await updateMaterial(s);
+            }
+        }
+    } catch (err) {
+        console.log(err);
+    }
+    BaseUtil.closePool();
+    if (errorStageTimes.length > 0) {
+        console.log('ERROR material info: ');
+        console.log(errorStageTimes);
+    }
+};
+
+const tenderId = process.argv[3];
+if (tenderId) {
+    doCompleteTest(tenderId);
+} else {
+    doComplete();
+}

+ 31 - 3
sql/update.sql

@@ -11,11 +11,11 @@ ALTER TABLE `zh_sub_project_info`
 ADD COLUMN `proj_intro` text NULL COMMENT '项目概述' AFTER `project_id`,
 ADD COLUMN `proj_intro` text NULL COMMENT '项目概述' AFTER `project_id`,
 ADD COLUMN `proj_cur_status` text NULL COMMENT '当前状态' AFTER `proj_intro`,
 ADD COLUMN `proj_cur_status` text NULL COMMENT '当前状态' AFTER `proj_intro`,
 ADD COLUMN `proj_status` varchar(20) NOT NULL DEFAULT '' COMMENT '建设状态' AFTER `proj_level`,
 ADD COLUMN `proj_status` varchar(20) NOT NULL DEFAULT '' COMMENT '建设状态' AFTER `proj_level`,
-ADD COLUMN `lx_tp` decimal(24, 8) NOT NULL COMMENT '立项-金额' AFTER `lx_code`,
+ADD COLUMN `lx_tp` decimal(24, 8) NOT NULL DEFAULT 0 COMMENT '立项-金额' AFTER `lx_code`,
 ADD COLUMN `lx_memo` text NULL COMMENT '立项-备注' AFTER `lx_tp`,
 ADD COLUMN `lx_memo` text NULL COMMENT '立项-备注' AFTER `lx_tp`,
 ADD COLUMN `cb_tp` decimal(24, 8) NOT NULL DEFAULT 0 COMMENT '初步-金额' AFTER `cb_code`,
 ADD COLUMN `cb_tp` decimal(24, 8) NOT NULL DEFAULT 0 COMMENT '初步-金额' AFTER `cb_code`,
 ADD COLUMN `cb_memo` text NULL COMMENT '初步-备注' AFTER `cb_tp`,
 ADD COLUMN `cb_memo` text NULL COMMENT '初步-备注' AFTER `cb_tp`,
-ADD COLUMN `sg_tp` decimal(24, 8) NOT NULL COMMENT '施工许可批复-金额' AFTER `sg_code`,
+ADD COLUMN `sg_tp` decimal(24, 8) NOT NULL DEFAULT 0 COMMENT '施工许可批复-金额' AFTER `sg_code`,
 ADD COLUMN `sg_memo` text NULL COMMENT '施工许可批复-备注' AFTER `sg_tp`,
 ADD COLUMN `sg_memo` text NULL COMMENT '施工许可批复-备注' AFTER `sg_tp`,
 ADD COLUMN `sgt_department` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL DEFAULT '' COMMENT '施工图-部门' AFTER `gcl_quantity`,
 ADD COLUMN `sgt_department` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL DEFAULT '' COMMENT '施工图-部门' AFTER `gcl_quantity`,
 ADD COLUMN `sgt_date` varchar(20) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL DEFAULT '' COMMENT '施工图-日期' AFTER `sgt_department`,
 ADD COLUMN `sgt_date` varchar(20) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL DEFAULT '' COMMENT '施工图-日期' AFTER `sgt_department`,
@@ -85,9 +85,37 @@ CREATE TABLE `zh_sub_project_file`  (
   PRIMARY KEY (`id`) USING BTREE
   PRIMARY KEY (`id`) USING BTREE
 ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_unicode_ci ROW_FORMAT = Dynamic;
 ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_unicode_ci ROW_FORMAT = Dynamic;
 
 
-ALTER TABLE `calculation`.`zh_tender_info`
+CREATE TABLE `zh_message_attachment` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `project_id` int(11) DEFAULT NULL COMMENT '项目id',
+  `mid` int(11) NOT NULL DEFAULT '0' COMMENT '通知id',
+  `uid` int(11) NOT NULL COMMENT '上传者id',
+  `filename` varchar(255) COLLATE utf8_unicode_ci NOT NULL COMMENT '文件名称',
+  `fileext` varchar(5) COLLATE utf8_unicode_ci NOT NULL COMMENT '文件后缀',
+  `filesize` varchar(255) COLLATE utf8_unicode_ci NOT NULL COMMENT '文件大小',
+  `filepath` varchar(255) COLLATE utf8_unicode_ci NOT NULL COMMENT '文件存储路径',
+  `upload_time` datetime NOT NULL COMMENT '上传时间',
+  PRIMARY KEY (`id`) USING BTREE,
+  KEY `idx_mid` (`mid`) USING BTREE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='通知附件表';
+
+ALTER TABLE `zh_tender_info`
 ADD COLUMN `over_range_check` varchar(255) NOT NULL DEFAULT '' AFTER `s_type`;
 ADD COLUMN `over_range_check` varchar(255) NOT NULL DEFAULT '' AFTER `s_type`;
 
 
+ALTER TABLE `zh_material_audit`
+ADD COLUMN `audit_type`  tinyint(4) UNSIGNED NOT NULL DEFAULT 1 COMMENT '审批类型(1个人,2会签,3或签)',
+ADD COLUMN `audit_order`  tinyint(4) UNSIGNED NOT NULL DEFAULT 0 COMMENT '审批顺序' AFTER `audit_type`;
+
+ALTER TABLE `zh_material`
+ADD COLUMN `final_auditor_str` varchar(50) NOT NULL DEFAULT '' COMMENT '终审人相关';
+
+ALTER TABLE `zh_filing_template`
+ADD COLUMN `tips` varchar(1000) NOT NULL DEFAULT '' COMMENT '提示' AFTER `filing_type`;
+ALTER TABLE `zh_filing`
+ADD COLUMN `tips` varchar(1000) NOT NULL DEFAULT '' COMMENT '提示' AFTER `file_count`;
+
+ALTER TABLE `zh_material_audit` ADD `is_old` TINYINT(1) NOT NULL DEFAULT '0' COMMENT '是否为旧流程(用于管理员修改流程时旧数据保留但不影响新流程)' AFTER `opinion`;
+
 ------------------------------------
 ------------------------------------
 -- 表数据
 -- 表数据
 ------------------------------------
 ------------------------------------