Browse Source

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

Tony Kang 2 năm trước cách đây
mục cha
commit
d11264c123
100 tập tin đã thay đổi với 31046 bổ sung2507 xóa
  1. 4 0
      app/const/account_group.js
  2. 27 0
      app/const/audit.js
  3. 35 0
      app/const/fun_set.js
  4. 23 1
      app/const/setting.js
  5. 4 4
      app/const/spread.js
  6. 1 0
      app/const/tender_info.js
  7. 30 1
      app/controller/budget_controller.js
  8. 47 0
      app/controller/change_controller.js
  9. 35 0
      app/controller/file_controller.js
  10. 27 58
      app/controller/ledger_audit_controller.js
  11. 7 0
      app/controller/ledger_controller.js
  12. 24 0
      app/controller/material_controller.js
  13. 6 25
      app/controller/measure_controller.js
  14. 2 14
      app/controller/report_archive_controller.js
  15. 2 14
      app/controller/report_controller.js
  16. 2 2
      app/controller/setting_controller.js
  17. 1 41
      app/controller/spss_controller.js
  18. 21 59
      app/controller/stage_controller.js
  19. 0 1
      app/controller/stage_rela_controller.js
  20. 13 260
      app/controller/tender_controller.js
  21. 50 2
      app/extend/helper.js
  22. 29 26
      app/lib/revise_price.js
  23. 15 2
      app/lib/spread_setting.js
  24. 2 1
      app/lib/stage_im.js
  25. 40 64
      app/middleware/stage_check.js
  26. BIN
      app/public/css/bg_participate_blue2.png
  27. BIN
      app/public/css/bg_participate_orange2.png
  28. 57 2
      app/public/css/main.css
  29. 3 2
      app/public/js/budget_compare.js
  30. 400 0
      app/public/js/budget_info.js
  31. 2 2
      app/public/js/budget_list.js
  32. 28 0
      app/public/js/file_detail.js
  33. 1 1
      app/public/js/global.js
  34. 6 0
      app/public/js/ledger.js
  35. 0 313
      app/public/js/ledger_audit.js
  36. 14 11
      app/public/js/material.js
  37. 123 115
      app/public/js/measure_stage.js
  38. 2 2
      app/public/js/path_tree.js
  39. 70 0
      app/public/js/payment_list.js
  40. 7 5
      app/public/js/schedule_plan.js
  41. 5 5
      app/public/js/schedule_stage_gcl.js
  42. 30 25
      app/public/js/setting.js
  43. 82 0
      app/public/js/setting_unit_list_order.js
  44. 118 1
      app/public/js/shares/ali_oss.js
  45. 27421 0
      app/public/js/shares/aliyun-oss-sdk.min.js
  46. 223 55
      app/public/js/shenpi.js
  47. 7 17
      app/public/js/stage.js
  48. 14 10
      app/public/js/stage_audit.js
  49. 2 3
      app/public/js/stage_compare.js
  50. 2 1
      app/public/js/stage_im.js
  51. 5 1
      app/public/js/tender_list.js
  52. 9 3
      app/public/js/tender_list_info.js
  53. 19 4
      app/public/js/tender_list_progress.js
  54. 5 1
      app/public/report/js/rpt_custom.js
  55. 3 5
      app/router.js
  56. 26 2
      app/service/change.js
  57. 5 0
      app/service/change_project.js
  58. 65 23
      app/service/ledger_audit.js
  59. 4 0
      app/service/material_bills.js
  60. 1 1
      app/service/material_list.js
  61. 9 0
      app/service/project_account.js
  62. 78 21
      app/service/shenpi_audit.js
  63. 123 38
      app/service/stage.js
  64. 837 545
      app/service/stage_audit.js
  65. 1 2
      app/service/stage_bills.js
  66. 16 49
      app/service/stage_bonus.js
  67. 21 1
      app/service/stage_change.js
  68. 2 2
      app/service/stage_detail.js
  69. 22 70
      app/service/stage_jgcl.js
  70. 19 58
      app/service/stage_other.js
  71. 1 2
      app/service/stage_pos.js
  72. 39 78
      app/service/stage_safe_prod.js
  73. 1 1
      app/service/stage_stash.js
  74. 18 55
      app/service/stage_temp_land.js
  75. 74 64
      app/service/tender_cache.js
  76. 2 2
      app/view/budget/detail_modal.ejs
  77. 157 0
      app/view/budget/info.ejs
  78. 2 2
      app/view/budget/list.ejs
  79. 4 3
      app/view/budget/sub_menu_list.ejs
  80. 11 1
      app/view/change/apply_information.ejs
  81. 9 8
      app/view/change/information.ejs
  82. 11 1
      app/view/change/plan_information.ejs
  83. 11 1
      app/view/change/project_information.ejs
  84. 5 5
      app/view/dashboard/index.ejs
  85. 3 0
      app/view/file/file.ejs
  86. 24 0
      app/view/file/file_modal.ejs
  87. 0 122
      app/view/ledger/audit.ejs
  88. 137 177
      app/view/ledger/audit_modal.ejs
  89. 9 1
      app/view/ledger/explode.ejs
  90. 1 0
      app/view/ledger/explode_modal.ejs
  91. 3 3
      app/view/measure/compare.ejs
  92. 6 2
      app/view/measure/stage.ejs
  93. 1 0
      app/view/measure/stage_modal.ejs
  94. 23 0
      app/view/payment/list_modal.ejs
  95. 1 1
      app/view/payment/process.ejs
  96. 97 3
      app/view/setting/fun.ejs
  97. 30 0
      app/view/setting/fun_modal.ejs
  98. 22 2
      app/view/setting/show.ejs
  99. 10 2
      app/view/setting/user_unit.ejs
  100. 0 0
      app/view/stage/audit_btn.ejs

+ 4 - 0
app/const/account_group.js

@@ -16,6 +16,8 @@ const accountGroup = {
     GZSJ: 6,
     ZBDW: 8,
     FBDW: 9,
+    HYZGBM: 10,
+    ZXSYS: 11,
     QT: 7,
 };
 
@@ -28,6 +30,8 @@ group[accountGroup.DJDW] = '代建单位';
 group[accountGroup.GZSJ] = '跟踪审计';
 group[accountGroup.ZBDW] = '总包单位';
 group[accountGroup.FBDW] = '分包单位';
+group[accountGroup.HYZGBM] = '行业主管部门';
+group[accountGroup.ZXSYS] = '中心试验室';
 group[accountGroup.QT] = '其他';
 
 module.exports = {

+ 27 - 0
app/const/audit.js

@@ -7,6 +7,22 @@
  * @date
  * @version
  */
+
+const auditType = (function () {
+    const types = [
+        { key: 'common', name: '个人', value: 1, short: '', long: '', class: '' },
+        { key: 'and', name: '会签', value: 2, short: '会', long: '多人会签', class: 'primary' },
+        { key: 'or', name: '或签', value: 3, short: '或', long: '多人或签', class: 'success' },
+    ];
+    const key = {};
+    const info = [];
+    for (const t of types) {
+        key[t.key] = t.value;
+        info[t.value] = t;
+    }
+    return { types, key, info };
+})();
+
 // 台账审批流程
 const ledger = (function() {
     const status = {
@@ -14,6 +30,7 @@ const ledger = (function() {
         checking: 2, // 待审批|审批中
         checked: 3, // 审批通过
         checkNo: 4, // 审批退回
+        checkAgain: 6, // 重新审批 // 该状态仅可用于,终审退回时,修改原终审的审批状态,并同时新增一条新的终审审批中记录
     };
 
     const statusString = [];
@@ -21,12 +38,14 @@ const ledger = (function() {
     statusString[status.checking] = '审批中';
     statusString[status.checked] = '审批完成';
     statusString[status.checkNo] = '审批退回';
+    statusString[status.checkAgain] = '重新审批';
 
     const statusClass = [];
     statusClass[status.uncheck] = '';
     statusClass[status.checking] = '';
     statusClass[status.checked] = 'text-success';
     statusClass[status.checkNo] = 'text-warning';
+    statusClass[status.checkAgain] = 'text-warning';
 
     // 标段概况页
     // 描述文本
@@ -35,12 +54,14 @@ const ledger = (function() {
     auditString[status.checking] = '审批中';
     auditString[status.checked] = '审批通过';
     auditString[status.checkNo] = '审批退回';
+    auditString[status.checkAgain] = '重新审批';
     // 文字样式
     const auditStringClass = [];
     auditStringClass[status.uncheck] = '';
     auditStringClass[status.checking] = 'text-warning';
     auditStringClass[status.checked] = 'text-success';
     auditStringClass[status.checkNo] = 'text-warning';
+    auditStringClass[status.checkAgain] = 'text-warning';
 
     // 金额概况
 
@@ -49,11 +70,13 @@ const ledger = (function() {
     tiStatusString[status.checking] = '审批中';
     tiStatusString[status.checked] = '审批通过';
     tiStatusString[status.checkNo] = '审批退回';
+    tiStatusString[status.checkAgain] = '审批中';
     const tiStatusStringClass = [];
     tiStatusStringClass[status.uncheck] = '';
     tiStatusStringClass[status.checking] = 'text-warning';
     tiStatusStringClass[status.checked] = 'text-success';
     tiStatusStringClass[status.checkNo] = 'text-warning';
+    tiStatusStringClass[status.checkAgain] = 'text-warning';
     return { status, statusString, statusClass, auditString, auditStringClass, tiStatusString, tiStatusStringClass };
 })();
 
@@ -116,6 +139,7 @@ const stage = (function() {
         checkNoPre: 5, // 审批退回上一人
         checkAgain: 6, // 重新审批 // 该状态仅可用于,终审退回时,修改原终审的审批状态,并同时新增一条新的终审审批中记录
         checkCancel: 7, // 撤回 // 该状态为上一审批人可发起,回到它到审批阶段,并同时新增一条新的审批中记录
+        checkSkip: 8, // 跳过
     };
 
     // 流程状态提示
@@ -168,6 +192,7 @@ const stage = (function() {
     auditString[status.checkNoPre] = '审批退回';
     auditString[status.checkAgain] = '重新审批';
     auditString[status.checkCancel] = '撤回';
+    auditString[status.checkSkip] = '审批通过';
     // 文字样式
     const auditStringClass = [];
     auditStringClass[status.uncheck] = '';
@@ -177,6 +202,7 @@ const stage = (function() {
     auditStringClass[status.checkNoPre] = 'text-warning';
     auditStringClass[status.checkAgain] = 'text-warning';
     auditStringClass[status.checkCancel] = 'text-warning';
+    auditStringClass[status.checkSkip] = 'text-success';
     /* ------------------------------------------------------- */
 
     /**
@@ -690,6 +716,7 @@ const pushType = {
 };
 
 module.exports = {
+    auditType,
     ledger,
     stage,
     revise,

+ 35 - 0
app/const/fun_set.js

@@ -13,6 +13,40 @@ const endMonth = [
     // { val: 3, name: '隔月' },
 ];
 const parseInfo = ['stage_start'];
+
+const changeClass = [
+    {
+        value: 1,
+        name: 'A类变更',
+        new_name: '',
+        checked: true,
+    },
+    {
+        value: 2,
+        name: 'B类变更',
+        new_name: '',
+        checked: true,
+    },
+    {
+        value: 3,
+        name: 'C类变更',
+        new_name: '',
+        checked: true,
+    },
+    {
+        value: 4,
+        name: 'D类变更',
+        new_name: '',
+        checked: true,
+    },
+    {
+        value: 5,
+        name: 'E类变更',
+        new_name: '',
+        checked: true,
+    },
+];
+
 const defaultInfo = {
     // 合同信息
     stage_start: {
@@ -20,6 +54,7 @@ const defaultInfo = {
         end_month: 0,
         end_day: 0,
     },
+    change_class: changeClass,
 };
 
 module.exports = {

+ 23 - 1
app/const/setting.js

@@ -29,10 +29,31 @@ const loginWay = {
     extraPsw: 1, // 副密码登录
 };
 
+const exMemoPos = [
+    { key: 'memo', hint: '该列位于备注之后' },
+    { key: 'name', hint: '该列位于名称之后' },
+];
+
+const exMemoPosRange = (function () {
+    const range = {
+        ex_memo1: [],
+        ex_memo2: ['memo', 'name'],
+        ex_memo3: [],
+    };
+    const result = {};
+    for (const r in range) {
+        result[r] = [];
+        for (const k of range[r]) {
+            result[r].push(exMemoPos.find(x => { return x.key === k; }))
+        }
+    }
+    return result;
+})();
+
 const sjsRela = {
     ledgerCol: [
         { field: 'ex_memo1', name: '备注1', show: 0, alias: '', hint: '该列显示于名称之前' },
-        { field: 'ex_memo2', name: '备注2', show: 0, alias: '', hint: '该列显示于备注之后' },
+        { field: 'ex_memo2', name: '备注2', show: 0, alias: '', pos: 'memo', hint: '该列显示于备注之后' },
         { field: 'ex_memo3', name: '备注3', show: 0, alias: '', hint: '该列显示于备注之后' },
     ]
 };
@@ -45,4 +66,5 @@ module.exports = {
     listPath,
     loginWay,
     sjsRela,
+    exMemoPos, exMemoPosRange,
 };

+ 4 - 4
app/const/spread.js

@@ -24,11 +24,11 @@ const withCl = {
     ledger: {
         cols: [
             {title: '项目节编号', colSpan: '1', rowSpan: '2', field: 'code', hAlign: 0, width: 145, formatter: '@', cellType: 'tree'},
-            {title: '清单编号', colSpan: '1', rowSpan: '2', field: 'b_code', hAlign: 0, width: 70, formatter: '@'},
-            {title: 'ex_memo1', colSpan: '1', rowSpan: '2', field: 'ex_memo1', hAlign: 0, width: 100, formatter: '@', cellType: 'ellipsisAutoTip'},
-            {title: '名称', colSpan: '1', rowSpan: '2', field: 'name', hAlign: 0, width: 185, formatter: '@'},
+            {title: '清单编号', colSpan: '1', rowSpan: '2', field: 'b_code', hAlign: 0, width: 70, formatter: '@',},
+            {title: 'ex_memo1', colSpan: '1', rowSpan: '2', field: 'ex_memo1', hAlign: 0, width: 100, formatter: '@', cellType: 'ellipsisAutoTip',},
+            {title: '名称', colSpan: '1', rowSpan: '2', field: 'name', hAlign: 0, width: 185, formatter: '@', },
             {title: '单位', colSpan: '1', rowSpan: '2', field: 'unit', hAlign: 1, width: 50, formatter: '@', cellType: 'unit'},
-            {title: '单价', colSpan: '1', rowSpan: '2', field: 'unit_price', hAlign: 2, width: 60, type: 'Number'},
+            {title: '单价', colSpan: '1', rowSpan: '2', field: 'unit_price', hAlign: 2, width: 60, type: 'Number',},
             {title: '项目节数量|数量1',  colSpan: '2|1', rowSpan: '1|1', field: 'dgn_qty1', hAlign: 2, width: 60, type: 'Number'},
             {title: '|数量2',  colSpan: '|1', rowSpan: '|1', field: 'dgn_qty2', hAlign: 2, width: 60, type: 'Number'},
             {title: '经济指标',  colSpan: '1', rowSpan: '2', field: 'dgn_price', hAlign: 2, width: 60, type: 'Number', readOnly: true},

+ 1 - 0
app/const/tender_info.js

@@ -206,6 +206,7 @@ const paymentDefaultInfo = (function () {
     result.deal_info = JSON.parse(JSON.stringify(defaultInfo.deal_info));
     result.construction_unit = JSON.parse(JSON.stringify(defaultInfo.construction_unit));
     result.tech_param = JSON.parse(JSON.stringify(defaultInfo.tech_param));
+    result.pay_account = JSON.parse(JSON.stringify(defaultInfo.pay_account));
 
     return result;
 })(defaultInfo);

+ 30 - 1
app/controller/budget_controller.js

@@ -13,6 +13,7 @@ const stdDataAddType = {
     next: 3,
 };
 const auditConst = require('../const/audit');
+const changeConst = require('../const/change');
 const LzString = require('lz-string');
 const accountGroup = require('../const/account_group').group;
 module.exports = app => {
@@ -46,6 +47,34 @@ module.exports = app => {
             }
         }
 
+        async budgetInfo(ctx) {
+            try {
+                // 获取变更费用前10的变更令
+                const changeList = await ctx.service.change.getListByBudgetInfo(ctx.budget.rela_tender);
+                // 获取变更后总金额
+                const total_change_tp = await ctx.service.change.getTotalTpByBudgetInfo(ctx.budget.rela_tender);
+                const renderData = {
+                    changeList,
+                    changeConst,
+                    total_change_tp,
+                    jsFiles: this.app.jsFiles.common.concat(this.app.jsFiles.budget.info),
+                    auditConst,
+                };
+                // const relaTenderId = ctx.helper._.map(ctx.budget.rela_tender.split(','), ctx.helper._.toInteger);
+                // const tenderList = await ctx.service.tender.getList4Select('stage');
+                // renderData.tenderList = relaTenderId.length > 0 ? tenderList.filter(x => {
+                //     return relaTenderId.indexOf(x.id) >= 0;
+                // }) : tenderList;
+                // renderData.tenderList = renderData.tenderList.map(y => {
+                //     return { id: y.id, name: y.name, lastStageOrder: y.lastStage.order, lastStageStatus: auditConst.stage.statusString[y.lastStage.status], category: y.category };
+                // });
+                // renderData.categoryData = await this.ctx.service.category.getAllCategory(this.ctx.session.sessionProject.id);
+                await this.layout('budget/info.ejs', renderData);
+            } catch (err) {
+                ctx.log(err);
+            }
+        }
+
         async compare(ctx) {
             try {
                 const renderData = {
@@ -316,4 +345,4 @@ module.exports = app => {
     }
 
     return BudgetController;
-};
+};

+ 47 - 0
app/controller/change_controller.js

@@ -606,6 +606,10 @@ module.exports = app => {
                     }
                 }
 
+                // 工程变更类别读取
+                const projectData = await ctx.service.project.getDataById(ctx.session.sessionProject.id);
+                const fun_set = await ctx.service.project.getFunSet(projectData.fun_set);
+
                 // 获取用户人验证手机号
                 const pa = await ctx.service.projectAccount.getDataById(ctx.session.sessionUser.accountId);
                 const auth_mobile = pa.auth_mobile;
@@ -614,6 +618,7 @@ module.exports = app => {
                     change,
                     othersChange,
                     changeConst,
+                    changeClass: fun_set.change_class,
                     auditStatus,
                     auditConst: audit.flow,
                     ledgerConsts: audit.ledger.status,
@@ -677,6 +682,12 @@ module.exports = app => {
                     renderData.changeList = changeList;
                     renderData.changeLedgerList = await ctx.service.changeLedger.getAllDataByCondition({ where: { tender_id: ctx.tender.id } });
                     renderData.changePosList = await ctx.service.changePos.getAllDataByCondition({ where: { tid: ctx.tender.id } });
+
+                    // 判断是否更新变更类别
+                    if (ctx.helper._.findIndex(fun_set.change_class, { value: change.class, checked: true }) === -1) {
+                        renderData.change.class = ctx.helper._.find(fun_set.change_class, { checked: true }).value;
+                        await ctx.service.change.saveInfo({ class: ctx.helper._.find(fun_set.change_class, { checked: true }).value });
+                    }
                 } else if (auditStatus === 3 || auditStatus === 4 || auditStatus === 5 || auditStatus === 7 || auditStatus === 8) {
                     // 展示页左侧审批流程列表和清单审批列表数据
                     const times = change.status === audit.flow.status.back ?
@@ -2070,6 +2081,35 @@ module.exports = app => {
             ctx.change.xsAuditors = await ctx.service.changeProjectXsAudit.getAuditList(ctx.change.id);
         }
 
+        // 变更类别获取及更新
+        async _getOrUpdateClass(ctx, serviceName) {
+            // 工程变更类别读取
+            const projectData = await ctx.service.project.getDataById(ctx.session.sessionProject.id);
+            const fun_set = await ctx.service.project.getFunSet(projectData.fun_set);
+            if (ctx.change.status === audit[serviceName].status.uncheck ||
+                (serviceName === 'changeProject' && ctx.change.status === audit[serviceName].status.back) ||
+                (serviceName !== 'changeProject' && ctx.change.status === audit[serviceName].status.checkNo)) {
+                const classInfo = ctx.helper._.find(fun_set.change_class, function(item) {
+                    return item.checked && (item.name === ctx.change.class || item.new_name === ctx.change.class);
+                });
+                if (!classInfo) {
+                    const classInfo2 = ctx.helper._.find(fun_set.change_class, { checked: true });
+                    ctx.change.class = classInfo2.new_name ? classInfo2.new_name : classInfo2.name;
+                    await ctx.service[serviceName].defaultUpdate({
+                        id: ctx.change.id,
+                        class: ctx.change.class,
+                    });
+                } else if (classInfo && classInfo.new_name && ctx.change.class !== classInfo.new_name) {
+                    ctx.change.class = classInfo.new_name;
+                    await ctx.service[serviceName].defaultUpdate({
+                        id: ctx.change.id,
+                        class: ctx.change.class,
+                    });
+                }
+            }
+            return fun_set.change_class;
+        }
+
         async projectInformation(ctx) {
             try {
                 const whiteList = this.ctx.app.config.multipart.whitelist;
@@ -2077,10 +2117,13 @@ module.exports = app => {
                 // 获取附件列表
                 const fileList = await ctx.service.changeProjectAtt.getAllChangeProjectAtt(ctx.tender.id, ctx.change.id);
                 await this._getChangeProjectAuditViewData(ctx);
+                const changeClass = await this._getOrUpdateClass(ctx, 'changeProject');
+                // 判断并更新
                 const renderData = {
                     tender,
                     change: ctx.change,
                     changeConst,
+                    changeClass,
                     auditConst: audit.changeProject,
                     fileList,
                     whiteList,
@@ -2672,12 +2715,14 @@ module.exports = app => {
                 const changeList = await ctx.service.changeApplyList.getList(ctx.change.id);
                 // 获取附件列表
                 const fileList = await ctx.service.changeApplyAtt.getAllChangeApplyAtt(ctx.tender.id, ctx.change.id);
+                const changeClass = await this._getOrUpdateClass(ctx, 'changeApply');
                 const renderData = {
                     tender,
                     change: ctx.change,
                     listRule: tender.c_apply_list_rule ? JSON.parse(tender.c_apply_list_rule) : { source: 1, rule: ['unit', 'unit_price'] },
                     changeList,
                     changeConst,
+                    changeClass,
                     auditConst: audit.changeApply,
                     fileList,
                     whiteList,
@@ -3294,11 +3339,13 @@ module.exports = app => {
                         }
                     }
                 }
+                const changeClass = await this._getOrUpdateClass(ctx, 'changePlan');
                 const renderData = {
                     tender,
                     change: ctx.change,
                     listRule: tender.c_plan_list_rule ? JSON.parse(tender.c_plan_list_rule) : { source: 1, rule: ['unit', 'unit_price'] },
                     changeList,
+                    changeClass,
                     changeConst,
                     auditConst: audit.changePlan,
                     fileList,

+ 35 - 0
app/controller/file_controller.js

@@ -216,6 +216,41 @@ module.exports = app => {
                 ctx.ajaxErrorBody(error, '编辑附件失败');
             }
         }
+        async uploadBigFile(ctx) {
+            try {
+                await this.checkCanUpload(ctx);
+                const data = JSON.parse(ctx.request.body.data);
+                if (!data.type || !data.filing_id || !data.fileInfo) throw '缺少参数';
+                const filing = await ctx.service.filing.getDataById(data.filing_id);
+                if (!filing || filing.is_deleted) throw '分类不存在,请刷新页面后重试';
+
+                let result;
+                const fileInfo = path.parse(data.fileInfo.filename);
+                switch(data.type) {
+                    case 'begin':
+                        const create_time = Date.parse(new Date()) / 1000;
+                        result = {
+                            filename: `sp/file/${filing.spid}/${ctx.moment().format('YYYYMMDD')}/${create_time + '_' + fileInfo.ext}`,
+                        };
+                        result.filepath = ctx.app.config.fujianOssFolder + result.filename;
+                        // todo 写入ossToken
+                        result.oss = await ctx.helper.getOssToken(ctx.app.fujianOss);
+                        break;
+                    case 'end':
+                        const user = await ctx.service.projectAccount.getDataById(ctx.session.sessionUser.accountId);
+                        const uploadFiles = [{
+                            filepath: data.filepath,
+                            filename: fileInfo.name, fileext: fileInfo.ext, filesize: data.fileInfo.filesize,
+                        }];
+                        result = await ctx.service.file.addFiles(filing, uploadFiles, user);
+                        break;
+                }
+                ctx.body = {err: 0, msg: '', data: result };
+            } catch (error) {
+                ctx.log(error);
+                ctx.body = this.ajaxErrorBody(error, '上传附件失败,请重试');
+            }
+        }
 
         async loadValidRelaTender(ctx) {
             try {

+ 27 - 58
app/controller/ledger_audit_controller.js

@@ -27,64 +27,6 @@ module.exports = app => {
         }
 
         /**
-         * 台账审批页面(get)
-         *
-         * @param ctx
-         * @return {Promise<void>}
-         */
-        async index(ctx) {
-            try {
-                const renderData = {
-                    tender: ctx.tender.data,
-                    tenderInfo: ctx.tender.info,
-                    tenderMenu: this.menu.tenderMenu,
-                    preUrl: '/tender/' + ctx.tender.id,
-                    measureType,
-                };
-                const [ledgerSpread, posSpread] = await spreadSetting.getLedgerSpreadSetting(ctx, ctx.tender.id, true);
-                const sjsRela = await this.ctx.service.project.getTenderSjsRela(ctx.session.sessionProject.id, ctx.tender.info.display.exMemo);
-                this.ctx.helper.refreshSpreadShow(sjsRela.ledgerCol, [ledgerSpread, posSpread]);
-
-                const curAuditor = await ctx.service.ledgerAudit.getCurAuditor(ctx.tender.id, ctx.tender.data.ledger_times);
-                if (!curAuditor && (ctx.tender.data.ledger_status !== auditConst.status.checkNo && ctx.tender.data.ledger_status !== auditConst.status.checked)) {
-                    throw '审核信息错误';
-                }
-
-                renderData.user = await ctx.service.projectAccount.getAccountInfoById(ctx.tender.data.user_id);
-                renderData.auditHistory = [];
-                const times = ctx.tender.data.ledger_status === auditConst.status.checkNo ? ctx.tender.data.ledger_times - 1 : ctx.tender.data.ledger_times;
-                const auditors = await ctx.service.ledgerAudit.getAuditorsWithOwner(ctx.tender.id, times);
-                if (ctx.tender.data.user_id !== ctx.session.sessionUser.accountId) {
-                    if (auditors.length > 0) {
-                        const auditor = auditors.find(function(a) { return a.audit_id === ctx.session.sessionUser.accountId; });
-                        if (!auditor) {
-                            throw '您无权查看台账审批';
-                        }
-                    }
-                }
-                if (times >= 1) {
-                    for (let i = 1; i <= times; i++) {
-                        renderData.auditHistory.push(await ctx.service.ledgerAudit.getAuditors(ctx.tender.id, i));
-                    }
-                }
-                const ledgerData = await ctx.service.ledger.getData(ctx.tender.id);
-                renderData.curAuditor = curAuditor;
-                renderData.auditConst = auditConst;
-                renderData.auditors = auditors;
-                renderData.ledger = JSON.stringify(ledgerData);
-                renderData.ledgerSpreadSetting = JSON.stringify(ledgerSpread);
-                renderData.posSpreadSetting = JSON.stringify(posSpread);
-                renderData.readOnly = true;
-                renderData.jsFiles = this.app.jsFiles.common.concat(this.app.jsFiles.ledger.audit);
-                renderData.nodeType = stdConst.nodeType;
-                await this.layout('ledger/audit.ejs', renderData, 'ledger/audit_modal.ejs');
-            } catch (err) {
-                this.log(err);
-                ctx.redirect('/tender/' + ctx.tender.id);
-            }
-        }
-
-        /**
          * 新增审批人(Ajax)
          *
          * @param ctx
@@ -219,6 +161,33 @@ module.exports = app => {
                 ctx.redirect(ctx.request.header.referer);
             }
         }
+
+        /**
+         * 重新审批(post)
+         *
+         * @param ctx
+         * @return {Promise<void>}
+         */
+        async checkAgain(ctx) {
+            try {
+                const tender = ctx.tender;
+                if (!tender.data || tender.data.ledger_status !== auditConst.status.checked) {
+                    throw '当前标段数据有误';
+                }
+                const revise = await this.ctx.service.ledgerRevise.getLastestRevise(tender.id);
+                if (revise) throw '当前标段存在台账修订,不可重新审批';
+                const stage = await this.ctx.service.stage.getDataByCondition({ tid: tender.id });
+                if (stage) throw '当前标段已开始计量,不可重新审批';
+
+                await ctx.service.ledgerAudit.checkAgain(tender.id, tender.data.ledger_times);
+
+                ctx.redirect('/tender/' + ctx.tender.id + '/ledger');
+            } catch (err) {
+                this.log(err);
+                ctx.session.postError = err.toString();
+                ctx.redirect(ctx.request.header.referer);
+            }
+        }
     }
 
     return LedgerAuditController;

+ 7 - 0
app/controller/ledger_controller.js

@@ -131,6 +131,12 @@ module.exports = app => {
                 const whiteList = this.ctx.app.config.multipart.whitelist;
                 const categoryData = await this.ctx.service.category.getAllCategory(this.ctx.session.sessionProject.id);
                 const syncLedger = await this.ctx.service.specPull.syncLedger(this.ctx.session.sessionProject.id);
+                // 是否已验证手机短信
+                const pa = await ctx.service.projectAccount.getDataById(ctx.session.sessionUser.accountId);
+                const revise = await this.ctx.service.ledgerRevise.getLastestRevise(tender.id);
+                tender.data.hasRevise = !!revise;
+                const stage = await this.ctx.service.stage.getDataByCondition({ tid: tender.id });
+                tender.data.hasStage = !!stage;
                 const renderData = {
                     tender: tender.data,
                     tenderInfo: tender.info,
@@ -154,6 +160,7 @@ module.exports = app => {
                     categoryData,
                     syncLedgerUrl: syncLedger ? `${ctx.app.config.url3f}/${syncLedger.pull_class}/sync-tz?pCode=${tender.id}` : '',
                     nodeType: stdConst.nodeType,
+                    authMobile: pa.auth_mobile,
                 };
                 if ((tender.data.ledger_status === auditConst.status.uncheck || tender.data.ledger_status === auditConst.status.checkNo) && tender.data.user_id === ctx.session.sessionUser.accountId) {
                     // renderData.accountGroup = accountGroup;

+ 24 - 0
app/controller/material_controller.js

@@ -382,6 +382,8 @@ module.exports = app => {
                 await this._setEditTaxPermission(ctx);
                 const renderData = await this._getDefaultRenderData(ctx);
                 renderData.materialBillsData = await this._getMaterialBillsData(ctx);
+                // 判断order是否重复,重复则重新排版并更新数据库
+                await this.checkBillsOrder(ctx, renderData.materialBillsData);
                 // 取对应期的截取上期的调差金额和应耗数量
                 if (ctx.material.highOrder !== ctx.material.order) {
                     for (const [mindex, mb] of renderData.materialBillsData.entries()) {
@@ -436,6 +438,28 @@ module.exports = app => {
             }
         }
 
+        // 判断order是否重复,重复则重新排版并更新数据库
+        async checkBillsOrder(ctx, billsList) {
+            if (billsList.length > 0) {
+                const needSet = ctx.helper._.uniq(ctx.helper._.map(billsList, 'order')).length !== billsList.length;
+                if (needSet) {
+                    let order = 1;
+                    const updateOrderList = [];
+                    for (const b of billsList) {
+                        if (b.order !== order) {
+                            b.order = order;
+                            updateOrderList.push({
+                                id: b.id,
+                                order,
+                            });
+                        }
+                        order = order + 1;
+                    }
+                    if (updateOrderList.length > 0) await ctx.service.materialBills.updateAllOrder(updateOrderList);
+                }
+            }
+        }
+
         /**
          * 调差清单页面 (Get)
          * @param {Object} ctx - egg全局变量

+ 6 - 25
app/controller/measure_controller.js

@@ -11,6 +11,7 @@
 const spreadConst = require('../const/spread');
 const codeRuleConst = require('../const/code_rule');
 const auditConst = require('../const/audit').stage;
+const auditType = require('../const/audit').auditType;
 const moment = require('moment');
 const measureType = require('../const/tender').measureType;
 
@@ -44,16 +45,13 @@ module.exports = app => {
                     preUrl: '/tender/' + ctx.tender.id,
                     auditConst,
                     auditConst2: JSON.stringify(auditConst),
+                    auditType,
                 };
                 renderData.stages = await ctx.service.stage.getValidStages(ctx.tender.id);
                 if (renderData.stages.length > 0) await this.ctx.service.stage.checkStageGatherData(renderData.stages[0], this.ctx.session.sessionUser.is_admin);
                 for (const s of renderData.stages) {
-                    // s.curAuditor = null;
                     // 根据期状态返回展示用户
-                    s.curAuditor = await ctx.service.stageAudit.getAuditorByStatus(s.id, s.status, s.times);
-                    if (s.status === auditConst.status.checkNoPre) {
-                        s.curAuditor2 = await ctx.service.stageAudit.getAuditorByStatus(s.id, auditConst.status.checking);
-                    }
+                    s.curAuditors = await ctx.service.stageAudit.getAuditorsByStatus(s.id, s.status, s.times);
                 }
                 await this.layout('measure/stage.ejs', renderData, 'measure/stage_modal.ejs');
             } catch (err) {
@@ -69,29 +67,12 @@ module.exports = app => {
          */
         async stageAuditors(ctx) {
             try {
-                const responseData = {
-                    err: 0, msg: '', data: {},
-                };
                 const order = JSON.parse(ctx.request.body.data).order;
                 const tenderId = ctx.params.id;
                 const stageInfo = await ctx.service.stage.getDataByCondition({ tid: tenderId, order });
-                // 获取审批流程中右边列表
-                const auditHistory = [];
-                const times = stageInfo.status === auditConst.status.checkNo ? stageInfo.times - 1 : stageInfo.times;
-                if (times >= 1) {
-                    for (let i = 1; i <= times; i++) {
-                        auditHistory.push(await ctx.service.stageAudit.getAuditors(stageInfo.id, i));
-                    }
-                }
-                responseData.data.auditHistory = auditHistory;
-                // 获取审批流程中左边列表
-                responseData.data.auditors = await ctx.service.stageAudit.getAuditGroupByListWithOwner(stageInfo.id, times);
-
-                responseData.data.user = await ctx.service.projectAccount.getAccountInfoById(stageInfo.user_id);
-                // 获取原报信息
-                // const stageAuditor = await ctx.service.projectAccount.getAccountInfoById(stageInfo.user_id);
-                // responseData.data.stageAuditor = stageAuditor;
-                ctx.body = responseData;
+                await ctx.service.stage.loadStageUser(stageInfo);
+                await ctx.service.stage.loadStageAuditViewData(stageInfo);
+                ctx.body = { err: 0, msg: '', data: stageInfo };
             } catch (error) {
                 this.log(error);
                 ctx.body = { err: 1, msg: error.toString(), data: null };

+ 2 - 14
app/controller/report_archive_controller.js

@@ -29,20 +29,7 @@ module.exports = app => {
          */
         async _getStageAuditViewData(ctx) {
             if (!ctx.stage) return;
-            const times = ctx.stage.status === auditConst.stage.status.checkNo ? ctx.stage.times - 1 : ctx.stage.times;
-
-            ctx.stage.user = await ctx.service.projectAccount.getAccountInfoById(ctx.stage.user_id);
-            ctx.stage.auditHistory = [];
-            if (times >= 1) {
-                for (let i = 1; i <= times; i++) {
-                    ctx.stage.auditHistory.push(await ctx.service.stageAudit.getAuditors(ctx.stage.id, i));
-                }
-            }
-            // 获取审批流程中左边列表
-            ctx.stage.auditors2 = await ctx.service.stageAudit.getAuditGroupByListWithOwner(ctx.stage.id, times);
-            if (ctx.stage.status === auditConst.stage.status.uncheck || ctx.stage.status === auditConst.stage.status.checkNo) {
-                ctx.stage.auditorList = await ctx.service.stageAudit.getAuditors(ctx.stage.id, ctx.stage.times);
-            }
+            await ctx.service.stage.loadStageAuditViewData(ctx.stage);
         }
 
         async index(ctx) {
@@ -147,6 +134,7 @@ module.exports = app => {
                 accountList,
                 isAdmin,
                 needFileMsg,
+                auditType: auditConst.auditType,
             };
             if (stage_id === -1) {
                 await this.layout('report/index_archive.ejs', renderData, 'report/archive_popup.ejs');

+ 2 - 14
app/controller/report_controller.js

@@ -47,20 +47,7 @@ module.exports = app => {
          */
         async _getStageAuditViewData(ctx) {
             if (!ctx.stage) return;
-            const times = ctx.stage.status === auditConst.stage.status.checkNo ? ctx.stage.times - 1 : ctx.stage.times;
-
-            ctx.stage.user = await ctx.service.projectAccount.getAccountInfoById(ctx.stage.user_id);
-            ctx.stage.auditHistory = [];
-            if (times >= 1) {
-                for (let i = 1; i <= times; i++) {
-                    ctx.stage.auditHistory.push(await ctx.service.stageAudit.getAuditors(ctx.stage.id, i));
-                }
-            }
-            // 获取审批流程中左边列表
-            ctx.stage.auditors2 = await ctx.service.stageAudit.getAuditGroupByListWithOwner(ctx.stage.id, times);
-            if (ctx.stage.status === auditConst.stage.status.uncheck || ctx.stage.status === auditConst.stage.status.checkNo) {
-                ctx.stage.auditorList = await ctx.service.stageAudit.getAuditors(ctx.stage.id, ctx.stage.times);
-            }
+            await ctx.service.stage.loadStageAuditViewData(ctx.stage);
         }
 
         async _chkIfStageAuditor(ctx, stage) {
@@ -307,6 +294,7 @@ module.exports = app => {
                     prePay: JSON.stringify(advance),
                     OSS_PATH: ctx.app.config.fujianOssPath,
                     viewPmData: PermissionCheck.viewPmData(this.ctx.session.sessionUser.permission),
+                    auditType: auditConst.auditType,
                 };
                 await this.layout('report/index.ejs', renderData, 'report/rpt_all_popup.ejs');
                 // await this.layout('report/index.ejs', renderData);

+ 2 - 2
app/controller/setting_controller.js

@@ -849,7 +849,7 @@ module.exports = app => {
                 }
                 const showList = await ctx.service.settingShow.getList(projectData.page_path);
                 const sjsRela = await ctx.service.project.getSjsRela(projectId);
-                const renderData = { projectData, showList, sjsRela };
+                const renderData = { projectData, showList, sjsRela, settingConst };
                 await this.layout('setting/show.ejs', renderData);
             } catch (error) {
                 this.log(error);
@@ -937,7 +937,7 @@ module.exports = app => {
                     imType,
                     endMonth: funSet.endMonth,
                     funSet: fun_set,
-                });
+                }, 'setting/fun_modal.ejs');
             } catch (error) {
                 ctx.helper.log(error);
                 ctx.redirect('/dashboard');

+ 1 - 41
app/controller/spss_controller.js

@@ -40,49 +40,9 @@ module.exports = app => {
         }
 
         async _checkStage(tid, sorder) {
-            const _ = this.ctx.helper._;
             const stage = await this.service.stage.getDataByCondition({ tid: tid, order: sorder });
             if (!stage) throw '期数据错误';
-
-            // 读取原报、审核人数据
-            stage.auditors = await this.service.stageAudit.getAuditors(stage.id, stage.times);
-            stage.curAuditor = await this.service.stageAudit.getCurAuditor(stage.id, stage.times);
-            const accountId = this.ctx.session.sessionUser.accountId, auditorIds = _.map(stage.auditors, 'aid'), shareIds = [];
-            const permission = this.ctx.session.sessionUser.permission;
-
-            if (accountId === stage.user_id) { // 原报
-                stage.curTimes = stage.times;
-                if (stage.status === status.uncheck || stage.status === status.checkNo) {
-                    stage.curOrder = 0;
-                } else if (stage.status === status.checked) {
-                    stage.curOrder = _.max(_.map(stage.auditors, 'order'));
-                } else {
-                    stage.curOrder = stage.curAuditor.aid === accountId ? stage.curAuditor.order : stage.curAuditor.order - 1;
-                }
-            } else if (auditorIds.indexOf(accountId) !== -1) { // 审批人
-                if (stage.status === status.uncheck) {
-                    throw '您无权查看该数据';
-                }
-                stage.curTimes = stage.status === status.checkNo ? stage.times - 1 : stage.times;
-                if (stage.status === status.checked) {
-                    stage.curOrder = _.max(_.map(stage.auditors, 'order'));
-                } else if (stage.status === status.checkNo) {
-                    const audit = await this.service.stageAudit.getDataByCondition({
-                        sid: stage.id, times: stage.times - 1, status: status.checkNo
-                    });
-                    stage.curOrder = audit.order;
-                } else {
-                    stage.curOrder = accountId === stage.curAuditor.aid ? stage.curAuditor.order : stage.curAuditor.order - 1;
-                }
-            } else if (shareIds.indexOf(accountId) !== -1 || (permission !== null && permission.tender !== undefined && permission.tender.indexOf('2') !== -1)) { // 分享人
-                if (stage.status === status.uncheck) {
-                    throw '您无权查看该数据';
-                }
-                stage.curTimes = stage.status === status.checkNo ? stage.times - 1 : stage.times;
-                stage.curOrder = stage.status === status.checked ? _.max(_.map(stage.auditors, 'order')) : stage.curAuditor.order - 1;
-            } else { // 其他不可见
-                throw '您无权查看该数据';
-            }
+            await this.service.stage.doCheckStage(stage);
             return stage;
         }
 

+ 21 - 59
app/controller/stage_controller.js

@@ -11,6 +11,7 @@
 const moment = require('moment');
 const auditConst = require('../const/audit').stage;
 const changeAudit = require('../const/audit').flow;
+const auditType = require('../const/audit').auditType;
 const spreadConst = require('../const/spread');
 const tenderConst = require('../const/tender');
 const shenpiConst = require('../const/shenpi');
@@ -63,6 +64,7 @@ module.exports = app => {
                     dagl: ctx.session.sessionProject.dagl_status,
                 },
                 shenpiConst,
+                auditType
             };
             if (((ctx.stage.status === auditConst.status.uncheck || ctx.stage.status === auditConst.status.checkNo) && ctx.session.sessionUser.accountId === ctx.stage.user_id) || ctx.session.sessionUser.is_admin) {
                 // data.accountGroup = accountGroup;
@@ -93,23 +95,7 @@ module.exports = app => {
          * @private
          */
         async _getStageAuditViewData(ctx) {
-            const times = ctx.stage.status === auditConst.status.checkNo ? ctx.stage.times - 1 : ctx.stage.times;
-
-            ctx.stage.user = await ctx.service.projectAccount.getAccountInfoById(ctx.stage.user_id);
-            ctx.stage.auditHistory = [];
-            if (times >= 1) {
-                for (let i = 1; i <= times; i++) {
-                    ctx.stage.auditHistory.push(await ctx.service.stageAudit.getAuditors(ctx.stage.id, i));
-                }
-            }
-            // 获取审批流程中左边列表
-            ctx.stage.auditors2 = ctx.stage.status === auditConst.status.checkNo && ctx.stage.user_id !== ctx.session.sessionUser.accountId ?
-                await ctx.service.stageAudit.getAuditGroupByListWithOwner(ctx.stage.id, times) :
-                await ctx.service.stageAudit.getAuditGroupByListWithOwner(ctx.stage.id, ctx.stage.times);
-            const { status } = ctx.stage;
-            if (status === auditConst.status.uncheck || status === auditConst.status.checkNo) {
-                ctx.stage.auditorList = await ctx.service.stageAudit.getAuditors(ctx.stage.id, ctx.stage.times);
-            }
+            await ctx.service.stage.loadStageAuditViewData(ctx.stage);
             await this._checkStageStart(ctx);
         }
 
@@ -152,10 +138,10 @@ module.exports = app => {
             }
         }
 
-        _checkStageCanModify(ctx) {
+        _checkStageCanModify(ctx, isCheck = false) {
             // 检查登录用户,是否可操作
             if (ctx.stage.readOnly) {
-                throw '该计量期当前您无权操作';
+                if (!isCheck || !ctx.stage.canCheck) throw '该计量期当前您无权操作';
             }
             if (ctx.stage.revising) {
                 throw '台账修订中,请勿修改提交期数据';
@@ -182,7 +168,6 @@ module.exports = app => {
                 renderData.jsFiles = this.app.jsFiles.common.concat(this.app.jsFiles.stage.index);
                 renderData.whiteList = this.ctx.app.config.multipart.whitelist;
                 renderData.imType = tenderConst.imType;
-                renderData.curAuditor = ctx.stage.curAuditor;
                 renderData.auditConst = auditConst;
                 // 获取附件列表
                 const attData = await ctx.service.stageAtt.getDataByTenderIdAndStageId(ctx.tender.id, ctx.params.order);
@@ -1246,7 +1231,7 @@ module.exports = app => {
                     throw '添加审核人失败';
                 }
 
-                const auditors = await ctx.service.stageAudit.getAuditGroupByListWithOwner(ctx.stage.id, ctx.stage.times);
+                const auditors = await ctx.service.stageAudit.getUserGroup(ctx.stage.id, ctx.stage.times);
                 ctx.body = { err: 0, msg: '', data: auditors };
             } catch (err) {
                 this.log(err);
@@ -1315,11 +1300,11 @@ module.exports = app => {
          */
         async checkAudit(ctx) {
             try {
-                this._checkStageCanModify(ctx);
+                this._checkStageCanModify(ctx, true);
                 if (!this.ctx.stage || (this.ctx.stage.status !== auditConst.status.checking && this.ctx.stage.status !== auditConst.status.checkNoPre)) {
                     throw '当前期数据有误';
                 }
-                if (!this.ctx.stage.curAuditor || this.ctx.stage.curAuditor.aid !== ctx.session.sessionUser.accountId) {
+                if (this.ctx.stage.curAuditorIds.indexOf(ctx.session.sessionUser.accountId) < 0) {
                     throw '您无权进行该操作';
                 }
                 const data = JSON.parse(ctx.request.body.data);
@@ -1331,11 +1316,6 @@ module.exports = app => {
                 if (!data.checkType || isNaN(data.checkType)) {
                     throw '提交数据错误';
                 }
-                if (data.checkType === auditConst.status.checkNo) {
-                    if (!data.checkType || isNaN(data.checkType)) {
-                        throw '提交数据错误';
-                    }
-                }
 
                 await ctx.service.stageAudit.check(ctx.stage.id, data, ctx.stage.times);
                 ctx.body = { err: 0, msg: '', data: [] };
@@ -1372,8 +1352,8 @@ module.exports = app => {
                     }
                 }
 
-                if ((ctx.stage.auditors[ctx.stage.auditors.length - 1].aid === ctx.session.sessionUser.accountId || (ctx.query.confirm === '确认设置终审审批' && ctx.session.sessionUser.is_admin)) && ctx.stage.status === auditConst.status.checked && ctx.stage.order === ctx.stage.highOrder) {
-                    await ctx.service.stageAudit.checkAgain(ctx.stage.id, ctx.stage.times);
+                if ((ctx.stage.finalAuditorIds.indexOf(ctx.session.sessionUser.accountId) >= 0 || (ctx.query.confirm === '确认设置终审审批' && ctx.session.sessionUser.is_admin)) && ctx.stage.status === auditConst.status.checked && ctx.stage.order === ctx.stage.highOrder) {
+                    await ctx.service.stageAudit.checkAgain(ctx.stage.id, ctx.stage.times, (ctx.query.confirm === '确认设置终审审批' && ctx.session.sessionUser.is_admin));
                     // ctx.redirect(ctx.request.header.referer);
                     ctx.body = {
                         err: 0,
@@ -1402,29 +1382,14 @@ module.exports = app => {
          */
         async checkAuditCancel(ctx) {
             try {
-                if (ctx.stage.revising) {
-                    throw '台账修订中,请勿修改提交期数据';
-                }
-                if (ctx.stage.cancancel) {
-                    await ctx.service.stageAudit.checkCancel(ctx.stage.id, ctx.stage.times);
-                    // ctx.redirect(ctx.request.header.referer);
-                    ctx.body = {
-                        err: 0,
-                        url: ctx.request.header.referer,
-                        msg: '',
-                    };
-                } else {
-                    throw '您无权进行该操作';
-                }
+                if (ctx.stage.revising) throw '台账修订中,请勿修改提交期数据';
+                if (!ctx.stage.cancancel) throw '您无权进行该操作';
+
+                await ctx.service.stageAudit.checkCancel(ctx.stage);
+                ctx.body = { err: 0, url: ctx.request.header.referer, msg: '' };
             } catch (err) {
                 this.log(err);
-                // ctx.session.postError = err.toString();
-                // ctx.redirect(ctx.request.header.referer);
-                ctx.body = {
-                    err: 1,
-                    // url: ctx.request.header.referer,
-                    msg: err,
-                };
+                ctx.body = { err: 1, msg: err };
             }
         }
 
@@ -1484,11 +1449,11 @@ module.exports = app => {
                 await this._getStageAuditViewData(ctx);
                 const renderData = await this._getDefaultRenderData(ctx);
                 if (ctx.stage.status === auditConst.status.checkNo && !ctx.stage.readOnly) {
-                    renderData.compareAuditors = ctx.stage.auditHistory[ctx.stage.curTimes - 2];
+                    renderData.compareAuditorGroup = ctx.stage.auditHistory[ctx.stage.curTimes - 2];
                 } else if (ctx.stage.times !== ctx.stage.curTimes) {
-                    renderData.compareAuditors = ctx.stage.auditHistory[ctx.stage.curTimes - 1];
+                    renderData.compareAuditorGroup = ctx.stage.auditHistory[ctx.stage.curTimes - 1];
                 } else {
-                    renderData.compareAuditors = ctx.stage.auditors;
+                    renderData.compareAuditorGroup = ctx.stage.auditHistory[ctx.stage.curTimes - 1];
                 }
                 renderData.ledgerSpread = JSON.parse(JSON.stringify(spreadConst.stageCompare.ledger));
                 renderData.posSpread = JSON.parse(JSON.stringify(spreadConst.stageCompare.pos));
@@ -2056,14 +2021,11 @@ module.exports = app => {
                 const renderData = await this._getDefaultRenderData(ctx);
                 // 获取已完成期审批列表
                 const lastStage = await ctx.service.stage.getLastestStage(ctx.tender.id, true);
-                const auditList = [];
+                let auditList = [];
                 if (lastStage) {
                     renderData.lastStageUser = await ctx.service.projectAccount.getDataById(lastStage.user_id);
                     const times = lastStage.status !== auditConst.status.checkNo ? lastStage.times : lastStage.times - 1;
-                    for (let i = times; i > 0; i--) {
-                        const timeAuditList = await ctx.service.stageAudit.getAuditors(lastStage.id, i, 'desc');
-                        auditList.push(timeAuditList);
-                    }
+                    auditList = await ctx.service.stageAudit.getAuditorHistory(lastStage.id, times, true);
                 }
                 renderData.lastStage = lastStage;
                 renderData.lastAuditList = auditList;

+ 0 - 1
app/controller/stage_rela_controller.js

@@ -102,7 +102,6 @@ module.exports = app => {
                 [renderData.ledgerSpread, renderData.posSpread] = await spreadSetting.getStageSpreadSetting(ctx, null, true, {minusNoValue: renderData.minusNoValue});
                 renderData.measureType = measureType;
                 renderData.whiteList = this.ctx.app.config.multipart.whitelist;
-                renderData.curAuditor = ctx.stage.curAuditor;
                 renderData.auditConst = auditConst;
                 renderData.thirdParty = {
                     gxby: ctx.session.sessionProject.gxby_status, dagl: ctx.session.sessionProject.dagl_status,

+ 13 - 260
app/controller/tender_controller.js

@@ -42,154 +42,6 @@ module.exports = app => {
             ctx.showTitle = true;
         }
 
-        async _getLedgerAuditInfo(tender) {
-            tender.cur_flow = {
-                title: '台账',
-                status: auditConst.ledger.tiStatusString[tender.ledger_status],
-                status_class: auditConst.ledger.tiStatusStringClass[tender.ledger_status],
-            };
-            if (tender.ledger_status === auditConst.ledger.status.uncheck) {
-                tender.cur_flow.name = tender.user_name;
-                tender.cur_flow.role = tender.user_role;
-            } else {
-                const cur = tender.ledger_status === auditConst.ledger.status.checkNo
-                    ? await this.ctx.service.ledgerAudit.getLastestAuditor(tender.id, tender.ledger_times - 1, auditConst.ledger.status.checkNo)
-                    : await this.ctx.service.ledgerAudit.getLastestAuditor(tender.id, tender.ledger_times, tender.ledger_status);
-                if (cur) {
-                    tender.cur_flow.name = cur.name;
-                    tender.cur_flow.role = cur.role;
-                    if (cur.audit_order === 1) {
-                        tender.pre_flow = { name: tender.user_name, time: cur.begin_time };
-                    } else {
-                        const pre = await this.ctx.service.ledgerAudit.getAuditorByOrder(tender.id, cur.audit_order - 1, cur.times);
-                        if (pre) tender.pre_flow = { name: pre.name, time: pre.end_time };
-                    }
-                } else {
-                    tender.cur_flow.name = '';
-                    tender.cur_flow.role = '';
-                }
-            }
-        }
-
-        async _getStageAuditInfo(tender, stage) {
-            tender.cur_flow = {
-                title: '第' + stage.order + '期',
-                status: auditConst.stage.tiStatusString[stage.status],
-                status_class: auditConst.stage.tiStatusStringClass[stage.status],
-            };
-            if (stage.status === auditConst.stage.status.uncheck) {
-                if (tender.user_id === stage.user_id) {
-                    tender.cur_flow.name = tender.user_name;
-                    tender.cur_flow.role = tender.user_role;
-                } else {
-                    const user = await this.ctx.service.projectAccount.getDataById(stage.user_id);
-                    tender.cur_flow.name = user.name;
-                    tender.cur_flow.role = user.role;
-                }
-                if (stage.order > 1) {
-                    const preStage = await this.ctx.service.stage.getDataByCondition({ tid: tender.id, order: stage.order - 1 });
-                    if (!preStage) return;
-
-                    const pre = await this.ctx.service.stageAudit.getLastestAuditor(preStage.id, preStage.times, auditConst.stage.status.checked);
-                    if (pre) tender.pre_flow = { name: pre.name, time: pre.end_time };
-                }
-            } else {
-                let cur;
-                if (stage.status === auditConst.stage.status.checkNo) {
-                    cur = await this.ctx.service.stageAudit.getLastestAuditor(stage.id, stage.times - 1, auditConst.stage.status.checkNo);
-                } else if (stage.status === auditConst.stage.status.checked) {
-                    cur = await this.ctx.service.stageAudit.getLastestAuditor(stage.id, stage.times, auditConst.stage.status.checked);
-                } else {
-                    cur = await this.ctx.service.stageAudit.getCurAuditor(stage.id, stage.times);
-                }
-                if (cur) {
-                    tender.cur_flow.name = cur.name;
-                    tender.cur_flow.role = cur.role;
-                    if (cur.order === 1) {
-                        tender.pre_flow = {};
-                        if (tender.user_id === stage.user_id) {
-                            tender.pre_flow.name = tender.user_name;
-                        } else {
-                            const user = await this.ctx.service.projectAccount.getDataById(stage.user_id);
-                            tender.pre_flow.name = user.name;
-                        }
-                        tender.pre_flow.time = cur.begin_time;
-                    } else {
-                        const pre = await this.ctx.service.stageAudit.getAuditorByOrder(stage.id, cur.order - 1, cur.times);
-                        if (pre) tender.pre_flow = { name: pre.name, time: pre.end_time };
-                    }
-                } else {
-                    tender.cur_flow.name = '';
-                    tender.cur_flow.role = '';
-                }
-            }
-        }
-
-        async _listDetail(view, modal = '') {
-            try {
-                // 获取用户新建标段权利
-                const accountInfo = await this.ctx.service.projectAccount.getDataById(this.ctx.session.sessionUser.accountId);
-                const userPermission = accountInfo !== undefined && accountInfo.permission !== ''
-                    ? JSON.parse(accountInfo.permission) : null;
-
-                const tenderList = await this.ctx.service.tender.getBuildList('', userPermission, this.ctx.session.sessionUser.is_admin);
-
-                for (const t of tenderList) {
-                    const tenderInfo = await this.ctx.service.tenderInfo.getTenderInfo(t.id);
-                    t.contract_price = tenderInfo.deal_param.contractPrice;
-
-                    let bCalcTp = t.user_id === this.ctx.session.sessionUser.accountId && (
-                        t.ledger_status === auditConst.ledger.status.checkNo || t.ledger_status === auditConst.ledger.status.uncheck);
-                    t.advance_tp = await this.ctx.service.advance.getSumAdvance(t.id);
-                    if (t.ledger_status === auditConst.ledger.status.checked) {
-                        t.lastStage = await this.ctx.service.stage.getLastestStage(t.id, true);
-                        if (t.lastStage && t.lastStage.status === auditConst.stage.status.uncheck &&
-                            t.lastStage.user_id !== this.ctx.session.sessionUser.accountId) {
-                            t.lastStage = await this.ctx.service.stage.getLastestStage(t.id);
-                        }
-                        if (t.lastStage) await this.ctx.service.stage.checkStageGatherData(t.lastStage, this.ctx.session.sessionUser.is_admin);
-                        t.completeStage = await this.ctx.service.stage.getLastestCompleteStage(t.id);
-                        if ((!bCalcTp) && t.measure_type === measureType.gcl.value) {
-                            bCalcTp = t.lastStage && t.lastStage.status !== auditConst.stage.status.checked && !t.lastStage.readOnly;
-                        }
-                    }
-
-                    if (bCalcTp) {
-                        const sum = await this.ctx.service.ledger.addUp({ tender_id: t.id/* , is_leaf: true*/ });
-                        t.total_price = sum.total_price;
-                        t.deal_tp = sum.deal_tp;
-                    }
-
-                    if (t.lastStage) {
-                        await this._getStageAuditInfo(t, t.lastStage);
-                    } else {
-                        await this._getLedgerAuditInfo(t);
-                    }
-                }
-                const categoryData = await this.ctx.service.category.getAllCategory(this.ctx.session.sessionProject.id);
-                const valuations = await this.ctx.service.valuation.getProjectValidValuation(this.ctx.session.sessionProject.id);
-                const renderData = {
-                    tenderList,
-                    tenderConst,
-                    settingConst,
-                    categoryData,
-                    measureType: tenderConst.measureType,
-                    jsFiles: this.app.jsFiles.common.concat(this.jsFiles),
-                    auditConst,
-                    userPermission,
-                    valuations,
-                    uid: this.ctx.session.sessionUser.accountId,
-                    pid: this.ctx.session.sessionProject.id,
-                };
-                renderData.selfCategoryLevel = await this.ctx.service.projectAccount.getSelfCategoryLevel(this.ctx.session.sessionUser.accountId);
-                renderData.is_finish = false;
-                await this.layout(view, renderData, modal);
-            } catch (err) {
-                this.log(err);
-                this.ctx.redirect('/dashboard');
-            }
-        }
-
         async _listDetailCache(view, modal = '') {
             try {
                 // 获取用户新建标段权利
@@ -310,51 +162,6 @@ module.exports = app => {
             }
         }
 
-        async _list(view, renderData, modal = '', list_status = '') {
-            try {
-                renderData.tenderList = await this.ctx.service.tender.getBuildList(list_status, renderData.userPermission, this.ctx.session.sessionUser.is_admin);
-
-                for (const t of renderData.tenderList) {
-                    t.visitor = (await this.ctx.service.tenderTourist.getTourists(t.id)).map(x => { return x.user_name; });
-                    if (t.ledger_status === auditConst.ledger.status.checked) {
-                        t.lastStage = await this.ctx.service.stage.getLastestStage(t.id, true);
-                        t.completeStage = await this.ctx.service.stage.getLastestCompleteStage(t.id);
-                    }
-                    if (t.lastStage) {
-                        if (t.lastStage.status === auditConst.stage.status.uncheck) {
-                            const status_name = await this.ctx.service.projectAccount.getAccountInfoById(t.lastStage.user_id);
-                            t.status_users = status_name ? status_name.name : '';
-                            // const status_name = await this.ctx.service.stageAudit.getStatusName(t.lastStage.id, t.lastStage.times - 1);
-                            // t.status_users = status_name ? status_name.name : '';
-                        } else {
-                            const status_name = await this.ctx.service.stageAudit.getAuditorByStatus(t.lastStage.id, t.lastStage.status, t.lastStage.times);
-                            t.status_users = status_name ? status_name.name : '';
-                        }
-                    } else {
-                        if (t.ledger_status !== auditConst.ledger.status.uncheck) {
-                            const status_name = await this.ctx.service.ledgerAudit.getStatusName(t.id, t.ledger_times);
-                            t.status_users = status_name ? status_name.name : '';
-                        }
-                    }
-                }
-                renderData.categoryData = await this.ctx.service.category.getAllCategory(this.ctx.session.sessionProject.id);
-                renderData.valuations = await this.ctx.service.valuation.getProjectValidValuation(this.ctx.session.sessionProject.id);
-                renderData.tenderConst = tenderConst;
-                renderData.settingConst = settingConst;
-                renderData.measureType = tenderConst.measureType;
-                renderData.jsFiles = this.app.jsFiles.common.concat(this.jsFiles);
-                renderData.auditConst = auditConst;
-                renderData.uid = this.ctx.session.sessionUser.accountId;
-                renderData.pid = this.ctx.session.sessionProject.id;
-                renderData.selfCategoryLevel = await this.ctx.service.projectAccount.getSelfCategoryLevel(this.ctx.session.sessionUser.accountId);
-                renderData.is_finish = false;
-                await this.layout(view, renderData, modal);
-            } catch (err) {
-                this.log(err);
-                this.ctx.redirect('/dashboard');
-            }
-        }
-
         async listDefault(ctx) {
             this.jsFiles = this.app.jsFiles.tender.list;
             const accountInfo = await this.ctx.service.projectAccount.getDataById(this.ctx.session.sessionUser.accountId);
@@ -411,62 +218,6 @@ module.exports = app => {
             }
         }
 
-        async listDefaultOrg(ctx) {
-            this.jsFiles = this.app.jsFiles.tender.list;
-            const accountInfo = await this.ctx.service.projectAccount.getDataById(this.ctx.session.sessionUser.accountId);
-            const userPermission = accountInfo !== undefined && accountInfo.permission !== '' ? JSON.parse(accountInfo.permission) : null;
-            const renderData = {
-                accountInfo,
-                userPermission,
-            };
-            await this._list('tender/index.ejs', renderData, 'tender/modal.ejs');
-        }
-
-        /**
-         * 标段概况(Get)
-         *
-         * @param {object} ctx - egg全局变量
-         * @return {void}
-         */
-        async listInfoOrg(ctx) {
-            this.jsFiles = this.app.jsFiles.tender.info;
-            await this._listDetail('tender/info.ejs', 'tender/modal.ejs');
-        }
-
-        /**
-         * 计量进度(Get)
-         *
-         * @param ctx
-         * @return {Promise<void>}
-         */
-        async listProgressOrg(ctx) {
-            this.jsFiles = this.app.jsFiles.tender.progress;
-            await this._listDetail('tender/progress.ejs', 'tender/modal.ejs');
-        }
-
-        /**
-         * 标段管理(Get)
-         *
-         * @param ctx
-         * @return {Promise<void>}
-         */
-        async listManageOrg(ctx) {
-            this.jsFiles = this.app.jsFiles.tender.manage;
-            // 先判断权限
-            // 获取用户新建标段权利
-            const accountInfo = await this.ctx.service.projectAccount.getDataById(this.ctx.session.sessionUser.accountId);
-            const userPermission = accountInfo !== undefined && accountInfo.permission !== '' ? JSON.parse(accountInfo.permission) : null;
-            if (userPermission !== null && userPermission.tender !== undefined && userPermission.tender.indexOf('1') !== -1) {
-                const renderData = {
-                    accountInfo,
-                    userPermission,
-                };
-                await this._list('tender/manage.ejs', renderData, 'tender/manage_modal.ejs', 'manage');
-            } else {
-                this.ctx.redirect(ctx.request.header.referer);
-            }
-        }
-
         async listDefaultFinish(ctx) {
             this.jsFiles = this.app.jsFiles.tender.list;
             const accountInfo = await this.ctx.service.projectAccount.getDataById(this.ctx.session.sessionUser.accountId);
@@ -643,16 +394,17 @@ module.exports = app => {
                         lastStage.status = lastStage.status === auditConst.stage.status.checkNoPre ? auditConst.stage.status.checking : lastStage.status;
                         let cur;
                         if (lastStage.status === auditConst.stage.status.checked) {
-                            cur = await this.ctx.service.stageAudit.getLastestAuditor(lastStage.id, lastStage.times, auditConst.stage.status.checked);
+                            cur = await this.ctx.service.stageAudit.getLastestAuditors(lastStage.id, lastStage.times, auditConst.stage.status.checked);
                         } else if (lastStage.status === auditConst.stage.status.checking) {
-                            cur = await this.ctx.service.stageAudit.getCurAuditor(lastStage.id, lastStage.times);
+                            cur = await this.ctx.service.stageAudit.getCurAuditors(lastStage.id, lastStage.times);
                         } else {
-                            cur = await this.ctx.service.stageAudit.getAuditorByStatus(lastStage.id, lastStage.status, lastStage.times);
+                            cur = await this.ctx.service.stageAudit.getAuditorsByStatus(lastStage.id, lastStage.status, lastStage.times);
                         }
                         // const status_name = await this.ctx.service.stageAudit.getAuditorByStatus(lastStage.id, lastStage.status, lastStage.times);
-                        lastStage.status_users = cur ? cur.name : '';
+                        lastStage.status_users = cur.length > 0 ? (cur[0].audit_type === auditConst.auditType.key.common ? cur[0].name : ctx.helper.transFormToChinese(cur[0].audit_order) + '审') : '';
                         const times = lastStage.status === auditConst.stage.status.checkNo ? lastStage.times - 1 : lastStage.times;
-                        lastStage.auditors = await ctx.service.stageAudit.getFinalAuditGroup(lastStage.id, times);
+                        const history = await ctx.service.stageAudit.getAuditorHistory(lastStage.id, times);
+                        lastStage.auditors = history[history.length - 1];
                     }
                 } else {
                     if (bCalcTp) {
@@ -1155,7 +907,7 @@ module.exports = app => {
             for (const sp of shenpiConst.sp_lc) {
                 sp.status = ctx.tender.info.shenpi ? ctx.tender.info.shenpi[sp.code] : shenpiConst.sp_status.sqspr;
                 if (sp.status === shenpiConst.sp_status.gdspl) {
-                    sp.auditList = await ctx.service.shenpiAudit.getAuditList(ctx.tender.id, sp.type, sp.status);
+                    sp.auditGroupList = await ctx.service.shenpiAudit.getAuditGroupList(ctx.tender.id, sp.type, sp.status);
                 } else if (sp.status === shenpiConst.sp_status.gdzs) {
                     sp.audit = await ctx.service.shenpiAudit.getAudit(ctx.tender.id, sp.type, sp.status);
                 }
@@ -1206,6 +958,7 @@ module.exports = app => {
                 measureType,
                 cooperationNum,
                 jsFiles: this.app.jsFiles.common.concat(this.app.jsFiles.tender.shenpi),
+                auditType: auditConst.auditType,
             };
             await this.layout('tender/shenpi.ejs', renderData, 'tender/shenpi_modal.ejs');
         }
@@ -1240,7 +993,7 @@ module.exports = app => {
                 await ctx.service.tenderInfo.saveTenderInfo(ctx.tender.id, { shenpi: postData });
                 let auditList = [];
                 if (data.status === shenpiConst.sp_status.gdspl) {
-                    auditList = await ctx.service.shenpiAudit.getAuditList(ctx.tender.id, shenpiConst.sp_type[data.code], data.status);
+                    auditList = await ctx.service.shenpiAudit.getAuditGroupList(ctx.tender.id, shenpiConst.sp_type[data.code], data.status);
                 } else if (data.status === shenpiConst.sp_status.gdzs) {
                     auditList = await ctx.service.shenpiAudit.getAudit(ctx.tender.id, shenpiConst.sp_type[data.code], data.status);
                 }
@@ -1264,10 +1017,7 @@ module.exports = app => {
                 let info = '';
                 switch (data.type) {
                     case 'add':
-                        const result = await ctx.service.shenpiAudit.addAudit(data);
-                        if (result) {
-                            throw '添加审批人失败';
-                        }
+                        info = await ctx.service.shenpiAudit.addAudit(data);
                         break;
                     case 'del':
                         await ctx.service.shenpiAudit.removeAudit(data);
@@ -1287,6 +1037,9 @@ module.exports = app => {
                     case 'audit-ass':
                         info = await ctx.service.auditAss.updateData(data);
                         break;
+                    case 'audit-type':
+                        await ctx.service.shenpiAudit.setAuditType(data);
+                        break;
                     default:break;
                 }
                 ctx.body = { err: 0, msg: '', data: info };

+ 50 - 2
app/extend/helper.js

@@ -952,7 +952,8 @@ module.exports = {
         return overWan ? getWan(overWan) + '万' + getWan(noWan) : getWan(num);
     },
     formatNum(num, pattern) {
-        const strarr = num ? num.toString().split('.') : ['0'];
+        const minus = num > 0 ? '' : '-'
+        const strarr = num ? Math.abs(num).toString().split('.') : ['0'];
         const fmtarr = pattern ? pattern.split('.') : [''];
         let retstr = '';
 
@@ -1002,7 +1003,7 @@ module.exports = {
                     break;
             }
         }
-        return retstr.replace(/^,+/, '').replace(/\.$/, '');
+        return minus + retstr.replace(/^,+/, '').replace(/\.$/, '');
     },
 
     dateTran(time, style = 'YYYY年MM月DD日 HH:mm') {
@@ -1606,5 +1607,52 @@ module.exports = {
             result.push(obj[prop][field]);
         }
         return result;
+    },
+
+    groupAuditors(auditors, orderField = 'order') {
+        auditors.sort((x, y) => {
+            return x[orderField] - y[orderField];
+        });
+        const Group = [];
+        for (const a of auditors) {
+            if (a[orderField] > 0) {
+                if (Group[a[orderField] - 1]) {
+                    Group[a[orderField] - 1].push(a);
+                } else {
+                    Group.push([a]);
+                }
+            } else {
+                Group.push([a]);
+            }
+        }
+        return Group;
+    },
+    groupAuditorsUniq(group, key = 'audit_order') {
+        const uniqGroup = [];
+        for (const g of group) {
+            uniqGroup[g[0][key]] = g;
+        }
+        return uniqGroup.filter(x => { return !!x });
+    },
+    async getOssToken(oss) {
+        try {
+            const { STS } = require('ali-oss');
+            let sts = new STS({
+                accessKeyId: 'LTAI5tSoKgJ9Ze5VcgMQcM3m',
+                accessKeySecret: '34sj51AEUpeDzr5V6DYjrxWOatw7Zr',
+            });
+            const token = {
+                bucket: oss.options.bucket,
+                endpoint: this.ctx.app.config.fujianOssPath,
+                cname: true,
+            };
+            const result = await sts.assumeRole('acs:ram::31196920:role/oss-readonly', '', '3000', this.ctx.app.uuid.v4());
+            token.accessKeyId = result.credentials.AccessKeyId;
+            token.accessKeySecret = result.credentials.AccessKeySecret;
+            token.stsToken = result.credentials.SecurityToken;
+            return token;
+        } catch (err) {
+            console.log(err);
+        }
     }
 };

+ 29 - 26
app/lib/revise_price.js

@@ -144,35 +144,38 @@ class revisePriceCalc {
         const said = this.ctx.session.sessionUser.accountId;
         billsTree.calculateAll(node => {
             if (node.children && node.children.length > 0) return;
-            const priceDiff = helper.sub(node.unit_price, node.pre_unit_price);
-            if (!priceDiff) return;
+            // const priceDiff = helper.sub(node.unit_price, node.pre_unit_price);
+            // if (!priceDiff) return;
             if (node.cur_id && (node.cur_contract_qty || node.cur_qc_qty)) {
-                node.cur_contract_tp = helper.mul(node.cur_contract_qty, node.unit_price, decimal.tp);
-                node.cur_qc_tp = helper.mul(node.cur_qc_qty, node.unit_price, decimal.tp);
-                node.cur_positive_qc_tp = helper.mul(node.cur_positive_qc_qty, node.unit_price, decimal.tp);
-                node.cur_negative_qc_tp = helper.mul(node.cur_negative_qc_qty, node.unit_price, decimal.tp);
-                result.ibData.push({
-                    tid: stage.tid, sid: stage.id, said,
-                    lid: node.id,
-                    times: stage.times, order: auditOrder,
-                    contract_qty: node.cur_contract_qty, contract_tp: node.cur_contract_tp,
-                    qc_qty: node.cur_qc_qty, qc_tp: node.cur_qc_tp,
-                    positive_qc_qty: node.cur_positive_qc_qty, positive_qc_tp: node.cur_positive_qc_tp,
-                    negative_qc_qty: node.cur_negative_qc_qty, negative_qc_tp: node.cur_negative_qc_tp,
-                    postil: node.postil,
-                });
+                const cur_contract_tp = helper.mul(node.cur_contract_qty, node.unit_price, decimal.tp);
+                const cur_qc_tp = helper.mul(node.cur_qc_qty, node.unit_price, decimal.tp);
+                const cur_positive_qc_tp = helper.mul(node.cur_positive_qc_qty, node.unit_price, decimal.tp);
+                const cur_negative_qc_tp = helper.mul(node.cur_negative_qc_qty, node.unit_price, decimal.tp);
+                if (cur_contract_tp !== node.cur_contract_tp || cur_qc_tp !== node.cur_qc_tp || cur_positive_qc_tp !== node.cur_positive_qc_tp || cur_negative_qc_tp !== node.cur_positive_qc_tp) {
+                    result.ibData.push({
+                        tid: stage.tid, sid: stage.id, said,
+                        lid: node.id,
+                        times: stage.times, order: auditOrder,
+                        contract_qty: node.cur_contract_qty, contract_tp: cur_contract_tp,
+                        qc_qty: node.cur_qc_qty, qc_tp: cur_qc_tp,
+                        positive_qc_qty: node.cur_positive_qc_qty, positive_qc_tp: cur_positive_qc_tp,
+                        negative_qc_qty: node.cur_negative_qc_qty, negative_qc_tp: cur_negative_qc_tp,
+                        postil: node.postil,
+                    });
+                }
             }
             if (node.pre_id && (node.pre_contract_qty || node.pre_qc_qty)) {
-                node.contract_pc_tp = helper.sub(helper.mul(node.pre_contract_qty, node.unit_price, decimal.tp), node.pre_contract_tp);
-                node.qc_pc_tp = helper.sub(helper.mul(node.pre_qc_qty, node.unit_price, decimal.tp), node.pre_qc_tp);
-                node.pc_tp = helper.add(node.contract_pc_tp, node.qc_pc_tp);
-                node.positive_qc_pc_tp = helper.sub(helper.mul(node.pre_positive_qc_qty, node.unit_price, decimal.tp), node.pre_positive_qc_tp);
-                node.negative_qc_pc_tp = helper.sub(helper.mul(node.pre_negative_qc_qty, node.unit_price, decimal.tp), node.pre_negative_qc_tp);
-                result.bpcData.push({
-                    tid: stage.tid, sid: stage.id, sorder: stage.order, lid: node.id, org_price: node.pre_unit_price, unit_price: node.unit_price,
-                    contract_pc_tp: node.contract_pc_tp, qc_pc_tp: node.qc_pc_tp, pc_tp: node.pc_tp,
-                    positive_qc_pc_tp: node.positive_qc_pc_tp, negative_qc_pc_tp: node.negative_qc_pc_tp,
-                });
+                const contract_pc_tp = helper.sub(helper.mul(node.pre_contract_qty, node.unit_price, decimal.tp), node.pre_contract_tp);
+                const qc_pc_tp = helper.sub(helper.mul(node.pre_qc_qty, node.unit_price, decimal.tp), node.pre_qc_tp);
+                const pc_tp = helper.add(contract_pc_tp, qc_pc_tp);
+                const positive_qc_pc_tp = helper.sub(helper.mul(node.pre_positive_qc_qty, node.unit_price, decimal.tp), node.pre_positive_qc_tp);
+                const negative_qc_pc_tp = helper.sub(helper.mul(node.pre_negative_qc_qty, node.unit_price, decimal.tp), node.pre_negative_qc_tp);
+                if (contract_pc_tp || qc_pc_tp || pc_tp || positive_qc_pc_tp || negative_qc_pc_tp) {
+                    result.bpcData.push({
+                        tid: stage.tid, sid: stage.id, sorder: stage.order, lid: node.id, org_price: node.pre_unit_price, unit_price: node.unit_price,
+                        contract_pc_tp, qc_pc_tp, pc_tp, positive_qc_pc_tp, negative_qc_pc_tp,
+                    });
+                }
             }
             const scDetail = stageChange.filter(x => { return x.lid === node.id; });
             for (const scd of scDetail) {

+ 15 - 2
app/lib/spread_setting.js

@@ -41,10 +41,23 @@ function refreshSpreadShow(sjsRela, sjsSetting) {
     for (const field of sjsRela) {
         if (field.show) {
             for (const ss of sjsSettings) {
-                const lCol = ss.cols.find(x => {
+                const lColIndex = ss.cols.findIndex(x => {
                     return x.field === field.field;
                 });
-                if (lCol) lCol.title = field.alias || field.name;
+                const lCol = ss.cols[lColIndex];
+                if (lCol) {
+                    lCol.title = field.alias || field.name;
+                    if (field.pos) {
+                        const preColIndex = ss.cols.findIndex(x => {
+                            return x.field === field.pos;
+                        });
+                        const preCol = ss.cols[preColIndex];
+                        if (preCol && preColIndex !== lColIndex - 1) {
+                            ss.cols.splice(lColIndex, 1);
+                            ss.cols.splice(preColIndex + 1, 0, lCol);
+                        }
+                    }
+                }
             }
         } else {
             for (const ss of sjsSetting) {

+ 2 - 1
app/lib/stage_im.js

@@ -129,7 +129,8 @@ class StageIm {
     _getNumberFormat(num, length) {
         let s = '0000000000';
         s = s + num;
-        return s.substr(s.length - length);
+        const numLength = num.toString().length;
+        return s.substr(s.length - Math.max(length, numLength));
     }
     _getNodeByLevel(node, level) {
         let cur = node;

+ 40 - 64
app/middleware/stage_check.js

@@ -21,7 +21,7 @@ module.exports = options => {
      * 写入ctx.stage数据
      * 其中:
      * stage.auditors: 审批人列表(退回原报时,加载上一流程)
-     * stage.curAuditor: 当前审批人(未上报为空,审批通过 or 退回原报时,为空)
+     * stage.curAuditors: 当前审批人(未上报为空,审批通过 or 退回原报时,为空)
      * stage.readonly: 登录人,是否可操作
      * stage.curTimes: 当前登录人,操作、查阅数据times
      * stage.curOrder: 当前登录人,操作、查阅数据order
@@ -47,8 +47,7 @@ module.exports = options => {
             }
 
             // 读取原报、审核人数据
-            stage.auditors = yield this.service.stageAudit.getAuditors(stage.id, stage.times);
-            stage.curAuditor = yield this.service.stageAudit.getCurAuditor(stage.id, stage.times);
+            yield this.service.stage.loadStageUser(stage);
 
             // 历史台账
             if (stage.status === status.checked) {
@@ -63,31 +62,20 @@ module.exports = options => {
             stage.hadMaterial = materials.find(function(item) {
                 return item.s_order.split(',').indexOf(stage.highOrder.toString()) !== -1;
             });
+
             // 权限相关
             // todo 校验权限 (标段参与人、分享、游客)
-            const accountId = this.session.sessionUser.accountId,
-                auditorIds = _.map(stage.auditors, 'aid');
-            let auditAssists = yield this.service.stageAuditAss.getData(stage);
-            // 过滤无效的协审人
-            auditAssists = auditAssists.filter(x => {
-                return x.user_id === stage.user_id || auditorIds.indexOf(x.user_id) >= 0;
-            });
-            stage.userAssists = auditAssists.filter(x => { return x.user_id === stage.user_id; }); // 原报协同人
-            stage.auditAssists = auditAssists.filter(x => { return x.user_id !== stage.user_id; }); // 审批协同人
-            const userAssistIds = _.map(stage.userAssists, 'ass_user_id'),
-                auditAssistIds = _.map(stage.auditAssists, 'ass_user_id'),
-                shareIds = [];
-            stage.users = stage.status === status.uncheck ? [stage.user_id, ...userAssistIds] : [stage.user_id, ...userAssistIds, ...auditorIds, ...auditAssistIds];
-            stage.relaAssists = auditAssists.filter(x => { return x.user_id === accountId });
+            const accountId = this.session.sessionUser.accountId;
+            const shareIds = [];
             if (stage.status === status.uncheck) {
-                stage.readOnly = accountId !== stage.user_id && userAssistIds.indexOf(accountId) < 0;
+                stage.readOnly = accountId !== stage.user_id && stage.userAssistIds.indexOf(accountId) < 0;
                 if (!stage.readOnly) {
                     stage.assist = stage.userAssists.find(x => { return x.ass_user_id === accountId; });
                 }
                 stage.curTimes = stage.times;
                 stage.curOrder = 0;
             } else if (stage.status === status.checkNo) {
-                stage.readOnly = accountId !== stage.user_id && userAssistIds.indexOf(accountId) < 0;
+                stage.readOnly = accountId !== stage.user_id && stage.userAssistIds.indexOf(accountId) < 0;
                 const checkNoAudit = yield this.service.stageAudit.getDataByCondition({
                     sid: stage.id, times: stage.times - 1, status: status.checkNo,
                 });
@@ -104,22 +92,26 @@ module.exports = options => {
                 stage.curTimes = stage.times;
                 stage.curOrder = _.max(_.map(stage.auditors, 'order'));
             } else {
-                const ass = stage.auditAssists.find(x => { return x.user_id === stage.curAuditor.aid && x.ass_user_id === accountId; });
-                stage.readOnly = stage.curAuditor.aid !== accountId && !ass;
+                const ass = stage.auditAssists.find(x => { return stage.flowAuditorIds.indexOf(x.user_id) >= 0 && x.ass_user_id === accountId; });
+                stage.readOnly = stage.flowAuditorIds.indexOf(accountId) < 0 && !ass;
                 stage.curTimes = stage.times;
                 if (!stage.readOnly) {
                     stage.assist = ass;
-                    stage.curOrder = stage.curAuditor.order;
+                    stage.curOrder = stage.curAuditors[0].order;
                 } else {
-                    stage.curOrder = stage.curAuditor.order - 1;
+                    stage.curOrder = stage.curAuditors[0].order - 1
+                }
+                if (!stage.readOnly) {
+                    stage.readOnly = !_.isEqual(stage.flowAuditorIds, stage.curAuditorIds);
+                    stage.canCheck = true;
                 }
             }
             if (stage.readOnly) {
-                stage.assist = accountId === stage.user_id || auditorIds.indexOf(accountId) >= 0 ? null : auditAssists.find(x => { return x.ass_user_id === accountId});
+                stage.assist = accountId === stage.user_id || stage.auditorIds.indexOf(accountId) >= 0 ? null : stage.auditAssists.find(x => { return x.ass_user_id === accountId});
             }
 
             const permission = this.session.sessionUser.permission;
-            if (stage.users.indexOf(accountId) >= 0 || this.session.sessionUser.is_admin) {
+            if (stage.userIds.indexOf(accountId) >= 0 || this.session.sessionUser.is_admin) {
                 stage.filePermission = true;
             } else {
                 if (shareIds.indexOf(accountId) !== -1 || (permission !== null && permission.tender !== undefined && permission.tender.indexOf('2') !== -1)) {// 分享人
@@ -128,64 +120,49 @@ module.exports = options => {
                     }
                     stage.filePermission = false;
                 } else if (this.tender.isTourist || this.session.sessionUser.is_admin) {
-                    stage.filePermission = this.tender.touristPermission.file || auditorIds.indexOf(accountId) !== -1;
+                    stage.filePermission = this.tender.touristPermission.file || stage.auditorIds.indexOf(accountId) !== -1;
                 } else {
                     throw '您无权查看该数据';
                 }
             }
+            // 判断stage流程可否撤回,是哪一种撤回
+            yield this.service.stage.doCheckStageCanCancel(stage);
 
-            // 获取当前审批人的上一个审批人,判断是否是当前登录人,并赋予撤回功能,(当审批人存在有审批过时,上一人不允许再撤回)
-            stage.cancancel = 0;
-            if (stage.status !== status.checked && stage.status !== status.uncheck) {
-                if (stage.status !== status.checkNo) {
-                    // 找出当前操作人上一个审批人,包括审批完成的和退回上一个审批人的,同时当前操作人为第一人时,就是则为原报
-                    const onAuditor = _.find(stage.auditors, function(item) {
-                        return item.aid === stage.curAuditor.aid && item.status === status.checking;
-                    });
-                    const preAudit = onAuditor.order !== 1 ? _.find(stage.auditors, { order: onAuditor.order - 1 }) : false;
-                    const preAid = preAudit ? (preAudit.status !== status.checkAgain ? preAudit.aid : false) : stage.user_id;// 已发起重审无法撤回
-                    if ((onAuditor.aid === preAid && preAudit.status === status.checkCancel) || preAudit.is_old === 1) {
-                        stage.cancancel = 0;// 不可以多次撤回
-                    } else if (preAid === accountId && preAid !== stage.user_id) {
-                        if (preAudit.status === status.checked) {
-                            stage.cancancel = 2;// 审批人撤回审批通过
-                        } else if (preAudit.status === status.checkNoPre) {
-                            stage.cancancel = 3;// 审批人撤回审批退回上一人
-                        }
-                        stage.preAudit = preAudit;
-                    } else if (preAid === accountId && preAid === stage.user_id) {
-                        stage.cancancel = 1;// 原报撤回
-                    }
-                } else {
-                    const lastAuditors = yield this.service.stageAudit.getAuditors(stage.id, stage.times - 1);
-                    const onAuditor = _.find(lastAuditors, { status: status.checkNo });
-                    if (onAuditor.aid === accountId) {
-                        stage.cancancel = 4;// 审批人撤回退回原报
-                    }
-                }
-            }
-
+            // 期是否台账修订中
             const lastRevise = yield this.service.ledgerRevise.getLastestRevise(this.tender.id);
             stage.revising = (lastRevise && lastRevise.status !== reviseStatus.checked) || false;
-            this.stage = stage;
             // 根据状态判断是否需要更新审批人列表
+            yield this.service.stage.doCheckStageCanCancel(stage);
+            this.stage = stage;
+
             if ((stage.status === status.uncheck || stage.status === status.checkNo) && this.tender.info.shenpi.stage !== shenpiConst.sp_status.sqspr) {
                 const shenpi_status = this.tender.info.shenpi.stage;
                 // 进一步比较审批流是否与审批流程设置的相同,不同则替换为固定审批流或固定的终审
                 const auditList = yield this.service.stageAudit.getAllDataByCondition({ where: { sid: stage.id, times: stage.times }, orders: [['order', 'asc']] });
-                const auditIdList = _.map(auditList, 'aid');
                 if (shenpi_status === shenpiConst.sp_status.gdspl) {
                     const shenpiList = yield this.service.shenpiAudit.getAllDataByCondition({ where: { tid: stage.tid, sp_type: shenpiConst.sp_type.stage, sp_status: shenpi_status } });
-                    const shenpiIdList = _.map(shenpiList, 'audit_id');
                     // 判断2个id数组是否相同,不同则删除原审批流,切换成固定的审批流
-                    if (!_.isEqual(auditIdList, shenpiIdList)) {
-                        yield this.service.stageAudit.updateNewAuditList(stage, 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.stageAudit.updateNewAuditList(stage, shenpiList);
+                        yield this.service.stage.loadStageUser(stage);
                     }
                 } else if (shenpi_status === shenpiConst.sp_status.gdzs) {
                     const shenpiInfo = yield this.service.shenpiAudit.getDataByCondition({ tid: stage.tid, sp_type: shenpiConst.sp_type.stage, sp_status: shenpi_status });
                     // 判断最后一个id是否与固定终审id相同,不同则删除原审批流中如果存在的id和添加终审
-                    if (shenpiInfo && shenpiInfo.audit_id !== _.last(auditIdList)) {
+                    const lastAuditors = auditList.filter(x => { x.order === auditList.order; });
+                    if (shenpiInfo && (lastAuditors.length === 0 || (lastAuditors.length > 1 || shenpiInfo.audit_id !== lastAuditors[0].aid))) {
                         yield this.service.stageAudit.updateLastAudit(stage, auditList, shenpiInfo.audit_id);
+                        yield this.service.stage.loadStageUser(stage);
                     } else if (!shenpiInfo) {
                         // 不存在终审人的状态下这里恢复为授权审批人
                         this.tender.info.shenpi.stage = shenpiConst.sp_status.sqspr;
@@ -194,7 +171,6 @@ module.exports = options => {
             }
             yield next;
         } catch (err) {
-            console.log(err);
             this.helper.log(err);
             // 输出错误到日志
             if (err.stack) {

BIN
app/public/css/bg_participate_blue2.png


BIN
app/public/css/bg_participate_orange2.png


+ 57 - 2
app/public/css/main.css

@@ -1940,17 +1940,72 @@ overflow-y: auto;
 .canyu-width{
   height: 98px;
 }
+.canyu-pill{
+  height: 72px;
+  border-radius: 40px;
+}
+.canyu-pill .icon{
+  width: 40px;
+  height:40px;
+  line-height: 40px;
+  text-align: center;
+  border-radius: 40px;
+  background: #fff;
+  display: inline-block;
+  float: left;
+  font-size:24px;
+}
+.canyu-pill h5{
+  margin-left:50px;
+  margin-bottom: 4px
+}
+.canyu-pill h6{
+  margin-left:50px;
+  font-size: 12px
+}
 .canyu-bg-blue{
-  background: url(bg_participate_blue.png) no-repeat;
+  background: url(bg_participate_blue.png) no-repeat ;
   background-size: 100% 100%;
 }
 .canyu-bg-yellow{
-  background: url(bg_participate_orange.png) no-repeat;
+  background: url(bg_participate_orange.png) no-repeat ;
+  background-size: 100% 100%;
+}
+.canyu-pill.canyu-bg-blue{
+  background: url(bg_participate_blue2.png) no-repeat ;
+  background-size: 100% 100%;
+}
+.canyu-pill.canyu-bg-yellow{
+   background: url(bg_participate_orange2.png) no-repeat ;
   background-size: 100% 100%;
 }
+.canyu-bg-yellow .icon{
+  color:#ff8033;
+}
+.canyu-bg-blue .icon{
+  color:#009DFF;
+}
 .canyu-text{
   font-size: 36px;
 }
+.canyu-band{
+  height: 100%;
+  padding-top: 5%
+}
+.canyu-band h1{
+  text-align: center;
+  font-size:72px;
+}
+.canyu-band h3{
+  text-align: center;
+  font-size:18px;
+}
+.canyu-band.text-success{
+  background:linear-gradient(#fff 30%, #28a745 350%);
+}
+.canyu-band.text-danger{
+  background:linear-gradient(#fff 30%, #dc3545 350%);
+}
 .list-text-vertical{
   overflow:hidden;
   text-overflow:ellipsis;

+ 3 - 2
app/public/js/budget_compare.js

@@ -27,7 +27,7 @@ $(document).ready(() => {
             {title: '投资估算|数量1/数量2', colSpan: '3|1', rowSpan: '1|1', field: 'gu_dgn_qty', hAlign: 2, width: 80, bc_type: 'number'},
             {title: '|经济指标', colSpan: '|1', rowSpan: '|1', field: 'gu_dgn_price', hAlign: 2, width: 80, type: 'Number', bc_type: 'number'},
             {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'gu_tp', hAlign: 2, width: 80, type: 'Number', bc_type: 'number'},
-            {title: '初步概算|数量1/数量2', colSpan: '3|1', rowSpan: '1|1', field: 'gai_dgn_qty', hAlign: 2, width: 80, bc_type: 'number'},
+            {title: '设计概算|数量1/数量2', colSpan: '3|1', rowSpan: '1|1', field: 'gai_dgn_qty', hAlign: 2, width: 80, bc_type: 'number'},
             {title: '|经济指标', colSpan: '|1', rowSpan: '|1', field: 'gai_dgn_price', hAlign: 2, width: 80, type: 'Number', bc_type: 'number'},
             {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'gai_tp', hAlign: 2, width: 80, type: 'Number', bc_type: 'number'},
             {title: '施工图预算|数量1/数量2', colSpan: '3|1', rowSpan: '1|1', field: 'yu_dgn_qty', hAlign: 2, width: 80, bc_type: 'number'},
@@ -187,6 +187,7 @@ $(document).ready(() => {
             const expandTag = getLocalCache('revise-compare-level');
             if (expandTag) compareObj.expand(compareTree, expandTag);
             this.calcStackedBar(compareTree);
+            console.log(compareTree);
             SpreadJsObj.loadSheetData(compareSheet, SpreadJsObj.DataType.Tree, compareTree);
         },
         loadFinalData(result, msg) {
@@ -443,4 +444,4 @@ $(document).ready(() => {
             }
         });
     });
-});
+});

+ 400 - 0
app/public/js/budget_info.js

@@ -0,0 +1,400 @@
+'use strict';
+
+/**
+ *
+ *
+ * @author Mai
+ * @date
+ * @version
+ */
+
+$(document).ready(() => {
+    autoFlashHeight();
+    const compareObj = {
+        curFinalId() {
+            return this.finalInfo ? this.finalInfo.id : undefined;
+        },
+        expand(tree, tag) {
+            switch (tag) {
+                case "1":
+                case "2":
+                case "3":
+                case "4":
+                case "5":
+                    tree.expandByLevel(parseInt(tag));
+                    break;
+                case "last":
+                    tree.expandByCustom(() => { return true; });
+                    break;
+            }
+        },
+        calcStackedBar(tree) {
+            const calcField = this.stackedBarField;
+            const calcFieldColor = { 'gu_tp': '#657798', 'gai_tp': '#EE6666', 'yu_tp': '#74CBED', 'total_price': '#FAC858', 'final_tp': '#62DAAB' };
+            const calcFieldCaption = { 'gu_tp': '估算', 'gai_tp': '概算', 'yu_tp': '预算', 'total_price': '台账', 'final_tp': '决算' };
+            const calc = function(node, base){
+                // const parent = tree.getParent(node);
+                // if (!parent) {
+                //     base = 0;
+                //     for (const cf of calcField) {
+                //         base = Math.max(node[cf], base);
+                //     }
+                // }
+                node.stackedBar = [];
+                node.stackedBarTips = [];
+                for (const cf of calcField) {
+                    node.stackedBar.push({color: calcFieldColor[cf], percent: ZhCalc.div(node[cf], base), field: cf});
+                    node.stackedBarTips.push(`${calcFieldCaption[cf]}: ${node[cf] || 0}`);
+                }
+                if (node.children) {
+                    for (const child of node.children) {
+                        calc(child, base);
+                    }
+                }
+            };
+            let commonBase = 0;
+            tree.children.forEach(x => {
+                for (const cf of calcField) {
+                    commonBase = Math.max(x[cf] || 0, commonBase);
+                }
+            });
+            for (const child of tree.children) {
+                calc(child, commonBase);
+            }
+        },
+        loadBudgetData(result) {
+            const compareTree = createNewPathTree('final', {
+                id: 'id',
+                pid: 'pid',
+                order: 'order',
+                level: 'level',
+                rootId: -1,
+            });
+            const setting = { id: 'tree_id', pid: 'tree_pid', order: 'order', level: 'level', rootId: -1, calcFields: ['total_price'] };
+            const guTree = createNewPathTree('ledger', setting);
+            guTree.loadDatas(result.gu);
+            treeCalc.calculateAll(guTree);
+            compareTree.loadTree(guTree, function (cur, source) {
+                cur.base = true;
+                cur.gu_dgn_qty1 = ZhCalc.add(cur.gu_dgn_qty1, source.dgn_qty1);
+                cur.gu_dgn_qty2 = ZhCalc.add(cur.gu_dgn_qty2, source.dgn_qty2);
+                cur.gu_tp = ZhCalc.add(cur.gu_tp, source.total_price);
+            });
+            const gaiTree = createNewPathTree('ledger', setting);
+            gaiTree.loadDatas(result.gai);
+            treeCalc.calculateAll(gaiTree);
+            compareTree.loadTree(gaiTree, function (cur, source) {
+                cur.base = true;
+                cur.gai_dgn_qty1 = ZhCalc.add(cur.gai_dgn_qty1, source.dgn_qty1);
+                cur.gai_dgn_qty2 = ZhCalc.add(cur.gai_dgn_qty2, source.dgn_qty2);
+                cur.gai_tp = ZhCalc.add(cur.gai_tp, source.total_price);
+            });
+            const yuTree = createNewPathTree('ledger', setting);
+            yuTree.loadDatas(result.yu);
+            treeCalc.calculateAll(yuTree);
+            compareTree.loadTree(yuTree, function (cur, source) {
+                cur.base = true;
+                cur.yu_dgn_qty1 = ZhCalc.add(cur.yu_dgn_qty1, source.dgn_qty1);
+                cur.yu_dgn_qty2 = ZhCalc.add(cur.yu_dgn_qty2, source.dgn_qty2);
+                cur.yu_tp = ZhCalc.add(cur.yu_tp, source.total_price);
+            });
+            compareTree.afterLoad(node => {
+                node.gu_dgn_price = ZhCalc.div(node.gu_tp, node.gu_dgn_qty1, 2);
+                node.gu_dgn_qty = node.gu_dgn_qty1
+                    ? (node.gu_dgn_qty2 ? node.gu_dgn_qty1 + '/' + node.gu_dgn_qty2 : node.gu_dgn_qty1)
+                    : (node.gu_dgn_qty2 ? '/' + node.gu_dgn_qty2 : '');
+                node.gai_dgn_price = ZhCalc.div(node.gai_tp, node.gai_dgn_qty1, 2);
+                node.gai_dgn_qty = node.gai_dgn_qty1
+                    ? (node.gai_dgn_qty2 ? node.gai_dgn_qty1 + '/' + node.gai_dgn_qty2 : node.gai_dgn_qty1)
+                    : (node.gai_dgn_qty2 ? '/' + node.gai_dgn_qty2 : '');
+                node.yu_dgn_price = ZhCalc.div(node.yu_tp, node.yu_dgn_qty1, 2);
+                node.yu_dgn_qty = node.yu_dgn_qty1
+                    ? (node.yu_dgn_qty2 ? node.yu_dgn_qty1 + '/' + node.yu_dgn_qty2 : node.yu_dgn_qty1)
+                    : (node.yu_dgn_qty2 ? '/' + node.yu_dgn_qty2 : '');
+            });
+            compareTree.resortChildrenByCustom(function (x, y) {
+                const iCode = compareCode(x.code, y.code);
+                if (iCode) return iCode;
+                if (!x.name) return -1;
+                if (!y.name) return 1;
+                return x.name.localeCompare(y.name);
+            });
+            const expandTag = getLocalCache('revise-compare-level');
+            if (expandTag) compareObj.expand(compareTree, expandTag);
+            this.calcStackedBar(compareTree);
+            // console.log(compareTree);
+            setPageData(compareTree);
+        },
+        loadFinalData(result, msg) {
+            if (msg) toastr.warning(msg);
+            this.finalInfo = result.finalInfo;
+            const finalTree = createNewPathTree('ledger', {
+                id: 'tree_id',
+                pid: 'tree_pid',
+                order: 'order',
+                level: 'level',
+                rootId: -1,
+            });
+            finalTree.loadDatas(result.final);
+            const expandTag = getLocalCache('revise-compare-level');
+            if (expandTag) compareObj.expand(finalTree, expandTag);
+            this.calcStackedBar(finalTree);
+            // console.log(finalTree);
+            setPageData(finalTree);
+
+        },
+        loadCacheData(){
+            const stackedBarCache = 'gai_tp,total_price,final_tp';
+            this.setStackedBarField(stackedBarCache.split(','));
+        },
+        setStackedBarField(field){
+            this.stackedBarField = field;
+        },
+    };
+    compareObj.loadCacheData();
+
+    function compareCode(str1, str2, symbol = '-') {
+        if (!str1) {
+            return 1;
+        } else if (!str2) {
+            return -1;
+        }
+
+        function compareSubCode(code1, code2) {
+            if (numReg.test(code1)) {
+                if (numReg.test(code2)) {
+                    return parseInt(code1) - parseInt(code2);
+                } else {
+                    return -1
+                }
+            } else {
+                if (numReg.test(code2)) {
+                    return 1;
+                } else {
+                    return code1 === code2 ? 0 : (code1 < code2 ? -1 : 1); //code1.localeCompare(code2);
+                }
+            }
+        }
+        const numReg = /^[0-9]+$/;
+        const aCodes = str1.split(symbol), bCodes = str2.split(symbol);
+        for (let i = 0, iLength = Math.min(aCodes.length, bCodes.length); i < iLength; ++i) {
+            const iCompare = compareSubCode(aCodes[i], bCodes[i]);
+            if (iCompare !== 0) {
+                return iCompare;
+            }
+        }
+        return aCodes.length - bCodes.length;
+    }
+
+    postData(window.location.pathname + '/compare/load', {}, function (result, msg) {
+        if (result.final) {
+            compareObj.loadFinalData(result, msg);
+        } else {
+            compareObj.loadBudgetData(result);
+        }
+    });
+
+    //金额对比
+    var chartDom = document.getElementById('jlchart2');
+    var myChart = echarts.init(chartDom);
+    var option = {
+        tooltip: {
+            trigger: 'axis',
+        },
+        grid: {
+            left: '3%',
+            right: '3%',
+            bottom: '4%',
+            containLabel: true
+        },
+        xAxis: {
+            type: 'category',
+            data: ['投资估算', '设计概算', '施工图预算', '台账', '变更后台账', '决算']
+        },
+        yAxis: {
+            type: 'value'
+        },
+        series: [
+            {
+                data: [0, 0, 0, 0, 0, 0],
+                type: 'bar',
+                showBackground: true,
+                backgroundStyle: {
+                    color: 'rgba(180, 180, 180, 0.2)'
+                },
+                itemStyle:{
+                    borderRadius: [30, 30, 0, 0]
+                },
+                barWidth : 30
+            }
+        ],
+
+        color:{
+            type: 'linear',
+            x: 0,
+            y: 0,
+            x2: 0,
+            y2: 1,
+            colorStops: [{
+                offset: 0, color: '#3A6FB5' // 0% 处的颜色
+            }, {
+                offset: 1, color: '#44BEE3' // 100% 处的颜色
+            }],
+            global: false // 缺省为 false
+        }
+    };
+
+    //全过程造价趋势
+    var chartDom2 = document.getElementById('jlchart3');
+    var myChart2 = echarts.init(chartDom2);
+    var option2 = {
+        title: {
+            text: ''
+        },
+        tooltip: {
+            trigger: 'axis'
+        },
+        legend: {
+        },
+        grid: {
+            left: '3%',
+            right: '5%',
+            bottom: '3%',
+            containLabel: true
+        },
+        xAxis: {
+            type: 'category',
+            boundaryGap: false,
+            data: ['投资估算', '设计概算', '施工图预算', '台账金额', '决算金额']
+        },
+        yAxis: {
+            type: 'value'
+        },
+        series: []
+    };
+
+    function setPageData(tree) {
+        console.log(tree);
+        if (tree.children.length > 0) {
+            const level2List = tree.children[0].children;
+            if (level2List.length > 0) {
+                let jianAnHtml = '';
+                for (const level2 of level2List) {
+                    jianAnHtml += `<tr>
+                                            <td class="text-left pl-3">${level2.name}</td>
+                                            <td>${level2.gai_tp ? ZhCalc.div(level2.gai_tp, 10000) : 0}</td>
+                                            <td>${level2.final_tp ? ZhCalc.div(level2.final_tp, 10000) : 0}</td>
+                                            <td class="${ZhCalc.sub(level2.final_tp, level2.gai_tp) > 0 ? 'text-danger' : ZhCalc.sub(level2.final_tp, level2.gai_tp) === 0 ? '' : 'text-success'}">${ZhCalc.div(ZhCalc.sub(level2.final_tp, level2.gai_tp), 10000)}</td>
+                                        </tr>`;
+                }
+                $('#jianan-table').html(jianAnHtml);
+            }
+            // 分析第一层结构,获取各个部分金额非全0的及除了回收金额层
+            const level1List = _.filter(tree.children, function (item) {
+                return item.name.indexOf('回收金额') === -1 &&
+                    (item.gu_tp || item.gai_tp || (item.final_tp !== undefined && item.final_tp) || item.yu_tp || (item.total_price !== undefined && item.total_price))
+            });
+            // 回收金额
+            const huishouInfo = _.find(tree.children, function (item) {
+                return item.name.indexOf('回收金额') !== -1 && (item.gu_tp || item.gai_tp || (item.final_tp !== undefined && item.final_tp) || item.yu_tp || (item.total_price !== undefined && item.total_price))
+            });
+            let total_rate = 0, total_gai_tp = 0, total_gu_tp = 0, total_yu_tp = 0, total_price = 0, total_final_tp = 0;
+            if (level1List.length > 0) {
+                if (huishouInfo) {
+                    total_gai_tp = ZhCalc.sub(ZhCalc.sum(_.map(level1List, 'gai_tp')), huishouInfo.gai_tp);
+                    total_final_tp = ZhCalc.sub(ZhCalc.sum(_.map(level1List, 'final_tp')), huishouInfo.final_tp);
+                    total_gu_tp = ZhCalc.sub(ZhCalc.sum(_.map(level1List, 'gu_tp')), huishouInfo.gu_tp);
+                    total_yu_tp = ZhCalc.sub(ZhCalc.sum(_.map(level1List, 'yu_tp')), huishouInfo.yu_tp);
+                    total_price = ZhCalc.sub(ZhCalc.sum(_.map(level1List, 'total_price')), huishouInfo.total_price);
+                    total_rate = ZhCalc.div(ZhCalc.sub(total_final_tp, total_gai_tp), total_gai_tp);
+                    $('#total_gai_tp').text(total_gai_tp ? ZhCalc.div(total_gai_tp, 10000) : 0);
+                    $('#total_final_tp').text(total_final_tp ? ZhCalc.div(total_final_tp, 10000) : 0);
+                    // level1List.push(huishouInfo);
+                } else {
+                    total_gai_tp = ZhCalc.sum(_.map(level1List, 'gai_tp'));
+                    total_final_tp = ZhCalc.sum(_.map(level1List, 'final_tp'));
+                    total_gu_tp = ZhCalc.sum(_.map(level1List, 'gu_tp'));
+                    total_yu_tp = ZhCalc.sum(_.map(level1List, 'yu_tp'));
+                    total_price = ZhCalc.sum(_.map(level1List, 'total_price'));
+                    total_rate = ZhCalc.div(ZhCalc.sub(total_final_tp, total_gai_tp), total_gai_tp);
+                    $('#total_gai_tp').text(total_gai_tp ? ZhCalc.div(total_gai_tp, 10000) : 0);
+                    $('#total_final_tp').text(total_final_tp ? ZhCalc.div(total_final_tp, 10000) : 0);
+                }
+            }
+            $('#total_rate').text((total_rate ? ZhCalc.round(ZhCalc.mul(total_rate,100), 2) : 0) + '%');
+            if (total_rate > 0) {
+                $('#total_rate').parents('.canyu-band').removeClass('text-success').addClass('text-danger');
+            } else if (total_rate < 0) {
+                $('#total_rate').parents('.canyu-band').removeClass('text-danger').addClass('text-success');
+            } else if (total_rate === 0) {
+                $('#total_rate').parents('.canyu-band').removeClass('text-success').removeClass('text-danger');
+            }
+            console.log(level1List);
+            option.series[0].data = [
+                ZhCalc.div(total_gu_tp, 10000),
+                ZhCalc.div(total_gai_tp, 10000),
+                ZhCalc.div(total_yu_tp, 10000),
+                ZhCalc.div(total_price, 10000),
+                ZhCalc.div(ZhCalc.add(total_change_tp, total_price), 10000),
+                ZhCalc.div(total_final_tp, 10000),
+            ];
+            if (huishouInfo) level1List.push(huishouInfo);
+            option2.legend.data = _.map(level1List, 'name');
+            for (const level1 of level1List) {
+                option2.series.push({
+                    name: level1.name,
+                    type: 'line',
+                    stack: 'Total',
+                    data: [ZhCalc.div(level1.gu_tp, 10000), ZhCalc.div(level1.gai_tp, 10000),
+                        ZhCalc.div(level1.yu_tp, 10000), ZhCalc.div(level1.total_price, 10000),
+                        ZhCalc.div(level1.final_tp, 10000)
+                    ]
+                });
+            }
+        }
+        myChart.setOption(option);
+        myChart2.setOption(option2);
+    }
+
+    let resizeTimer = null;
+    $(window).bind('resize', function () {
+        if (resizeTimer) clearTimeout(resizeTimer);
+        resizeTimer = setTimeout(function () {
+            echartsReset();
+        }, 500);
+    });
+    function echartsReset() {
+        myChart.resize();
+        myChart2.resize();
+    }
+
+    function setDashboardHeight() {
+        function getObjHeight(select) {
+            return select.length > 0 ? select.outerHeight(true) : 0;
+        }
+        $('.dashboard-height').height($(window).height() - 34 - 16);
+        $('.agency-partheight').height($('.dashboard-height').height()/2);
+        $('.contant-height-one').height($('.agency-partheight').height() - 52 - 20);
+        $('.contant-height-two').height($('.agency-partheight').height() - 52 - getObjHeight($(".echart-height")) - 20);
+        // $('.echart-height').width(parseInt($(".echart-height").width()));
+    }
+    setDashboardHeight();
+    $(window).resize(setDashboardHeight);
+
+    $.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();
+        }
+    });
+});

+ 2 - 2
app/public/js/budget_list.js

@@ -38,7 +38,7 @@ $(document).ready(() => {
                     }
                 } else {
                     html.push(`<span class="text-muted mr-2">${tree.isLastSibling(node) ? '└' : '├'}</span>`);
-                    html.push(`<a href="/budget/${node.budget_id}/compare" name="name" id="${node.id}">`, node.name, '</a>');
+                    html.push(`<a href="/budget/${node.budget_id}" name="name" id="${node.id}">`, node.name, '</a>');
                 }
                 html.push('</td>');
                 // 概预算标准
@@ -188,4 +188,4 @@ $(document).ready(() => {
         if (!srSelect) srSelect = new srObject();
         srSelect.init();
     });
-});
+});

+ 28 - 0
app/public/js/file_detail.js

@@ -232,6 +232,27 @@ $(document).ready(function() {
                 if (callback) callback();
             });
         }
+        uploadBigFile(file, callback) {
+            if (file.size > 500 * 1024 * 1024) {
+                toastr.error('上传文件大小超过500MB。');
+                return false;
+            }
+            const fileext = '.' + file.name.toLowerCase().split('.').splice(-1)[0];
+            if (whiteList.indexOf(fileext) === -1) {
+                toastr.error('仅支持office文档、图片、压缩包格式,请勿上传' + fileext + '格式文件。');
+                return false;
+            }
+            AliOss.uploadBigFile(file, 'file/upload/big', { filing_id: filingObj.curFiling.id },
+                { progressObj: $('#upload-big-file-progress'), resumeObj: $('#add-big-file-resume'), stopObj: $('#add-big-file-stop') },
+                function(result) {
+                    filingObj.curFiling.source_node.files.unshift(...result.files);
+                    filingObj.updateFilingFileCount(filingObj.curFiling, result.filing.file_count);
+                    filingObj.refreshFilesTable();
+                    filingObj.refreshPages();
+                    if (callback) callback();
+            });
+
+        }
         delFiles(files, callback) {
             postData('file/del', { del: files }, async function(data) {
                 for (const id of data.del) {
@@ -515,6 +536,13 @@ $(document).ready(function() {
             $('#add-file').modal('hide');
         });
     });
+    $('#add-big-file-ok').click(() => {
+        const input = $('#upload-big-file');
+        filingObj.uploadBigFile(input[0].files[0], function () {
+            $('#upload-big-file').val('');
+            $('#add-big-file').modal('hide');
+        });
+    });
     $('body').on('mouseenter', ".table-file", function(){
         $(this).children(".btn-group-table").css("display","block");
     });

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

@@ -1190,7 +1190,7 @@ function hpackArrBack(data) {
 const spreadColor = {
     stage: {
         over: '#FF9595', //'#ff6f5c', '#f8d7da'
-        differ: '#F2DEDE',
+        differ: '#FF9595', // '#F2DEDE',
     },
     gcl: {
         differ: '#FFE699', // 同编号时,名称、单位、单价存在不同

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

@@ -3939,3 +3939,9 @@ function checkAuditorFrom () {
         return true;
     }
 }
+// texterea换行
+function auditCheck(i) {
+    const opinion = $('textarea[name="opinion"]').eq(i).val().replace(/\r\n/g, '<br/>').replace(/\n/g, '<br/>').replace(/\s/g, ' ');
+    $('textarea[name="opinion"]').eq(i).val(opinion);
+    return true;
+}

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

@@ -1,313 +0,0 @@
-'use strict';
-
-/**
- *
- *
- * @author Mai
- * @date 2018/5/30
- * @version
- */
-
-const ckBillsSpread = window.location.pathname + '-billsSelect';
-function getTenderId() {
-    return window.location.pathname.split('/')[2];
-}
-
-function checkTzMeasureType () {
-    return tender.measure_type === measureType.tz.value;
-}
-
-$(document).ready(() => {
-    autoFlashHeight();
-    const ledgerSpread = SpreadJsObj.createNewSpread($('#ledger-spread')[0]);
-    const treeSetting = {
-        id: 'ledger_id',
-        pid: 'ledger_pid',
-        order: 'order',
-        level: 'level',
-        rootId: -1,
-        keys: ['id', 'tender_id', 'ledger_id'],
-        preUrl: '/ledger',
-        autoExpand: 3,
-        // markFoldKey: 'bills-fold',
-        // markFoldSubKey: window.location.pathname.split('/')[2],
-        markExpandKey: 'bills-expand',
-        markExpandSubKey: window.location.pathname.split('/')[2],
-    };
-    if (checkTzMeasureType()) {
-        treeSetting.calcFields = ['sgfh_tp', 'sjcl_tp', 'qtcl_tp', 'total_price'];
-    } else {
-        treeSetting.calcFields = ['deal_tp', 'sgfh_tp', 'sjcl_tp', 'qtcl_tp', 'total_price'];
-    }
-    treeSetting.calcFun = function (node) {
-        node.dgn_price = ZhCalc.round(ZhCalc.div(node.total_price, node.dgn_qty1), 2);
-    };
-    const ledgerTree = createNewPathTree('fx', treeSetting);
-    sjsSettingObj.setFxTreeStyle(ledgerSpreadSetting, sjsSettingObj.FxTreeStyle.jz);
-    if (thousandth) sjsSettingObj.setTpThousandthFormat(ledgerSpreadSetting);
-    sjsSettingObj.setNodeTypeCol(ledgerSpreadSetting.cols, [{field: 'node_type'}]);
-    SpreadJsObj.initSheet(ledgerSpread.getActiveSheet(), ledgerSpreadSetting);
-
-    // 初始化 部位明细
-    const pos = new PosData({
-        id: 'id', ledgerId: 'lid',
-    });
-    const posSpread = SpreadJsObj.createNewSpread($('#pos-spread')[0]);
-
-    const posSearch = $.posSearch({selector: '#pos-search', searchSpread: posSpread});
-    $.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();
-            ledgerSpread.refresh();
-            posSpread.refresh();
-        }
-    });
-
-    const loadCurPosData = function () {
-        const node = SpreadJsObj.getSelectObject(ledgerSpread.getActiveSheet());
-        SpreadJsObj.resetTopAndSelect(posSpread.getActiveSheet());
-        if (node) {
-            const posData = pos.ledgerPos[itemsPre + node.id] || [];
-            SpreadJsObj.loadSheetData(posSpread.getActiveSheet(), 'data', posData);
-        } else {
-            SpreadJsObj.loadSheetData(posSpread.getActiveSheet(), 'data', []);
-        }
-    };
-    // 台账模式加载部位明细数据
-    if (checkTzMeasureType()) {
-        $.divResizer({
-            select: '#main-resize',
-            callback: function () {
-                ledgerSpread.refresh();
-                let bcontent = $(".bcontent-wrap") ? $(".bcontent-wrap").height() : 0;
-                $(".sp-wrap").height(bcontent-30);
-                posSpread.refresh();
-            }
-        });
-        sjsSettingObj.setGridSelectStyle(posSpreadSetting);
-        SpreadJsObj.initSheet(posSpread.getActiveSheet(), posSpreadSetting);
-    }
-
-    $.contextMenu({
-        selector: '#ledger-spread',
-        build: function ($trigger, e) {
-            const target = SpreadJsObj.safeRightClickSelection($trigger, e, ledgerSpread);
-            return target.hitTestType === spreadNS.SheetArea.viewport || target.hitTestType === spreadNS.SheetArea.rowHeader;
-        },
-        items: {
-            showLast: {
-                name: '显示至最底层',
-                callback: function (key, opt, menu, e) {
-                    const node = SpreadJsObj.getSelectObject(ledgerSpread.getActiveSheet());
-                    setTimeout(() => {
-                        showWaitingView();
-                        ledgerTree.expandByCustom(x => {
-                            return x.expanded || (x.id === node.id) || (x.full_path.indexOf(node.ledger_id + '-') >= 0);
-                        });
-                        SpreadJsObj.refreshTreeRowVisible(ledgerSpread.getActiveSheet());
-                        closeWaitingView();
-                    }, 100);
-                },
-            },
-        }
-    });
-
-    postData('/tender/' + getTenderId() + '/ledger/load', null, function (data) {
-        ledgerTree.loadDatas(data.bills);
-        treeCalc.calculateAll(ledgerTree);
-        SpreadJsObj.loadSheetData(ledgerSpread.getActiveSheet(), 'tree', ledgerTree);
-        SpreadJsObj.loadTopAndSelect(ledgerSpread.getActiveSheet(), ckBillsSpread);
-
-        checkShowLast(data.bills.length);
-
-        pos.loadDatas(data.pos);
-        loadCurPosData();
-        SpreadJsObj.resetTopAndSelect(posSpread.getActiveSheet());
-    }, null, true);
-
-    ledgerSpread.bind(spreadNS.Events.SelectionChanged, function (e, info) {
-        loadCurPosData();
-        SpreadJsObj.saveTopAndSelect(info.sheet, ckBillsSpread);
-        posSearch.search($('#pos-keyword').val());
-    });
-    ledgerSpread.bind(spreadNS.Events.TopRowChanged, function (e, info) {
-        SpreadJsObj.saveTopAndSelect(info.sheet, ckBillsSpread);
-    });
-
-    let dealBills, searchLedger;
-    $.divResizer({
-        select: '#right-spr',
-        callback: function () {
-            ledgerSpread.refresh();
-            if (posSpread) {
-                posSpread.refresh();
-            }
-            if (dealBills) {
-                dealBills.spread.refresh();
-            }
-        }
-    });
-    // 展开收起 签约清单|清单意见
-    $('a', '.side-menu').bind('click', function () {
-        const tab = $(this), tabPanel = $(tab.attr('content'));
-        const showSideTools = function (show) {
-            const left = $('#left-view'), right = $('#right-view'), parent = left.parent();
-            if (show) {
-                right.show();
-                autoFlashHeight();
-                /**
-                 * right.show()后, parent被撑开成2倍left.height, 导致parent.width减少了10px
-                 * 第一次left.width调整后,parent的缩回left.height, 此时parent.width又增加了10px
-                 * 故需要通过最终的parent.width再计算一次left.width
-                 *
-                 * Q: 为什么不通过先计算left.width的宽度,以避免计算两次left.width?
-                 * A: 右侧工具栏不一定显示,当右侧工具栏显示过一次后,就必须使用parent和right来计算left.width
-                 *
-                 */
-                // left.css('width', parent.width() - right.outerWidth());
-                // left.css('width', parent.width() - right.outerWidth());
-                const percent = 100 - right.outerWidth() /parent.width() * 100;
-                left.css('width', percent + '%');
-            } else {
-                left.width(parent.width());
-                right.hide();
-            }
-        };
-        if (!tab.hasClass('active')) {
-            $('a', '.side-menu').removeClass('active');
-            tab.addClass('active');
-            $('.tab-content .tab-pane').removeClass('active');
-            tabPanel.addClass('active');
-            showSideTools(tab.hasClass('active'));
-            if (tab.attr('content') === '#deal-bills' && !dealBills) {
-                dealBills = new DealBills($('#deal-bills-spread')[0], {
-                    cols: [
-                        {title: '清单编号', field: 'code', width: 85, formatter: '@', readOnly: true},
-                        {title: '名称', field: 'name', width: 150, formatter: '@', readOnly: true},
-                        {title: '单位', field: 'unit', width: 50, formatter: '@', readOnly: true},
-                        {title: '单价', field: 'unit_price', width: 50, readOnly: true},
-                        {title: '数量', field: 'quantity', width: 50, readOnly: true},
-                        {title: '金額', field: 'total_price', width: 50, readOnly: true},
-                    ],
-                    emptyRows: 0,
-                    headRows: 1,
-                    headRowHeight: [32],
-                    defaultRowHeight: 21,
-                    headerFont: '12px 微软雅黑',
-                    font: '12px 微软雅黑',
-                    headColWidth: [30],
-                    selectedBackColor: '#fffacd',
-                });
-                dealBills.loadData();
-            } else if (tab.attr('content') === '#search' && !searchLedger) {
-                if (!searchLedger) {
-                    searchLedger = $.billsSearch({
-                        selector: '#search',
-                        searchSpread: ledgerSpread,
-                        resultSpreadSetting: {
-                            cols: [
-                                {title: '项目节编号', field: 'code', hAlign: 0, width: 90, formatter: '@'},
-                                {title: '清单编号', field: 'b_code', hAlign: 0, width: 80, formatter: '@'},
-                                {title: '名称', field: 'name', width: 150, hAlign: 0, formatter: '@'},
-                                {title: '单位', field: 'unit', width: 50, hAlign: 1, formatter: '@'},
-                                {title: '单价', field: 'unit_price', hAlign: 2, width: 50},
-                                {title: '数量', field: 'quantity', hAlign: 2, width: 50},
-                                {title: '部位', field: 'bw', hAlign: 2, width: 90},
-                            ],
-                            emptyRows: 0,
-                            headRows: 1,
-                            headRowHeight: [32],
-                            headColWidth: [30],
-                            defaultRowHeight: 21,
-                            headerFont: '12px 微软雅黑',
-                            font: '12px 微软雅黑',
-                            selectedBackColor: '#fffacd',
-                            readOnly: true
-                        },
-                        afterLocated: function () {
-                            loadCurPosData();
-                        },
-                        calcSum: function (result) {
-                            const sum = { name: '合计' };
-                            for (const r of result) {
-                                sum.quantity = ZhCalc.add(r.quantity, sum.quantity);
-                            }
-                            return sum;
-                        }
-                    });
-                }
-                searchLedger.spread.refresh();
-            }
-        } else {
-            tab.removeClass('active');
-            tabPanel.removeClass('active');
-            showSideTools(tab.hasClass('active'));
-        }
-        ledgerSpread.refresh();
-        if (posSpread) {
-            posSpread.refresh();
-        }
-    });
-    class DealBills {
-        constructor (obj, spreadSetting) {
-            this.obj = obj;
-            this.url = '/tender/' + getTenderId() + '/deal';
-            this.spreadSetting = spreadSetting;
-            this.spread = SpreadJsObj.createNewSpread(this.obj);
-            SpreadJsObj.initSheet(this.spread.getActiveSheet(), this.spreadSetting);
-        }
-        loadData () {
-            const self = this;
-            postData(this.url+'/get-data', {}, function (data) {
-                SpreadJsObj.loadSheetData(self.spread.getActiveSheet(), 'data', data);
-            });
-        }
-    }
-    // 显示层次
-    (function (select, sheet) {
-        $(select).click(function () {
-            if (!sheet.zh_tree) return;
-            const tag = $(this).attr('tag');
-            const tree = sheet.zh_tree;
-            setTimeout(() => {
-                showWaitingView();
-                switch (tag) {
-                    case "1":
-                    case "2":
-                    case "3":
-                    case "4":
-                    case "5":
-                        tree.expandByLevel(parseInt(tag));
-                        SpreadJsObj.refreshTreeRowVisible(sheet);
-                        break;
-                    case "last":
-                        tree.expandByCustom(() => { return true; });
-                        SpreadJsObj.refreshTreeRowVisible(sheet);
-                        break;
-                    case "leafXmj":
-                        tree.expandToLeafXmj();
-                        SpreadJsObj.refreshTreeRowVisible(sheet);
-                        break;
-                }
-                closeWaitingView();
-            }, 100);
-        });
-    })('a[name=showLevel]', ledgerSpread.getActiveSheet());
-});
-// texterea换行
-function auditCheck(i) {
-    const opinion = $('textarea[name="opinion"]').eq(i).val().replace(/\r\n/g, '<br/>').replace(/\n/g, '<br/>').replace(/\s/g, ' ');
-    $('textarea[name="opinion"]').eq(i).val(opinion);
-    return true;
-}

+ 14 - 11
app/public/js/material.js

@@ -1337,23 +1337,26 @@ $(document).ready(() => {
     function setCurBillSourceList(mb_id, ms_id = null) {
         const showSourceList = [];
         if (mb_id) {
-            const list = _.filter(materialListData2, { mb_id, ms_id });
+            const list = _.filter(materialListData2, { mb_id, ms_id, is_join: 1 });
             console.log(list);
             if (list.length > 0) {
                 for (const l of list) {
                     const gcl = _.find(gclGatherData, function (item) {
                         return item.leafXmjs && item.leafXmjs.length > 0 && _.findIndex(item.leafXmjs, { gcl_id: l.gcl_id }) !== -1;
                     });
-                    const index = _.findIndex(showSourceList, { code: gcl.b_code, name: gcl.name, unit: gcl.unit });
-                    if (gcl && index === -1) {
-                        showSourceList.push({
-                            code: gcl.b_code,
-                            name: gcl.name,
-                            unit: gcl.unit,
-                            quantity: ZhCalc.mul(l.gather_qty, l.quantity),
-                        });
-                    } else if (gcl && index !== -1) {
-                        showSourceList[index].quantity = ZhCalc.add(showSourceList[index].quantity, ZhCalc.mul(l.gather_qty, l.quantity));
+                    console.log(gcl);
+                    if (gcl) {
+                        const index = _.findIndex(showSourceList, { code: gcl.b_code, name: gcl.name, unit: gcl.unit });
+                        if (index === -1) {
+                            showSourceList.push({
+                                code: gcl.b_code,
+                                name: gcl.name,
+                                unit: gcl.unit,
+                                quantity: ZhCalc.mul(l.gather_qty, l.quantity),
+                            });
+                        } else {
+                            showSourceList[index].quantity = ZhCalc.add(showSourceList[index].quantity, ZhCalc.mul(l.gather_qty, l.quantity));
+                        }
                     }
                 }
             }

+ 123 - 115
app/public/js/measure_stage.js

@@ -7,6 +7,19 @@
  * @date 2018/12/7
  * @version
  */
+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 () {
@@ -14,135 +27,130 @@ $('a[data-target="#sp-list" ]').on('click', function () {
         order: $(this).attr('s-order'),
     };
     postData('/tender/' + tenderId + '/measure/stage/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) {
-                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 {
-                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').append(auditorsHTML);
-        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) => {
+                console.log(group);
                 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.beginYear}<span>${group.beginDate}</span><span>${group.beginTime}</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 {
-                    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>`;
-                    } 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]}">
-                                        ${auditor.status !== auditConst.status.uncheck ? auditConst.statusString[auditor.status] : ''}
-                                        ${auditor.status === auditConst.status.checkNo ? user.name : ''}
-                                        ${auditor.status === auditConst.status.checkNoPre && auditor.sort - 1 > 0 ? _.find(auditors, { sort: auditor.sort - 1 }).name : ''}
-                                    </span>
-                                </p>
-                                <p class="text-muted mb-0">${auditor.role}</p>
-                            </div>
-                        </div>`;
+                    historyHTML.push('<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.push('</div>');
                     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.push('</div></div>');
+                historyHTML.push('</div>');
+                historyHTML.push('</li>');
             });
-            historyHTML += '</ul></div>';
+            historyHTML.push('</div>');
+            historyHTML.push('</ul>');
         });
         $('#audit-list').empty();
-        $('#audit-list').append(historyHTML);
-    })
-
+        $('#audit-list').append(historyHTML.join(''));
+    });
 });
 
 

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

@@ -2067,10 +2067,10 @@ const treeCalc = {
             }
         }
     },
-    calculateParent: function (tree, node) {
+    calculateParent: function (tree, node, converse = -1) {
         const setting = tree.setting;
         const nodes = tree.getFullPathNodes(node[setting.fullPath]);
-        tree.sortByLevel(nodes);
+        converse === -1 ? tree.sortByLevel(nodes) : tree.sortByLevelConverse(nodes);
         for (const n of nodes) {
             this.calculateNode(tree, n);
         }

+ 70 - 0
app/public/js/payment_list.js

@@ -418,6 +418,7 @@ $(function () {
                     property.deal_info = result.tender_info.deal_info;
                     property.construction_unit = result.tender_info.construction_unit;
                     property.tech_param = result.tender_info.tech_param;
+                    property.pay_account = result.tender_info.pay_account;
                     loadCommonProperty(property);
                     $('#sync-attr').modal('hide');
                 });
@@ -505,9 +506,57 @@ function loadCommonProperty () {
     $('#real-start-date').val(property.tech_param.realStartDate);
     $('#real-end-date').val(property.tech_param.realEndDate);
     $('#structure-scale').val(property.tech_param.structureScale);
+
+    // 付款账号
+    $('#project-name').val(property.pay_account.project.name);
+    $('#project-bank').val(property.pay_account.project.bank);
+    $('#project-account').val(property.pay_account.project.account);
+    $('#project-rate').val(property.pay_account.project.rate);
+    $('#project-contact').val(property.pay_account.project.contact);
+    $('#project-phone').val(property.pay_account.project.phone);
+    $('#project-num').val(property.pay_account.project.num);
+
+    $('#worker-name').val(property.pay_account.worker.name);
+    $('#worker-bank').val(property.pay_account.worker.bank);
+    $('#worker-account').val(property.pay_account.worker.account);
+    $('#worker-rate').val(property.pay_account.worker.rate);
+    $('#worker-contact').val(property.pay_account.worker.contact);
+    $('#worker-phone').val(property.pay_account.worker.phone);
+    $('#worker-num').val(property.pay_account.worker.num);
 }
 
 function postAttr () {
+    let flag = false;
+    const rate_reg = /^(([1-9][0-9]*)|(([0]\.\d{1,2}|[1-9][0-9]*\.\d{1,2}))|)$/;
+    const phone_reg = /^((0\d{2,3}-\d{7,8})|(1[3456789]\d{9}))$/;
+
+    const project_rate = $('#project-rate').val();
+    if (!rate_reg.test(project_rate) && project_rate !== '') {
+        toastr.error('工程款账户分账划拨比例数字格式有误且最多保留2位小数');
+        flag = true;
+    }
+
+    const project_phone = $('#project-phone').val();
+    if (!phone_reg.test(project_phone) && project_phone !== '') {
+        toastr.error('工程款账户联系电话格式有误');
+        flag = true;
+    }
+
+    const worker_rate = $('#worker-rate').val();
+    if (!rate_reg.test(worker_rate) && worker_rate !== '') {
+        toastr.error('农民工工资专用账户分账划拨比例数字格式有误且最多保留2位小数');
+        flag = true;
+    }
+
+    const worker_phone = $('#worker-phone').val();
+    if (!phone_reg.test(worker_phone) && worker_phone !== '') {
+        toastr.error('农民工工资专用账户联系电话格式有误');
+        flag = true;
+    }
+    if (flag) {
+        return false;
+    }
+
     const info = {
         deal_info: {
             buildName: $('#build-name').val(),
@@ -563,12 +612,33 @@ function postAttr () {
             realEndDate: $('#real-end-date').val(),
             structureScale: $('#structure-scale').val(),
         },
+        pay_account: {
+            project: {
+                name: $('#project-name').val(),
+                bank: $('#project-bank').val(),
+                account: $('#project-account').val(),
+                rate: $('#project-rate').val(),
+                contact: $('#project-contact').val(),
+                phone: $('#project-phone').val(),
+                num: $('#project-num').val(),
+            },
+            worker: {
+                name: $('#worker-name').val(),
+                bank: $('#worker-bank').val(),
+                account: $('#worker-account').val(),
+                rate: $('#worker-rate').val(),
+                contact: $('#worker-contact').val(),
+                phone: $('#worker-phone').val(),
+                num: $('#worker-num').val(),
+            },
+        },
     };
     const tenderId = window.location.pathname.split('/')[2];
     postData('/payment/info/save', { type: 'info', postData: { id: tenderId, info} }, function (data) {
         property.deal_info = info.deal_info;
         property.construction_unit = info.construction_unit;
         property.tech_param = info.tech_param;
+        property.pay_account = info.pay_account;
         $('#bd-attr').modal('hide');
     });
 }

+ 7 - 5
app/public/js/schedule_plan.js

@@ -257,12 +257,14 @@ $(function () {
                     } else {
                         select[yearmonth + '_tp'] = plan_tp;
                     }
-                    const nodes = treeCalc.calculateParent(info.sheet.zh_tree, select);
+                    const nodes = treeCalc.calculateParent(info.sheet.zh_tree, select, 1);
+                    console.log(nodes, select);
                     const refreshNode = ledgerTree.loadPostData({update: nodes});
+                    console.log(refreshNode);
                     ledgerSpreadObj.refreshTree(info.sheet, refreshNode);
                 },function () {
                     select[col.field] = orgValue;
-                    const nodes = treeCalc.calculateParent(info.sheet.zh_tree, select);
+                    const nodes = treeCalc.calculateParent(info.sheet.zh_tree, select, 1);
                     const refreshNode = ledgerTree.loadPostData({update: nodes});
                     ledgerSpreadObj.refreshTree(info.sheet, refreshNode);
                 })
@@ -303,7 +305,7 @@ $(function () {
                 if (datas.length > 0) {
                     postData(window.location.pathname + '/save', {type: 'ledger_paste', postData: datas}, function (result) {
                         for (const uul of filterNodes) {
-                            const nodes = treeCalc.calculateParent(sheet.zh_tree, uul);
+                            const nodes = treeCalc.calculateParent(sheet.zh_tree, uul, 1);
                             const refreshNode = ledgerTree.loadPostData({update: nodes});
                             ledgerSpreadObj.refreshTree(sheet, refreshNode);
                         }
@@ -381,7 +383,7 @@ $(function () {
             if (datas.length > 0) {
                 postData(window.location.pathname + '/save', {type: 'ledger_paste', postData: datas}, function (result) {
                     for (const uul of filterNodes) {
-                        const nodes = treeCalc.calculateParent(info.sheet.zh_tree, uul);
+                        const nodes = treeCalc.calculateParent(info.sheet.zh_tree, uul, 1);
                         const refreshNode = ledgerTree.loadPostData({update: nodes});
                         ledgerSpreadObj.refreshTree(info.sheet, refreshNode);
                     }
@@ -449,7 +451,7 @@ $(function () {
             schedule.mode = this_mode;
             if (update_nodes.length > 0) {
                 for (const uul of update_nodes) {
-                    const nodes = treeCalc.calculateParent(ledgerSpread.getActiveSheet().zh_tree, uul);
+                    const nodes = treeCalc.calculateParent(ledgerSpread.getActiveSheet().zh_tree, uul, 1);
                     const refreshNode = ledgerTree.loadPostData({update: nodes});
                     ledgerSpreadObj.refreshTree(ledgerSpread.getActiveSheet(), refreshNode);
                 }

+ 5 - 5
app/public/js/schedule_stage_gcl.js

@@ -226,7 +226,7 @@ $(function () {
                 huizongSelect.end_sj_gcl = huizongSelect.end_sj_gcl ? ZhCalc.add(ZhCalc.sub(huizongSelect.end_sj_gcl, orgValue), validText) : validText;
                 huizongSelect.end_sj_tp = huizongSelect.dgn_price && huizongSelect.dgn_price !== 0 ? ZhCalc.round(ZhCalc.mul(huizongSelect.end_sj_gcl, huizongSelect.dgn_price), 0) : 0;
             }
-            const nodes = treeCalc.calculateParent(huizongSpread.getActiveSheet().zh_tree, huizongSelect);
+            const nodes = treeCalc.calculateParent(huizongSpread.getActiveSheet().zh_tree, huizongSelect, 1);
             const refreshNode = huizongTree.loadPostData({update: nodes});
             huizongObj.refreshTree(huizongSpread.getActiveSheet(), refreshNode);
         },
@@ -266,13 +266,13 @@ $(function () {
                 console.log(updateData);
                 postData(window.location.pathname + '/save', {type: 'ledger_edit', postData: updateData}, function (result) {
                     select[yearmonth + '_sj_tp'] = sj_tp;
-                    const nodes = treeCalc.calculateParent(info.sheet.zh_tree, select);
+                    const nodes = treeCalc.calculateParent(info.sheet.zh_tree, select, 1);
                     const refreshNode = ledgerTree.loadPostData({update: nodes});
                     ledgerSpreadObj.refreshTree(info.sheet, refreshNode);
                     ledgerSpreadObj.reCalcHuizong(info.row, yearmonth, orgValue, validText);
                 },function () {
                     select[col.field] = orgValue;
-                    const nodes = treeCalc.calculateParent(info.sheet.zh_tree, select);
+                    const nodes = treeCalc.calculateParent(info.sheet.zh_tree, select, 1);
                     const refreshNode = ledgerTree.loadPostData({update: nodes});
                     ledgerSpreadObj.refreshTree(info.sheet, refreshNode);
                 })
@@ -313,7 +313,7 @@ $(function () {
                 if (datas.length > 0) {
                     postData(window.location.pathname + '/save', {type: 'ledger_paste', postData: datas}, function (result) {
                         for (const uul of filterNodes) {
-                            const nodes = treeCalc.calculateParent(sheet.zh_tree, uul);
+                            const nodes = treeCalc.calculateParent(sheet.zh_tree, uul, 1);
                             const refreshNode = ledgerTree.loadPostData({update: nodes});
                             ledgerSpreadObj.refreshTree(sheet, refreshNode);
                         }
@@ -382,7 +382,7 @@ $(function () {
             if (datas.length > 0) {
                 postData(window.location.pathname + '/save', {type: 'ledger_paste', postData: datas}, function (result) {
                     for (const uul of filterNodes) {
-                        const nodes = treeCalc.calculateParent(info.sheet.zh_tree, uul);
+                        const nodes = treeCalc.calculateParent(info.sheet.zh_tree, uul, 1);
                         const refreshNode = ledgerTree.loadPostData({update: nodes});
                         ledgerSpreadObj.refreshTree(info.sheet, refreshNode);
                     }

+ 30 - 25
app/public/js/setting.js

@@ -227,32 +227,12 @@ $(document).ready(() => {
     })
 
     // 参建单位页切换单位右侧显示
-    $('#unit_list tr').click(function () {
+    $('#unit_list tr').on('click', function () {
+        console.log('hello');
         const id = parseInt($(this).data('id'));
-        const one = _.find(unitList, { id });
-        if (one) {
-            $(this).siblings('tr').removeClass('table-warning');
-            $(this).addClass('table-warning');
-            $('#unit_name').val(one.name);
-            $('#unit_corporation').val(one.corporation);
-            $('#unit_credit_code').val(one.credit_code);
-            $('#unit_tel').val(one.tel);
-            $('#unit_website').val(one.website);
-            $('#unit_region').val(one.region);
-            $('#unit_address').val(one.address);
-            $('#unit_basic').val(one.basic);
-            $('#unit_type').val(one.type);
-            if(one.sign_path) {
-                $('#sign-show').html('<img src="' + fujianOssPath + one.sign_path + '" width="120">');
-                $('#delete-sign').show();
-                $('#upload-sign').hide();
-            } else {
-                $('#sign-show').html('');
-                $('#delete-sign').hide();
-                $('#upload-sign').show();
-            }
-            oneUnit = one;
-        }
+        $(this).siblings('tr').removeClass('table-warning');
+        $(this).addClass('table-warning');
+        setUnitRightHtml(id);
     });
 
     // 参建单位编辑
@@ -447,6 +427,31 @@ function checkPasswordForm() {
     }
 }
 
+function setUnitRightHtml(id) {
+    const one = _.find(unitList, { id });
+    if (one) {
+        $('#unit_name').val(one.name);
+        $('#unit_corporation').val(one.corporation);
+        $('#unit_credit_code').val(one.credit_code);
+        $('#unit_tel').val(one.tel);
+        $('#unit_website').val(one.website);
+        $('#unit_region').val(one.region);
+        $('#unit_address').val(one.address);
+        $('#unit_basic').val(one.basic);
+        $('#unit_type').val(one.type);
+        if(one.sign_path) {
+            $('#sign-show').html('<img src="' + fujianOssPath + one.sign_path + '" width="120">');
+            $('#delete-sign').show();
+            $('#upload-sign').hide();
+        } else {
+            $('#sign-show').html('');
+            $('#delete-sign').hide();
+            $('#upload-sign').show();
+        }
+        oneUnit = one;
+    }
+}
+
 /**
  * 表单检测
  */

+ 82 - 0
app/public/js/setting_unit_list_order.js

@@ -0,0 +1,82 @@
+'use strict';
+
+/**
+ *
+ *
+ * @author Mai
+ * @date
+ * @version
+ */
+
+const unitListOrder = (function () {
+    let orderSetting = getLocalCache('zh-calc-unit-list-order');
+    if (!orderSetting) orderSetting = 'create_time|up';
+    const pinyin = new PinYinOrder();
+    function CompareStr (x, y) {
+        return pinyin.compareWord(x, y);
+    }
+    function reOrderUnits (orderStr, htmlClass = '#unit_list') {
+        if (orderStr) {
+            orderSetting = orderStr;
+            setLocalCache('zh-calc-unit-list-order', orderStr);
+        }
+        const orders = orderSetting.split('|');
+        if (orders[0] === 'name') {
+            unitList.sort(function (a, b) {
+                return orders[1] === 'up'
+                    ? CompareStr(a[orders[0]], b[orders[0]])
+                    : CompareStr(b[orders[0]], a[orders[0]]);
+            });
+        } else if (orders[0] === 'create_time') {
+            unitList.sort(function (a, b){
+                return orders[1] === 'up'
+                    ? Date.parse(a[orders[0]]) - Date.parse(b[orders[0]])
+                    : Date.parse(b[orders[0]]) - Date.parse(a[orders[0]]);
+            })
+        } else if (orders[0] === 'type') {
+            unitList.sort(function (a, b){
+                return orders[1] === 'up'
+                    ? a[orders[0]] - b[orders[0]]
+                    : b[orders[0]] - a[orders[0]];
+            })
+        }
+        resetHeaderHtml();
+        $(htmlClass).html(getUnitsHtml());
+        setUnitRightHtml(unitList[0].id);
+    }
+    function getOrderButton(field) {
+        const orders = orderSetting.split('|');
+        const button = field === orders[0]
+            ? (orders[1] === 'up'
+                ? '<i class="fa fa-sort-amount-asc" aria-hidden="true" onclick="unitListOrder.reOrderUnits(\'' + field + '|down' + '\')"></i>'
+                : '<i class="fa fa-sort-amount-desc" aria-hidden="true" onclick="unitListOrder.reOrderUnits(\'' + field + '|up' + '\')"></i>')
+            : '<i class="fa fa-sort" aria-hidden="true" onclick="unitListOrder.reOrderUnits(\'' + field + '|up' + '\')"></i>';
+        return '<a href="javascript:void(0)" class="btn btn-sm ml-1">' + button + '</a>';
+    }
+
+    function resetHeaderHtml() {
+        $('#unit_header').html(`<tr>
+                                        <th>序号</th>
+                                        <th>单位名称 ${unitListOrder.getOrderButton('name')}</th>
+                                        <th>账号数</th>
+                                        <th>类型 ${unitListOrder.getOrderButton('type')}</th>
+                                        <th class="text-center">备注</th></tr>`);
+    }
+
+    function getUnitsHtml() {
+        let html = '';
+        if (unitList.length > 0) {
+            for (const [index, u] of unitList.entries()) {
+                html += `<tr ${index === 0 ? 'class="table-warning"' : ''} data-id="${u.id}">
+                                        <td width="50px">${index+1}</td>
+                                        <td ><a href="javascript:void(0)">${u.name}</a></td>
+                                        <td width="80px">${u.account_num}</td>
+                                        <td width="100px">${accountGroup[u.type]}</td>
+                                        <td width="150px">${u.basic ? u.basic : ''}</td>
+                                    </tr>`;
+            }
+        }
+        return html;
+    }
+    return { reOrderUnits, getOrderButton, resetHeaderHtml }
+})();

+ 118 - 1
app/public/js/shares/ali_oss.js

@@ -2,6 +2,7 @@ const AliOss = (function (){
     const setting = {
         hint: true
     };
+    let client, uploadInfo;
 
     const downloadFile = function (url, filename) {
         axios.get(url, {responseType: 'blob' }).then(res => {
@@ -54,5 +55,121 @@ const AliOss = (function (){
         setting.hint = data.hint || setting.hint;
     };
 
-    return { downloadFile, downloadFileSync, zipFiles, setSetting }
+    const stopUpload = async function() {
+        if (!client) toastr.warning('当前没有上传中的大文件');
+
+        client.cancel();
+        uploadInfo.isStop = true;
+        if (uploadInfo.stopObj) uploadInfo.stopObj.attr('disabled', 'disabled');
+    };
+
+    const resumeUpload = async function() {
+        if (!client || !uploadInfo.abortCheckpoint) return;
+
+        if (uploadInfo.stopObj) uploadInfo.stopObj.removeAttr('disabled');
+        if (uploadInfo.resumeObj) uploadInfo.resumeObj.attr('disabled', 'disabled');
+        try {
+            const result = await client.multipartUpload(uploadInfo.filepath, uploadInfo.file, {
+                checkpoint: uploadInfo.abortCheckpoint,
+                progress: (p, cpt, res) => {
+                    uploadInfo.abortCheckpoint = cpt;
+                    // 刷新进度条
+                    if (uploadInfo.progressObj) {
+                        uploadInfo.progressObj.attr('aria-valuenow', p * 100);
+                        uploadInfo.progressObj.width((p * 100) + '%');
+                    }
+                }
+            });
+
+            return await endUploadBigFile();
+        } catch (e) {
+            if (uploadInfo.resumeObj) uploadInfo.resumeObj.removeAttr('disabled');
+        }
+    };
+
+    const resetRelaObj = function() {
+        if (uploadInfo.progressObj) {
+            uploadInfo.progressObj.attr('aria-valuenow', 0);
+            uploadInfo.progressObj.width('0%');
+        }
+        if (uploadInfo.stopObj) {
+            uploadInfo.stopObj.show();
+            uploadInfo.stopObj.removeAttr('disabled');
+            uploadInfo.stopObj.bind('click', function() {
+                stopUpload();
+            });
+        }
+        if (uploadInfo.resumeObj) {
+            uploadInfo.resumeObj.show();
+            uploadInfo.resumeObj.attr('disabled', 'disabled');
+            uploadInfo.resumeObj.bind('click', function() {
+                resumeUpload();
+            });
+        }
+    };
+
+    const clearRelaObj = function() {
+        if (uploadInfo.stopObj) {
+            uploadInfo.stopObj.attr('disabled', 'disabled');
+            uploadInfo.stopObj.unbind('click');
+        }
+        if (uploadInfo.resumeObj) {
+            uploadInfo.resumeObj.attr('disabled', 'disabled');
+            uploadInfo.resumeObj.unbind('click');
+        }
+    };
+
+    const beginUploadBigFile = async function () {
+        const beingData = JSON.parse(JSON.stringify(uploadInfo.defaultData));
+        beingData.type = 'begin';
+        beingData.fileInfo = uploadInfo.fileInfo;
+        const info = await postDataAsync(uploadInfo.url, beingData, false);
+        uploadInfo.filepath = info.filepath;
+        uploadInfo.filename = info.filename;
+        if (!info.oss) throw '授权信息错误';
+        uploadInfo.oss = info.oss;
+    };
+    const endUploadBigFile = async function() {
+        client = null;
+        const endData = JSON.parse(JSON.stringify(uploadInfo.defaultData));
+        endData.type = 'end';
+        endData.filepath = uploadInfo.filename;
+        endData.fileInfo = uploadInfo.fileInfo;
+        clearRelaObj();
+        const uploadRes = await postDataAsync(uploadInfo.url, endData, false);
+        if (uploadInfo.callback) {
+            uploadInfo.callback(uploadRes);
+        }
+        uploadInfo = {};
+        return uploadRes;
+    };
+
+    const uploadBigFile = async function (file, url, data, relaObj, callback) {
+        uploadInfo = { url, file, ...relaObj, callback };
+        uploadInfo.fileInfo = { filename: file.name, filesize: file.size, type: file.type, lastModified: file.lastModified };
+        uploadInfo.defaultData = data;
+        await beginUploadBigFile();
+
+        try {
+            client = new OSS(uploadInfo.oss);
+            resetRelaObj();
+            const result = await client.multipartUpload(uploadInfo.filepath, uploadInfo.file, {
+                progress: (p, cpt, res) => {
+                    uploadInfo.abortCheckpoint = cpt;
+                    // 刷新进度条
+                    if (uploadInfo.progressObj) {
+                        uploadInfo.progressObj.attr('aria-valuenow', p * 100);
+                        uploadInfo.progressObj.width((p * 100) + '%');
+                    }
+                }
+            });
+
+            return await endUploadBigFile();
+        } catch (err) {
+            if (uploadInfo.resumeObj) uploadInfo.resumeObj.removeAttr('disabled');
+            if (uploadInfo.stopObj) uploadInfo.stopObj.attr('disabled', 'disabled');
+        }
+    };
+
+    return { downloadFile, downloadFileSync, zipFiles, setSetting, uploadBigFile, resumeUpload }
 })();

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 27421 - 0
app/public/js/shares/aliyun-oss-sdk.min.js


+ 223 - 55
app/public/js/shenpi.js

@@ -222,6 +222,114 @@ function getShenpiHtml (this_code) {
     return html.join('');
 }
 $(document).ready(function () {
+    const auditUtils = {
+        getAuditHtml: function(audit) {
+            return '<span class="d-inline-block"><span class="badge badge-light">'+ audit.name +' <span class="dropdown">\n' +
+                '                                                            <a href="javascript:void(0);" class="btn-sm text-danger px-1" title="移除" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"><i class="fa fa-remove"></i></a>\n' +
+                '                                                            <div class="dropdown-menu">\n' +
+                '                                                                <a class="dropdown-item" href="javascript:void(0);">确认移除审批人?</a>\n' +
+                '                                                                <div class="dropdown-divider"></div>\n' +
+                '                                                                <div class="px-2 py-1 text-center">\n' +
+                '                                                                    <button class="remove-audit btn btn-sm btn-danger" data-id="' + audit.audit_id + '">移除</button>\n' +
+                '                                                                    <button class="btn btn-sm btn-secondary">取消</button>\n' +
+                '                                                                </div>\n' +
+                '                                                            </div>\n' +
+                '                                                        </span> ' +
+                '                                            </span></span>\n'
+        },
+        getAuditTypeHtml: function(code, type) {
+            const html = [];
+            const hide = code !== 'stage' ? 'style="display: none;"' : '';
+            html.push(`<span class="d-inline-block"><select class="form-control form-control-sm" ${hide} data-type="${type}">`);
+            for (const t of auditType.types) {
+                html.push(`<option value="${t.value}" ${t.value === type ? 'selected' : ''}>${t.name}</option>`);
+            }
+            html.push('</select></span>');
+            return html.join('');
+        },
+        getSelectAuditHtml: function (code) {
+            let divhtml = '';
+            accountGroup.forEach((group, idx) => {
+                let didivhtml = '';
+                if(group) {
+                    group.groupList.forEach(item => {
+                        didivhtml += (item.id !== cur_uid || (item.id === cur_uid && needYB.indexOf(code) !== -1)) ? '<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';
+                }
+            });
+            const html =
+                '                                            <span class="d-inline-block">\n' +
+                '                                                <div class="dropdown text-right">\n' +
+                '                                                    <button class="btn btn-outline-primary btn-sm dropdown-toggle" type="button" id="' + code + '_dropdownMenuButton" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">\n' +
+                '                                                        选择审批人\n' +
+                '                                                    </button>\n' +
+                '                                                    <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">\n' + divhtml +
+                '                                                        </dl>\n' +
+                '                                                    </div>\n' +
+                '                                                </div>\n' +
+                '                                            </span>\n';
+            return html;
+        },
+        // 以下i从1开始
+        getAuditGroupInnerHtml: function(code, auditGroup, i) {
+            const html = [];
+            const type = auditGroup.length > 0 ? auditGroup[0].audit_type : auditType.key.common;
+            html.push(`<span class="col-auto">${transFormToChinese(i)}审</span><span class="col-7 spr-span">`);
+            html.push(this.getAuditTypeHtml(code, type));
+            for (const audit of auditGroup) {
+                html.push(this.getAuditHtml(audit));
+            }
+            if (type !== auditType.key.common || auditGroup.length === 0) {
+                html.push(this.getSelectAuditHtml(code));
+            }
+            html.push('</span>');
+            return html.join('');
+        },
+        getAuditGroupHtml: function (code, auditGroup, i) {
+            return `<li class="d-flex justify-content-start align-items-center mb-3">${this.getAuditGroupInnerHtml(code, auditGroup, i)}</li>`;
+        },
+        getFinalAuditHtml: function (audit) {
+            const html = [];
+            html.push('<li class="d-flex justify-content-start mb-3"><span class="col-auto">终审</span><span class="col-7 spr-span">');
+            html.push(this.getAuditHtml(audit));
+            html.push('</span></span></li>');
+            return html.join('');
+        },
+        // 以下i从0开始
+        addAudit: function (code, user, i) {
+            const flow = sp_lc.find(x => { return x.code === code });
+            if (!flow.auditGroupList) flow.auditGroupList = [];
+            if (!flow.auditGroupList[i]) flow.auditGroupList[i] = [];
+            flow.auditGroupList[i].push(user);
+            return flow.auditGroupList[i];
+        },
+        removeAudit: function (code, audit_id, i) {
+            const flow = sp_lc.find(x => { return x.code === code });
+            if (flow.auditGroupList[i].length === 1) {
+                flow.auditGroupList.splice(i, 1);
+                return null;
+            }
+            flow.auditGroupList[i].splice(flow.auditGroupList[i].findIndex(x => { return x.audit_id === audit_id; }), 1);
+            return flow.auditGroupList[i];
+        },
+        setAuditType: function (code, audit_type, i) {
+            const flow = sp_lc.find(x => { return x.code === code });
+            if (!flow || !flow.auditGroupList || !flow.auditGroupList[i]) return;
+            flow.auditGroupList[i].forEach(x => { x.audit_type = audit_type});
+            return flow.auditGroupList[i];
+        }
+    };
+
     let timer = null;
     let oldSearchVal = null;
     const needYB = ['advance', 'ledger', 'revise', 'change', 'audit-ass'];
@@ -304,13 +412,15 @@ $(document).ready(function () {
                 _self.parents('.form-group').siblings('.lc-show').html('');
             } else if (this_status === sp_status.gdspl) {
                 let addhtml = '<ul class="list-unstyled">\n';
+                const flow = sp_lc.find(x => { return x.code === this_code; });
+                flow.auditGroupList = data;
                 if (data.length !== 0) {
-                    for(const [i, audit] of data.entries()) {
-                        addhtml += makeAudit(audit, transFormToChinese(i+1));
+                    for(const [i, auditGroup] of data.entries()) {
+                        addhtml += auditUtils.getAuditGroupHtml(this_code, auditGroup, i + 1);
                     }
                     addhtml += '<li class="pl-3"><a href="javascript:void(0);" class="add-audit"><i class="fa fa-plus"></i> 添加流程</a></li>';
                 } else {
-                    addhtml += makeSelectAudit(this_code, '一');
+                    addhtml += auditUtils.getAuditGroupHtml(this_code, [], 1);
                 }
                 addhtml += '</ul>\n';
                 _self.parents('.form-group').siblings('.lc-show').html(addhtml);
@@ -370,25 +480,33 @@ $(document).ready(function () {
                 audit_id: id,
                 type: 'add',
             };
+            if (this_status === sp_status.gdspl) {
+                prop.audit_type = parseInt($(this).parents('li').find('select')[0].value);
+                prop.audit_order = $(this).parents('li').index() + 1;
+            }
             const _self = $(this);
             postData('/tender/' + cur_tenderid + '/shenpi/audit/save', prop, function (data) {
                 if (this_status === sp_status.gdspl) {
-                    _self.parents('ul').append('<li class="pl-3"><a href="javascript:void(0);" class="add-audit"><i class="fa fa-plus"></i> 添加流程</a></li>');
+                    const auditGroup = auditUtils.addAudit(this_code, { audit_id: data.audit_id, name: user.name, audit_type: data.audit_type, audit_order: data.audit_order }, prop.audit_order - 1);
+                    if (_self.parents('ul').find('.add-audit').length === 0) {
+                        _self.parents('ul').append('<li class="pl-3"><a href="javascript:void(0);" class="add-audit"><i class="fa fa-plus"></i> 添加流程</a></li>');
+                    }
+                    _self.parents('li').html(auditUtils.getAuditGroupInnerHtml(this_code, auditGroup, prop.audit_order));
+                } else {
+                    _self.parents('.spr-span').html('<span class="d-inline-block"></span>\n' +
+                        '<span class="d-inline-block"><span class="badge badge-light">'+ user.name +' <span class="dropdown">\n' +
+                        '                                                            <a href="javascript:void(0);" class="btn-sm text-danger px-1" title="移除" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"><i class="fa fa-remove"></i></a>\n' +
+                        '                                                            <div class="dropdown-menu">\n' +
+                        '                                                                <a class="dropdown-item" href="javascript:void(0);">确认移除审批人?</a>\n' +
+                        '                                                                <div class="dropdown-divider"></div>\n' +
+                        '                                                                <div class="px-2 py-1 text-center">\n' +
+                        '                                                                    <button class="remove-audit btn btn-sm btn-danger" data-id="' + user.id + '">移除</button>\n' +
+                        '                                                                    <button class="btn btn-sm btn-secondary">取消</button>\n' +
+                        '                                                                </div>\n' +
+                        '                                                            </div>\n' +
+                        '                                                        </span> ' +
+                        '                                            </span></span></span>\n');
                 }
-                _self.parents('.spr-span').html('<span class="d-inline-block"></span>\n' +
-                    '<span class="d-inline-block"><span class="badge badge-light">'+ user.name +' <span class="dropdown">\n' +
-                    '                                                            <a href="javascript:void(0);" class="btn-sm text-danger px-1" title="移除" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"><i class="fa fa-remove"></i></a>\n' +
-                    '                                                            <div class="dropdown-menu">\n' +
-                    '                                                                <a class="dropdown-item" href="javascript:void(0);">确认移除审批人?</a>\n' +
-                    '                                                                <div class="dropdown-divider"></div>\n' +
-                    '                                                                <div class="px-2 py-1 text-center">\n' +
-                    '                                                                    <button class="remove-audit btn btn-sm btn-danger" data-id="' + user.id + '">移除</button>\n' +
-                    '                                                                    <button class="btn btn-sm btn-secondary">取消</button>\n' +
-                    '                                                                </div>\n' +
-                    '                                                            </div>\n' +
-                    '                                                        </span> ' +
-                    '                                            </span></span></span>\n');
-                // <a href="javascript:void(0);" class="remove-audit btn-sm text-danger px-1" title="移除" data-id="'+ user.id +'"><i class="fa fa-remove"></i></a></span> </span>');
             });
         }
     });
@@ -407,17 +525,20 @@ $(document).ready(function () {
         const _self = $(this);
         postData('/tender/' + cur_tenderid + '/shenpi/audit/save', prop, function (data) {
             if (this_status === sp_status.gdspl) {
-                const _selflc = _self.parents('.lc-show');
-                _self.parents('li').remove();
-                const aid_num = parseInt(_selflc.children('ul').find('li.d-flex').length);
-                if (aid_num === 0) {
-                    let addhtml = '<ul class="list-unstyled">\n';
-                    addhtml += makeSelectAudit(this_code, '一');
-                    addhtml += '</ul>\n';
-                    _selflc.html(addhtml);
+                const index = _self.parents('li').index();
+                const auditGroup = auditUtils.removeAudit(this_code, id, index);
+                if (auditGroup) {
+                    _self.parents('li').html(auditUtils.getAuditGroupInnerHtml(this_code, auditGroup, index + 1));
                 } else {
-                    for (let i = 0; i < aid_num; i++) {
-                        _selflc.find('li.d-flex').eq(i).find('.col-auto').text(transFormToChinese(i+1) + '审');
+                    const _selflc = _self.parents('.lc-show');
+                    _self.parents('li').remove();
+                    const aid_num = parseInt(_selflc.children('ul').find('li.d-flex').length);
+                    if (aid_num === 0) {
+                        _selflc.html(auditUtils.getAuditGroupHtml(this_code, [], 1));
+                    } else {
+                        for (let i = 0; i < aid_num; i++) {
+                            _selflc.find('li.d-flex').eq(i).find('.col-auto').text(transFormToChinese(i+1) + '审');
+                        }
                     }
                 }
             } else if (this_status === sp_status.gdzs) {
@@ -439,7 +560,8 @@ $(document).ready(function () {
     $('body').on('click', '.add-audit', function () {
         const num = $(this).parents('ul').children('li').length;
         const this_code = $(this).parents('.lc-show').siblings('.form-group').find('input:checked').data('code');
-        const addhtml = makeSelectAudit(this_code, transFormToChinese(num));
+        //const addhtml = makeSelectAudit(this_code, transFormToChinese(num));
+        const addhtml = auditUtils.getAuditGroupHtml(this_code, [], num);
         $(this).parents('ul').append(addhtml);
         $(this).parents('li').remove();
     });
@@ -505,23 +627,29 @@ $(document).ready(function () {
         return html;
     }
 
-
     initTenderTree();
 
     $('.set-otherTender').on('click', function () {
         const this_code = $(this).data('code');
         const this_status = parseInt($(this).siblings('.lc-show').siblings('.form-group').find('input:checked').val());
-        const aid_num = $(this).siblings('.lc-show').children('ul').find('.remove-audit').length;
-        const aidList = [];
-        for (let i = 0; i < aid_num; i++) {
-            const aid = parseInt($(this).siblings('.lc-show').children('ul').find('.remove-audit').eq(i).data('id'));
-            aidList.push(aid);
-        }
+        const lis = $(this).siblings('.lc-show').find('li');
+        const auditList = [], aidList = [];
+        lis.each((i, li) => {
+            const removes = $(li).find('.remove-audit');
+            if (removes.length === 0) return;
+
+            const select = $(li).find('select');
+            const audit_type = select.length > 0 ? parseInt(select.val()) : 1;
+            for (const remove of removes) {
+                auditList.push({ audit_id: parseInt(remove.getAttribute('data-id')), audit_type, audit_order: i + 1 });
+                aidList.push(parseInt(remove.getAttribute('data-id')));
+            }
+        });
         const html = getTenderTreeHtml(this_code, this_status, aidList);
         $('#shenpi-name').text($(this).data('name'));
         $('#shenpi_code').val(this_code);
         $('#shenpi_status').val(this_status);
-        $('#shenpi_auditors').val(aidList.join(','));
+        $('#shenpi_auditors').val(JSON.stringify(auditList));
         $('#tender-list').html(html);
         $('#search-audit').val('');
         $('#search-result').text('0/0');
@@ -543,7 +671,7 @@ $(document).ready(function () {
             code: $('#shenpi_code').val(),
         };
         if(data.status !== shenpi_status.gdspl) {
-            data.aidList = $('#shenpi_auditors').val();
+            data.auditList = JSON.parse($('#shenpi_auditors').val());
         }
         // 获取已选中的标段
         const tenderList = [];
@@ -563,7 +691,20 @@ $(document).ready(function () {
     });
 
     $('.set-otherShenpi').on('click', function () {
+        let canSetOther = true;
         const this_code = $(this).data('code');
+        if (this_code === 'stage') {
+            const select = $(this).siblings('.lc-show').find('select');
+            select.each((i, s) => {
+                if (s.value !== '1') canSetOther = false;
+            });
+        }
+        if (!canSetOther) {
+            toastr.warning('该流程含有会签或签,不可同步至其他流程');
+            $('#batch2').modal('hide');
+            return;
+        }
+
         const this_status = parseInt($(this).siblings('.lc-show').siblings('.form-group').find('input:checked').val());
         const aid_num = $(this).siblings('.lc-show').children('ul').find('.remove-audit').length;
         const aidList = [];
@@ -578,6 +719,7 @@ $(document).ready(function () {
         $('#shenpi_auditors2').val(aidList.join(','));
         $('#shenpi-list').html(html);
         setTimeout(function () { $("#shenpi-list [data-toggle='tooltip']").tooltip(); },800);
+        $('#batch2').modal('show');
     });
 
     $('#save-other-shenpi').click(function () {
@@ -610,6 +752,36 @@ $(document).ready(function () {
         })
     });
 
+    // 设置会签、或签
+    $('body').on('change', 'select', function() {
+        const removes = $(this).parents('.d-flex').find('.remove-audit');
+        if (removes.length === 0) return;
+
+        const this_status = parseInt($(this).parents('.lc-show').siblings('.form-group').find('input:checked').val());
+        const this_code = $(this).parents('.lc-show').siblings('.form-group').find('input:checked').data('code');
+        const ids = [];
+        const liParent = $(this).parents('li');
+        removes.each((i, r) => { ids.push(parseInt(r.getAttribute('data-id'))); });
+        const prop = {
+            status: this_status,
+            code: sp_type[this_code],
+            audit_id: ids,
+            audit_type: parseInt(this.value),
+            type: 'audit-type',
+        };
+        if (prop.audit_type === auditType.key.common && ids.length > 1) {
+            toastr.warning('设置个人审批前请先删除多余的审批人');
+            this.value = this.getAttribute('data-type');
+            return;
+        }
+        const _self = this;
+        postData('shenpi/audit/save', prop, function () {
+            _self.setAttribute('data-type', _self.value);
+            const auditGroup = auditUtils.setAuditType(this_code, prop.audit_type, liParent.index());
+            liParent.html(auditUtils.getAuditGroupInnerHtml(this_code, auditGroup, liParent.index() + 1));
+        });
+    });
+
     class AuditAss {
         constructor() {
             this.spread = SpreadJsObj.createNewSpread($('#ledger-spread')[0]);
@@ -659,26 +831,22 @@ $(document).ready(function () {
                 // 更新新的多人协同表格信息
                 const newUidList = [];
                 $('.stage_div ul li').each(function (k, v) {
+                    const audit_type = parseInt($(v).find('select').val());
+                    if (audit_type !== auditType.key.common) return;
+
                     const uid = $(v).find('button').eq(0).data('id');
-                    if(uid) newUidList.push(uid);
+                    if(uid) newUidList.push({ order: k + 1, audit_id: uid });
                 });
-                const oldUidList = [];
-                $('#stage_audits option').each(function (k, v) {
-                    const uid = parseInt($(v).val());
-                    if(k !== 0) oldUidList.push(uid);
-                });
-                if (!_.isEqual(oldUidList, newUidList)) {
-                    const yb = _.find(accountList, { 'id': cur_uid });
-                    let newhtml = '<option value="' + yb.id + '">' + yb.name + '(原报)</option>';
-                    if(newUidList.length > 0) {
-                        for (const [i,id] of newUidList.entries()) {
-                            const audit = _.find(accountList, { 'id': id });
-                            newhtml += '<option value="' + audit.id + '">' + audit.name + '(' + transFormToChinese(i+1) + '审)</option>';
-                        }
+                const yb = _.find(accountList, { 'id': cur_uid });
+                let newhtml = '<option value="' + yb.id + '">' + yb.name + '(原报)</option>';
+                if(newUidList.length > 0) {
+                    for (const id of newUidList) {
+                        const audit = _.find(accountList, { 'id': id.audit_id });
+                        newhtml += '<option value="' + audit.id + '">' + audit.name + '(' + transFormToChinese(id.order) + '审)</option>';
                     }
-                    $('#stage_audits').html(newhtml);
-                    self.uid = cur_uid;
                 }
+                $('#stage_audits').html(newhtml);
+                self.uid = cur_uid;
                 self.initLedgerTree(cur_uid);
             });
             $('#del-audit-ass').click(function () {
@@ -796,7 +964,7 @@ $(document).ready(function () {
         }
         refreshOperate() {
             const node = SpreadJsObj.getSelectObject(this.sheet);
-            if (node.ass_audit_id) {
+            if (node && node.ass_audit_id) {
                 $('#del-audit-ass').show();
                 $('#audit-ass_dropdownMenuButton')[0].innerHTML = '替换协同人';
             } else {

+ 7 - 17
app/public/js/stage.js

@@ -3823,7 +3823,7 @@ $(document).ready(() => {
 
             this.changeSheet.bind(spreadNS.Events.SelectionChanged, function (e, info) {
                 const change = SpreadJsObj.getSelectObject(self.changeSheet);
-                self.refreshChangeDetailData(change);
+                if (change) self.refreshChangeDetailData(change);
             });
             this.changes = null;
             this.reloadChangeData();
@@ -3918,7 +3918,7 @@ $(document).ready(() => {
                                 }
                             });
                         },
-                        disable: function (key, opt) {
+                        disabled: function (key, opt) {
                             const curChange = SpreadJsObj.getSelectObject(self.changeSheet);
                             const changeBills = SpreadJsObj.getSelectObject(self.changeBillsSheet);
                             return !changeBills || curChange.is_import;
@@ -3934,8 +3934,8 @@ $(document).ready(() => {
                 selector: '#' + setting.changeObj.attr('id'),
                 build: function ($trigger, e) {
                     const target = SpreadJsObj.safeRightClickSelection($trigger, e, self.changeSpread);
-                    const change = self.changes[target.cellTypeHitInfo.row];
-                    self.refreshChangeDetailData(change);
+                    const change = target.cellTypeHintInfo ? self.changes[target.cellTypeHitInfo.row] : null;
+                    if (change) self.refreshChangeDetailData(change);
                     return target.hitTestType === spreadNS.SheetArea.viewport || target.hitTestType === spreadNS.SheetArea.rowHeader;
                 },
                 items: {
@@ -3964,10 +3964,10 @@ $(document).ready(() => {
                                 }
                             });
                         },
-                        disable: function (key, opt) {
+                        disabled: function (key, opt) {
                             const curChange = SpreadJsObj.getSelectObject(self.changeSheet);
                             const changeBills = SpreadJsObj.getSelectObject(self.changeBillsSheet);
-                            return !changeBills || curChange.is_import;
+                            return !curChange || !changeBills || curChange.is_import;
                         },
                         visible: function (key, opt) {
                             return is_debug && stage.status === 1;
@@ -3981,7 +3981,7 @@ $(document).ready(() => {
                                 window.location.reload();
                             });
                         },
-                        disable: function (key, opt) {
+                        disabled: function (key, opt) {
                             const curChange = SpreadJsObj.getSelectObject(self.changeSheet);
                             return !curChange || curChange.is_import;
                         },
@@ -4250,9 +4250,6 @@ $(document).ready(() => {
     });
     // 上传附件
     $('#upload-file-btn').click(function () {
-        // if (curAuditor && curAuditor.aid !== cur_uid) {
-        //     return toastr.error('当前操作没有权限!');
-        // }
         const node = SpreadJsObj.getSelectObject(slSpread.getActiveSheet());
         if (!node) return
         const files = $('#upload-file')[0].files;
@@ -4315,13 +4312,6 @@ $(document).ready(() => {
             if (parseInt(cur_uid) === att.uid) {
                 $('#btn-att').show();
                 const showDel = stage.status === auditConst.status.checked ? Boolean(att.extra_upload) : true;
-                // if (!curAuditor) {
-                //     stage.status === auditConst.status.checked && parseInt(att.re_upload) && (showDel = true)
-                //     stage.status === auditConst.status.uncheck && parseInt(cur_uid) === stage.user_id && (showDel = true)
-                //     stage.status === auditConst.status.checkNo && parseInt(cur_uid) === stage.user_id && (showDel = true)
-                // } else {
-                //     curAuditor.aid === parseInt(cur_uid) && (showDel = true)
-                // }
                 if (showDel) $('#btn-att a').eq(3).show();
                 // $('#btn-att a').eq(3).show();
                 $('#btn-att a').eq(2).hide();

+ 14 - 10
app/public/js/stage_audit.js

@@ -135,17 +135,21 @@ $(document).ready(function () {
                 const auditorshtml = [];
                 for (const [index,data] of datas.entries()) {
                     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">');
+                        // todo 添加会签或签时
+                        // html.push('<span class="badge badge-pill badge-primary badge-bg-small"><small></small></span>');
                         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('<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('<i class="fa ' + (index+1 === datas.length ? 'fa-stop-circle' : 'fa-chevron-circle-down') + '"></i> ');
@@ -165,8 +169,8 @@ $(document).ready(function () {
         }
     });
     // 删除审批人
-    $('body').on('click', '#auditors li>a', function () {
-        const li = $(this).parent();
+    $('body').on('click', '#auditors li a', function () {
+        const li = $(this).parents('li');
         const data = {
             auditorId: parseInt(li.attr('auditorId')),
         };

+ 2 - 3
app/public/js/stage_compare.js

@@ -26,7 +26,7 @@ function initSpreadSettingWithRoles(compareRoles) {
             addExtraCols(fieldSufs[index], Roles[index]);
         }
     }
-    const fieldSufs = ['0'], roles = ['原报'], trs = $('tr[auditorId]');
+    const fieldSufs = ['0'], roles = ['原报'], trs = $('tr[audit-order]');
     for (let r of compareRoles) {
         if (r > 0) {
             const tr = trs[r-1];
@@ -240,7 +240,6 @@ $(document).ready(function () {
                     compareRoles.push(order + 1);
                 }
             }
-            // setLocalCache(scCacheKey, compareRoles.join(','));
             initSpreadSettingWithRoles(compareRoles);
             SpreadJsObj.initSheet(ledgerSpread.getActiveSheet(), ledgerSpreadSetting);
             treeCalc.calculateAll(scTree);
@@ -249,7 +248,7 @@ $(document).ready(function () {
             SpreadJsObj.initSheet(posSpread.getActiveSheet(), posSpreadSetting);
             loadPosData(0);
         }
-        let loadData = [], showData = [], trs = $('tr[auditorId]');
+        let loadData = [], showData = [], trs = $('tr[audit-order]');
         for (let order = 0, iLength = trs.length; order < iLength; order++) {
             const tr = trs[order];
             if ($('input[type=checkbox]', tr).length > 0 && $('input[type=checkbox]', tr)[0].checked) {

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

@@ -127,7 +127,8 @@ const stageIm = (function () {
     function getNumberFormat(num, length) {
         let s = '0000000000';
         s = s + num;
-        return s.substr(s.length - length);
+        const numLength = num.toString().length;
+        return s.substr(s.length - Math.max(length, numLength));
     }
 
     /**

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

@@ -63,7 +63,11 @@ const tenderListSpec = (function(){
         html.push('<td style="width: 15%">');
         if (!node.cid && node.cur_flow) {
             if (node.stage_status !== undefined) {
-                html.push(`<span class="${node.progress.status_class}">${node.progress.status}</span>(${node.cur_flow.name})`);
+                if (node.cur_flow instanceof Array && node.cur_flow[0].audit_type && node.cur_flow[0].audit_type !== auditType.key.common) {
+                    html.push(`<span class="${node.progress.status_class}">${node.progress.status}</span>(${transFormToChinese(node.cur_flow[0].audit_order)}审)`);
+                } else {
+                    html.push(`<span class="${node.progress.status_class}">${node.progress.status}</span>(${node.cur_flow instanceof Array ? node.cur_flow[0].name : node.cur_flow.name })`);
+                }
             } else {
                 html.push(`<span class="${node.lastStage ? auditConst.stage.tiStatusStringClass[node.lastStage.status] : auditConst.ledger.tiStatusStringClass[node.ledger_status]}">`);
                 html.push(node.lastStage ? auditConst.stage.statusString[node.lastStage.status] : auditConst.ledger.statusString[node.ledger_status]);

+ 9 - 3
app/public/js/tender_list_info.js

@@ -42,8 +42,10 @@ const tenderListSpec = (function(){
         // 当前流程
         html.push('<td style="width: 230px">');
         if (!node.cid && node.cur_flow) {
+            const curUser = node.cur_flow instanceof Array && node.cur_flow[0].audit_type && node.cur_flow[0].audit_type !== auditType.key.common
+                ? transFormToChinese(node.cur_flow[0].audit_order) + '审'
+                : node.cur_flow instanceof Array ? (node.cur_flow[0].name + (node.cur_flow[0].role ? '-'+node.cur_flow[0].role  : '')): (node.cur_flow.name + (node.cur_flow.role ? '-'+node.cur_flow.role  : ''));
             if (node.stage_status !== undefined) {
-                const curUser = node.cur_flow.name + (node.cur_flow.role ? '-'+node.cur_flow.role  : '');
                 html.push((node.stage_status === auditConst.stage.status.uncheck || node.ledger_status === auditConst.ledger.status.uncheck)
                     ? curUser
                     : `<a href="#sp-list" data-toggle="modal" data-target="#sp-list"  data-type="${node.stage_count ? 'stage' : 'ledger'}" data-tender="${node.id}" data-order="${node.stage_count ? node.stage_count + '' : ''}">${curUser}</a>`
@@ -53,10 +55,14 @@ const tenderListSpec = (function(){
                 html.push((node.lastStage && node.lastStage.status === auditConst.stage.status.uncheck) || (!node.lastStage && node.ledger_status === auditConst.ledger.status.uncheck ) ? '' :
                     '<a href="#sp-list" data-toggle="modal" data-target="#sp-list"  data-type="'+ (node.lastStage ? 'stage' : 'ledger') +'"' +
                     ' data-tender="'+ node.id +'" data-order="'+ (node.lastStage ? node.lastStage.order : '') +'">');
-                html.push(node.cur_flow.name+ (node.cur_flow.role ? '-'+node.cur_flow.role  : ''));
+                html.push(curUser);
                 html.push((node.lastStage && node.lastStage.status === auditConst.stage.status.uncheck) || (!node.lastStage && node.ledger_status === auditConst.ledger.status.uncheck ) ? ' ':
                     '</a> ');
-                html.push('<span class="' + node.cur_flow.status_class +'">' + node.cur_flow.status + '</span>');
+                if (node.cur_flow instanceof Array) {
+                    html.push('<span class="' + node.cur_flow[0].status_class +'">' + node.cur_flow[0].status + '</span>');
+                } else {
+                    html.push('<span class="' + node.cur_flow.status_class +'">' + node.cur_flow.status + '</span>');
+                }
             }
         }
         html.push('</td>');

+ 19 - 4
app/public/js/tender_list_progress.js

@@ -53,8 +53,11 @@ const tenderListSpec = (function(){
         // 当前流程
         html.push('<td style="width: 8%">');
         if (!node.cid && node.cur_flow) {
+            if (node.id === 4811) console.log(node.cur_flow);
+            const curUser = node.cur_flow instanceof Array && node.cur_flow[0].audit_type && node.cur_flow[0].audit_type !== auditType.key.common
+                ? transFormToChinese(node.cur_flow[0].audit_order) + '审'
+                : node.cur_flow instanceof Array ? (node.cur_flow[0].name + (node.cur_flow[0].role ? '-'+node.cur_flow[0].role  : '')): (node.cur_flow.name + (node.cur_flow.role ? '-'+node.cur_flow.role  : ''));
             if (node.stage_status !== undefined) {
-                const curUser = node.cur_flow.name + (node.cur_flow.role ? '-'+node.cur_flow.role  : '');
                 html.push((node.stage_status === auditConst.stage.status.uncheck || node.ledger_status === auditConst.ledger.status.uncheck)
                     ? curUser
                     : `<a href="#sp-list" data-toggle="modal" data-target="#sp-list"  data-type="${node.stage_count ? 'stage' : 'ledger'}" data-tender="${node.id}" data-order="${node.stage_count ? node.stage_count + '' : ''}">${curUser}</a>`
@@ -64,17 +67,29 @@ const tenderListSpec = (function(){
                 html.push((node.lastStage && node.lastStage.status === auditConst.stage.status.uncheck) || (!node.lastStage && node.ledger_status === auditConst.ledger.status.uncheck ) ? '' :
                     '<a href="#sp-list" data-toggle="modal" data-target="#sp-list"  data-type="'+ (node.lastStage ? 'stage' : 'ledger') +'"' +
                     ' data-tender="'+ node.id +'" data-order="'+ (node.lastStage ? node.lastStage.order : '') +'">');
-                html.push(node.cur_flow.name+ (node.cur_flow.role ? '-'+node.cur_flow.role  : ''));
+                html.push(curUser);
                 html.push((node.lastStage && node.lastStage.status === auditConst.stage.status.uncheck) || (!node.lastStage && node.ledger_status === auditConst.ledger.status.uncheck ) ? ' ':
                     '</a> ');
-                html.push('<span class="' + node.cur_flow.status_class +'">' + node.cur_flow.status + '</span>');
+                if (node.cur_flow instanceof Array) {
+                    html.push('<span class="' + node.cur_flow[0].status_class +'">' + node.cur_flow[0].status + '</span>');
+                } else {
+                    html.push('<span class="' + node.cur_flow.status_class +'">' + node.cur_flow.status + '</span>');
+                }
             }
         }
         html.push('</td>');
         // 上一流程审批时间
         html.push('<td style="width: 8%">');
         if (!node.cid && node.pre_flow) {
-            html.push(node.pre_flow.name + ' ' + moment(node.pre_flow.time).format('YYYY-MM-DD'));
+            if (node.pre_flow instanceof Array) {
+                if (node.pre_flow.length > 1) {
+                    html.push(transFormToChinese(node.pre_flow[0].audit_order) + '审' + ' ' + moment(node.pre_flow[0].time).format('YYYY-MM-DD'));
+                } else {
+                    html.push(node.pre_flow[0].name + ' ' + moment(node.pre_flow[0].time).format('YYYY-MM-DD'));
+                }
+            } else {
+                html.push(node.pre_flow.name + ' ' + moment(node.pre_flow.time).format('YYYY-MM-DD'));
+            }
         }
         html.push('</td>');
         // 签约合同价

+ 5 - 1
app/public/report/js/rpt_custom.js

@@ -219,8 +219,12 @@ const rptCustomObj = (function () {
     const getStageFlowSelectHtml = function (select, id) {
         const html = [];
         html.push('<select style="width: 80%" id="' + id + '" sf-title="' + select.title + '">');
+        const maxOrder = stageFlow.reduce((rst, sf) => { return sf.audit_order && sf.audit_order > rst ? sf.audit_order : rst;}, 1);
         for (const sf of stageFlow) {
-            html.push(`<option ${sf.visible ? '' : 'style="display: none"'}>${sf.name}-${sf.role}</option>`);
+            const flow = sf.audit_order
+                ? ( sf.audit_order === maxOrder ? '终审' : transFormToChinese(sf.audit_order) + '审')
+                : '原报';
+            html.push(`<option ${sf.visible ? '' : 'style="display: none"'}>${sf.name}-${sf.role}(${flow})</option>`);
         }
         html.push('</select>');
         return html.join('');

+ 3 - 5
app/router.js

@@ -145,21 +145,17 @@ module.exports = app => {
      */
     // 金额概况
     app.get('/list', sessionAuth, 'tenderController.listDefault');
-    app.get('/listOrg', sessionAuth, 'tenderController.listDefaultOrg');
     app.get('/list/finish', sessionAuth, 'tenderController.listDefaultFinish');
     app.post('/list/load', sessionAuth, 'tenderController.listLoad');
     app.post('/list/load2', sessionAuth, 'tenderController.listLoad2');
     app.get('/list/info', sessionAuth, 'tenderController.listInfo');
-    app.get('/list/infoOrg', sessionAuth, 'tenderController.listInfoOrg');
     app.get('/list/info/finish', sessionAuth, 'tenderController.listInfoFinish');
 
     // 计量进度
     app.get('/list/progress', sessionAuth, 'tenderController.listProgress');
-    app.get('/list/progressOrg', sessionAuth, 'tenderController.listProgressOrg');
 
     // 管理标段
     app.get('/list/manage', sessionAuth, 'tenderController.listManage');
-    app.get('/list/manageOrg', sessionAuth, 'tenderController.listManageOrg');
     app.get('/list/manage/finish', sessionAuth, 'tenderController.listManageFinish');
     app.post('/list/add', sessionAuth, 'tenderController.addTender');
     app.post('/list/update', sessionAuth, 'tenderController.updateTender');
@@ -227,11 +223,11 @@ module.exports = app => {
     app.post('/tender/:id/ledger/compresse/file', sessionAuth, tenderCheck, uncheckTenderCheck, 'ledgerController.downloadZip');
 
     // 台账审批相关
-    app.get('/tender/:id/ledger/audit', sessionAuth, tenderCheck, uncheckTenderCheck, 'ledgerAuditController.index');
     app.post('/tender/:id/ledger/audit/add', sessionAuth, tenderCheck, uncheckTenderCheck, 'ledgerAuditController.add');
     app.post('/tender/:id/ledger/audit/delete', sessionAuth, tenderCheck, uncheckTenderCheck, 'ledgerAuditController.remove');
     app.post('/tender/:id/ledger/audit/start', sessionAuth, tenderCheck, uncheckTenderCheck, ledgerAuditCheck, tenderBuildCheck, 'ledgerAuditController.start');
     app.post('/tender/:id/ledger/audit/check', sessionAuth, tenderCheck, uncheckTenderCheck, tenderBuildCheck, 'ledgerAuditController.check');
+    app.post('/tender/:id/ledger/audit/check/again', sessionAuth, tenderCheck, uncheckTenderCheck, tenderBuildCheck, 'ledgerAuditController.checkAgain');
 
     // 部位台账
     app.get('/tender/:id/ledger/bwtz', sessionAuth, tenderCheck, uncheckTenderCheck, 'ledgerController.bwtz');
@@ -720,6 +716,7 @@ module.exports = app => {
     app.post('/subproj/memberSave', sessionAuth, projectManagerCheck, 'subProjController.memberSave');
     // 概算投资
     app.get('/budget', sessionAuth, 'budgetController.list');
+    app.get('/budget/:id', sessionAuth, budgetCheck, 'budgetController.budgetInfo');
     app.get('/budget/:id/compare', sessionAuth, budgetCheck, 'budgetController.compare');
     app.post('/budget/:id/compare/load', sessionAuth, budgetCheck, 'budgetController.compareLoad');
     app.post('/budget/:id/compare/final', sessionAuth, budgetCheck, 'budgetController.compareFinal');
@@ -739,6 +736,7 @@ module.exports = app => {
     app.post('/sp/:id/filing/move', sessionAuth, subProjectCheck, 'fileController.moveFiling');
     app.post('/sp/:id/file/load', sessionAuth, subProjectCheck, 'fileController.loadFile');
     app.post('/sp/:id/file/upload', sessionAuth, subProjectCheck, 'fileController.uploadFile');
+    app.post('/sp/:id/file/upload/big', sessionAuth, subProjectCheck, 'fileController.uploadBigFile');
     app.post('/sp/:id/file/del', sessionAuth, subProjectCheck, 'fileController.delFile');
     app.post('/sp/:id/file/save', sessionAuth, subProjectCheck, 'fileController.saveFile');
     app.post('/sp/:id/file/rela', sessionAuth, subProjectCheck, 'fileController.relaFile');

+ 26 - 2
app/service/change.js

@@ -117,8 +117,13 @@ module.exports = app => {
                         change.org_code = planInfo.new_code;
                         change.new_code = planInfo.c_new_code;
                         change.new_name = planInfo.design_name;
-                        const classIndex = planInfo.class && this._.indexOf(changeConst.className, planInfo.class) !== -1 ? this._.indexOf(changeConst.className, planInfo.class) : 0;
-                        change.class = classIndex ? classIndex : changeConst.class.A.value;
+                        // 工程变更类别读取
+                        const projectData = await this.ctx.service.project.getDataById(this.ctx.session.sessionProject.id);
+                        const fun_set = await this.ctx.service.project.getFunSet(projectData.fun_set);
+                        const classInfo = this._.find(fun_set.change_class, function(item) {
+                            return item.name === planInfo.class || (item.checked && item.new_name === planInfo.class);
+                        });
+                        change.class = classInfo ? classInfo.value : this._.find(fun_set.change_class, { checked: true }).value;
                         const qualityIndex = planInfo.quality && this._.indexOf(changeConst.qualityName, planInfo.quality) !== -1 ? this._.indexOf(changeConst.qualityName, planInfo.quality) : 0;
                         change.quality = qualityIndex ? qualityIndex : changeConst.quality.common.value;
                         let content = planInfo.reason ? planInfo.reason.replace(/[\r\n]/g, '\r\n') : '';
@@ -1695,6 +1700,25 @@ module.exports = app => {
             const sql = `SELECT * FROM ${this.tableName} WHERE tid = ? AND negative_tp < 0 ORDER BY code asc`;
             return this.db.query(sql, [tenderId]);
         }
+
+        async getListByBudgetInfo(tenders) {
+            if (tenders) {
+                const sql = 'SELECT * FROM ?? WHERE `status` = ? AND `tid` in (' + this.ctx.helper.getInArrStrSqlFilter(tenders.split(',')) + ') ORDER BY `total_price` DESC LIMIT 0, 10';
+                const params = [this.tableName, audit.flow.status.checked];
+                return await this.db.query(sql, params);
+            }
+            return [];
+        }
+
+        async getTotalTpByBudgetInfo(tenders) {
+            if (tenders) {
+                const sql = 'SELECT SUM(`total_price`) AS tp FROM ?? WHERE `status` = ? AND `tid` in (' + this.ctx.helper.getInArrStrSqlFilter(tenders.split(',')) + ')';
+                const sqlParam = [this.tableName, audit.flow.status.checked];
+                const result = await this.db.queryOne(sql, sqlParam);
+                return result && result.tp ? result.tp : 0;
+            }
+            return 0;
+        }
     }
 
     return Change;

+ 5 - 0
app/service/change_project.js

@@ -41,6 +41,10 @@ module.exports = app => {
                 throw '立项书编号重复';
             }
 
+            // 工程变更类别读取
+            const projectData = await this.ctx.service.project.getDataById(this.ctx.session.sessionProject.id);
+            const fun_set = await this.ctx.service.project.getFunSet(projectData.fun_set);
+            const classInfo = this._.find(fun_set.change_class, { checked: true });
             // 初始化事务
             this.transaction = await this.db.beginTransaction();
             let result = false;
@@ -55,6 +59,7 @@ module.exports = app => {
                     code,
                     name,
                     quality: changeConst.quality.common.name,
+                    class: classInfo.new_name ? classInfo.new_name : classInfo.name,
                 };
                 const operate = await this.transaction.insert(this.tableName, change);
 

+ 65 - 23
app/service/ledger_audit.js

@@ -410,11 +410,15 @@ module.exports = app => {
                         where: { tender_id: tenderId, times },
                         columns: ['tender_id', 'audit_order', 'audit_id'],
                     });
+                    const insertAuditors = [];
                     for (const a of auditors) {
+                        if (insertAuditors.find(x => { return x.audit_id === a.audit_id; })) continue;
+                        a.audit_order = insertAuditors.length + 1;
                         a.times = times + 1;
                         a.status = auditConst.status.uncheck;
+                        insertAuditors.push(a)
                     }
-                    await transaction.insert(this.tableName, auditors);
+                    await transaction.insert(this.tableName, insertAuditors);
 
                     // const users = this._.pull(this._.map(auditList, 'audit_id'), audit.id);
                     await this.ctx.helper.sendAliSms(users, smsTypeConst.const.TZ, smsTypeConst.judge.result.toString(), SmsAliConst.template.ledger_result, {
@@ -436,6 +440,66 @@ module.exports = app => {
             }
         }
 
+
+        /**
+         * 重新审批
+         * @param {Number} stageId - 标段id
+         * @param {Number} times - 第几次审批
+         * @return {Promise<void>}
+         */
+        async checkAgain(tenderId, times = 1) {
+            const time = new Date();
+            // 整理当前流程审核人状态更新
+            const auditors = await this.getAllDataByCondition({
+                where: { tender_id: tenderId, times },
+                orders: [['audit_order', 'desc']],
+            });
+            if (auditors.length <= 0) throw '台账审核数据错误';
+            const selfAudit = auditors[0];
+            if (selfAudit.audit_id !== this.ctx.session.sessionUser.accountId) throw '当前台账您无权重审';
+            const tender = this.ctx.service.tender.getDataById(tenderId);
+            if (!tender) throw '标段数据错误';
+
+            let otherAuditIds = [tender.user_id, ...auditors.map(x => { return x.audit_id })];
+            otherAuditIds = this._.uniq(otherAuditIds).filter(x => { return x !== selfAudit.audit_id; });
+
+            const checkAgainAuditor = {
+                tender_id: tenderId, times, audit_order: selfAudit.audit_order + 1, audit_id: selfAudit.audit_id,
+                begin_time: time, end_time: time, opinion: '', status: auditConst.status.checkAgain,
+            };
+            const checkingAuditor = {
+                tender_id: tenderId, times, audit_order: selfAudit.audit_order + 2, audit_id: selfAudit.audit_id,
+                begin_time: time, end_time: null, opinion: '', status: auditConst.status.checking,
+            };
+            const transaction = await this.db.beginTransaction();
+            try {
+                // 当前审批人2次添加至流程中
+                await transaction.insert(this.tableName, checkAgainAuditor);
+                await transaction.insert(this.tableName, checkingAuditor);
+                // 同步标段信息
+                await transaction.update(this.ctx.service.tender.tableName, {
+                    id: tenderId,
+                    ledger_status: auditConst.status.checking,
+                });
+                await this.ctx.service.tenderCache.updateLedgerCache(transaction, tenderId, auditConst.status.checking, auditConst.status.checkAgain, checkingAuditor);
+
+                if (otherAuditIds.length > 0) {
+                    await this.ctx.helper.sendAliSms(otherAuditIds, smsTypeConst.const.TZ, smsTypeConst.judge.approval.toString(), SmsAliConst.template.ledger_check);
+                    // 微信模板通知
+                    const wechatData = {
+                        status: wxConst.status.check,
+                        tips: wxConst.tips.check,
+                        begin_time: Date.parse(time),
+                    };
+                    await this.ctx.helper.sendWechat(otherAuditIds, smsTypeConst.const.TZ, smsTypeConst.judge.approval.toString(), wxConst.template.ledger, wechatData);
+                }
+                await transaction.commit();
+            } catch (err) {
+                await transaction.rollback();
+                throw err;
+            }
+        }
+
         /**
          * 获取审核人需要审核的标段列表
          *
@@ -485,28 +549,6 @@ module.exports = app => {
         }
 
         /**
-         * 获取 某时间后 审批进度 更新的台账
-         * @param {Integer} pid - 项目id
-         * @param {Integer} uid - 查询人id
-         * @param {Date} noticeTime - 查询事件
-         * @return {Promise<*>}
-         */
-        async getNoticeTender(pid, uid, noticeTime) {
-            let notice = await this.db.select('zh_notice', {
-                where: { pid, type: pushType.ledger, uid },
-                orders: [['create_time', 'desc']],
-                limit: 10,
-                offset: 0,
-            });
-            notice = notice.map(v => {
-                const extra = JSON.parse(v.content);
-                delete v.content;
-                return { ...v, ...extra };
-            });
-            return notice;
-        }
-
-        /**
          * 用于添加推送所需的content内容
          * @param {Number} id  标段id
          * @param {Number} pid 项目id

+ 4 - 0
app/service/material_bills.js

@@ -908,6 +908,10 @@ module.exports = app => {
                 }
             }
         }
+
+        async updateAllOrder(updateList) {
+            await this.db.updateRows(this.tableName, updateList);
+        }
     }
 
     return MaterialBills;

+ 1 - 1
app/service/material_list.js

@@ -377,7 +377,7 @@ module.exports = app => {
          * @return {void}
          */
         async getMaterialData(tid, mid) {
-            const sql = 'SELECT ml.`id`, mb.`code`, mb.`name`, mb.`unit`, ml.`order`, ml.`gather_qty`, ml.`quantity`, ml.`expr`, ml.`mb_id`, ml.`gcl_id`, ml.`xmj_id`, ml.`mx_id`, ml.`ms_id`, ml.`tid`, ml.`mid`, mb.m_spread, ml.ms_id' +
+            const sql = 'SELECT ml.`id`, mb.`code`, mb.`name`, mb.`unit`, ml.`order`, ml.`gather_qty`, ml.`quantity`, ml.`expr`, ml.`mb_id`, ml.`gcl_id`, ml.`xmj_id`, ml.`mx_id`, ml.`ms_id`, ml.`tid`, ml.`mid`, mb.m_spread, ml.ms_id, ml.is_join' +
                 ' FROM ' + this.tableName + ' as ml' +
                 ' LEFT JOIN ' + this.ctx.service.materialBills.tableName + ' as mb' +
                 ' ON ml.`mb_id` = mb.`id`' +

+ 9 - 0
app/service/project_account.js

@@ -923,6 +923,15 @@ module.exports = app => {
             return this._.assign(result, defaultData);
         }
 
+        async getAccountCacheDatas(ids, defaultData) {
+            const result  = await this.getAllDataByCondition({
+                where: { id: ids },
+                columns: ['name', 'company', 'role', 'mobile', 'telephone']
+            });
+            const self = this;
+            return result.map(x => { return self._.assign(x, defaultData)});
+        }
+
         async getSelfCategoryLevel(id) {
             const result = await this.getDataById(id);
             return result ? result.self_category_level : '';

+ 78 - 21
app/service/shenpi_audit.js

@@ -25,20 +25,41 @@ module.exports = app => {
         }
 
         async getAudit(tid, type, status) {
-            const sql = 'SELECT sp.audit_id, pa.name FROM ?? AS sp LEFT JOIN ?? AS pa ON sp.audit_id = pa.id' +
+            const sql = 'SELECT sp.audit_id, sp.audit_type, pa.name FROM ?? AS sp LEFT JOIN ?? AS pa ON sp.audit_id = pa.id' +
                 ' WHERE sp.tid = ? AND sp.sp_type = ? AND sp.sp_status = ?';
             const sqlParam = [this.tableName, this.ctx.service.projectAccount.tableName, tid, type, status];
             return await this.db.queryOne(sql, sqlParam);
         }
 
         async getAuditList(tid, type, status) {
-            const sql = 'SELECT sp.audit_id, pa.name FROM ?? AS sp LEFT JOIN ?? AS pa ON sp.audit_id = pa.id' +
-                ' WHERE sp.tid = ? AND sp.sp_type = ? AND sp.sp_status = ? ORDER BY sp.id ASC';
+            const sql = 'SELECT sp.audit_id, sp.audit_type, pa.name FROM ?? AS sp LEFT JOIN ?? AS pa ON sp.audit_id = pa.id' +
+                ' WHERE sp.tid = ? AND sp.sp_type = ? AND sp.sp_status = ? ORDER BY sp.audit_order ASC, sp.id ASC';
             const sqlParam = [this.tableName, this.ctx.service.projectAccount.tableName, tid, type, status];
             return await this.db.query(sql, sqlParam);
         }
 
+        async getAuditGroupList(tid, type, status) {
+            const sql = 'SELECT sp.audit_id, sp.audit_type, sp.audit_order, pa.name FROM ?? AS sp LEFT JOIN ?? AS pa ON sp.audit_id = pa.id' +
+                ' WHERE sp.tid = ? AND sp.sp_type = ? AND sp.sp_status = ? ORDER BY sp.audit_order ASC, sp.id ASC';
+            const sqlParam = [this.tableName, this.ctx.service.projectAccount.tableName, tid, type, status];
+            const audits = await this.db.query(sql, sqlParam);
+            const result = [];
+            for (const a of audits) {
+                if (a.audit_order > 0) {
+                    if (result[a.audit_order - 1]) {
+                        result[a.audit_order - 1].push(a);
+                    } else {
+                        result.push([a]);
+                    }
+                } else {
+                    result.push([a]);
+                }
+            }
+            return result;
+        }
+
         async addAudit(data) {
+            let result;
             const transaction = await this.db.beginTransaction();
             try {
                 if (parseInt(data.code) === shenpiConst.sp_type.stage && parseInt(data.status) === shenpiConst.sp_status.gdspl) {
@@ -59,25 +80,36 @@ module.exports = app => {
                     sp_status: data.status,
                     audit_id: data.audit_id,
                 };
-                const result = await transaction.insert(this.tableName, insertData);
+                if (data.audit_type) insertData.audit_type = data.audit_type;
+                if (data.audit_order) insertData.audit_order = data.audit_order;
+                result = await transaction.insert(this.tableName, insertData);
                 await transaction.commit();
-                return result.effectRows === 1;
             } catch (err) {
                 await transaction.rollback();
                 throw err;
             }
+            if (result.affectedRows !== 1) throw '添加审批人失败';
+            data.id = result.insertId;
+            return data;
         }
 
         async removeAudit(data) {
+            const delData = {
+                tid: this.ctx.tender.id,
+                sp_type: data.code,
+                sp_status: data.status,
+                audit_id: data.audit_id,
+            };
+            const audit = await this.getDataByCondition(delData);
+            const allAudit = await this.getAllDataByCondition({ where: { tid: this.ctx.tender.id, sp_type: data.code, sp_status: data.status } });
+            const updateData = [];
+            for (const aa of allAudit) {
+                if (aa.audit_order > audit.audit_order) updateData.push({ id: aa.id, audit_order: aa.audit_order - 1});
+            }
             const transaction = await this.db.beginTransaction();
             try {
-                const delData = {
-                    tid: this.ctx.tender.id,
-                    sp_type: data.code,
-                    sp_status: data.status,
-                    audit_id: data.audit_id,
-                };
                 await transaction.delete(this.tableName, delData);
+                if (updateData.length > 0) await transaction.updateRows(this.tableName, updateData);
                 if (parseInt(data.code) === shenpiConst.sp_type.stage && parseInt(data.status) === shenpiConst.sp_status.gdspl) {
                     const options = {
                         where: {
@@ -127,16 +159,22 @@ module.exports = app => {
                 for (const t of tenders) {
                     if (shenpi_status !== shenpiConst.sp_status.sqspr) {
                         await transaction.delete(this.tableName, { tid: t.id, sp_type: shenpiConst.sp_type[data.code], sp_status: shenpi_status });
-                        for (const aid of data.aidList.split(',')) {
-                            if (aid && parseInt(aid) !== t.user_id || (parseInt(aid) === t.user_id && canYB)) {
-                                const insertData = {
-                                    tid: t.id,
-                                    sp_type: shenpiConst.sp_type[data.code],
-                                    sp_status: shenpi_status,
-                                    audit_id: parseInt(aid),
-                                };
-                                insertList.push(insertData);
+                        const sourceList = data.auditList.filter(x => { return x.audit_id && (x.audit_id !== t.user_id || canYB) });
+                        sourceList.sort((a, b) => { return a.audit_order - b.audit_order; });
+                        let audit_order = 0, curAuditOrder = 0;
+                        for (const s of sourceList) {
+                            if (s.audit_order !== curAuditOrder) {
+                                curAuditOrder = s.audit_order;
+                                audit_order = audit_order + 1;
                             }
+                            insertList.push({
+                                tid: t.id,
+                                sp_type: shenpiConst.sp_type[data.code],
+                                sp_status: shenpi_status,
+                                audit_id: s.audit_id,
+                                audit_type: s.audit_type,
+                                audit_order: audit_order,
+                            });
                         }
                     }
                 }
@@ -198,6 +236,24 @@ module.exports = app => {
             }
         }
 
+        async setAuditType(data) {
+            const conn = await this.db.beginTransaction();
+            try {
+                const updateData = { audit_type: data.audit_type };
+                const condition = {
+                    tid: this.ctx.tender.id,
+                    sp_type: data.code,
+                    sp_status: data.status,
+                    audit_id: data.audit_id,
+                };
+                await conn.update(this.tableName, updateData, { where: condition });
+                await conn.commit();
+            } catch (err) {
+                await conn.rollback();
+                throw err;
+            }
+        }
+
         // 更新审批流程
         async updateAuditList(transaction, tenderId, sp_status, sp_type, ids) {
             if (sp_status === shenpiConst.sp_status.gdspl) {
@@ -209,12 +265,13 @@ module.exports = app => {
                 // 更新固定审批流
                 await transaction.delete(this.tableName, { tid: tenderId, sp_type, sp_status });
                 const insertDatas = [];
-                for (const id of ids) {
+                for (const [i, id] of ids.entries()) {
                     insertDatas.push({
                         tid: tenderId,
                         sp_type,
                         sp_status,
                         audit_id: id,
+                        audit_order: i + 1,
                     });
                 }
                 await transaction.insert(this.tableName, insertDatas);

+ 123 - 38
app/service/stage.js

@@ -8,7 +8,7 @@
  * @version
  */
 
-const auditConst = require('../const/audit').stage;
+const auditConst = require('../const/audit');
 const payConst = require('../const/deal_pay.js');
 const roleRelSvr = require('./role_rpt_rel');
 const fs = require('fs');
@@ -70,31 +70,68 @@ module.exports = app => {
         }
 
         async loadStageUser(stage) {
-            const status = auditConst.status;
+            const status = auditConst.stage.status;
             const accountId = this.ctx.session.sessionUser.accountId;
 
-            stage.auditors = await this.ctx.service.stageAudit.getAuditors(stage.id, stage.times);
-            stage.curAuditor = await this.ctx.service.stageAudit.getCurAuditor(stage.id, stage.times);
-
-            stage.assists = await this.service.stageAuditAss.getData(stage);
-            stage.userAssists = stage.assists.filter(x => { return x.user_id === stage.user_id; }); // 原报协同人
-            stage.auditAssists = stage.assists.filter(x => { return x.user_id !== stage.user_id; }); // 审批协同人
-            stage.relaAssists = stage.assists.filter(x => { return x.user_id === accountId });
+            stage.user = await this.ctx.service.projectAccount.getAccountInfoById(stage.user_id);
+            stage.auditors = await this.ctx.service.stageAudit.getAuditors(stage.id, stage.times); // 全部参与的审批人
             stage.auditorIds = this._.map(stage.auditors, 'aid');
+            stage.curAuditors = stage.auditors.filter(x => { return x.status === status.checking; }); // 当前流程中审批中的审批人
+            stage.curAuditorIds = this._.map(stage.curAuditors, 'aid');
+            stage.flowAuditors = stage.curAuditors.length > 0 ? stage.auditors.filter(x => { return x.order === stage.curAuditors[0].order; }) : []; // 当前流程中参与的审批人(包含会签时,审批通过的人)
+            stage.flowAuditorIds = this._.map(stage.flowAuditors, 'aid');
+            stage.auditorGroups = this.ctx.helper.groupAuditors(stage.auditors);
+            stage.userGroups = this.ctx.helper.groupAuditorsUniq(stage.auditorGroups);
+            stage.userGroups.unshift([{
+                aid: stage.user.id, order: 0, times: stage.times, audit_order: 0, audit_type: auditConst.auditType.key.common,
+                name: stage.user.name, role: stage.user.role, company: stage.user.company
+            }]);
+            stage.finalAuditorIds = stage.userGroups[stage.userGroups.length - 1].map(x => { return x.aid; });
+
+            stage.assists = await this.service.stageAuditAss.getData(stage); // 全部协同人
+            stage.assists = stage.assists.filter(x => {
+                return x.user_id === stage.user_id || stage.auditorIds.indexOf(x.user_id) >= 0;
+            }); // 过滤无效协同人
+            stage.userAssists = stage.assists.filter(x => { return x.user_id === stage.user_id; }); // 原报协同人
             stage.userAssistIds = this._.map(stage.userAssists, 'ass_user_id');
+            stage.auditAssists = stage.assists.filter(x => { return x.user_id !== stage.user_id; }); // 审批协同人
             stage.auditAssistIds = this._.map(stage.auditAssists, 'ass_user_id');
-            stage.users = stage.status === status.uncheck
+            stage.relaAssists = stage.assists.filter(x => { return x.user_id === accountId }); // 登录人的协同人
+            stage.userIds = stage.status === status.uncheck // 当前流程下全部参与人id
                 ? [stage.user_id, ...stage.userAssistIds]
                 : [stage.user_id, ...stage.userAssistIds, ...stage.auditorIds, ...stage.auditAssistIds];
         }
 
+        async loadStageAuditViewData(stage) {
+            const times = stage.status === auditConst.stage.status.checkNo ? stage.times - 1 : stage.times;
+
+            if (!stage.user) stage.user = await this.ctx.service.projectAccount.getAccountInfoById(stage.user_id);
+            stage.auditHistory = await this.ctx.service.stageAudit.getAuditorHistory(stage.id, times);
+            // 获取审批流程中左边列表
+            if (stage.status === auditConst.stage.status.checkNo && stage.user_id !== this.ctx.session.sessionUser.accountId) {
+                const auditors = await this.ctx.service.stageAudit.getAuditors(stage.id, times); // 全部参与的审批人
+                const auditorGroups = this.ctx.helper.groupAuditors(auditors);
+                stage.auditors2 = this.ctx.helper.groupAuditorsUniq(auditorGroups);
+                stage.auditors2.unshift([{
+                    aid: stage.user.id, order: 0, times: stage.times - 1, audit_order: 0, audit_type: auditConst.auditType.key.common,
+                    name: stage.user.name, role: stage.user.role, company: stage.user.company
+                }]);
+            } else {
+                stage.auditors2 = stage.userGroups;
+            }
+            if (stage.status === auditConst.stage.status.uncheck || stage.status === auditConst.stage.status.checkNo) {
+                stage.auditorList = await this.ctx.service.stageAudit.getAuditors(stage.id, stage.times);
+            }
+        }
+
         async doCheckStage(stage, force = false) {
-            const status = auditConst.status;
+            const status = auditConst.stage.status;
             await this.loadStageUser(stage);
 
             const accountId = this.ctx.session.sessionUser.accountId, shareIds = [];
             const isTenderTourist = await this.service.tenderTourist.getDataByCondition({ tid: stage.tid, user_id: accountId });
             const permission = this.ctx.session.sessionUser.permission;
+
             if (stage.status === status.uncheck) {
                 stage.readOnly = accountId !== stage.user_id && stage.userAssistIds.indexOf(accountId) < 0;
                 if (!stage.readOnly) {
@@ -120,14 +157,19 @@ module.exports = app => {
                 stage.curTimes = stage.times;
                 stage.curOrder = _.max(_.map(stage.auditors, 'order'));
             } else {
-                const ass = stage.auditAssists.find(x => { return x.user_id === stage.curAuditor.aid && x.ass_user_id === accountId; });
-                stage.readOnly = stage.curAuditor.aid !== accountId && !ass;
+                const ass = stage.auditAssists.find(x => { return stage.flowAuditorIds.indexOf(x.user_id) >= 0 && x.ass_user_id === accountId; });
+                stage.readOnly = stage.flowAuditorIds.indexOf(accountId) < 0 && !ass;
                 stage.curTimes = stage.times;
                 if (!stage.readOnly) {
                     stage.assist = ass;
-                    stage.curOrder = stage.curAuditor.order;
+                    stage.curOrder = stage.curAuditors[0].order;
                 } else {
-                    stage.curOrder = stage.curAuditor.order - 1;
+                    stage.curOrder = stage.curAuditors[0].order - 1;
+                }
+                // 会签,会签人审批通过时,只读,但是curOrder需按原来的取值
+                if (!stage.readOnly) {
+                    stage.readOnly = !_.isEqual(stage.flowAuditorIds, stage.curAuditorIds);
+                    stage.canCheck = true;
                 }
             }
             if (stage.readOnly) {
@@ -135,23 +177,13 @@ module.exports = app => {
                     ? null
                     : stage.assists.find(x => { return x.ass_user_id === accountId});
             }
-            if (stage.users.indexOf(accountId) >= 0) {
+            if (stage.userIds.indexOf(accountId) >= 0) {
                 stage.filePermission = true;
             } else if (!!isTenderTourist || force) {
                 stage.filePermission = this.tender && this.tender.touristPermission ? this.tender.touristPermission.file : false;
             } else {
                 stage.filePermission = false;
             }
-            // } else {
-            //     if (shareIds.indexOf(accountId) !== -1 || (permission !== null && permission.tender !== undefined && permission.tender.indexOf('2') !== -1)) {// 分享人
-            //         if (stage.status === status.uncheck) {
-            //             throw '您无权查看该数据';
-            //         }
-            //         stage.filePermission = false;
-            //     } else {
-            //         throw '您无权查看该数据';
-            //     }
-            // }
 
             let time = stage.readOnly ? stage.cache_time_r : stage.cache_time_l;
             if (!time) time = stage.in_time ? stage.in_time : new Date();
@@ -162,9 +194,61 @@ module.exports = app => {
                 stage.ledgerHis = await this.service.ledgerHistory.getDataById(stage.his_id);
             }
 
+            // 是否台账修订中
+            const lastRevise = await this.service.ledgerRevise.getLastestRevise(stage.tid);
+            stage.revising = (lastRevise && lastRevise.status !== auditConst.revise.status.checked) || false;
+
             return stage;
         }
 
+        async doCheckStageCanCancel(stage) {
+            // 获取当前审批人的上一个审批人,判断是否是当前登录人,并赋予撤回功能,(当审批人存在有审批过时,上一人不允许再撤回)
+            const status = auditConst.stage.status;
+            const accountId = this.ctx.session.sessionUser.accountId;
+            stage.cancancel = 0;
+            if (stage.status !== status.checked && stage.status !== status.uncheck) {
+                if (stage.status !== status.checkNo) {
+                    // 找出当前操作人上一个审批人,包括审批完成的和退回上一个审批人的,同时当前操作人为第一人时,就是则为原报
+                    if (stage.flowAuditors.find(x => { return x.status !== auditConst.stage.status.checking}) && stage.flowAuditorIds.indexOf(accountId) < 0) return; // 当前流程存在审批人审批通过时,不可撤回
+                    const flowAssists = stage.auditAssists.filter(x => { return stage.flowAuditorIds.indexOf(x.user_id) >= 0; });
+                    if (flowAssists.find(x => { return x.confirm; })) return; //当前流程存在协审人确认时,不可撤回
+                    if (stage.curAuditorIds.indexOf(accountId) < 0 && stage.flowAuditorIds.indexOf(accountId) >= 0) {
+                        stage.cancancel = 5; // 会签未全部审批通过时,审批人撤回审批通过
+                        return;
+                    }
+
+                    const preAuditors = stage.curAuditors[0].order !== 1 ? stage.auditors.filter(x => { return x.order === stage.curAuditors[0].order - 1; }) : [];
+                    const preAuditorCheckAgain = preAuditors.find(pa => { return pa.status === status.checkAgain; });
+                    const preAuditorCheckCancel = preAuditors.find(pa => { return pa.status === status.checkCancel; });
+                    const preAuditorHasOld = preAuditors.find(pa => { return pa.is_old === 1; });
+                    const preAuditorIds = (preAuditorCheckAgain ? [] : preAuditors.map(x => { return x.aid })); // 重审不可撤回
+                    if ((this._.isEqual(stage.flowAuditorIds, preAuditorIds) && preAuditorCheckCancel) || preAuditorHasOld) {
+                        return; // 不可以多次撤回
+                    }
+
+                    const preAuditChecked = preAuditors.find(pa => { return pa.status === status.checked && pa.aid === accountId; });
+                    const preAuditCheckNoPre = preAuditors.find(pa => { return pa.status === status.checkNoPre && pa.aid === accountId; });
+                    if (preAuditorIds.indexOf(accountId) >= 0) {
+                        if (preAuditChecked) {
+                            stage.cancancel = 2;// 审批人撤回审批通过
+                        } else if (preAuditCheckNoPre) {
+                            stage.cancancel = 3;// 审批人撤回审批退回上一人
+                        }
+                        stage.preAuditors = preAuditors;
+                    } else if (preAuditors.length === 0 && accountId === stage.user_id) {
+                        stage.cancancel = 1;// 原报撤回
+                    }
+                } else {
+                    const lastAuditors = await this.service.stageAudit.getAuditors(stage.id, stage.times - 1);
+                    const onAuditor = _.findLast(lastAuditors, { status: status.checkNo });
+                    if (onAuditor.aid === accountId) {
+                        stage.cancancel = 4;// 审批人撤回退回原报
+                        stage.preAuditors = lastAuditors.filter(x => { return x.order === onAuditor.order });
+                    }
+                }
+            }
+        }
+
         async checkStage(sid) {
             if (!this.ctx.stage) {
                 const stage = await this.ctx.service.stage.getDataById(sid);
@@ -188,7 +272,7 @@ module.exports = app => {
             });
             if (!includeUnCheck) {
                 this.sqlBuilder.setAndWhere('status', {
-                    value: auditConst.status.uncheck,
+                    value: auditConst.stage.status.uncheck,
                     operate: '!=',
                 });
             }
@@ -211,7 +295,7 @@ module.exports = app => {
                 operate: '=',
             });
             this.sqlBuilder.setAndWhere('status', {
-                value: auditConst.status.checked,
+                value: auditConst.stage.status.checked,
                 operate: '=',
             });
             this.sqlBuilder.orderBy = [['order', 'desc']];
@@ -245,7 +329,7 @@ module.exports = app => {
 
         async checkStageGatherData(stage, force = false) {
             // 最新一期计量(未审批完成),当前操作人的期详细数据,应实时计算
-            if (stage.status !== auditConst.status.checked) {
+            if (stage.status !== auditConst.stage.status.checked) {
                 await this.doCheckStage(stage, force);
                 if (!stage.readOnly && stage.check_calc) {
                     const tpData = await this.ctx.service.stageBills.getSumTotalPrice(stage);
@@ -303,7 +387,7 @@ module.exports = app => {
             }
             if (stages.length !== 0 && !this.ctx.session.sessionUser.is_admin) {
                 const lastStage = stages[0];
-                if (lastStage.status === auditConst.status.uncheck && !this.ctx.tender.isTourist) {
+                if (lastStage.status === auditConst.stage.status.uncheck && !this.ctx.tender.isTourist) {
                     const assist = await this.ctx.service.auditAss.getAllDataByCondition({ where: { tid: tenderId, user_id: lastStage.user_id } });
                     const assistId = assist.map(x => { return x.ass_user_id });
                     if (lastStage.user_id !== this.ctx.session.sessionUser.accountId && assistId.indexOf(this.ctx.session.sessionUser.accountId) < 0) {
@@ -353,7 +437,7 @@ module.exports = app => {
                 order: ['order'],
             });
             const preStage = stages[stages.length - 1];
-            if (stages.length > 0 && stages[stages.length - 1].status !== auditConst.status.checked) {
+            if (stages.length > 0 && stages[stages.length - 1].status !== auditConst.stage.status.checked) {
                 throw '上一期未审批通过,请等待上一期审批通过后,再新增数据';
             }
             const order = stages.length + 1;
@@ -365,7 +449,7 @@ module.exports = app => {
                 s_time: date,
                 period,
                 times: 1,
-                status: auditConst.status.uncheck,
+                status: auditConst.stage.status.uncheck,
                 user_id: this.ctx.session.sessionUser.accountId,
                 check_calc: false,
             };
@@ -753,7 +837,8 @@ module.exports = app => {
             for (const sid of stage_id_list) {
                 const stage = await this.getDataById(sid);
                 stage.auditors = await this.service.stageAudit.getAuditors(stage.id, stage.times);
-                stage.curAuditor = await this.service.stageAudit.getCurAuditor(stage.id, stage.times);
+                // todo 不确定是否使用,暂时注释,待测试验证
+                // stage.curAuditor = await this.service.stageAudit.getCurAuditor(stage.id, stage.times);
                 stage.curOrder = _.max(_.map(stage.auditors, 'order'));
                 stage.curTimes = stage.times;
                 list.push(stage);
@@ -763,11 +848,11 @@ module.exports = app => {
 
         async getStageByDataCollect(tenderId) {
             const stages = await this.db.select(this.tableName, {
-                columns: ['id', 'times', 'status', 's_time', 'contract_tp', 'qc_tp', 'pc_tp', 'pre_contract_tp', 'pre_qc_tp', 'tp_history'],
+                columns: ['id', 'user_id', 'times', 'status', 's_time', 'contract_tp', 'qc_tp', 'pc_tp', 'pre_contract_tp', 'pre_qc_tp', 'tp_history'],
                 where: { tid: tenderId },
                 orders: [['order', 'desc']],
             });
-            if (stages.length > 0 && stages[0].status === auditConst.status.uncheck) {
+            if (stages.length > 0 && stages[0].status === auditConst.stage.status.uncheck) {
                 stages.splice(0, 1);
             }
             // 最新一期计量(未审批完成),取上一个人的期详细数据,应实时计算
@@ -783,7 +868,7 @@ module.exports = app => {
         }
 
         async doCheckStageByDataCollect(stage) {
-            const status = auditConst.status;
+            const status = auditConst.stage.status;
             await this.loadStageUser(stage);
             if (stage.status === status.checkNo) {
                 stage.readOnly = false;
@@ -799,14 +884,14 @@ module.exports = app => {
             } else {
                 stage.readOnly = false;
                 stage.curTimes = stage.times;
-                stage.curOrder = stage.curAuditor.order - 1;
+                stage.curOrder = stage.curAuditors[0].order - 1;
             }
             return stage;
         }
 
         async checkStageGatherDataByDataCollect(stage) {
             // 最新一期计量(未审批完成),当前操作人的期详细数据,应实时计算
-            if (stage.status !== auditConst.status.checked) {
+            if (stage.status !== auditConst.stage.status.checked) {
                 await this.doCheckStageByDataCollect(stage);
                 if (!stage.readOnly && stage.check_calc) {
                     const tpData = await this.ctx.service.stageBills.getSumTotalPrice(stage);

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 837 - 545
app/service/stage_audit.js


+ 1 - 2
app/service/stage_bills.js

@@ -126,7 +126,7 @@ module.exports = app => {
             return stageBills[0];
         }
 
-        async updateStageBills4CheckCancel(sid, newSaid, newTimes, newOrder, oldSaid, oldTimes, oldOrder, transaction) {
+        async updateStageBills4CheckCancel(sid, newTimes, newOrder, oldTimes, oldOrder, transaction) {
             const oldSBLists = await this.getAllDataByCondition({ where: { sid, times: oldTimes, order: oldOrder } });
             const newSBLists = await this.getAllDataByCondition({ where: { sid, times: newTimes, order: newOrder } });
             const delidList = [];
@@ -140,7 +140,6 @@ module.exports = app => {
             if (delidList.length > 0) await transaction.delete(this.tableName, { id: delidList });
             if (oldSBLists.length > 0) {
                 await transaction.update(this.tableName, {
-                    said: newSaid,
                     times: newTimes,
                     order: newOrder,
                 }, { where: { id: this._.map(oldSBLists, 'id') } });

+ 16 - 49
app/service/stage_bonus.js

@@ -175,68 +175,35 @@ module.exports = app => {
             }
         }
 
-        async updateHistory(stage, transaction) {
+        async updateHistory4TimesOrder(stage, times, order, transaction) {
             const datas = await this.getStageData(stage.id);
             if (datas.length === 0) return;
 
             const updateDatas = [];
-            const times = this.ctx.stage.curTimes, order = this.ctx.stage.curOrder;
             for (const d of datas) {
-                const history = d.shistory && d.shistory !== '' ? JSON.parse(d.shistory) : [];
-                const his = history.find(function (x) {
-                    return x.stimes && x.stimes === times
-                        && x.sorder !== undefined && x.sorder !== null && x.sorder === order;
-                });
-                if (his) {
-                    his.tp = d.tp;
-                } else {
-                    history.push({ stimes: this.ctx.stage.curTimes, sorder: this.ctx.stage.curOrder, tp: d.tp });
+                for (const curOrder of order) {
+                    const history = d.shistory && d.shistory !== '' ? JSON.parse(d.shistory) : [];
+                    const his = history.find(function (x) {
+                        return x.stimes && x.stimes === times
+                            && x.sorder !== undefined && x.sorder !== null && x.sorder === curOrder;
+                    });
+                    if (his) {
+                        his.tp = d.tp;
+                    } else {
+                        history.push({ stimes: times, sorder: curOrder, tp: d.tp });
+                    }
+                    updateDatas.push({ id: d.id, shistory: JSON.stringify(history) });
                 }
-                updateDatas.push({ id: d.id, shistory: JSON.stringify(history) });
             }
             await transaction.updateRows(this.tableName, updateDatas);
         }
 
-        async updateHistory4CheckCancel(stage, newTimes, newOrder, transaction) {
-            const datas = await this.getStageData(stage.id, true);
-            if (datas.length === 0) return;
-
-            const updateDatas = [];
-            for (const d of datas) {
-                const history = d.shistory && d.shistory !== '' ? JSON.parse(d.shistory) : [];
-                const his = history.find(function (x) {
-                    return x.stimes === newTimes && x.sorder === newOrder;
-                });
-                if (his) {
-                    his.tp = d.tp;
-                } else {
-                    history.push({ stimes: newTimes, sorder: newOrder, tp: d.tp });
-                }
-                updateDatas.push({ id: d.id, shistory: JSON.stringify(history) });
-            }
-            await transaction.updateRows(this.tableName, updateDatas);
+        async updateHistory(stage, transaction) {
+            await this.updateHistory4TimesOrder(stage, stage.curTimes, [stage.curOrder], transaction);
         }
 
         async updateHistory4CheckAgain(stage, transaction) {
-            const datas = await this.getStageData(stage.id);
-            if (datas.length === 0) return;
-
-            const updateDatas = [];
-            const times = this.ctx.stage.curTimes, order = this.ctx.stage.curOrder + 1;
-            for (const d of datas) {
-                const history = d.shistory && d.shistory !== '' ? JSON.parse(d.shistory) : [];
-                const his = history.find(function (x) {
-                    return x.stimes && x.stimes === times
-                        && x.sorder && x.sorder === order;
-                });
-                if (his) {
-                    his.tp = d.tp;
-                } else {
-                    history.push({ stimes: times, sorder: order, tp: d.tp });
-                }
-                updateDatas.push({ id: d.id, shistory: JSON.stringify(history) });
-            }
-            await transaction.updateRows(this.tableName, updateDatas);
+            await this.updateHistory4TimesOrder(stage, stage.curTimes, [stage.curOrder + 1], transaction);
         }
 
         async deleteStageTimesData(sid, times, transaction) {

+ 21 - 1
app/service/stage_change.js

@@ -585,7 +585,7 @@ module.exports = app => {
                 const p = pos.find(y => { return y.id === x.pid });
                 return {
                     ...x,
-                    ledger_id: b.ledger_id, l_code: b.b_code, l_name: b.name, l_unit: b.unit, l_up: b.unit_price,
+                    ledger_id: b ? b.ledger_id : -1, l_code: b.b_code, l_name: b.name, l_unit: b.unit, l_up: b.unit_price,
                     l_deal_qty: b.deal_qty, l_deal_tp: b.deal_tp, l_qty: b.quantity, l_tp: b.total_price, l_drawing_code: b.drawing_code,
                     p_name: p ? p.name : '', p_drawing_code: p ? p.drawing_code : '', p_qty: p ? p.quantity : '',
                 };
@@ -824,6 +824,26 @@ module.exports = app => {
                 throw '保存导入数据失败';
             }
         }
+
+        async updateStageChange4CheckCancel(sid, newTimes, newOrder, oldTimes, oldOrder, transaction) {
+            const oldSBLists = await this.getAllDataByCondition({ where: { sid, stimes: oldTimes, sorder: oldOrder } });
+            const newSBLists = await this.getAllDataByCondition({ where: { sid, stimes: newTimes, sorder: newOrder } });
+            const delidList = [];
+            for (const o of oldSBLists) {
+                const newSBInfo = this._.find(newSBLists, { lid: o.lid });
+                if (newSBInfo) {
+                    // 删除
+                    delidList.push(newSBInfo.id);
+                }
+            }
+            if (delidList.length > 0) await transaction.delete(this.tableName, { id: delidList });
+            if (oldSBLists.length > 0) {
+                await transaction.update(this.tableName, {
+                    stimes: newTimes,
+                    sorder: newOrder,
+                }, { where: { id: this._.map(oldSBLists, 'id') } });
+            }
+        }
     }
 
     return StageChange;

+ 2 - 2
app/service/stage_detail.js

@@ -149,7 +149,7 @@ module.exports = app => {
         async saveDetailData(data) {
             if (data.uuid) {
                 const org = await this.getLastestImStageData(this.ctx.tender.id, this.ctx.stage.id, data.lid, data.uuid);
-                const order = this.ctx.stage.curAuditor ? this.ctx.stage.curAuditor.order : 0;
+                const order = this.ctx.stage.curAuditors.length > 0 ? this.ctx.stage.curAuditors[0].order : 0;
                 if (org.times === this.ctx.stage.times && org.order === order) {
                     delete org.flow;
                     const newData = this._.assign(org, data);
@@ -169,7 +169,7 @@ module.exports = app => {
                 data.tid = this.ctx.tender.id;
                 data.sid = this.ctx.stage.id;
                 data.times = this.ctx.stage.times;
-                data.order = this.ctx.stage.curAuditor ? this.ctx.stage.curAuditor.order : 0;
+                data.order = this.ctx.stage.curAuditors.length > 0 ? this.ctx.stage.curAuditors[0].order : 0;
                 await this.db.insert(this.tableName, data);
                 return data;
             }

+ 22 - 70
app/service/stage_jgcl.js

@@ -194,90 +194,42 @@ module.exports = app => {
             }
         }
 
-        async updateHistory(stage, transaction) {
+        async updateHistory4TimesOrder(stage, times, order, transaction) {
             const datas = await this.getStageData(stage);
             if (datas.length === 0) return;
 
             const updateDatas = [];
-            const times = this.ctx.stage.curTimes, order = this.ctx.stage.curOrder;
             for (const d of datas) {
-                const history = d.shistory && d.shistory !== '' ? JSON.parse(d.shistory) : [];
-                const his = history.find(function (x) {
-                    return x.stimes && x.stimes === times
-                        && x.sorder !== undefined && x.sorder !== null && x.sorder === order;
-                });
-                if (his) {
-                    his.arrive_qty = d.arrive_qty;
-                    his.arrive_tp = d.arrive_tp;
-                    his.deduct_qty = d.deduct_qty;
-                    his.deduct_tp = d.deduct_tp;
-                } else {
-                    history.push({
-                        stimes: this.ctx.stage.curTimes, sorder: this.ctx.stage.curOrder,
-                        arrive_qty: d.arrive_qty, arrive_tp: d.arrive_tp,
-                        deduct_qty: d.deduct_qty, deduct_tp: d.deduct_tp
+                for (const curOrder of order) {
+                    const history = d.shistory && d.shistory !== '' ? JSON.parse(d.shistory) : [];
+                    const his = history.find(function (x) {
+                        return x.stimes && x.stimes === times
+                            && x.sorder !== undefined && x.sorder !== null && x.sorder === curOrder;
                     });
+                    if (his) {
+                        his.arrive_qty = d.arrive_qty;
+                        his.arrive_tp = d.arrive_tp;
+                        his.deduct_qty = d.deduct_qty;
+                        his.deduct_tp = d.deduct_tp;
+                    } else {
+                        history.push({
+                            stimes: times, sorder: curOrder,
+                            arrive_qty: d.arrive_qty, arrive_tp: d.arrive_tp,
+                            deduct_qty: d.deduct_qty, deduct_tp: d.deduct_tp
+                        });
+                    }
+                    updateDatas.push({ id: d.id, shistory: JSON.stringify(history) });
                 }
-                updateDatas.push({ id: d.id, shistory: JSON.stringify(history) });
             }
             await transaction.updateRows(this.tableName, updateDatas);
         }
 
-        async updateHistory4CheckCancel(stage, newTimes, newOrder, transaction) {
-            const datas = await this.getStageData(stage, true);
-            if (datas.length === 0) return;
-
-            const updateDatas = [];
-            for (const d of datas) {
-                const history = d.shistory && d.shistory !== '' ? JSON.parse(d.shistory) : [];
-                const his = history.find(function (x) {
-                    return x.stimes === newTimes && x.sorder === newOrder;
-                });
-                if (his) {
-                    his.arrive_qty = d.arrive_qty;
-                    his.arrive_tp = d.arrive_tp;
-                    his.deduct_qty = d.deduct_qty;
-                    his.deduct_tp = d.deduct_tp;
-                } else {
-                    history.push({
-                        stimes: newTimes, sorder: newOrder,
-                        arrive_qty: d.arrive_qty, arrive_tp: d.arrive_tp,
-                        deduct_qty: d.deduct_qty, deduct_tp: d.deduct_tp
-                    });
-                }
-                updateDatas.push({ id: d.id, shistory: JSON.stringify(history) });
-            }
-            await transaction.updateRows(this.tableName, updateDatas);
+        async updateHistory(stage, transaction) {
+            await this.updateHistory4TimesOrder(stage, stage.curTimes, [stage.curOrder], transaction);
         }
 
         async updateHistory4CheckAgain(stage, transaction) {
-            const datas = await this.getStageData(stage);
-            if (datas.length === 0) return;
-
-            const updateDatas = [];
-            const times = this.ctx.stage.curTimes, order = this.ctx.stage.curOrder + 1;
-            for (const d of datas) {
-                const history = d.shistory && d.shistory !== '' ? JSON.parse(d.shistory) : [];
-                const his = history.find(function (x) {
-                    return x.stimes && x.stimes === times
-                        && x.sorder && x.sorder === order;
-                });
-                if (his) {
-                    his.unit_price = d.unit_price;
-                    his.arrive_qty = d.arrive_qty;
-                    his.arrive_tp = d.arrive_tp;
-                    his.deduct_qty = d.deduct_qty;
-                    his.deduct_tp = d.deduct_tp;
-                } else {
-                    history.push({
-                        stimes: times, sorder: order, unit_price: d.unit_price,
-                        arrive_qty: d.arrive_qty, arrive_tp: d.arrive_tp,
-                        deduct_qty: d.deduct_qty, deduct_tp: d.deduct_tp
-                    });
-                }
-                updateDatas.push({ id: d.id, shistory: JSON.stringify(history) });
-            }
-            await transaction.updateRows(this.tableName, updateDatas);
+            await this.updateHistory4TimesOrder(stage, stage.curTimes, [stage.curOrder + 1], transaction);
         }
 
         async addInitialStageData(stage, preStage, transaction) {

+ 19 - 58
app/service/stage_other.js

@@ -164,77 +164,38 @@ module.exports = app => {
             }
         }
 
-        async updateHistory(stage, transaction) {
+        async updateHistory4TimesOrder(stage, times, order, transaction) {
             const datas = await this.getStageData(stage);
             if (datas.length === 0) return;
 
             const updateDatas = [];
-            const times = this.ctx.stage.curTimes, order = this.ctx.stage.curOrder;
             for (const d of datas) {
-                const history = d.shistory && d.shistory !== '' ? JSON.parse(d.shistory) : [];
-                const his = history.find(function (x) {
-                    return x.stimes && x.stimes === times
-                        && x.sorder !== undefined && x.sorder !== null && x.sorder === order;
-                });
-                if (his) {
-                    his.tp = d.tp;
-                    if (d.sid === d.add_sid) his.total_price = d.total_price;
-                } else {
-                    const nHis = { stimes: this.ctx.stage.curTimes, sorder: this.ctx.stage.curOrder, tp: d.tp };
-                    if (d.sid === d.add_sid) nHis.total_price = d.total_price;
-                    history.push(nHis);
+                for (const curOrder of order) {
+                    const history = d.shistory && d.shistory !== '' ? JSON.parse(d.shistory) : [];
+                    const his = history.find(function (x) {
+                        return x.stimes && x.stimes === times
+                            && x.sorder !== undefined && x.sorder !== null && x.sorder === curOrder;
+                    });
+                    if (his) {
+                        his.tp = d.tp;
+                        if (d.sid === d.add_sid) his.total_price = d.total_price;
+                    } else {
+                        const nHis = { stimes: times, sorder: curOrder, tp: d.tp };
+                        if (d.sid === d.add_sid) nHis.total_price = d.total_price;
+                        history.push(nHis);
+                    }
+                    updateDatas.push({ id: d.id, shistory: JSON.stringify(history) });
                 }
-                updateDatas.push({ id: d.id, shistory: JSON.stringify(history) });
             }
             await transaction.updateRows(this.tableName, updateDatas);
         }
 
-        async updateHistory4CheckCancel(stage, newTimes, newOrder, transaction) {
-            const datas = await this.getStageData(stage, true);
-            if (datas.length === 0) return;
-
-            const updateDatas = [];
-            for (const d of datas) {
-                const history = d.shistory && d.shistory !== '' ? JSON.parse(d.shistory) : [];
-                const his = history.find(function (x) {
-                    return x.stimes === newTimes && x.sorder === newOrder;
-                });
-                if (his) {
-                    his.tp = d.tp;
-                    if (d.sid === d.add_sid) his.total_price = d.total_price;
-                } else {
-                    const nHis = { stimes: newTimes, sorder: newOrder, tp: d.tp };
-                    if (d.sid === d.add_sid) nHis.total_price = d.total_price;
-                    history.push(nHis);
-                }
-                updateDatas.push({ id: d.id, shistory: JSON.stringify(history) });
-            }
-            await transaction.updateRows(this.tableName, updateDatas);
+        async updateHistory(stage, transaction) {
+            await this.updateHistory4TimesOrder(stage, stage.curTimes, [stage.curOrder], transaction);
         }
 
         async updateHistory4CheckAgain(stage, transaction) {
-            const datas = await this.getStageData(stage);
-            if (datas.length === 0) return;
-
-            const updateDatas = [];
-            const times = this.ctx.stage.curTimes, order = this.ctx.stage.curOrder + 1;
-            for (const d of datas) {
-                const history = d.shistory && d.shistory !== '' ? JSON.parse(d.shistory) : [];
-                const his = history.find(function (x) {
-                    return x.stimes && x.stimes === times
-                        && x.sorder && x.sorder === order;
-                });
-                if (his) {
-                    his.tp = d.tp;
-                    if (d.sid === d.add_sid) his.total_price = d.total_price;
-                } else {
-                    const nHis = { stimes: times, sorder: order, tp: d.tp };
-                    if (d.sid === d.add_sid) nHis.total_price = d.total_price;
-                    history.push(nHis);
-                }
-                updateDatas.push({ id: d.id, shistory: JSON.stringify(history) });
-            }
-            await transaction.updateRows(this.tableName, updateDatas);
+            await this.updateHistory4TimesOrder(stage, stage.curTimes, [stage.curOrder + 1], transaction);
         }
 
         async addInitialStageData(stage, preStage, transaction) {

+ 1 - 2
app/service/stage_pos.js

@@ -125,7 +125,7 @@ module.exports = app => {
             return this._filterLastestData(stagePos);
         }
 
-        async updateStagePos4CheckCancel(sid, newSaid, newTimes, newOrder, oldSaid, oldTimes, oldOrder, transaction) {
+        async updateStagePos4CheckCancel(sid, newTimes, newOrder, oldTimes, oldOrder, transaction) {
             const oldSBLists = await this.getAllDataByCondition({ where: { sid, times: oldTimes, order: oldOrder } });
             const newSBLists = await this.getAllDataByCondition({ where: { sid, times: newTimes, order: newOrder } });
             const delidList = [];
@@ -141,7 +141,6 @@ module.exports = app => {
                 await transaction.update(this.tableName, {
                     times: newTimes,
                     order: newOrder,
-                    said: newSaid,
                 }, { where: { id: this._.map(oldSBLists, 'id') } });
             }
         }

+ 39 - 78
app/service/stage_safe_prod.js

@@ -176,98 +176,59 @@ module.exports = app => {
             }
         }
 
-        async updateHistory(stage, transaction) {
+        /**
+         * 更新历史
+         * @param stage
+         * @param times
+         * @param [array] order
+         * @param transaction
+         * @returns {Promise<void>}
+         */
+        async updateHistory4TimesOrder(stage, times, order, transaction) {
             const datas = await this.getStageData(stage);
             if (datas.length === 0) return;
 
             const updateDatas = [];
-            const times = this.ctx.stage.curTimes, order = this.ctx.stage.curOrder;
             for (const d of datas) {
-                const history = d.shistory && d.shistory !== '' ? JSON.parse(d.shistory) : [];
-                const his = history.find(function (x) {
-                    return x.stimes && x.stimes === times
-                        && x.sorder !== undefined && x.sorder !== null && x.sorder === order;
-                });
-                if (his) {
-                    his.qty = d.qty;
-                    his.tp = d.tp;
-                    if (d.sid === d.add_sid) {
-                        his.quantity = d.quantity;
-                        his.total_price = d.total_price;
-                    }
-                } else {
-                    const nHis = { stimes: this.ctx.stage.curTimes, sorder: this.ctx.stage.curOrder, qty: d.qty, tp: d.tp };
-                    if (d.sid === d.add_sid) {
-                        nHis.quantity = d.quantity;
-                        nHis.total_price = d.total_price;
+                for (const curOrder of order) {
+                    const history = d.shistory && d.shistory !== '' ? JSON.parse(d.shistory) : [];
+                    const his = history.find(function (x) {
+                        return x.stimes && x.stimes === times
+                            && x.sorder !== undefined && x.sorder !== null && x.sorder === curOrder;
+                    });
+                    if (his) {
+                        his.qty = d.qty;
+                        his.tp = d.tp;
+                        if (d.sid === d.add_sid) {
+                            his.quantity = d.quantity;
+                            his.total_price = d.total_price;
+                        }
+                    } else {
+                        const nHis = { stimes: times, sorder: curOrder, qty: d.qty, tp: d.tp };
+                        if (d.sid === d.add_sid) {
+                            nHis.quantity = d.quantity;
+                            nHis.total_price = d.total_price;
+                        }
+                        history.push(nHis);
                     }
-                    history.push(nHis);
+                    updateDatas.push({ id: d.id, shistory: JSON.stringify(history) });
                 }
-                updateDatas.push({ id: d.id, shistory: JSON.stringify(history) });
             }
             await transaction.updateRows(this.tableName, updateDatas);
         }
 
-        async updateHistory4CheckCancel(stage, newTimes, newOrder, transaction) {
-            const datas = await this.getStageData(stage, true);
-            if (datas.length === 0) return;
-
-            const updateDatas = [];
-            for (const d of datas) {
-                const history = d.shistory && d.shistory !== '' ? JSON.parse(d.shistory) : [];
-                const his = history.find(function (x) {
-                    return x.stimes === newTimes && x.sorder === newOrder;
-                });
-                if (his) {
-                    his.qty = d.qty;
-                    his.tp = d.tp;
-                    if (d.sid === d.add_sid) {
-                        his.quantity = d.quantity;
-                        his.total_price = d.total_price;
-                    }
-                } else {
-                    const nHis = { stimes: newTimes, sorder: newOrder, qty: d.qty, tp: d.tp };
-                    if (d.sid === d.add_sid) {
-                        nHis.quantity = d.quantity;
-                        nHis.total_price = d.total_price;
-                    }
-                    history.push(nHis);
-                }
-                updateDatas.push({ id: d.id, shistory: JSON.stringify(history) });
-            }
-            await transaction.updateRows(this.tableName, updateDatas);
+        /**
+         *
+         * @param stage
+         * @param transaction
+         * @returns {Promise<void>}
+         */
+        async updateHistory(stage, transaction) {
+            await this.updateHistory4TimesOrder(stage, stage.curTimes, [stage.curOrder], transaction);
         }
 
         async updateHistory4CheckAgain(stage, transaction) {
-            const datas = await this.getStageData(stage);
-            if (datas.length === 0) return;
-
-            const updateDatas = [];
-            const times = this.ctx.stage.curTimes, order = this.ctx.stage.curOrder + 1;
-            for (const d of datas) {
-                const history = d.shistory && d.shistory !== '' ? JSON.parse(d.shistory) : [];
-                const his = history.find(function (x) {
-                    return x.stimes && x.stimes === times
-                        && x.sorder && x.sorder === order;
-                });
-                if (his) {
-                    his.qty = d.qty;
-                    his.tp = d.tp;
-                    if (d.sid === d.add_sid) {
-                        his.quantity = d.quantity;
-                        his.total_price = d.total_price;
-                    }
-                } else {
-                    const nHis = { stimes: times, sorder: order, qty: d.qty, tp: d.tp };
-                    if (d.sid === d.add_sid) {
-                        nHis.quantity = d.quantity;
-                        nHis.total_price = d.total_price;
-                    }
-                    history.push(nHis);
-                }
-                updateDatas.push({ id: d.id, shistory: JSON.stringify(history) });
-            }
-            await transaction.updateRows(this.tableName, updateDatas);
+            await this.updateHistory4TimesOrder(stage, stage.curTimes, [stage.curOrder + 1], transaction);
         }
 
         async addInitialStageData(stage, preStage, transaction) {

+ 1 - 1
app/service/stage_stash.js

@@ -176,7 +176,7 @@ module.exports = app => {
             const result = await this.db.insert(this.tableName, {
                 pid: this.ctx.session.sessionProject.id, tid: stage.tid, sid: stage.id, sorder: stage.order,
                 uid: this.ctx.session.sessionUser.accountId, uname: this.ctx.session.sessionUser.name,
-                filepath, info: JSON.stringify({ status: stage.status, flow: stage.curAuditor ? stage.curAuditor.user_id : stage.uid }), remark
+                filepath, info: JSON.stringify({ status: stage.status, flow: stage.curAuditorIds.length > 0 ? stage.curAuditorIds : stage.uid }), remark
             });
             return result.insertId;
         }

+ 18 - 55
app/service/stage_temp_land.js

@@ -167,74 +167,37 @@ module.exports = app => {
             }
         }
 
-        async updateHistory(stage, transaction) {
+        async updateHistory4TimesOrder(stage, times, order, transaction) {
             const datas = await this.getStageData(stage);
             if (datas.length === 0) return;
 
             const updateDatas = [];
-            const times = this.ctx.stage.curTimes, order = this.ctx.stage.curOrder;
             for (const d of datas) {
-                const history = d.shistory && d.shistory !== '' ? JSON.parse(d.shistory) : [];
-                const his = history.find(function (x) {
-                    return x.stimes && x.stimes === times
-                        && x.sorder !== undefined && x.sorder !== null && x.sorder === order;
-                });
-                if (his) {
-                    his.qty = d.qty;
-                    his.tp = d.tp;
-                } else {
-                    const nHis = { stimes: this.ctx.stage.curTimes, sorder: this.ctx.stage.curOrder, qty: d.qty, tp: d.tp };
-                    history.push(nHis);
+                for (const curOrder of order) {
+                    const history = d.shistory && d.shistory !== '' ? JSON.parse(d.shistory) : [];
+                    const his = history.find(function (x) {
+                        return x.stimes && x.stimes === times
+                            && x.sorder !== undefined && x.sorder !== null && x.sorder === curOrder;
+                    });
+                    if (his) {
+                        his.qty = d.qty;
+                        his.tp = d.tp;
+                    } else {
+                        const nHis = { stimes: times, sorder: curOrder, qty: d.qty, tp: d.tp };
+                        history.push(nHis);
+                    }
+                    updateDatas.push({ id: d.id, shistory: JSON.stringify(history) });
                 }
-                updateDatas.push({ id: d.id, shistory: JSON.stringify(history) });
             }
             await transaction.updateRows(this.tableName, updateDatas);
         }
 
-        async updateHistory4CheckCancel(stage, newTimes, newOrder, transaction) {
-            const datas = await this.getStageData(stage, true);
-            if (datas.length === 0) return;
-
-            const updateDatas = [];
-            for (const d of datas) {
-                const history = d.shistory && d.shistory !== '' ? JSON.parse(d.shistory) : [];
-                const his = history.find(function (x) {
-                    return x.stimes === newTimes && x.sorder === newOrder;
-                });
-                if (his) {
-                    his.qty = d.qty;
-                    his.tp = d.tp;
-                } else {
-                    const nHis = { stimes: newTimes, sorder: newOrder, qty: d.qty, tp: d.tp };
-                    history.push(nHis);
-                }
-                updateDatas.push({ id: d.id, shistory: JSON.stringify(history) });
-            }
-            await transaction.updateRows(this.tableName, updateDatas);
+        async updateHistory(stage, transaction) {
+            await this.updateHistory4TimesOrder(stage, stage.curTimes, [stage.curOrder], transaction);
         }
 
         async updateHistory4CheckAgain(stage, transaction) {
-            const datas = await this.getStageData(stage);
-            if (datas.length === 0) return;
-
-            const updateDatas = [];
-            const times = this.ctx.stage.curTimes, order = this.ctx.stage.curOrder + 1;
-            for (const d of datas) {
-                const history = d.shistory && d.shistory !== '' ? JSON.parse(d.shistory) : [];
-                const his = history.find(function (x) {
-                    return x.stimes && x.stimes === times
-                        && x.sorder && x.sorder === order;
-                });
-                if (his) {
-                    his.qty = d.qty;
-                    his.tp = d.tp;
-                } else {
-                    const nHis = { stimes: times, sorder: order, qty: d.qty, tp: d.tp };
-                    history.push(nHis);
-                }
-                updateDatas.push({ id: d.id, shistory: JSON.stringify(history) });
-            }
-            await transaction.updateRows(this.tableName, updateDatas);
+            await this.updateHistory4TimesOrder(stage, stage.curTimes, [stage.curOrder + 1], transaction);
         }
 
         async addInitialStageData(stage, preStage, transaction) {

+ 74 - 64
app/service/tender_cache.js

@@ -31,7 +31,11 @@ module.exports = app => {
         _analysisTenderCache(tender, cache, uid) {
             commonField.forEach(f => { tender[f] = cache[f]; });
             tender.ledger_tp = cache.ledger_tp ? JSON.parse(cache.ledger_tp) : {};
-            if (!cache.stage_count || (cache.stage_count === 1 && cache.stage_status === auditConst.stage.status.uncheck && cache.stage_flow_cur_uid !== uid)) {
+
+            uid = uid + '';
+            cache.stage_flow_cur_uid = cache.stage_flow_cur_uid ? cache.stage_flow_cur_uid.split(',') : [];
+            cache.stage_flow_pre_uid = cache.stage_flow_pre_uid ? cache.stage_flow_pre_uid.split(',') : [];
+            if (!cache.stage_count || (cache.stage_count === 1 && cache.stage_status === auditConst.stage.status.uncheck && cache.stage_flow_cur_uid.indexOf(uid) < 0)) {
                 tender.cur_flow = JSON.parse(cache.ledger_flow_cur_info || cache.ledger_flow_pre_info);
                 tender.cur_flow.title = '台账';
                 tender.pre_flow = cache.ledger_flow_pre_info ? JSON.parse(cache.ledger_flow_pre_info) : null;
@@ -41,7 +45,7 @@ module.exports = app => {
                     status: auditConst.ledger.tiStatusString[cache.ledger_status],
                     status_class: auditConst.ledger.tiStatusStringClass[cache.ledger_status],
                 };
-            } else if (cache.stage_status !== auditConst.stage.status.uncheck || cache.stage_flow_cur_uid === uid) {
+            } else if (cache.stage_status !== auditConst.stage.status.uncheck || cache.stage_flow_cur_uid.indexOf(uid) >= 0) {
                 tender.stage_status = cache.stage_status;
                 tender.stage_count = tender.stage_count;
                 tender.stage_complete_count = tender.stage_complete_count;
@@ -59,7 +63,7 @@ module.exports = app => {
                 tender.stage_status = auditConst.stage.status.checked;
                 tender.stage_count = tender.stage_complete_count;
                 tender.stage_complete_count = tender.stage_complete_count;
-                tender.cur_flow = (cache.stage_status !== auditConst.stage.status.uncheck || cache.stage_flow_cur_uid !== uid)
+                tender.cur_flow = (cache.stage_status !== auditConst.stage.status.uncheck || cache.stage_flow_cur_uid.indexOf(uid) < 0)
                     ? JSON.parse(cache.stage_flow_pre_info) : JSON.parse(cache.stage_flow_cur_info || cache.stage_flow_pre_info);
                 tender.pre_flow = cache.stage_flow_pre_info ? JSON.parse(cache.stage_flow_pre_info) : null;
                 tender.stage_tp = cache.stage_flow_pre_tp ? JSON.parse(cache.stage_flow_pre_tp) : {};
@@ -114,12 +118,12 @@ module.exports = app => {
 
         async updateLedgerCache(transaction, tid, status, checkType, next, tp) {
             const orgCache = await this.getDataById(tid);
-            const preInfo = JSON.parse(orgCache.ledger_flow_cur_info);
+            const preInfo = JSON.parse(orgCache.ledger_flow_cur_info || orgCache.ledger_flow_pre_info);
             preInfo.time = new Date();
             preInfo.status = checkType;
             const data = {
                 id: tid, ledger_status: status,
-                ledger_flow_pre_uid: orgCache.ledger_flow_cur_uid, ledger_flow_pre_info: JSON.stringify(preInfo),
+                ledger_flow_pre_uid: orgCache.ledger_flow_cur_uid || orgCache.ledger_flow_pre_uid, ledger_flow_pre_info: JSON.stringify(preInfo),
             };
             if (next) {
                 const info = await this.ctx.service.projectAccount.getAccountCacheData(next.audit_id);
@@ -165,15 +169,15 @@ module.exports = app => {
                     pre_positive_qc_tp: preStage.pre_positive_qc_tp, pre_negative_qc_tp: preStage.pre_positive_qc_tp,
                     pre_yf_tp: preStage.pre_yf_tp, pre_sf_tp: preStage.pre_sf_tp,
                 });
-                const auditor = await this.ctx.service.stageAudit.getLastestAuditor(preStage.id, preStage.times, preStage.status);
-                const user_info = JSON.stringify({
-                    order: preStage.order, status: preStage.status, time: auditor.end_time,
+                const auditors = await this.ctx.service.stageAudit.getLastestAuditors(preStage.id, preStage.times, preStage.status);
+                const user_info = auditors.length > 0 ? JSON.stringify(auditors.map(auditor => { return {
+                    order: preStage.order, status: preStage.status, time: auditor.end_time, audit_order: auditor.audit_order,
                     name: auditor.name, company: auditor.company, role: auditor.role, mobile: auditor.mobile, telephone: auditor.telephone,
-                });
+                }})) : '';
                 await transaction.update(this.tableName, {
                     id: delStage.tid, stage_count: preStage.order, stage_complete_count: preStage.order, stage_status: preStage.status,
                     stage_flow_cur_uid: 0, stage_flow_cur_info: '', stage_flow_cur_tp: '',
-                    stage_flow_pre_uid: auditor.aid, stage_flow_pre_info: user_info, stage_flow_pre_tp: tp,
+                    stage_flow_pre_uid: auditors.map(x => { return x.aid; }).join(','), stage_flow_pre_info: user_info, stage_flow_pre_tp: tp,
                 });
             } else {
                 await transaction.update(this.tableName, {
@@ -184,7 +188,7 @@ module.exports = app => {
             }
         }
 
-        async updateStageCache4Start(transaction, stage, status, auditor, ledgerTp, stageTp) {
+        async updateStageCache4Start(transaction, stage, status, auditors, ledgerTp, stageTp) {
             const orgCache = await this.getDataById(stage.tid);
             const data = { id: stage.tid, stage_status: status };
             if (ledgerTp) data.ledger_tp = JSON.stringify(ledgerTp);
@@ -196,42 +200,41 @@ module.exports = app => {
             data.stage_flow_pre_info = JSON.stringify(info);
             data.stage_flow_pre_tp = JSON.stringify(tp);
 
-            data.stage_flow_cur_uid = auditor.aid;
-            const cur_flow_info = await this.ctx.service.projectAccount.getAccountCacheData(auditor.aid);
-            cur_flow_info.order = stage.order;
-            cur_flow_info.status = auditConst.stage.status.checking;
+            const auditIds = auditors.map(x => { return x.aid; });
+            data.stage_flow_cur_uid = auditIds.join(',');
+            const cur_flow_info = await this.ctx.service.projectAccount.getAccountCacheDatas(auditIds,
+                {order: stage.order, audit_type: auditors[0].audit_type, audit_order: 1, status: auditConst.stage.status.checking});
             data.stage_flow_cur_info = JSON.stringify(cur_flow_info);
             data.stage_flow_cur_tp = JSON.stringify(tp);
             await transaction.update(this.tableName, data);
         }
 
-        async updateStageCache4Flow(transaction, stage, status, auditor, preAuditor, ledgerTp, stageTp, pcTp) {
+        async updateStageCache4Flow(transaction, stage, status, auditors, preAuditors, ledgerTp, stageTp, pcTp) {
             const orgCache = await this.getDataById(stage.tid);
             const data = { id: stage.tid, stage_status: status };
             if (ledgerTp) data.ledger_tp = JSON.stringify(ledgerTp);
             const tp = orgCache.stage_flow_cur_tp ? JSON.parse(orgCache.stage_flow_cur_tp) : (orgCache.stage_flow_pre_tp ? JSON.parse(orgCache.stage_flow_pre_tp) : {});
             if (stageTp) this._.assign(tp, stageTp);
             if (pcTp) this._.assign(tp, pcTp);
-            if (preAuditor) {
-                data.stage_flow_pre_uid = preAuditor.aid;
-                const info = await this.ctx.service.projectAccount.getAccountCacheData(preAuditor.aid);
-                info.order = preAuditor.order;
-                info.status = preAuditor.status;
-                info.time = new Date();
-                data.stage_flow_pre_info = JSON.stringify(info);
+            if (preAuditors && preAuditors.length > 0) {
+                const preAuditorIds = preAuditors.map(x => { return x.aid; });
+                data.stage_flow_pre_uid = preAuditorIds.join(',');
+                const info = await this.ctx.service.projectAccount.getAccountCacheDatas(preAuditorIds,
+                    { order: stage.order, status: preAuditors[0].status, audit_type:preAuditors[0].audit_type, audit_order: preAuditors[0].audit_order, time: new Date() });
+                data.stage_flow_pre_info = info.length > 0 ? JSON.stringify(info) : '';
             } else {
                 data.stage_flow_pre_uid = orgCache.stage_flow_cur_uid;
                 data.stage_flow_pre_info = orgCache.stage_flow_cur_info;
             }
             data.stage_flow_pre_tp = JSON.stringify(tp);
-            if (auditor) {
-                data.stage_flow_cur_uid = auditor.aid;
-                const info = await this.ctx.service.projectAccount.getAccountCacheData(auditor.aid);
-                info.order = auditor.order;
-                info.status = status;
-                data.stage_flow_cur_info = JSON.stringify(info);
+
+            if (auditors && auditors.length > 0) {
+                const auditorIds = auditors.map(x => { return x.aid; });
+                data.stage_flow_cur_uid = auditorIds.join(',');
+                const info = await this.ctx.service.projectAccount.getAccountCacheDatas(auditorIds, { order: stage.order, audit_type: auditors[0].audit_type, status, audit_order: auditors[0].audit_order });
+                data.stage_flow_cur_info = info.length > 0 ? JSON.stringify(info) : '';
             }
-            if (status === auditConst.stage.status.checked && !auditor) data.stage_complete_count = stage.order;
+            if (status === auditConst.stage.status.checked && (!auditors || auditors.length === 0)) data.stage_complete_count = stage.order;
             data.stage_flow_cur_tp = JSON.stringify(tp);
             await transaction.update(this.tableName, data);
         }
@@ -362,12 +365,13 @@ module.exports = app => {
                 const preStage = lastStage.order > 1 ? await this.ctx.service.stage.getDataByCondition({ tid: lastStage.tid, order: lastStage.order - 1}) : null;
                 if (lastStage.status === auditConst.stage.status.uncheck) {
                     if (preStage) {
-                        const preAuditor = await this.ctx.service.stageAudit.getLastestAuditor(preStage.id, preStage.times, preStage.status);
-                        data.stage_flow_pre_uid = preAuditor.aid;
-                        data.stage_flow_pre_info = JSON.stringify({
-                            order: preAuditor.order, status: preAuditor.status, time: preAuditor.end_time,
+                        const preAuditors = await this.ctx.service.stageAudit.getLastestAuditors(preStage.id, preStage.times, preStage.status);
+                        const preAuditorIds = preAuditors.map(x => { return x.aid; });
+                        data.stage_flow_pre_uid = preAuditorIds.join(',');
+                        data.stage_flow_pre_info = preAuditors.length > 0 ? JSON.stringify(preAuditors.map(preAuditor => { return {
+                            order: preAuditor.order, audit_type: preAuditor.audit_type, audit_order: preAuditor.order, status: preAuditor.status, time: preAuditor.end_time,
                             name: preAuditor.name, company: preAuditor.company, role: preAuditor.role, mobile: preAuditor.mobile, telephone: preAuditor.telephone,
-                        });
+                        }})) : '';
                         data.stage_flow_pre_tp = JSON.stringify({
                             contract_tp: preStage.contract_tp || 0, qc_tp: preStage.qc_tp || 0, contract_pc_tp: preStage.contract_pc_tp, qc_pc_tp: preStage.qc_pc_tp, pc_tp: preStage.pc_tp,
                             positive_qc_tp: preStage.positive_qc_tp, positive_qc_pc_tp: preStage.positive_qc_pc_tp, negative_qc_tp: preStage.negative_qc_pc_tp, negative_qc_pc_tp: preStage.negative_qc_pc_tp,
@@ -380,34 +384,37 @@ module.exports = app => {
                     lastStage.curOrder = 0;
                     await this._calcTp(lastStage, tp);
                     data.stage_flow_cur_uid = lastStage.user_id;
-                    const curInfo = await this.ctx.service.projectAccount.getAccountCacheData(lastStage.user_id, { order: 0, status: lastStage.status });
+                    const curInfo = await this.ctx.service.projectAccount.getAccountCacheData(lastStage.user_id, { order: lastStage.order, audit_order: 0, audit_type: auditConst.auditType.key.common, status: lastStage.status });
                     data.stage_flow_cur_info = JSON.stringify(curInfo);
                     data.stage_flow_cur_tp = JSON.stringify(tp);
                 } else if (lastStage.status === auditConst.stage.status.checked) {
-                    const preAuditor = await this.ctx.service.stageAudit.getLastestAuditor(lastStage.id, lastStage.times, lastStage.status);
-                    lastStage.curOrder = preAuditor.order;
+                    const preAuditors = await this.ctx.service.stageAudit.getLastestAuditors(lastStage.id, lastStage.times, lastStage.status);
+                    const preAuditorIds = preAuditors.map(x => { return x.aid; });
+                    lastStage.curOrder = preAuditors[0].order;
                     await this._calcTp(lastStage, tp);
-                    data.stage_flow_pre_uid = preAuditor.aid;
-                    data.stage_flow_pre_info = JSON.stringify({
-                        order: preAuditor.order, status: preAuditor.status, time: preAuditor.end_time,
+                    data.stage_flow_pre_uid = preAuditorIds.join(',');
+                    data.stage_flow_pre_info = preAuditors.length > 0 ? JSON.stringify(preAuditors.map(preAuditor => { return {
+                        order: lastStage.order, audit_type: preAuditor.audit_type, audit_order: preAuditor.order, status: preAuditor.status, time: preAuditor.end_time,
                         name: preAuditor.name, company: preAuditor.company, role: preAuditor.role, mobile: preAuditor.mobile, telephone: preAuditor.telephone,
-                    });
+                    }})) : '';
                     data.stage_flow_cur_tp = JSON.stringify(tp);
                     data.stage_flow_pre_tp = JSON.stringify(tp);
                 } else if (lastStage.status === auditConst.stage.status.checkNo) {
                     lastStage.curOrder = 0;
                     await this._calcTp(lastStage, tp);
                     data.stage_flow_cur_uid = lastStage.user_id;
-                    const curInfo = await this.ctx.service.projectAccount.getAccountCacheData(lastStage.user_id, { order: 0, status: auditConst.stage.status.uncheck });
+                    const curInfo = await this.ctx.service.projectAccount.getAccountCacheData(lastStage.user_id,
+                        { order: lastStage.order, audit_order: 0, audit_type: auditConst.auditType.key.common, status: auditConst.stage.status.uncheck });
                     data.stage_flow_cur_info = JSON.stringify(curInfo);
                     data.stage_flow_cur_tp = JSON.stringify(tp);
-                    const preAuditor = await this.ctx.service.stageAudit.getLastestAuditor(lastStage.id, lastStage.times - 1, lastStage.status);
-                    data.stage_flow_pre_uid = preAuditor.aid;
-                    data.stage_flow_pre_info = JSON.stringify({
-                        order: preAuditor.order, status: preAuditor.status, time: preAuditor.end_time,
+                    const preAuditors = await this.ctx.service.stageAudit.getLastestAuditors(lastStage.id, lastStage.times - 1, lastStage.status);
+                    const preAuditorIds = preAuditors.map(x => { return x.aid; });
+                    data.stage_flow_pre_uid = preAuditorIds.join(',');
+                    data.stage_flow_pre_info = preAuditors.length > 0 ? JSON.stringify(preAuditors.map(preAuditor => { return {
+                        order: lastStage.order, audit_type: preAuditor.audit_type, audit_order: preAuditor.order, status: preAuditor.status, time: preAuditor.end_time,
                         name: preAuditor.name, company: preAuditor.company, role: preAuditor.role, mobile: preAuditor.mobile, telephone: preAuditor.telephone,
-                    });
-                    const his = lastStage.tp_history.find(x => { return x.times === preAuditor.times && x.order === preAuditor.order; });
+                    }})) : '';
+                    const his = lastStage.tp_history.find(x => { return x.times === preAuditors[0].times && x.order === preAuditors[0].order; });
                     if (his) {
                         tp.contract_tp = his.contract_tp;
                         tp.qc_tp = his.qc_tp;
@@ -418,29 +425,32 @@ module.exports = app => {
                     }
                     data.stage_flow_pre_tp = JSON.stringify(tp);
                 } else {
-                    const curAuditor = await this.ctx.service.stageAudit.getLastestAuditor(lastStage.id, lastStage.times, lastStage.status);
-                    lastStage.curOrder = curAuditor.order;
+                    const curAuditors = await this.ctx.service.stageAudit.getLastestAuditors(lastStage.id, lastStage.times, lastStage.status);
+                    const curAuditorIds = curAuditors.map(x => { return x.aid; });
+                    lastStage.curOrder = curAuditors[0].order;
                     await this._calcTp(lastStage, tp);
-                    data.stage_flow_cur_uid = curAuditor.aid;
-                    data.stage_flow_cur_info = JSON.stringify({
-                        order: curAuditor.order, status: curAuditor.status,
+                    data.stage_flow_cur_uid = curAuditorIds.join(',');
+                    data.stage_flow_cur_info = JSON.stringify(curAuditors.map(curAuditor => { return {
+                        order: lastStage.order, audit_order: curAuditor.order, status: curAuditor.status,
                         name: curAuditor.name, company: curAuditor.company, role: curAuditor.role, mobile: curAuditor.mobile, telephone: curAuditor.telephone,
-                    });
+                    }}));
                     data.stage_flow_cur_tp = JSON.stringify(tp);
-                    const preAuditor = curAuditor.order > 1 ? await this.ctx.service.stageAudit.getAuditorByOrder(lastStage.id, lastStage.times, curAuditor.order - 1) : null;
-                    if (preAuditor) {
-                        data.stage_flow_pre_uid = preAuditor.aid;
-                        data.stage_flow_pre_info = JSON.stringify({
-                            order: preAuditor.order, status: preAuditor.status, time: curAuditor.begin_time,
+                    const preAuditors = lastStage.curOrder > 1 ? await this.ctx.service.stageAudit.getAuditorsByOrder(lastStage.id, lastStage.times, lastStage.curOrder - 1) : [];
+                    if (preAuditors.length > 0) {
+                        const preAuditorIds = preAuditors.map(x => { return x.aid; });
+                        data.stage_flow_pre_uid = preAuditorIds.join(',');
+                        data.stage_flow_pre_info = JSON.stringify(preAuditors.map(preAuditor => { return {
+                            order: lastStage.order, audit_type: preAuditor.audit_type, audit_order: preAuditor.audit_order, status: preAuditor.status, time: preAuditor.end_time,
                             name: preAuditor.name, company: preAuditor.company, role: preAuditor.role, mobile: preAuditor.mobile, telephone: preAuditor.telephone,
-                        });
+                        }}));
                     } else {
                         data.stage_flow_pre_uid = lastStage.user_id;
-                        const preInfo = await this.ctx.service.projectAccount.getAccountCacheData(lastStage.user_id, { order: 0, status: auditConst.stage.status.uncheck, time: curAuditor.begin_time });
+                        const preInfo = await this.ctx.service.projectAccount.getAccountCacheData(lastStage.user_id,
+                            { order: lastStage.order, audit_order: 0, audit_type: 1, status: auditConst.stage.status.uncheck, time: curAuditors[0].begin_time });
                         data.stage_flow_pre_info = JSON.stringify(preInfo);
                     }
-                    const his = preAuditor
-                        ? lastStage.tp_history.find(x => { return x.times === preAuditor.times && x.order === preAuditor.order; })
+                    const his = preAuditors.length > 0
+                        ? lastStage.tp_history.find(x => { return x.times === preAuditors[0].times && x.order === preAuditors[0].order; })
                         : lastStage.tp_history.find(x => { return x.times === lastStage.times && x.order === 0; });
                     if (his) {
                         tp.contract_tp = his.contract_tp;

+ 2 - 2
app/view/budget/detail_modal.ejs

@@ -7,7 +7,7 @@
                 <h5 class="modal-title">设置小数位数</h5>
             </div>
             <div class="modal-body">
-                <h6 id="del-node-hint">设置对投资估算、初步概算、施工图预算同时生效。</h6>
+                <h6 id="del-node-hint">设置对投资估算、设计概算、施工图预算同时生效。</h6>
                 <div class="form-group">
                     <div class="row">
                         <div class="col-4">
@@ -46,4 +46,4 @@
             </div>
         </div>
     </div>
-</div>
+</div>

+ 157 - 0
app/view/budget/info.ejs

@@ -0,0 +1,157 @@
+<% include ./sub_menu.ejs %>
+<div class="panel-content">
+    <div class="panel-title">
+        <div class="title-main d-flex">
+            <% include ./sub_mini_menu.ejs %>
+            <div>
+                <div class="d-inline-block">
+                    投资概况
+                </div>
+            </div>
+            <div class="ml-auto">
+            </div>
+        </div>
+    </div>
+    <div class="content-wrap my-3">
+        <div class="dashboard-height mx-3">
+            <div class="row agency-partheight mt-3">
+                <div class="col-4 px-0">
+                    <div class="card ml-3">
+                        <div class="card-header card-white d-flex justify-content-between">
+                            <div class="card-big-htext"><span class="card-icon mr-2"></span>建安费增减情况</div>
+                            <div class="mt-1">万元</div>
+                        </div>
+                        <div class="card-body p-0">
+                            <div class="contant-height-one">
+                                <table class="table table-middle">
+                                    <thead class="text-center thead-light">
+                                    <tr>
+                                        <th class="pl-3">费用名称</th>
+                                        <th width="20%">设计概算</th>
+                                        <th width="20%">工程决算</th>
+                                        <th width="20%">增减金额</th>
+                                    </tr>
+                                    </thead>
+                                    <tbody class="text-right" id="jianan-table">
+                                    </tbody>
+                                </table>
+                            </div>
+                        </div>
+                    </div>
+                </div>
+                <div class="col-4 px-0">
+                    <div class="card ml-3">
+                        <div class="card-header card-white d-flex justify-content-between">
+                            <div class="card-big-htext"><span class="card-icon mr-2"></span>增减幅度</div>
+                            <div class="mt-1">万元</div></div>
+                        <div class="card-body p-0">
+                            <style>
+                                @media (max-width: 1400px) {
+                                    .echart-height h5 {
+                                        font-size: 1rem;
+                                    }
+                                    #total_rate {
+                                        font-size: 3.5rem;
+                                    }
+                                }
+                            </style>
+                            <div class="row mx-0 echart-height" style="width: 99.91%;padding:13px 0">
+                                <div class="col-6 px-0 text-right">
+                                    <div class="canyu-pill canyu-bg-yellow ml-2 mr-1 my-2 p-3 text-white">
+                                        <div class="icon"><i class="fa fa-pie-chart"></i></div>
+                                        <div class="pr-2">
+                                            <h5><b id="total_gai_tp">0</b></h5> <h6>设计概算</h6>
+                                        </div>
+                                    </div>
+                                </div>
+                                <div class="col-6 px-0 text-right">
+                                    <div class="canyu-pill canyu-bg-blue my-2 ml-1 mr-2 p-3 text-white">
+                                        <div class="icon"><i class="fa fa-pie-chart"></i></div>
+                                        <div class="pr-2">
+                                            <h5><b id="total_final_tp">0</b></h5> <h6>工程决算</h6>
+                                        </div>
+                                    </div>
+                                </div>
+                            </div>
+                            <div class="contant-height-two">
+                                <div class="canyu-band">
+                                    <h1 id="total_rate" class="">0%</h1>
+                                    <h3 class="text-muted">增减幅度</h3>
+                                </div>
+                            </div>
+                        </div>
+                    </div>
+                </div>
+                <div class="col-4 pl-0">
+                    <div class="card ml-3">
+                        <div class="card-header card-white d-flex justify-content-between">
+                            <div class="card-big-htext"><span class="card-icon mr-2"></span>金额对比</div>
+                            <div class="mt-1">万元</div>
+                        </div>
+                        <div class="card-body p-0">
+                            <div class="contant-height-one">
+                                <div id="jlchart2" style="height: 100%; width: 100%;">
+                                </div>
+                            </div>
+                        </div>
+                    </div>
+                </div>
+            </div>
+            <div class="row agency-partheight">
+                <div class="col-6 px-0">
+                    <div class="card ml-3">
+                        <div class="card-header card-white d-flex justify-content-between">
+                            <div class="card-big-htext"><span class="card-icon mr-2"></span>全过程造价趋势</div>
+                            <div class="mt-1">万元</div>
+                        </div>
+                        <div class="card-body p-0">
+                            <div class="contant-height-one">
+                                <div id="jlchart3" style="height: 90%; width: 100%;">
+                                </div>
+                            </div>
+                        </div>
+                    </div>
+                </div>
+                <div class="col-6 pl-0">
+                    <div class="card ml-3">
+                        <div class="card-header card-white d-flex justify-content-between">
+                            <div class="card-big-htext"><span class="card-icon mr-2"></span>工程变更费用TOP10</div>
+                            <div class="mt-1">元</div>
+                        </div>
+                        <div class="card-body p-0">
+                            <div class="contant-height-one">
+                                <table class="table table-middle">
+                                    <thead  class="text-center thead-light">
+                                    <tr>
+                                        <th width="8%">序号</th>
+                                        <th width="13%">变更令号</th>
+                                        <th width="">变更名称</th>
+                                        <th width="17%">变更性质</th>
+                                        <th width="12%">变更金额</th>
+                                    </tr>
+                                    </thead>
+                                    <tbody class="text-left">
+                                    <% if (changeList.length > 0) { %>
+                                        <% for (const [index, c] of changeList.entries()) { %>
+                                            <tr>
+                                                <td class="text-center"><%- index+1 %></td>
+                                                <td><%- c.p_code %></td>
+                                                <td><%- c.name %></td>
+                                                <td class="text-center"><%- changeConst.qualityName[c.quality] %></td>
+                                                <td class="text-right" ><%- c.total_price %></td>
+                                            </tr>
+                                        <% } %>
+                                    <% } %>
+                                    </tbody>
+                                </table>
+                            </div>
+                        </div>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </div>
+</div>
+<script>
+    const total_change_tp = <%- total_change_tp %>;
+</script>

+ 2 - 2
app/view/budget/list.ejs

@@ -13,7 +13,7 @@
                 </div>
                 <% } else { %>
                 <table class="table table-hover table-bordered">
-                    <tr class="text-center"><th style="min-width: 200px">项目名称</th><th>概预算标准</th><th>创建时间</th><th>投资估算</th><th>初步概算</th><th>施工图预算</th><th>操作</th></tr>
+                    <tr class="text-center"><th style="min-width: 200px">项目名称</th><th>概预算标准</th><th>创建时间</th><th>投资估算</th><th>设计概算</th><th>施工图预算</th><th>操作</th></tr>
                     <tbody id="budgetList">
                     </tbody>
                 </table>
@@ -25,4 +25,4 @@
 <script>
     const budgetList = JSON.parse(unescape('<%- escape(JSON.stringify(budgetList)) %>'));
     const category = JSON.parse(unescape('<%- escape(JSON.stringify(categoryData)) %>'));
-</script>
+</script>

+ 4 - 3
app/view/budget/sub_menu_list.ejs

@@ -1,8 +1,9 @@
 <nav-menu title="返回" url="/budget" tclass="text-primary" ml="1" icon="fa-chevron-left"></nav-menu>
+<nav-menu title="投资概况" url="/budget/<%= ctx.budget.id %>" ml="3" active="<%= ctx.url === '/budget/' + ctx.budget.id ? 1 : -1 %>"></nav-menu>
 <nav-menu title="造价对比" url="/budget/<%= ctx.budget.id %>/compare" ml="3" active="<%= ctx.url.indexOf('/compare') %>"></nav-menu>
 <nav-menu title="投资估算" url="/budget/<%= ctx.budget.id %>/gu" ml="3" active="<%= ctx.url.indexOf('/gu') %>"></nav-menu>
-<nav-menu title="初步概算" url="/budget/<%= ctx.budget.id %>/gai%>" ml="3" active="<%= ctx.url.indexOf('/gai') %>"></nav-menu>
+<nav-menu title="设计概算" url="/budget/<%= ctx.budget.id %>/gai%>" ml="3" active="<%= ctx.url.indexOf('/gai') %>"></nav-menu>
 <nav-menu title="施工图预算" url="/budget/<%= ctx.budget.id %>/yu" ml="3" active="<%= ctx.url.indexOf('/yu') %>"></nav-menu>
-<% if (!ctx.budget.readOnly && ctx.url.indexOf('/compare') === -1 ) { %>
+<% if (!ctx.budget.readOnly && ctx.url.indexOf('/compare') === -1 && ctx.url !== '/budget/' + ctx.budget.id) { %>
 <div class="contarl-box"><button class="btn btn-primary btn-sm btn-block" data-toggle="modal" data-target="#budget-set">设置</button></div>
-<% } %>
+<% } %>

+ 11 - 1
app/view/change/apply_information.ejs

@@ -86,7 +86,17 @@
                             </tr>
                             <tr>
                                 <th width="" class="text-center" style="vertical-align: middle">工程变更类别</th>
-                                <td><input class="form-control form-control-sm" type="text" value="<%- change.class %>" data-name="class" <% if (change.readOnly) { %>readonly<% } %> placeholder=""></td>
+                                <% if (change.readOnly) { %>
+                                    <td><input class="form-control form-control-sm" type="text" value="<%- change.class %>" data-name="class" readonly placeholder=""></td>
+                                <% } else { %>
+                                    <td><select class="form-control form-control-sm" name="class" data-name="class" <% if (change.readOnly) { %>readonly<% } %>>
+                                            <% for (const cc of changeClass) { %>
+                                                <% if (cc.checked) { %>
+                                                    <option <% if (cc.name === change.class || cc.new_name === change.class) { %> selected<% } %>><%- cc.new_name ? cc.new_name : cc.name %></option>
+                                                <% } %>
+                                            <% } %>
+                                        </select></td>
+                                <% } %>
                                 <th width="" class="text-center" style="vertical-align: middle">变更后工程造价(元)</th>
                                 <td><input class="form-control form-control-sm" type="text" value="<%- change.change_price %>" data-name="change_price" <% if (change.readOnly) { %>readonly<% } %> placeholder=""></td>
                             </tr>

+ 9 - 8
app/view/change/information.ejs

@@ -200,9 +200,10 @@
                                 <div class="form-group">
                                     <label>工程变更类别 </label>
                                     <select class="form-control form-control-sm" name="class">
-                                        <% for (const c in changeConst.class) { %>
-                                            <% const cClass = changeConst.class[c] %>
-                                            <option value="<%- cClass.value %>"<% if (cClass.value === change.class) { %> selected<% } %>><%- cClass.name %></option>
+                                        <% for (const cc of changeClass) { %>
+                                            <% if (cc.checked) { %>
+                                            <option value="<%- cc.value %>"<% if (cc.value === change.class) { %> selected<% } %>><%- cc.new_name ? cc.new_name : cc.name %></option>
+                                            <% } %>
                                         <% } %>
                                     </select>
                                 </div>
@@ -309,10 +310,9 @@
                                 <div class="form-group">
                                     <label>工程变更类别 </label>
                                     <select class="form-control form-control-sm" disabled>
-                                        <% for (const c in changeConst.class) { %>
-                                            <% const cClass = changeConst.class[c] %>
-                                            <% if (cClass.value === change.class) { %>
-                                                <option value="<%- cClass.value %>"><%- cClass.name %></option>
+                                        <% for (const cc of changeClass) { %>
+                                            <% if (cc.value === change.class) { %>
+                                                <option value="<%- cc.value %>"><%- cc.new_name ? cc.new_name : cc.name %></option>
                                             <% } %>
                                         <% } %>
                                     </select>
@@ -488,7 +488,7 @@
         expr: JSON.parse(unescape('<%- escape(JSON.stringify(change.expr ? ctx.helper.replaceRntoBr(change.expr) : '')) %>')),
         memo: JSON.parse(unescape('<%- escape(JSON.stringify(change.memo ? ctx.helper.replaceRntoBr(change.memo) : '')) %>')),
         type: '<%- change.type %>',
-        class: '<%- change.class %>',
+        class: '<%- ctx.helper._.find(changeClass, { value: change.class }).checked ? change.class : changeClass[0].value %>',// 防止下拉不存在导致数据丢失上报问题
         quality: '<%- change.quality %>',
         company: JSON.parse(unescape('<%- escape(JSON.stringify((change.company ? change.company : (companyList && companyList[0] ? companyList[0].name : '')))) %>')),
         charge: '<%- change.charge %>',
@@ -497,6 +497,7 @@
     let changeInfo = Object.assign({}, back_changeInfo);
     let changeUsedData = JSON.parse(unescape('<%- escape(JSON.stringify(changeUsedData)) %>'));
     let changeOrder = parseInt('<%- change.order_by %>');
+    console.log(changeInfo);
     console.log(changeUsedData);
 </script>
 <script src="/public/js/change_information_set.js?202206211"></script>

+ 11 - 1
app/view/change/plan_information.ejs

@@ -82,7 +82,17 @@
                             </tr>
                             <tr>
                                 <th width="" class="text-center" style="vertical-align: middle">工程变更类别</th>
-                                <td><input class="form-control form-control-sm" type="text" value="<%- change.class %>" data-name="class" <% if (change.readOnly) { %>readonly<% } %> placeholder=""></td>
+                                <% if (change.readOnly) { %>
+                                    <td><input class="form-control form-control-sm" type="text" value="<%- change.class %>" data-name="class" readonly placeholder=""></td>
+                                <% } else { %>
+                                    <td><select class="form-control form-control-sm" name="class" data-name="class" <% if (change.readOnly) { %>readonly<% } %>>
+                                            <% for (const cc of changeClass) { %>
+                                                <% if (cc.checked) { %>
+                                                    <option <% if (cc.name === change.class || cc.new_name === change.class) { %> selected<% } %>><%- cc.new_name ? cc.new_name : cc.name %></option>
+                                                <% } %>
+                                            <% } %>
+                                        </select></td>
+                                <% } %>
                                 <th width="" class="text-center" style="vertical-align: middle">工程变更性质</th>
                                 <% if (change.readOnly) { %>
                                     <td><input class="form-control form-control-sm" type="text" value="<%- change.quality %>" data-name="quality" readonly placeholder=""></td>

+ 11 - 1
app/view/change/project_information.ejs

@@ -66,7 +66,17 @@
                         </tr>
                         <tr>
                             <th width="140" class="text-center" style="vertical-align: middle">工程变更类别</th>
-                            <td><input class="form-control form-control-sm" type="text" value="<%- change.class %>" data-name="class" <% if (change.readOnly) { %>readonly<% } %> placeholder=""></td>
+                            <% if (change.readOnly) { %>
+                                <td><input class="form-control form-control-sm" type="text" value="<%- change.class %>" data-name="class" readonly placeholder=""></td>
+                            <% } else { %>
+                                <td><select class="form-control form-control-sm" name="class" data-name="class" <% if (change.readOnly) { %>readonly<% } %>>
+                                        <% for (const cc of changeClass) { %>
+                                        <% if (cc.checked) { %>
+                                            <option <% if (cc.name === change.class || cc.new_name === change.class) { %> selected<% } %>><%- cc.new_name ? cc.new_name : cc.name %></option>
+                                        <% } %>
+                                        <% } %>
+                                    </select></td>
+                            <% } %>
                             <th width="140" class="text-center" style="vertical-align: middle">工程变更性质</th>
                             <% if (change.readOnly) { %>
                             <td><input class="form-control form-control-sm" type="text" value="<%- change.quality %>" data-name="quality" readonly placeholder=""></td>

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

@@ -99,7 +99,7 @@
                                                     <td><a href="/tender/<%- t.id %>"><%- t.name %></a></td>
                                                     <td>台账</td>
                                                     <td><%- (t.begin_time ? ctx.moment(t.begin_time).format('YYYY/MM/DD HH:mm') : '') %></td>
-                                                    <td><a href="/tender/<%- t.id %>/ledger/audit" class="btn btn-outline-primary btn-sm btn-table" role="button">审 批</a></td>
+                                                    <td><a href="/tender/<%- t.id %>/ledger" class="btn btn-outline-primary btn-sm btn-table" role="button">审批</a></td>
                                                 </tr>
                                             <% } else { %>
                                                 <tr data-type="5">
@@ -107,7 +107,7 @@
                                                     <td><a href="/tender/<%- t.id %>"><%- t.name %></a></td>
                                                     <td>台账</td>
                                                     <td><%- (t.end_time ? ctx.moment(t.end_time).format('YYYY/MM/DD HH:mm') : '') %></td>
-                                                    <td><a href="/tender/<%- t.id %>/ledger/audit" class="btn btn-outline-warning btn-sm btn-table text-warning" role="button">重新上报</a></td>
+                                                    <td><a href="/tender/<%- t.id %>/ledger" class="btn btn-outline-warning btn-sm btn-table text-warning" role="button">重新上报</a></td>
                                                 </tr>
                                             <% } %>
                                         <% } %>
@@ -131,7 +131,7 @@
                                                     <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><%- 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"><% if (audit.sstatus === acStage.status.checkNoPre) { %>重新<% } %>审 批</a></td>
+                                                    <td><a href="/tender/<%- audit.tid %>/measure/stage/<%- audit.sorder %>" class="btn btn-outline-primary btn-sm btn-table" role="button"><% if (audit.sstatus === acStage.status.checkNoPre) { %>重新<% } %>审批</a></td>
                                                 </tr>
                                             <% } else { %>
                                                 <tr data-type="2">
@@ -241,9 +241,9 @@
                                                     <td>第<%- audit.sorder %>期</td>
                                                     <td><%- ctx.moment(audit.begin_time).format('YYYY/MM/DD HH:mm') %></td>
                                                     <% if (audit.type) { %>
-                                                    <td><a href="/payment/<%- audit.tender_id %>/safe/<%- audit.td_id %>/bills" class="btn btn-outline-primary btn-sm btn-table" role="button"><% if (audit.sstatus === acStage.status.checkNoPre) { %>重新<% } %>审 批</a></td>
+                                                    <td><a href="/payment/<%- audit.tender_id %>/safe/<%- audit.td_id %>/bills" class="btn btn-outline-primary btn-sm btn-table" role="button"><% if (audit.sstatus === acStage.status.checkNoPre) { %>重新<% } %>审批</a></td>
                                                     <% } else { %>
-                                                    <td><a href="/payment/<%- audit.tender_id %>/detail/<%- audit.td_id %>" class="btn btn-outline-primary btn-sm btn-table" role="button"><% if (audit.sstatus === acStage.status.checkNoPre) { %>重新<% } %>审 批</a></td>
+                                                    <td><a href="/payment/<%- audit.tender_id %>/detail/<%- audit.td_id %>" class="btn btn-outline-primary btn-sm btn-table" role="button"><% if (audit.sstatus === acStage.status.checkNoPre) { %>重新<% } %>审批</a></td>
                                                     <% } %>
                                                 </tr>
                                             <% } else { %>

+ 3 - 0
app/view/file/file.ejs

@@ -35,6 +35,9 @@
                     <div class="d-flex flex-row">
                         <% if (canUpload) { %>
                         <div class="py-2 pr-2"><a href="#add-file" data-toggle="modal" data-target="#add-file">上传文件</a></div>
+                        <% if (ctx.app.config.is_debug) { %>
+                        <div class="py-2 pr-2"><a href="#add-big-file" data-toggle="modal" data-target="#add-big-file">大文件上传</a></div>
+                        <% } %>
                         <div class="p-2" id="rela-file-btn"><a href="#rela-file" data-toggle="modal" data-target="#rela-file">导入文件</a></div>
                         <div class="p-2"><a href="javascript: void(0)" id="batch-del-file-btn">批量删除</a></div>
                         <% } %>

+ 24 - 0
app/view/file/file_modal.ejs

@@ -82,6 +82,30 @@
         </div>
     </div>
 </div>
+<div class="modal fade" id="add-big-file" data-backdrop="static">
+    <div class="modal-dialog" role="document">
+        <div class="modal-content">
+            <div class="modal-header">
+                <h5 class="modal-title">上传附件</h5>
+            </div>
+            <div class="modal-body">
+                <div class="form-group">
+                    <label for="formGroupExampleInput">文件大小限制:500MB,支持<span data-toggle="tooltip" data-placement="bottom" title="" data-original-title="doc,docx,xls,xlsx,ppt,pptx,pdf">office等文档格式</span>、<span data-toggle="tooltip" data-placement="bottom" title="" data-original-title="jpg,png,bmp">图片格式</span>、<span data-toggle="tooltip" data-placement="bottom" title="" data-original-title="rar,zip">压缩包格式</span></label>
+                    <input type="file" class="" id="upload-big-file">
+                </div>
+                <div class="form-group progress">
+                    <div id="upload-big-file-progress" class="progress-bar" role="progressbar" aria-valuenow="20" aria-valuemin="0" aria-valuemax="100"></div>
+                </div>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-primary btn-sm" id="add-big-file-stop" style="display: none">暂停</button>
+                <button type="button" class="btn btn-primary btn-sm" id="add-big-file-resume" style="display: none">重新上传</button>
+                <button type="button" class="btn btn-secondary btn-sm" data-dismiss="modal">关闭</button>
+                <button type="button" class="btn btn-primary btn-sm" id="add-big-file-ok">确认</button>
+            </div>
+        </div>
+    </div>
+</div>
 <div class="modal fade" id="batch-del-file" data-backdrop="static" style="display: none;" aria-hidden="true">
     <div class="modal-dialog" role="document">
         <div class="modal-content">

+ 0 - 122
app/view/ledger/audit.ejs

@@ -1,122 +0,0 @@
-<% 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 %>
-            <div class="d-inline-block">
-                <div class="dropdown">
-                    <button class="btn btn-sm btn-light dropdown-toggle text-primary" type="button" id="dropdownMenuButton" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
-                        <i class="fa fa-list-ol"></i> 显示层级
-                    </button>
-                    <div class="dropdown-menu" aria-labelledby="dropdownMenuButton">
-                        <a class="dropdown-item" name="showLevel" tag="1" href="javascirpt: void(0);">第一层</a>
-                        <a class="dropdown-item" name="showLevel" tag="2" href="javascirpt: void(0);">第二层</a>
-                        <a class="dropdown-item" name="showLevel" tag="3" href="javascirpt: void(0);">第三层</a>
-                        <a class="dropdown-item" name="showLevel" tag="4" href="javascirpt: void(0);">第四层</a>
-                        <a class="dropdown-item" name="showLevel" tag="5" href="javascirpt: void(0);">第五层</a>
-                        <a class="dropdown-item" name="showLevel" tag="last" href="javascirpt: void(0);">最底层</a>
-                        <a class="dropdown-item" name="showLevel" tag="leafXmj" href="javascirpt: void(0);">只显示项目节</a>
-                    </div>
-                </div>
-            </div>
-            <div></div>
-            <div class="ml-auto">
-                <% if (tender.ledger_status === auditConst.status.checkNo) { %>
-                    <a href="#sp-list"  data-type="hide" data-toggle="modal" data-target="#sp-list" class="btn btn-outline-warning btn-sm text-dark mr-1 sp-list-btn">退回意见</a>
-                <% } else if (tender.ledger_status === auditConst.status.checking) { %>
-                    <% if (curAuditor.audit_id === ctx.session.sessionUser.accountId) { %>
-                        <a href="#sp-done" data-toggle="modal" data-target="#sp-done" class="btn btn-success btn-sm mr-1">审批通过</a>
-                        <a href="#sp-back" data-toggle="modal" data-target="#sp-back" class="btn btn-warning btn-sm mr-1">审批退回</a>
-                    <% } else {%>
-                        <a href="#sp-list"  data-type="hide" data-toggle="modal" data-target="#sp-list" class="btn btn-outline-warning btn-sm text-dark mr-1 sp-list-btn">审批中</a>
-                    <% } %>
-                <% } else if (tender.ledger_status === auditConst.status.checked) { %>
-                <a href="#sp-list" data-type="hide" data-toggle="modal" data-target="#sp-list" class="btn btn-outline-success btn-sm text-dark mr-1 sp-list-btn">审批通过</a>
-                <% } %>
-            </div>
-        </div>
-    </div>
-    <div class="content-wrap row pr-46">
-        <div class="c-header p-0 col-12">
-        </div>
-        <!--核心内容(两栏)-->
-        <div class="row w-100 sub-content">
-            <!--左栏-->
-            <div class="c-body" id="left-view" style="width: 100%">
-                <div class="sjs-height-1" id="ledger-spread">
-                </div>
-                <% if (tender.measure_type === measureType.tz.value) { %>
-                <div id="main-resize" r-Type="height" div1="#ledger-spread" div2="#main-bottom" store-id="audit-main" store-version="1.0.0" min="100"></div>
-                <div class="bcontent-wrap" id="main-bottom">
-                    <div class="bc-bar mb-1">
-                        <ul class="nav nav-tabs">
-                            <li class="nav-item">
-                                <a class="nav-link active" href="javascript:void(0)">计量单元</a>
-                            </li>
-                            <li class="nav-item" id="pos-search">
-                            </li>
-                        </ul>
-                    </div>
-                    <div class="sp-wrap" id="pos-spread">
-                    </div>
-                </div>
-                <% } %>
-            </div>
-            <!--右栏-->
-            <div class="c-body" id="right-view" style="display: none; width: 33%">
-                <div class="resize-x" id="right-spr" r-Type="width" div1="#left-view" div2="#right-view" title="调整大小" a-type="percent"><!--调整左右高度条--></div>
-                <div class="tab-content">
-                    <div id="deal-bills" class="tab-pane">
-                        <div class="sjs-bar-2">
-                            签约清单
-                            <a href="/tender/<%- ctx.tender.id %>/deal/download/签约清单.xlsx" class="btn btn-sm btn-primary" style="display: none">下载签约清单</a>
-                        </div>
-                        <div class="sjs-sh-2" id="deal-bills-spread">
-                        </div>
-                    </div>
-                    <div id="search" class="tab-pane">
-                    </div>
-                </div>
-            </div>
-        </div>
-        <!--右侧菜单-->
-        <div class="side-menu">
-            <!--右侧菜单-->
-            <ul class="nav flex-column right-nav">
-                <li class="nav-item">
-                    <a class="nav-link" href="javascript: void(0);" role="tab" content="#deal-bills">签约清单</a>
-                </li>
-                <li class="nav-item">
-                    <a class="nav-link" href="javascript: void(0);" role="tab" content="#search">查找定位</a>
-                </li>
-            </ul>
-        </div>
-    </div>
-</div>
-<script type="text/javascript">
-    const tender = JSON.parse('<%- JSON.stringify(tender) %>');
-    const thousandth = <%- ctx.tender.info.display.thousandth %>;
-    const measureType = JSON.parse('<%- JSON.stringify(measureType) %>');
-    let ledgerSpreadSetting = '<%- ledgerSpreadSetting %>';
-    ledgerSpreadSetting = JSON.parse(ledgerSpreadSetting);
-    ledgerSpreadSetting.localCache = {
-        key: 'ledger-bills',
-        colWidth: true,
-    };
-    let posSpreadSetting = JSON.parse('<%- posSpreadSetting %>');
-    posSpreadSetting.localCache = {
-        key: 'ledger-pos',
-        colWidth: true,
-    };
-    $('.sp-list-btn').click(function () {
-        const type = $(this).data('type')
-        if (type === 'hide') {
-            $('.sp-list-item').hide()
-            $('.modal-title').text('审批流程')
-        } else {
-            $('.sp-list-item').show()
-            $('.modal-title').text('重新上报')
-        }
-    });
-    const nodeType = JSON.parse('<%- JSON.stringify(nodeType) %>');
-</script>

+ 137 - 177
app/view/ledger/audit_modal.ejs

@@ -147,9 +147,8 @@
                                             <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] : ''%>
+                                                        <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 ? auditors[index-1].name : '' %>
                                                         </span>
@@ -383,193 +382,63 @@
         </form>
     </div>
 </div>
-<% } else { %>
-<!--审批流程/结果-->
-<div class="modal fade" id="sp-list" data-backdrop="static">
-    <div class="modal-dialog modal-lg" role="document">
+<% } %>
+<% if (tender.ledger_status === auditConst.status.checked && auditors[auditors.length - 1].audit_id === ctx.session.sessionUser.accountId && !tender.hasStage && !tender.hasRevise) { %>
+<% if (!authMobile && ctx.session.sessionUser.loginStatus === 0) { %>
+<!--终审重新审批-->
+<div class="modal fade" id="sp-down-back" data-backdrop="static">
+    <div class="modal-dialog" role="document">
         <div class="modal-content">
             <div class="modal-header">
-                <h5 class="modal-title"><%- tender.ledger_status === auditConst.status.checked ? '审批流程' : '重新上报' %></h5>
+                <h5 class="modal-title">重新审批</h5>
             </div>
             <div class="modal-body">
-                <div class="row">
-                    <div class="col-4">
-                        <% if(tender.ledger_status === auditConst.status.checkNo) { %>
-                            <a class="sp-list-item" href="#sub-sp" data-toggle="modal" data-target="#sub-sp" id="hideSp">修改审批流程</a>
-                        <% } %>
-                        <div class="card mt-3">
-                            <ul class="list-group list-group-flush">
-                                <% auditors.forEach((item, idx) => { %>
-                                <% if (idx === 0) { %>
-                                <li class="list-group-item" data-auditorId="<%- item.audit_id %>">
-                                    <i class="fa fa fa-play-circle fa-rotate-90"></i> <%- item.name %>
-                                    <small class="text-muted"><%- item.role %></small>
-                                    <span class="pull-right">原报</span>
-                                </li>
-                                <% } else if(idx === auditors.length -1 && idx !== 0) { %>
-                                <li class="list-group-item" data-auditorId="<%- item.audit_id %>">
-                                    <i class="fa fa fa-stop-circle"></i> <%- item.name %>
-                                    <small class="text-muted"><%- item.role %></small>
-                                    <span class="pull-right">终审</span>
-                                </li>
-                                <% } else {%>
-                                <li class="list-group-item" data-auditorId="<%- item.audit_id %>">
-                                    <i class="fa fa-chevron-circle-down"></i> <%- item.name %>
-                                    <small class="text-muted"><%- item.role %></small>
-                                    <span class="pull-right"><%= ctx.helper.transFormToChinese(idx) %>审</span>
-                                </li>
-                                <% } %>
-                                <% }) %>
-                            </ul>
-                        </div>
-                    </div>
-                    <div class="col-8 modal-height-500" style="overflow: auto">
-                        <% auditHistory.forEach((auditors, idx) => { %>
-                            <!-- 展开/收起历史流程 -->
-                        <% if(idx === auditHistory.length - 1 && auditHistory.length !== 1) { %>
-                            <div class="text-right">
-                                <a href="javascript: void(0);" id="fold-btn" data-target="show">展开历史审批流程</a>
-                            </div>
-                        <% } %>
-                        <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) => { %>
-                                <% if (index === 0) { %>
-                                <li class="timeline-list-item pb-2">
-                                    <div class="timeline-item-date">
-                                        <%- ctx.helper.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">
-                                        <%- ctx.helper.formatDate(auditor.end_time) %>
-                                    </div>
-                                    <% if(index < auditors.length - 1) { %>
-                                    <div class="timeline-item-tail"></div>
-                                    <% } %>
-                                    <% if(auditor.status === auditConst.status.checked) { %>
-                                    <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) {%>
-                                    <div class="timeline-item-icon bg-warning text-light">
-                                        <i class="fa fa-level-up"></i>
-                                    </div>
-                                    <% } else if(auditor.status === auditConst.status.checking) { %>
-                                    <div class="timeline-item-icon bg-warning text-light">
-                                        <i class="fa fa-ellipsis-h"></i>
-                                    </div>
-                                    <% } else {%>
-                                    <div class="timeline-item-icon bg-secondary text-light">
-                                    </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"><%- 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) { %>
-                                            <div class="card-body p-3 border-top">
-                                                <p style="margin: 0;"><%- auditor.opinion %></p>
-                                            </div>
-                                            <% } %>
-                                        </div>
-                                    </div>
-                                </li>
-                                <% } else {%>
-                                <li class="timeline-list-item pb-2">
-                                    <div class="timeline-item-date">
-                                        <%- ctx.helper.formatDate(auditor.end_time) %>
-                                    </div>
-                                    <% if(index < auditors.length - 1) { %>
-                                    <div class="timeline-item-tail"></div>
-                                    <% } %>
-                                    <% if(auditor.status === auditConst.status.checked) { %>
-                                    <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) {%>
-                                    <div class="timeline-item-icon bg-warning text-light">
-                                        <i class="fa fa-level-up"></i>
-                                    </div>
-                                    <% } else if(auditor.status === auditConst.status.checking) { %>
-                                    <div class="timeline-item-icon bg-warning text-light">
-                                        <i class="fa fa-ellipsis-h"></i>
-                                    </div>
-                                    <% } else { %>
-                                    <div class="timeline-item-icon bg-secondary text-light">
-                                    </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"><%- 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 : '' %>
-                                                        </span>
-                                                    </p>
-                                                    <p class="text-muted mb-0"><%- auditor.role %></p>
-                                                </div>
-                                            </div>
-                                            <!--审批意见-->
-                                            <% if (auditor.opinion) { %>
-                                            <div class="card-body p-3 border-top">
-                                                <p style="margin: 0;"><%- auditor.opinion %></p>
-                                            </div>
-                                            <% } %>
-                                        </div>
-                                    </div>
-                                </li>
-                                <% } %>
-                                <% }) %>
-                            </ul>
+                <h5>重新审批需要您的手机短信验证</h5>
+                <h5>您目前还没设置认证手机,请先设置。</h5>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-sm btn-secondary" data-dismiss="modal">取消</button>
+                <a href="/profile/sms" class="btn btn-sm btn-primary">去设置</a>
+            </div>
+        </div>
+    </div>
+</div>
+<% } else { %>
+<div class="modal fade" id="sp-down-back" data-backdrop="static">
+    <div class="modal-dialog" role="document">
+        <form class="modal-content" action="<%- preUrl %>/ledger/audit/check/again" method="post"
+              onsubmit="return auditAgainCheck();">
+            <div class="modal-header">
+                <h5 class="modal-title">重新审批</h5>
+            </div>
+            <div class="modal-body">
+                <h5>确认由「终审-<%= auditors[auditors.length-1].name %>」重新审批 ?
+                </h5>
+                <% if (ctx.session.sessionUser.loginStatus === 0) { %>
+                <div class="form-group">
+                    <label>重审需要验证码确认,验证码将发送至尾号<%- authMobile.slice(-4) %>的手机</label>
+                    <div class="input-group input-group-sm mb-3">
+                        <input class="form-control" type="text" readonly="readonly" name="code"
+                               placeholder="输入短信中的6位验证码" />
+                        <div class="input-group-append">
+                            <button class="btn btn-outline-secondary" type="button" id="get-code">获取验证码</button>
                         </div>
-
-                        <% }) %>
                     </div>
                 </div>
+                <% } %>
             </div>
-            <form class="modal-footer" method="post" action="<%- preUrl %>/ledger/audit/start" onsubmit="return checkAuditorFrom()">
-                <input type="hidden" name="_csrf_j" value="<%= ctx.csrf %>">
+            <div class="modal-footer">
                 <button type="button" class="btn btn-secondary btn-sm" data-dismiss="modal">关闭</button>
-                <% if(tender.ledger_status === auditConst.status.checkNo && ctx.session.sessionUser.accountId === tender.user_id) { %>
-                    <button class="btn btn-primary btn-sm sp-list-item" type="submit">确认上报</button>
-                <% } %>
-            </form>
-        </div>
+                <input type="hidden" name="_csrf_j" value="<%= ctx.csrf %>" />
+                <button <% if (ctx.session.sessionUser.loginStatus === 0) { %>disabled<% } %> id="re-shenpi-btn"
+                        class="btn btn-warning btn-sm">确定重审</button>
+            </div>
+        </form>
     </div>
 </div>
 <% } %>
+<% } %>
 <script>
-    const cur_uid  = parseInt('<%- ctx.session.sessionUser.accountId %>');
     $('.sp-location-list').on('shown.bs.modal', function () {
         const scrollBox = $(this).find('div[class="col-8 modal-height-500"]');
         const bdiv = (scrollBox.offset() && scrollBox.offset().top) || 0;
@@ -603,4 +472,95 @@
             })
         }
     });
+
+    // 重新审批获取手机验证码
+    // 获取验证码
+    let isPosting = false;
+    $("#get-code").click(function() {
+        if (isPosting) {
+            return false;
+        }
+        const btn = $(this);
+
+        $.ajax({
+            url: '/profile/code?_csrf_j=' + csrf,
+            type: 'post',
+            data: { mobile: authMobile, type: 'shenpi' },
+            dataTye: 'json',
+            error: function() {
+                isPosting = false;
+            },
+            beforeSend: function() {
+                isPosting = true;
+            },
+            success: function(response) {
+                isPosting = false;
+                if (response.err === 0) {
+                    codeSuccess(btn);
+                    $("input[name='code']").removeAttr('readonly');
+                    $("#re-shenpi-btn").removeAttr('disabled');
+                } else {
+                    toast(response.msg, 'error');
+                }
+            }
+        });
+    });
+    /**
+     * 获取成功后的操作
+     *
+     * @param {Object} btn - 点击的按钮
+     * @return {void}
+     */
+    function codeSuccess(btn) {
+        let counter = 60;
+        btn.addClass('disabled').text('重新获取 ' + counter + 'S');
+        btn.parent().siblings('input').removeAttr('readonly').attr('placeholder', '输入短信中的6位验证码');
+        const bindBtn = $("#bind-btn");
+        bindBtn.removeClass('btn-secondary disabled').addClass('btn-primary');
+
+        const countDown = setInterval(function() {
+            const countString = counter - 1 <= 0 ? '' : ' ' + (counter - 1) + 'S';
+            // 倒数结束后
+            if (countString === '') {
+                clearInterval(countDown);
+                btn.removeClass('disabled');
+            }
+            const text = '重新获取' + countString;
+            btn.text(text);
+            counter -= 1;
+        }, 1000);
+    }
+
+    // 重新审批按钮
+    $("#re-shenpi-btn").click(function () {
+        const data = {
+        };
+
+        $.ajax({
+            url: '<%- preUrl %>/audit/check/again',
+            type: 'get',
+            data: data,
+            dataTye: 'json',
+            success: function (response) {
+                if (response.err === 0) {
+                    window.location.href = response.url;
+                } else {
+                    toast(response.msg, 'error');
+                }
+            }
+        });
+    });
+    function auditAgainCheck() {
+        <% if (ctx.session.sessionUser.loginStatus === 0) { %>
+        const code = $("#sp-down-back input[name='code']").val();
+        if ($(this).hasClass('disabled')) {
+            return false;
+        }
+        if (code.length < 6) {
+            toastr.error('请填写正确的验证码');
+            return false;
+        }
+        <% } %>
+        return true;
+    }
 </script>

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

@@ -52,7 +52,7 @@
                 <a class="btn btn-sm btn-primary mr-1" id="ledger-check2" href="javascript: void(0);">数据检查</a>
                 <% if (tender.ledger_status === auditConst.status.checkNo) { %>
                     <a href="#sp-list" data-type="hide" data-toggle="modal" data-target="#sp-list" class="btn btn-outline-warning btn-sm text-dark sp-list-btn">审批退回</a>
-                <% } else if (tender.ledger_status === auditConst.status.checking) { %>
+                <% } else if (tender.ledger_status === auditConst.status.checking && curAuditor.audit_id !== ctx.session.sessionUser.accountId) { %>
                     <a href="#sp-list" data-type="hide" data-toggle="modal" data-target="#sp-list" class="btn btn-outline-warning btn-sm text-dark sp-list-btn">审批中</a>
                 <% } else if (tender.ledger_status === auditConst.status.checked) { %>
                     <a href="#sp-list" data-type="hide" data-toggle="modal" data-target="#sp-list" class="btn btn-outline-secondary btn-sm sp-list-btn">审批完成</a>
@@ -64,6 +64,14 @@
                         <a href="#sp-list" data-type="show" data-toggle="modal" data-target="#sp-list" class="btn btn-primary btn-sm sp-list-btn" style="margin-right: 5px;">重新上报</a>
                     <% } %>
                 <% } %>
+
+                <% if (tender.ledger_status === auditConst.status.checking && curAuditor.audit_id === ctx.session.sessionUser.accountId) { %>
+                <a href="#sp-done" data-toggle="modal" data-target="#sp-done" class="btn btn-success btn-sm mr-1">审批通过</a>
+                <a href="#sp-back" data-toggle="modal" data-target="#sp-back" class="btn btn-warning btn-sm mr-1">审批退回</a>
+                <% }%>
+                <% if (tender.ledger_status === auditConst.status.checked && auditors[auditors.length - 1].audit_id === ctx.session.sessionUser.accountId && !tender.hasStage && !tender.hasRevise) { %>
+                <a href="javascript: void(0);" data-toggle="modal" data-target="#sp-down-back" class="btn btn-warning btn-sm mr-1">重新审批</a>
+                <% }%>
             </div>
         </div>
     </div>

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

@@ -452,3 +452,4 @@
 <% include ../shares/new_tag_modal.ejs %>
 <% include ../shares/tender_select_modal.ejs %>
 <% include ../shares/db2full_code.ejs %>
+<% include ./audit_modal.ejs %>

+ 3 - 3
app/view/measure/compare.ejs

@@ -43,15 +43,15 @@
                     <div class="d-inline-block" style="vertical-align: middle">
                         <div class="form-check form-check-inline">
                             <input class="form-check-input pt-1" type="radio" id="radio_contract" value="contract" name="compare-data">
-                            <label class="form-check-label" for="radio_contract">合同</label>
+                            <label class="form-check-label" for="radio_contract">合同计量</label>
                         </div>
                         <div class="form-check form-check-inline">
                             <input class="form-check-input" type="radio" id="radio_qc" value="qc" name="compare-data">
-                            <label class="form-check-label" for="radio_qc">变更</label>
+                            <label class="form-check-label" for="radio_qc">变更计量</label>
                         </div>
                         <div class="form-check form-check-inline">
                             <input class="form-check-input" type="radio" id="radio_gather" value="gather" checked name="compare-data">
-                            <label class="form-check-label" for="radio_gather">完成</label>
+                            <label class="form-check-label" for="radio_gather">完成计量</label>
                         </div>
                     </div>
                 </div>

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

@@ -80,8 +80,12 @@
                         <td class="text-right"><%- (s.sf_tp ? s.sf_tp : '') %></td>
                         <% } %>
                         <td class="<%- auditConst.auditProgressClass[s.status] %>">
-                            <% if (s.curAuditor) { %>
-                            <a href="#sp-list" data-toggle="modal" data-target="#sp-list" s-order="<%- s.order %>"><%- s.curAuditor.name %><%if (s.curAuditor.role !== '' && s.curAuditor.role !== null) { %>-<%- s.curAuditor.role %><% } %></a>
+                            <% 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>

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

@@ -156,6 +156,7 @@
     });
     const tenderId = '<%- ctx.tender.id %>';
     const auditConst = JSON.parse('<%- auditConst2 %>');
+    const auditType = JSON.parse('<%- JSON.stringify(auditType) %>');
 
     $('#audit-list').on('click', 'a', function() {
     const type = $(this).data('target')

+ 23 - 0
app/view/payment/list_modal.ejs

@@ -228,6 +228,9 @@
                         <li class="nav-item">
                             <a class="nav-link" data-toggle="tab" href="#jscs" role="tab">技术参数</a>
                         </li>
+                        <li class="nav-item">
+                            <a class="nav-link" data-toggle="tab" href="#fkzh" role="tab">付款账号</a>
+                        </li>
                     </ul>
                     <div class="tab-content">
                         <div class="tab-pane active" id="htxx">
@@ -576,6 +579,26 @@
                                 </div>
                             </div>
                         </div>
+                        <div class="tab-pane" id="fkzh">
+                            <table class="table table-bordered">
+                                <tr><th colspan="2" class="text-center">工程款账户</th></tr>
+                                <tr><td width="120">开户名称</td><td><input type="text" id="project-name" class="form-control form-control-sm" oninput="limitReturn(this)"></td></tr>
+                                <tr><td>开户银行</td><td><input type="text" id="project-bank" class="form-control form-control-sm" oninput="limitReturn(this)"></td></tr>
+                                <tr><td>开户账号</td><td><input type="text" id="project-account" class="form-control form-control-sm" oninput="limitReturn(this)"></td></tr>
+                                <tr><td>分账划拨比例(%)</td><td><input type="text" id="project-rate" class="form-control form-control-sm" oninput="limitReturn(this)"></td></tr>
+                                <tr><td>联系人</td><td><input type="text" id="project-contact" class="form-control form-control-sm" oninput="limitReturn(this)"></td></tr>
+                                <tr><td>联系电话</td><td><input type="text" id="project-phone" class="form-control form-control-sm" oninput="limitReturn(this)"></td></tr>
+                                <tr><td>行号</td><td><input type="text" id="project-num" class="form-control form-control-sm" oninput="limitReturn(this)"></td></tr>
+                                <tr><th colspan="2" class="text-center">农民工工资专用账户</th></tr>
+                                <tr><td>开户名称</td><td><input type="text" id="worker-name" class="form-control form-control-sm" oninput="limitReturn(this)"></td></tr>
+                                <tr><td>开户银行</td><td><input type="text" id="worker-bank" class="form-control form-control-sm" oninput="limitReturn(this)"></td></tr>
+                                <tr><td>开户账号</td><td><input type="text" id="worker-account" class="form-control form-control-sm" oninput="limitReturn(this)"></td></tr>
+                                <tr><td>分账划拨比例(%)</td><td><input type="text" id="worker-rate" class="form-control form-control-sm" oninput="limitReturn(this)"></td></tr>
+                                <tr><td>联系人</td><td><input type="text" id="worker-contact" class="form-control form-control-sm" oninput="limitReturn(this)"></td></tr>
+                                <tr><td>联系电话</td><td><input type="text" id="worker-phone" class="form-control form-control-sm" oninput="limitReturn(this)"></td></tr>
+                                <tr><td>行号</td><td><input type="text" id="worker-num" class="form-control form-control-sm" oninput="limitReturn(this)"></td></tr>
+                            </table>
+                        </div>
                     </div>
                 </div>
             </div>

+ 1 - 1
app/view/payment/process.ejs

@@ -27,7 +27,7 @@
                                 <div class="col-8 mt-3">
                                     <div class="card" id="safe">
                                         <div class="card-body">
-                                            <h5 class="card-title">安全生费</h5>
+                                            <h5 class="card-title">安全生费</h5>
                                             <div  class="my-3">
                                                 <div class="d-flex justify-content-start align-items-center mt-3" name="user">
                                                     <div class="mr-2">上报人<b class="text-danger">*</b></div>

+ 97 - 3
app/view/setting/fun.ejs

@@ -143,10 +143,13 @@
                             </div>
                         </div>
                         <div class="row">
-                            <div class="col-5">
+                            <div class="col-6">
                                 <div class="card mb-3">
                                     <div class="card-body">
                                         <h5 class="card-title">工程变更</h5>
+                                        <div class="alert alert-dark py-1 px-2 mb-2" role="alert">
+                                            页面显示
+                                        </div>
                                         <div class="form-group mb-1">
                                             <div class="form-check form-check-inline">
                                                 <input class="form-check-input" type="checkbox" id="openChangeProject" <% if(ctx.session.sessionProject.page_show.openChangeProject) { %>checked<% } %> onchange="updateSetting(1);">
@@ -165,10 +168,17 @@
                                                 <label class="form-check-label" for="openChangePlan">显示「变更方案」页面</label>
                                             </div>
                                         </div>
+                                        <div class="alert alert-dark py-1 px-2 mb-2" role="alert">
+                                            功能设置
+                                        </div>
+                                        <div class="my-2">
+                                            自定义变更类别
+                                            <a class="pull-right mr-3" href="#bgclass" data-toggle="modal" data-target="#bgclass">设置</a>
+                                        </div>
                                     </div>
                                 </div>
                             </div>
-                            <div class="col-7">
+                            <div class="col-6">
                                 <div class="card mb-3">
                                     <div class="card-body">
                                         <h5 class="card-title">材料调差</h5>
@@ -226,10 +236,44 @@
 </div>
 <script src="/public/js/setting.js"></script>
 <script>
+    let changeClass = JSON.parse(unescape('<%- escape(JSON.stringify(funSet.change_class)) %>'));
     $(() => {
         autoFlashHeight();
+        // 自定义变更类别
+        $('body').on('click', '#change_class_div input[type="checkbox"]', function () {
+            if ($(this).is(':checked')) {
+                $(this).parents('.input-group-prepend').siblings('input').removeAttr('readonly');
+            } else {
+                $(this).parents('.input-group-prepend').siblings('input').attr('readonly', true).val('');
+            }
+        });
+        $('body').on('change', '#change_class_div input[type="text"]', function () {
+            if ($(this).val().length > 8) {
+                toastr.error('类别名称不能超过8个字符');
+            }
+        });
+
+        $("#bgclass").on('show.bs.modal', function () {
+            let html = '';
+            for (const cc of changeClass) {
+                html += `<div class="input-group input-group-sm mb-1">
+                    <div class="input-group-prepend">
+                        <div class="input-group-text">
+                            <input type="checkbox" value="${cc.value}" aria-label="Checkbox for following text input" ${cc.checked ? 'checked' : ''}>
+                        </div>
+                        <span class="input-group-text">${cc.name}</span>
+                    </div>
+                    <input type="text" class="form-control" value="${cc.new_name}" ${!cc.checked ? 'readonly' : ''} aria-label="Text input with checkbox" placeholder="">
+                </div>`;
+            }
+            $('#change_class_div').html(html);
+        });
+
+        $('#set_change_class_btn').click(function () {
+            updateSetting(false, 1);
+        });
     });
-    const updateSetting = function (tab = false) {
+    const updateSetting = function (tab = false, setChangeClass = false) {
         if (!$('#openChangeApply')[0].checked && $('#openChangeProject')[0].checked && tab === 1) {
             $('#openChangeApply').prop('checked', true);
             $('#openChangePlan').prop('checked', true);
@@ -296,6 +340,50 @@
             $('#end_month').removeAttr('disabled');
             $('#end_day').removeAttr('disabled');
         }
+        if (setChangeClass) {
+            const change_class = [];
+            let flag = false;
+            let allUnchecked = true;
+            $('#change_class_div .input-group').each(function () {
+                const one_class = _.find(changeClass, { value: parseInt($(this).find('input[type="checkbox"]').val())});
+                if ($(this).find('input[type="checkbox"]').is(':checked') && $(this).find('input[type="text"]').val().length > 8) {
+                    toastr.error(one_class.name + ' 类别名称字符不能超过8个');
+                    flag = true;
+                }
+                const new_name = $(this).find('input[type="checkbox"]').is(':checked') ? $(this).find('input[type="text"]').val() : '';
+                if (new_name) {
+                    const classInfo = _.find(changeClass, function(item) {
+                        return item.name === new_name && item.value !== one_class.value;
+                    })
+                    if (classInfo) {
+                        toastr.error(one_class.name + '的类别名称不能与其他变更名称相同');
+                        flag = true;
+                    }
+                    if (_.findIndex(change_class, { new_name }) !== -1) {
+                        toastr.error(one_class.name + '的类别名称不能与其他类别名称相同');
+                        flag = true;
+                    }
+                }
+                if ($(this).find('input[type="checkbox"]').is(':checked')) {
+                    allUnchecked = false;
+                }
+                const one_updateClass = {
+                    value: one_class.value,
+                    name: one_class.name,
+                    new_name,
+                    checked: $(this).find('input[type="checkbox"]').is(':checked'),
+                }
+                change_class.push(one_updateClass);
+            });
+            if (flag) {
+                return;
+            }
+            if (allUnchecked) {
+                toastr.error('至少保留一个变更类别');
+                return;
+            }
+            pushData.change_class = change_class;
+        }
         postData('/setting/fun/update', {
             imType: parseInt($('[name=im_type]:checked').val()),
             banOver: $('[name=ban_over]')[0].checked,
@@ -313,6 +401,12 @@
             openMaterialEditForAudit: $('#openMaterialEditForAudit')[0].checked,
             openStageStart: $('#openStageStart')[0].checked,
             addFunSet: _.size(pushData) !== 0 ? pushData : null,
+        }, function (result) {
+            if (setChangeClass) {
+                changeClass = pushData.change_class;
+                toastr.success('设置成功');
+                $('#bgclass').modal('hide');
+            }
         });
     }
 </script>

+ 30 - 0
app/view/setting/fun_modal.ejs

@@ -0,0 +1,30 @@
+<!--设置z自定义变更类别-->
+<div class="modal fade" id="bgclass" data-backdrop="static">
+    <div class="modal-dialog" role="document">
+        <div class="modal-content">
+            <div class="modal-header">
+                <h5 class="modal-title">自定义变更类别</h5>
+            </div>
+            <div class="modal-body">
+                <div class="alert alert-warning">设置后,工程变更-变更类别仅显示显示勾选的类别。</div>
+                <div id="change_class_div">
+                    <% for (const cc of funSet.change_class) { %>
+                        <div class="input-group input-group-sm mb-1">
+                            <div class="input-group-prepend">
+                                <div class="input-group-text">
+                                    <input type="checkbox" value="<%- cc.value %>" aria-label="Checkbox for following text input" <% if (cc.checked) { %>checked<% } %>>
+                                </div>
+                                <span class="input-group-text"><%- cc.name %></span>
+                            </div>
+                            <input type="text" class="form-control" value="<%- cc.new_name %>" <% if (!cc.checked) { %>readonly<% } %> aria-label="Text input with checkbox" placeholder="">
+                        </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="set_change_class_btn">确认</button>
+                </div>
+            </div>
+        </div>
+    </div>
+</div>

+ 22 - 2
app/view/setting/show.ejs

@@ -37,7 +37,7 @@
                         <div class="form-group">
                             <label>台账列显示</label>
                             <table class="table table-hover table-bordered">
-                                <thead><tr><th>名称</th><th>是否显示</th><th>别名</th><th></th></tr></thead>
+                                <thead><tr><th>名称</th><th>是否显示</th><th>别名</th><th>位置</th></tr></thead>
                                 <tbody id="ledger_col_list">
                                 <% for (const sl of sjsRela.ledgerCol) { %>
                                 <tr>
@@ -53,7 +53,19 @@
                                             <input type="text" class="form-control" value="<%- sl.alias %>" onchange="updateColAlias('ledgerCol', '<%- sl.field%>', this);">
                                         </div>
                                     </td>
-                                    <td><span class="form-control-sm text-warning"><%- sl.hint || '' %></span></td>
+                                    <td>
+                                        <% if (sl.pos) { %>
+                                        <div class="input-group input-group-sm">
+                                            <select class="form-control form-control-sm" onchange="updateColPos('ledgerCol', '<%- sl.field%>', this)" value="<%- sl.pos %>" org="<%- sl.pos %>">
+                                                <% for (const r of settingConst.exMemoPosRange[sl.field]) { %>
+                                                <option value="<%- r.key %>" <% if (sl.pos === r.key) {%>selected<% } %>><%- r.hint %></option>
+                                                <% } %>
+                                            </select>
+                                        </div>
+                                        <% } else { %>
+                                        <span class="form-control-sm text-warning"><%- sl.hint || '' %></span>
+                                        <% } %>
+                                    </td>
                                 </tr>
                                 <% } %>
                                 </tbody>
@@ -87,4 +99,12 @@
             obj.value = obj.getAttribute('org');
         });
     };
+    const updateColPos = function(sub, field, obj) {
+        const data = { sub, field, key: 'pos', value: obj.value };
+        postData('show/update-sjs', data, function() {
+            obj.setAttribute('org', obj.value);
+        }, function () {
+            obj.value = obj.getAttribute('org');
+        });
+    }
 </script>

+ 10 - 2
app/view/setting/user_unit.ejs

@@ -25,7 +25,7 @@
                         <div class="row">
                             <div class="col-6">
                                 <table class="table table-hover table-bordered table-sm">
-                                    <thead>
+                                    <thead id="unit_header">
                                     <tr>
                                         <th>序号</th>
                                         <th>单位名称</th>
@@ -129,6 +129,8 @@
 </div>
 <script>
 </script>
+<script src="/public/js/PinYinOrder.bundle.js"></script>
+<script src="/public/js/setting_unit_list_order.js"></script>
 <script src="/public/js/setting.js"></script>
 <script>
     const unitList = JSON.parse(unescape('<%- escape(JSON.stringify(unitList)) %>'));
@@ -136,4 +138,10 @@
     const fujianOssPath = JSON.parse(unescape('<%- escape(JSON.stringify(fujianOssPath)) %>'));
     let oneUnit = unitList.length > 0 ? unitList[0] : null;
 </script>
-<script>autoFlashHeight();</script>
+<script>
+    autoFlashHeight();
+    $(function () {
+        unitListOrder.resetHeaderHtml();
+        unitListOrder.reOrderUnits();
+    })
+</script>

+ 0 - 0
app/view/stage/audit_btn.ejs


Một số tệp đã không được hiển thị bởi vì quá nhiều tập tin thay đổi trong này khác