Browse Source

会签或签,第一次提交代码

MaiXinRong 2 years ago
parent
commit
5b32689bf9
40 changed files with 2341 additions and 2112 deletions
  1. 20 0
      app/const/audit.js
  2. 4 20
      app/controller/measure_controller.js
  3. 2 14
      app/controller/report_archive_controller.js
  4. 2 14
      app/controller/report_controller.js
  5. 14 49
      app/controller/stage_controller.js
  6. 7 255
      app/controller/tender_controller.js
  7. 26 0
      app/extend/helper.js
  8. 31 62
      app/middleware/stage_check.js
  9. 115 113
      app/public/js/measure_stage.js
  10. 209 47
      app/public/js/shenpi.js
  11. 0 10
      app/public/js/stage.js
  12. 14 10
      app/public/js/stage_audit.js
  13. 2 3
      app/public/js/stage_compare.js
  14. 5 1
      app/public/js/tender_list.js
  15. 9 3
      app/public/js/tender_list_info.js
  16. 18 4
      app/public/js/tender_list_progress.js
  17. 0 4
      app/router.js
  18. 9 0
      app/service/project_account.js
  19. 63 12
      app/service/shenpi_audit.js
  20. 116 37
      app/service/stage.js
  21. 775 533
      app/service/stage_audit.js
  22. 1 2
      app/service/stage_bills.js
  23. 16 49
      app/service/stage_bonus.js
  24. 20 0
      app/service/stage_change.js
  25. 22 70
      app/service/stage_jgcl.js
  26. 19 58
      app/service/stage_other.js
  27. 1 2
      app/service/stage_pos.js
  28. 39 78
      app/service/stage_safe_prod.js
  29. 18 55
      app/service/stage_temp_land.js
  30. 57 52
      app/service/tender_cache.js
  31. 1 0
      app/view/measure/stage_modal.ejs
  32. 3 3
      app/view/stage/audit_btn.ejs
  33. 303 378
      app/view/stage/audit_modal.ejs
  34. 8 8
      app/view/stage/compare_modal.ejs
  35. 0 2
      app/view/stage/index.ejs
  36. 177 73
      app/view/tender/detail.ejs
  37. 176 73
      app/view/tender/modal.ejs
  38. 37 1
      app/view/tender/shenpi.ejs
  39. 0 16
      app/view/tender/sub_menu.ejs
  40. 2 1
      sql/update.sql

+ 20 - 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 = {
@@ -116,6 +132,7 @@ const stage = (function() {
         checkNoPre: 5, // 审批退回上一人
         checkAgain: 6, // 重新审批 // 该状态仅可用于,终审退回时,修改原终审的审批状态,并同时新增一条新的终审审批中记录
         checkCancel: 7, // 撤回 // 该状态为上一审批人可发起,回到它到审批阶段,并同时新增一条新的审批中记录
+        checkSkip: 8, // 跳过
     };
 
     // 流程状态提示
@@ -168,6 +185,7 @@ const stage = (function() {
     auditString[status.checkNoPre] = '审批退回';
     auditString[status.checkAgain] = '重新审批';
     auditString[status.checkCancel] = '撤回';
+    auditString[status.checkSkip] = '审批通过';
     // 文字样式
     const auditStringClass = [];
     auditStringClass[status.uncheck] = '';
@@ -177,6 +195,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 +709,7 @@ const pushType = {
 };
 
 module.exports = {
+    auditType,
     ledger,
     stage,
     revise,

+ 4 - 20
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,6 +45,7 @@ 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);
@@ -69,29 +71,11 @@ 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.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);

+ 14 - 49
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);
         }
 
@@ -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);
@@ -1319,7 +1304,7 @@ module.exports = app => {
                 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: [] };
@@ -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));

+ 7 - 255
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);
@@ -1155,7 +906,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 +957,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 +992,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 +1016,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 +1036,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 };

+ 26 - 0
app/extend/helper.js

@@ -1606,5 +1606,31 @@ 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 });
     }
 };

+ 31 - 62
app/middleware/stage_check.js

@@ -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,23 @@ 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.curAuditorIds.indexOf(x.user_id) >= 0 && x.ass_user_id === accountId; });
+                stage.readOnly = stage.curAuditorIds.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;
                 }
+                stage.readOnly = stage.readOnly && this._.isEqual(stage.flowAuditorIds, stage.curAuditorIds);
             }
             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,63 +117,43 @@ 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.audit_order !== audit.audit_order) sameAudit = false;
+                        }
+                    }
+                    if (!sameAudit) {
+                        yield this.service.stageAudit.updateNewAuditList(stage, shenpiList);
                     }
                 } 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 > 1 || shenpiInfo.audit_id !== lastAuditors[0].aid)) {
                         yield this.service.stageAudit.updateLastAudit(stage, auditList, shenpiInfo.audit_id);
                     } else if (!shenpiInfo) {
                         // 不存在终审人的状态下这里恢复为授权审批人

+ 115 - 113
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,133 +27,122 @@ $('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>
+                <span class="badge badge-light badge-pill ml-auto"><small>终审</small></span>
+                ${getAuditTypeHtml(group[0].audit_type)}
+                </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"></i></span>
+                <span class="text-muted">${getGroupAuditHtml(group)}</span>
+                <span class="badge badge-light badge-pill ml-auto"><small>${transFormToChinese(idx)}审</small></span>
+                ${getAuditTypeHtml(group[0].audit_type)}
+                </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 title="${group.beginTime}">${group.beginDate}</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 title="${group.beginTime}">${group.beginDate}</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">');
+                historyHTML.push(`<div class="py-1">
+                        <span class="text-black-50">
+                        ${ !group.is_final ? group.audit_order + '' : '终' }审 ${getAuditTypeText(group.audit_type)}
+                        </span>
+                        <span class="pull-right ${auditConst.statusClass[group.status]}">${auditConst.statusString[group.status]}</span>
+                    </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(''));
     })
 
 });

+ 209 - 47
app/public/js/shenpi.js

@@ -222,6 +222,115 @@ 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>');
+            console.log(html.join('\n'));
+            return html.join('');
+        },
+        getAuditGroupHtml: function (code, auditGroup, i) {
+            return `<li class="d-flex justify-content-start 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.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 +413,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, [], i + 1);
                 }
                 addhtml += '</ul>\n';
                 _self.parents('.form-group').siblings('.lc-show').html(addhtml);
@@ -370,25 +481,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);
+                    _self.parents('li').html(auditUtils.getAuditGroupInnerHtml(this_code, auditGroup, prop.audit_order));
+                    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>');
+                    }
+                } 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 +526,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 +561,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,7 +628,6 @@ $(document).ready(function () {
         return html;
     }
 
-
     initTenderTree();
 
     $('.set-otherTender').on('click', function () {
@@ -563,7 +685,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 +713,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 +746,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 +825,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);
-                });
-                const oldUidList = [];
-                $('#stage_audits option').each(function (k, v) {
-                    const uid = parseInt($(v).val());
-                    if(k !== 0) oldUidList.push(uid);
+                    if(uid) newUidList.push({ order: k + 1, audit_id: 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 +958,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 {

+ 0 - 10
app/public/js/stage.js

@@ -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) {

+ 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) {
+                    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.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
+                ? transFormToChinese(node.cur_flow[0].audit_order) + '审'
+                : 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>');

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

@@ -53,8 +53,10 @@ const tenderListSpec = (function(){
         // 当前流程
         html.push('<td style="width: 8%">');
         if (!node.cid && node.cur_flow) {
+            const curUser = node.cur_flow instanceof Array
+                ? transFormToChinese(node.cur_flow[0].audit_order) + '审'
+                : 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 +66,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>');
         // 签约合同价

+ 0 - 4
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');

+ 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 : '';

+ 63 - 12
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.update(updateData);
                 if (parseInt(data.code) === shenpiConst.sp_type.stage && parseInt(data.status) === shenpiConst.sp_status.gdspl) {
                     const options = {
                         where: {
@@ -198,6 +230,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 +259,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);

+ 116 - 37
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) {
+                stage.auditors2 = stage.userGroups;
+            } else {
+                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
+                }]);
+            }
+            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,38 +157,30 @@ 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.curAuditorIds.indexOf(x.user_id) >= 0 && x.ass_user_id === accountId; });
+                stage.readOnly = stage.curAuditorIds.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需按原来的取值
+                stage.readOnly = stage.readOnly && this._.isEqual(stage.flowAuditorIds, stage.curAuditorIds);
             }
             if (stage.readOnly) {
                 stage.assist = accountId === stage.user_id || stage.auditorIds.indexOf(accountId) >= 0
                     ? 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 +191,59 @@ 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})) 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 = _.find(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 +267,7 @@ module.exports = app => {
             });
             if (!includeUnCheck) {
                 this.sqlBuilder.setAndWhere('status', {
-                    value: auditConst.status.uncheck,
+                    value: auditConst.stage.status.uncheck,
                     operate: '!=',
                 });
             }
@@ -211,7 +290,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 +324,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 +382,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 +432,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 +444,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,
             };
@@ -763,11 +842,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 +862,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 +878,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);

File diff suppressed because it is too large
+ 775 - 533
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) {

+ 20 - 0
app/service/stage_change.js

@@ -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;

+ 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) {

+ 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) {

+ 57 - 52
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) : {};
@@ -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 = 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,39 @@ 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, 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();
+            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_order: preAuditors[0].audit_order, time: new Date() });
                 data.stage_flow_pre_info = 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;
+
+            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, status, audit_order: auditors[0].audit_order });
                 data.stage_flow_cur_info = 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);
         }
@@ -384,14 +385,15 @@ module.exports = app => {
                     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 = JSON.stringify(preAuditors.map(preAuditor => { return {
+                        order: lastStage.order, 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) {
@@ -401,13 +403,14 @@ module.exports = app => {
                     const curInfo = await this.ctx.service.projectAccount.getAccountCacheData(lastStage.user_id, { order: 0, 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 = JSON.stringify(preAuditors.map(preAuditor => { return {
+                        order: lastStage.order, 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 +421,31 @@ 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) : null;
+                    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_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,
-                        });
+                        }}));
                     } 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 });
                         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;

+ 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')

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

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

+ 303 - 378
app/view/stage/audit_modal.ejs

@@ -43,14 +43,25 @@
                         </div>
                         <div class="modal-height-500" style="overflow: auto">
                             <ul class="list-group list-group-flush" id="auditors">
-                                <% for (let i = 0, iLen = ctx.stage.auditorList.length; i < iLen; i++) { %>
-                                <li class="list-group-item" auditorId="<%- ctx.stage.auditorList[i].aid %>">
-                                    <% if ((ctx.tender.info.shenpi.stage === shenpiConst.sp_status.sqspr ||
-                                            (ctx.tender.info.shenpi.stage === shenpiConst.sp_status.gdzs && i+1 !== iLen)) && ctx.session.sessionUser.accountId === ctx.stage.user_id && !ctx.tender.isTourist) { %>
-                                    <a href="javascript: void(0)" class="text-danger pull-right">移除</a>
-                                    <% } %>
-                                    <span><%- ctx.stage.auditorList[i].order %> <%- ctx.stage.auditorList[i].name %></span>
-                                    <small class="text-muted"><%- ctx.stage.auditorList[i].role %></small>
+                                <% for (let i = 0, iLen = ctx.stage.auditorGroups.length; i < iLen; i++) { %>
+                                <li class="list-group-item d-flex" auditorId="<%- ctx.stage.auditorGroups[i][0].aid %>">
+                                    <div class="col-auto"><%- i+1 %></div>
+                                    <div class="col">
+                                        <% for (const auditor of ctx.stage.auditorGroups[i]) { %>
+                                        <div class="d-inline-block mx-1" auditorId="<%- auditor.aid %>">
+                                            <i class="fa fa-user text-muted"></i> <%- auditor.name %> <small class="text-muted"><%- auditor.role %></small>
+                                        </div>
+                                        <% } %>
+                                    </div>
+                                    <div class="col-auto">
+                                        <% if (ctx.stage.auditorGroups[i][0].audit_type !== auditType.key.common) { %>
+                                            <span class="badge badge-pill badge-<%- auditType.info[ctx.stage.auditorGroups[i][0].audit_type].class %> badge-bg-small"><small><%- auditType.info[ctx.stage.auditorGroups[i][0].audit_type].long%></small></span>
+                                        <% } %>
+                                        <% if ((ctx.tender.info.shenpi.stage === shenpiConst.sp_status.sqspr ||
+                                                        (ctx.tender.info.shenpi.stage === shenpiConst.sp_status.gdzs && i+1 !== iLen)) && ctx.session.sessionUser.accountId === ctx.stage.user_id && !ctx.tender.isTourist) { %>
+                                        <a href="javascript: void(0)" class="text-danger pull-right">移除</a>
+                                        <% } %>
+                                    </div>
                                 </li>
                                 <% } %>
                             </ul>
@@ -68,9 +79,8 @@
         </div>
     </div>
     <% } %>
-
 <% } %>
-<% if(ctx.stage && (ctx.stage !== auditConst.status.uncheck)) { %>
+<% if(ctx.stage && (ctx.stage.status !== auditConst.status.uncheck)) { %>
     <!--审批流程/结果-->
     <div class="modal fade" id="sp-list" data-backdrop="static">
         <div class="modal-dialog modal-lg" role="document">
@@ -92,22 +102,40 @@
                                 <ul class="list-group list-group-flush auditors-list" id="auditors-list">
                                     <% ctx.stage.auditors2.forEach((item, idx) => { %>
                                     <% if (idx === 0) { %>
-                                    <li class="list-group-item" data-auditorId="<%- item.aid %>">
-                                        <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 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">
+                                            <% for (const u of item) { %>
+                                            <small class="d-inline-block text-dark mx-1" title="<%- u.role %>" data-auditorId="<%- u.aid %>"><%- u.name %></small>
+                                            <% } %>
+                                        </span>
+                                        <span class="badge badge-light badge-pill ml-auto"><small>原报</small></span>
                                     </li>
                                     <% } else if(idx === ctx.stage.auditors2.length -1 && idx !== 0) { %>
-                                    <li class="list-group-item" data-auditorId="<%- item.aid %>">
-                                        <i class="fa fa fa-stop-circle"></i> <%- item.name %>
-                                        <small class="text-muted"><%- item.role %></small>
-                                        <span class="pull-right">终审</span>
+                                    <li class="list-group-item d-flex justify-content-between align-items-center">
+                                        <span class="mr-1"><i class="fa fa fa-stop-circle"></i></span>
+                                        <span class="text-muted">
+                                            <% for (const u of item) { %>
+                                            <small class="d-inline-block text-dark mx-1" title="<%- u.role %>" data-auditorId="<%- u.aid %>"><%- u.name %></small>
+                                            <% } %>
+                                        </span>
+                                        <span class="badge badge-light badge-pill ml-auto"><small>终审</small></span>
+                                        <% if (item[0].audit_type !== auditType.key.common) { %>
+                                        <div class="li-subscript"><span class="badge badge-pill badge-<%-  auditType.info[item[0].audit_type].class %> p-1 badge-bg-small"><small><%- auditType.info[item[0].audit_type].short %></small></span></div>
+                                        <% } %>
                                     </li>
                                     <% } else {%>
-                                    <li class="list-group-item" data-auditorId="<%- item.aid %>">
-                                        <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 class="list-group-item d-flex justify-content-between align-items-center">
+                                        <span class="mr-1"><i class="fa fa-chevron-circle-down"></i></span>
+                                        <span class="text-muted">
+                                            <% for (const u of item) { %>
+                                            <small class="d-inline-block text-dark mx-1" title="<%- u.role %>" data-auditorId="<%- u.aid %>"><%- u.name %></small>
+                                            <% } %>
+                                        </span>
+                                        <span class="badge badge-light badge-pill ml-auto"><small><%= ctx.helper.transFormToChinese(idx) %>审</small></span>
+                                        <% if (item[0].audit_type !== auditType.key.common) { %>
+                                        <div class="li-subscript"><span class="badge badge-pill badge-<%-  auditType.info[item[0].audit_type].class %> p-1 badge-bg-small"><small><%- auditType.info[item[0].audit_type].short %></small></span></div>
+                                        <% } %>
                                     </li>
                                     <% } %>
                                     <% }) %>
@@ -115,7 +143,7 @@
                             </div>
                         </div>
                         <div class="col-8 modal-height-500" style="overflow: auto">
-                            <% ctx.stage.auditHistory.forEach((auditors, idx) => { %>
+                            <% ctx.stage.auditHistory.forEach((his, idx) => { %>
                                 <!-- 展开/收起历史流程 -->
                                 <% if(idx === ctx.stage.auditHistory.length - 1 && ctx.stage.auditHistory.length !== 1) { %>
                                     <div class="text-right">
@@ -125,125 +153,90 @@
                                 <div class="<%- idx < ctx.stage.auditHistory.length - 1 ? 'fold-card' : '' %>">
                                     <div class="text-center text-muted"><%- idx+1 %>#</div>
                                     <ul class="timeline-list list-unstyled mt-2 <% if (idx === ctx.stage.auditHistory.length - 1 && ctx.stage.auditHistory.length !== 1) { %>last-auditor-list<% } %>">
-                                        <% auditors.forEach((auditor, index) => { %>
+                                        <% his.forEach((group, index) => { %>
                                         <% if (index === 0) { %>
                                         <li class="timeline-list-item pb-2">
                                             <div class="timeline-item-date">
-                                                <%- ctx.helper.formatDate(auditor.begin_time) %>
+                                                <%- group.beginYear %>
+                                                <span title="<%- group.beginTime %>"><%- group.beginDate %></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-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"><%- ctx.stage.user.name %></span><span
-                                                                    class="pull-right text-success"><%- idx !== 0 ? '重新' : '' %>上报审批</span>
-                                                            </p>
-                                                            <p class="text-muted mb-0"><%- ctx.stage.user.role %></p>
-                                                        </div>
-                                                    </div>
+                                                <div class="py-1">
+                                                    <span class="text-black-50">原报</span>
+                                                    <span class="pull-right text-success"><%- idx !== 0 ? '重新' : '' %>上报审批</span>
                                                 </div>
-                                            </div>
-                                        </li>
-                                        <li class="timeline-list-item pb-2 <% if (auditor.status === auditConst.status.uncheck && idx === ctx.stage.auditHistory.length - 1 && ctx.stage.auditHistory.length !== 1) { %>is_uncheck<% } %>">
-                                            <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 || auditor.status === auditConst.status.checkCancel) {%>
-                                            <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 class="card-body px-3 py-0">
+                                                        <div class="card-text p-2 py-3 row">
+                                                            <div class="col">
+                                                                <span class="h6"><%- ctx.stage.user.name %></span>
+                                                                <span class="text-muted ml-1"><%- ctx.stage.user.role %></span>
+                                                            </div>
+                                                            <div class="col">
+                                                                <span class="pull-right text-success"><i class="fa fa-check-circle"></i></span>
+                                                            </div>
                                                         </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 <% if (auditor.status === auditConst.status.uncheck && idx === ctx.stage.auditHistory.length - 1 && ctx.stage.auditHistory.length !== 1) { %>is_uncheck<% } %>">
+                                        <% } %>
+                                        <li class="timeline-list-item pb-2 <% if (group.status === auditConst.status.uncheck && idx === ctx.stage.auditHistory.length - 1 && ctx.stage.auditHistory.length !== 1) { %>is_uncheck<% } %>">
+                                            <% if (group.endYear) { %>
                                             <div class="timeline-item-date">
-                                                <%- ctx.helper.formatDate(auditor.end_time) %>
+                                                <%- group.beginYear %>
+                                                <span title="<%- group.beginTime %>"><%- group.beginDate %></span>
                                             </div>
-                                            <% if(index < auditors.length - 1) { %>
+                                            <% } %>
+                                            <% if (index < his.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 || auditor.status === auditConst.status.checkCancel) {%>
-                                            <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>
+                                            <% if (group.status === auditConst.status.checked) { %>
+                                            <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) { %>
+                                            <div class="timeline-item-icon bg-warning text-light"><i class="fa fa-level-up"></i></div>
+                                            <% } else if (group.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-icon bg-secondary text-light"></div>
                                             <% } %>
                                             <div class="timeline-item-content">
+                                                <div class="py-1">
+                                                        <span class="text-black-50">
+                                                            <%- (!group.is_final ? group.audit_order : '终') %>审
+                                                            <% if (group.audit_type !== auditType.key.common) { %><span class="text-<%- auditType.info[group.audit_type].class %> "><%- auditType.info[group.audit_type].long %></span><% } %>
+                                                        </span>
+                                                    <span class="pull-right <%- auditConst.statusClass[group.status] %>"><%- auditConst.statusString[group.status] %></span>
+                                                </div>
                                                 <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 ? ctx.stage.user.name : '' %>
-                                                                    <%- auditor.status === auditConst.status.checkNoPre && auditor.sort - 1 > 0 ? ctx.helper._.find(auditors, { sort: auditor.sort - 1 }).name : '' %>
-                                                                </span>
-                                                            </p>
-                                                            <p class="text-muted mb-0"><%- auditor.role %></p>
+                                                    <div class="card-body px-3 py-0">
+                                                        <% for (const [i, auditor] of group.auditors.entries()) { %>
+                                                        <div class="card-text p-2 py-3 row <%- ( i > 0 ? 'border-top' : '') %>">
+                                                            <div class="col">
+                                                                <span class="h6"><%- auditor.name %></span>
+                                                                <span class="text-muted ml-1"><%- auditor.role %></span>
+                                                            </div>
+                                                            <div class="col">
+                                                                <% if (auditor.status === auditConst.status.checked) { %>
+                                                                <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) { %>
+                                                                <span class="pull-right text-warning"><i class="fa fa-share-square fa-rotate-270"></i></span>
+                                                                <% } %>
+                                                            </div>
+                                                            <% if (auditor.opinion) { %>
+                                                            <div class="col-12 py-1 bg-light"><i class="fa fa-commenting-o mr-1"></i><%- auditor.opinion%></div>
+                                                            <% } %>
                                                         </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>
                                 </div>
-
                             <% }) %>
                         </div>
                     </div>
@@ -260,7 +253,7 @@
     </div>
 <% } %>
 <% if (ctx.stage && (ctx.stage.status === auditConst.status.checking || ctx.stage.status === auditConst.status.checkNoPre)) { %>
-    <% if (ctx.stage.curAuditor && ctx.stage.curAuditor.aid === ctx.session.sessionUser.accountId) { %>
+    <% if (ctx.stage.curAuditorIds.indexOf(ctx.session.sessionUser.accountId) >= 0) { %>
     <!--审批通过-->
     <div class="modal fade sp-location-list" id="sp-done" data-backdrop="static">
         <div class="modal-dialog modal-lg" role="document">
@@ -279,22 +272,41 @@
                                 <ul class="list-group list-group-flush auditors-list">
                                     <% ctx.stage.auditors2.forEach((item, idx) => { %>
                                     <% if (idx === 0) { %>
-                                    <li class="list-group-item" data-auditorId="<%- item.aid %>">
-                                        <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 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">
+                                            <% for (const u of item) { %>
+                                            <small class="d-inline-block text-dark mx-1" title="<%- u.role %>" data-auditorId="<%- u.aid %>"><%- u.name %></small>
+                                            <% } %>
+                                        </span>
+                                        <span class="badge badge-light badge-pill ml-auto"><small>原报</small></span>
                                     </li>
                                     <% } else if(idx === ctx.stage.auditors2.length -1 && idx !== 0) { %>
-                                    <li class="list-group-item" data-auditorId="<%- item.aid %>">
-                                        <i class="fa fa fa-stop-circle"></i> <%- item.name %>
-                                        <small class="text-muted"><%- item.role %></small>
-                                        <span class="pull-right">终审</span>
+
+                                    <li class="list-group-item d-flex justify-content-between align-items-center">
+                                        <span class="mr-1"><i class="fa fa fa-stop-circle"></i></span>
+                                        <span class="text-muted">
+                                            <% for (const u of item) { %>
+                                            <small class="d-inline-block text-dark mx-1" title="<%- u.role %>" data-auditorId="<%- u.aid %>"><%- u.name %></small>
+                                            <% } %>
+                                        </span>
+                                        <span class="badge badge-light badge-pill ml-auto"><small>终审</small></span>
+                                        <% if (item[0].audit_type !== auditType.key.common) { %>
+                                        <div class="li-subscript"><span class="badge badge-pill badge-<%-  auditType.info[item[0].audit_type].class %> p-1 badge-bg-small"><small><%- auditType.info[item[0].audit_type].short %></small></span></div>
+                                        <% } %>
                                     </li>
                                     <% } else {%>
-                                    <li class="list-group-item" data-auditorId="<%- item.aid %>">
-                                        <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 class="list-group-item d-flex justify-content-between align-items-center">
+                                        <span class="mr-1"><i class="fa fa-chevron-circle-down"></i></span>
+                                        <span class="text-muted">
+                                            <% for (const u of item) { %>
+                                            <small class="d-inline-block text-dark mx-1" title="<%- u.role %>" data-auditorId="<%- u.aid %>"><%- u.name %></small>
+                                            <% } %>
+                                        </span>
+                                        <span class="badge badge-light badge-pill ml-auto"><small><%= ctx.helper.transFormToChinese(idx) %>审</small></span>
+                                        <% if (item[0].audit_type !== auditType.key.common) { %>
+                                        <div class="li-subscript"><span class="badge badge-pill badge-<%-  auditType.info[item[0].audit_type].class %> p-1 badge-bg-small"><small><%- auditType.info[item[0].audit_type].short %></small></span></div>
+                                        <% } %>
                                     </li>
                                     <% } %>
                                     <% }) %>
@@ -302,7 +314,7 @@
                             </div>
                         </div>
                         <div class="col-8 modal-height-500" style="overflow: auto">
-                            <% ctx.stage.auditHistory.forEach((auditors, idx) => { %>
+                            <% ctx.stage.auditHistory.forEach((his, idx) => { %>
                                 <!-- 展开/收起历史流程 -->
                             <% if(idx === ctx.stage.auditHistory.length - 1 && ctx.stage.auditHistory.length !== 1) { %>
                                 <div class="text-right">
@@ -312,136 +324,97 @@
                             <div class="<%- idx < ctx.stage.auditHistory.length - 1 ? 'fold-card' : '' %>">
                                 <div class="text-center text-muted"><%- idx+1 %>#</div>
                                 <ul class="timeline-list list-unstyled mt-2 <% if (idx === ctx.stage.auditHistory.length - 1 && ctx.stage.auditHistory.length !== 1) { %>last-auditor-list<% } %>">
-                                    <% auditors.forEach((auditor, index) => { %>
+                                    <% his.forEach((group, index) => { %>
                                     <% if (index === 0) { %>
                                     <li class="timeline-list-item pb-2">
                                         <div class="timeline-item-date">
-                                            <%- ctx.helper.formatDate(auditor.begin_time) %>
+                                            <%- group.beginYear %>
+                                            <span title="<%- group.beginTime %>"><%- group.beginDate %></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-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"><%- ctx.stage.user.name %></span><span
-                                                                class="pull-right text-success"><%- idx !== 0 ? '重新' : '' %>上报审批</span>
-                                                        </p>
-                                                        <p class="text-muted mb-0"><%- ctx.stage.user.role %></p>
-                                                    </div>
-                                                </div>
+                                            <div class="py-1">
+                                                <span class="text-black-50">原报</span>
+                                                <span class="pull-right text-success"><%- idx !== 0 ? '重新' : '' %>上报审批</span>
                                             </div>
-                                        </div>
-                                    </li>
-                                    <li class="timeline-list-item pb-2 <% if (auditor.status === auditConst.status.uncheck && idx === ctx.stage.auditHistory.length - 1 && ctx.stage.auditHistory.length !== 1) { %>is_uncheck<% } %>">
-                                        <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 || auditor.status === auditConst.status.checkCancel) {%>
-                                        <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 class="card-body px-3 py-0">
+                                                    <div class="card-text p-2 py-3 row">
+                                                        <div class="col">
+                                                            <span class="h6"><%- ctx.stage.user.name %></span>
+                                                            <span class="text-muted ml-1"><%- ctx.stage.user.role %></span>
+                                                        </div>
+                                                        <div class="col">
+                                                            <span class="pull-right text-success"><i class="fa fa-check-circle"></i></span>
+                                                        </div>
                                                     </div>
                                                 </div>
-                                                <!--审批意见-->
-                                                <% if(auditor.status !== auditConst.status.uncheck) { %>
-                                                <div class="card-body p-3 border-top">
-                                                    <% if (ctx.stage.times === idx + 1 && auditor.status === auditConst.status.checking) { %>
-                                                    <label>审批意见<b class="text-danger">*</b></label>
-                                                    <textarea class="form-control form-control-sm"
-                                                        name="opinion">同意</textarea>
-                                                    <% } else { %>
-                                                    <p style="margin: 0;"><%- auditor.opinion %></p>
-                                                    <% } %>
-                                                </div>
-                                                <% } %>
                                             </div>
                                         </div>
                                     </li>
-                                    <% } else {%>
-                                    <li class="timeline-list-item pb-2 <% if (auditor.status === auditConst.status.uncheck && idx === ctx.stage.auditHistory.length - 1 && ctx.stage.auditHistory.length !== 1) { %>is_uncheck<% } %>">
+                                    <% } %>
+                                    <li class="timeline-list-item pb-2 <% if (group.status === auditConst.status.uncheck && idx === ctx.stage.auditHistory.length - 1 && ctx.stage.auditHistory.length !== 1) { %>is_uncheck<% } %>">
+                                        <% if (group.endYear) { %>
                                         <div class="timeline-item-date">
-                                            <%- ctx.helper.formatDate(auditor.end_time) %>
+                                            <%- group.beginYear %>
+                                            <span title="<%- group.beginTime %>"><%- group.beginDate %></span>
                                         </div>
-                                        <% if(index < auditors.length - 1) { %>
+                                        <% } %>
+                                        <% if (index < his.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 || auditor.status === auditConst.status.checkCancel) {%>
-                                        <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>
+                                        <% if (group.status === auditConst.status.checked) { %>
+                                        <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) { %>
+                                        <div class="timeline-item-icon bg-warning text-light"><i class="fa fa-level-up"></i></div>
+                                        <% } else if (group.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-icon bg-secondary text-light"></div>
                                         <% } %>
                                         <div class="timeline-item-content">
+                                            <div class="py-1">
+                                                        <span class="text-black-50">
+                                                            <%- (!group.is_final ? group.audit_order : '终') %>审
+                                                            <% if (group.audit_type !== auditType.key.common) { %><span class="text-<%- auditType.info[group.audit_type].class %> "><%- auditType.info[group.audit_type].long %></span><% } %>
+                                                        </span>
+                                                <span class="pull-right <%- auditConst.statusClass[group.status] %>"><%- auditConst.statusString[group.status] %></span>
+                                            </div>
                                             <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 ? ctx.stage.user.name : '' %>
-                                                                <%- auditor.status === auditConst.status.checkNoPre && auditor.sort - 1 > 0 ? ctx.helper._.find(auditors, { sort: auditor.sort - 1 }).name : '' %>
-                                                            </span>
-                                                        </p>
-                                                        <p class="text-muted mb-0"><%- auditor.role %></p>
+                                                <div class="card-body px-3 py-0">
+                                                    <% for (const [i, auditor] of group.auditors.entries()) { %>
+                                                    <div class="card-text p-2 py-3 row <%- ( i > 0 ? 'border-top' : '') %>">
+                                                        <div class="col">
+                                                            <span class="h6"><%- auditor.name %></span>
+                                                            <span class="text-muted ml-1"><%- auditor.role %></span>
+                                                        </div>
+                                                        <div class="col">
+                                                            <% if (auditor.status === auditConst.status.checked) { %>
+                                                            <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) { %>
+                                                            <span class="pull-right text-warning"><i class="fa fa-share-square fa-rotate-270"></i></span>
+                                                            <% } else if (auditor.status === auditConst.status.checking) { %>
+                                                            <span class="pull-right text-warning"><i class="fa fa-commenting"></i></span>
+                                                            <% } %>
+                                                        </div>
+                                                        <% if (auditor.status !== auditConst.status.uncheck && auditor.opinion) { %>
+                                                        <div class="col-12 py-1 bg-light"><i class="fa fa-commenting-o mr-1"></i><%- auditor.opinion%></div>
+                                                        <% } %>
+                                                        <% if (auditor.status === auditConst.status.checking && auditor.aid === ctx.session.sessionUser.accountId) { %>
+                                                        <div class="col-12 py-1 bg-light">
+                                                            <textarea class="form-control form-control-sm" name="opinion">同意</textarea>
+                                                        </div>
+                                                        <% } %>
                                                     </div>
-                                                </div>
-                                                <!--审批意见-->
-                                                <% if(auditor.status !== auditConst.status.uncheck) { %>
-                                                <div class="card-body p-3 border-top">
-                                                    <% if (ctx.stage.times === idx + 1 && auditor.status === auditConst.status.checking) { %>
-                                                    <label>审批意见<b class="text-danger">*</b></label>
-                                                    <textarea class="form-control form-control-sm"
-                                                        name="opinion">同意</textarea>
-                                                    <% } else { %>
-                                                    <p style="margin: 0;"><%- auditor.opinion %></p>
                                                     <% } %>
                                                 </div>
-                                                <% } %>
                                             </div>
                                         </div>
                                     </li>
-                                    <% } %>
                                     <% }) %>
                                 </ul>
                             </div>
-
                             <% }) %>
                         </div>
                     </div>
@@ -473,22 +446,41 @@
                                 <ul class="list-group list-group-flush auditors-list">
                                     <% ctx.stage.auditors2.forEach((item, idx) => { %>
                                     <% if (idx === 0) { %>
-                                    <li class="list-group-item" data-auditorId="<%- item.aid %>">
-                                        <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 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">
+                                            <% for (const u of item) { %>
+                                            <small class="d-inline-block text-dark mx-1" title="<%- u.role %>" data-auditorId="<%- u.aid %>"><%- u.name %></small>
+                                            <% } %>
+                                        </span>
+                                        <span class="badge badge-light badge-pill ml-auto"><small>原报</small></span>
                                     </li>
                                     <% } else if(idx === ctx.stage.auditors2.length -1 && idx !== 0) { %>
-                                    <li class="list-group-item" data-auditorId="<%- item.aid %>">
-                                        <i class="fa fa fa-stop-circle"></i> <%- item.name %>
-                                        <small class="text-muted"><%- item.role %></small>
-                                        <span class="pull-right">终审</span>
+
+                                    <li class="list-group-item d-flex justify-content-between align-items-center">
+                                        <span class="mr-1"><i class="fa fa fa-stop-circle"></i></span>
+                                        <span class="text-muted">
+                                            <% for (const u of item) { %>
+                                            <small class="d-inline-block text-dark mx-1" title="<%- u.role %>" data-auditorId="<%- u.aid %>"><%- u.name %></small>
+                                            <% } %>
+                                        </span>
+                                        <span class="badge badge-light badge-pill ml-auto"><small>终审</small></span>
+                                        <% if (item[0].audit_type !== auditType.key.common) { %>
+                                        <div class="li-subscript"><span class="badge badge-pill badge-<%-  auditType.info[item[0].audit_type].class %> p-1 badge-bg-small"><small><%- auditType.info[item[0].audit_type].short %></small></span></div>
+                                        <% } %>
                                     </li>
                                     <% } else {%>
-                                    <li class="list-group-item" data-auditorId="<%- item.aid %>">
-                                        <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 class="list-group-item d-flex justify-content-between align-items-center">
+                                        <span class="mr-1"><i class="fa fa-chevron-circle-down"></i></span>
+                                        <span class="text-muted">
+                                            <% for (const u of item) { %>
+                                            <small class="d-inline-block text-dark mx-1" title="<%- u.role %>" data-auditorId="<%- u.aid %>"><%- u.name %></small>
+                                            <% } %>
+                                        </span>
+                                        <span class="badge badge-light badge-pill ml-auto"><small><%= ctx.helper.transFormToChinese(idx) %>审</small></span>
+                                        <% if (item[0].audit_type !== auditType.key.common) { %>
+                                        <div class="li-subscript"><span class="badge badge-pill badge-<%-  auditType.info[item[0].audit_type].class %> p-1 badge-bg-small"><small><%- auditType.info[item[0].audit_type].short %></small></span></div>
+                                        <% } %>
                                     </li>
                                     <% } %>
                                     <% }) %>
@@ -496,7 +488,7 @@
                             </div>
                         </div>
                         <div class="col-8 modal-height-500" style="overflow: auto">
-                            <% ctx.stage.auditHistory.forEach((auditors, idx) => { %>
+                            <% ctx.stage.auditHistory.forEach((his, idx) => { %>
                             <!-- 展开/收起历史流程 -->
                             <% if(idx === ctx.stage.auditHistory.length - 1 && ctx.stage.auditHistory.length !== 1) { %>
                                 <div class="text-right"><a href="javascript: void(0);" id="fold-btn" data-target="show"
@@ -505,176 +497,109 @@
                             <div class="<%- idx < ctx.stage.auditHistory.length - 1 ? 'fold-card' : '' %>">
                                 <div class="text-center text-muted"><%- idx+1 %>#</div>
                                 <ul class="timeline-list list-unstyled mt-2 <% if (idx === ctx.stage.auditHistory.length - 1 && ctx.stage.auditHistory.length !== 1) { %>last-auditor-list<% } %>">
-                                    <% auditors.forEach((auditor, index) => { %>
+                                    <% his.forEach((group, index) => { %>
                                     <% if (index === 0) { %>
                                     <li class="timeline-list-item pb-2">
                                         <div class="timeline-item-date">
-                                            <%- ctx.helper.formatDate(auditor.begin_time) %>
+                                            <%- group.beginYear %>
+                                            <span title="<%- group.beginTime %>"><%- group.beginDate %></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-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"><%- ctx.stage.user.name %></span><span
-                                                                class="pull-right text-success"><%- idx !== 0 ? '重新' : '' %>上报审批</span>
-                                                        </p>
-                                                        <p class="text-muted mb-0"><%- ctx.stage.user.role %></p>
-                                                    </div>
-                                                </div>
+                                            <div class="py-1">
+                                                <span class="text-black-50">原报</span>
+                                                <span class="pull-right text-success"><%- idx !== 0 ? '重新' : '' %>上报审批</span>
                                             </div>
-                                        </div>
-                                    </li>
-                                    <li class="timeline-list-item pb-2 <% if (auditor.status === auditConst.status.uncheck && idx === ctx.stage.auditHistory.length - 1 && ctx.stage.auditHistory.length !== 1) { %>is_uncheck<% } %>">
-                                        <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 || auditor.status === auditConst.status.checkCancel) {%>
-                                        <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.times === ctx.stage.times && auditor.status !== auditConst.status.uncheck) { %>
-                                                <div class="card-body p-3 border-top">
-                                                    <% if (ctx.stage.times === idx + 1 && auditor.status === auditConst.status.checking) { %>
-                                                    <label>审批意见<b class="text-danger">*</b></label>
-                                                    <textarea class="form-control form-control-sm"
-                                                        name="opinion">不同意</textarea>
-                                                    <% if (ctx.stage.curAuditor.aid === auditor.aid) { %>
-                                                    <div id="reject-process" class="alert alert-warning"
-                                                        style="margin-top: 15px;">
-                                                        <div class="form-check form-check-inline">
-                                                            <input class="form-check-input" type="radio" name="checkType"
-                                                                id="inlineRadio1" value="<%- auditConst.status.checkNo %>">
-                                                            <label class="form-check-label" for="inlineRadio1">退回原报
-                                                                <%- ctx.stage.user.name %></label>
+                                                <div class="card-body px-3 py-0">
+                                                    <div class="card-text p-2 py-3 row">
+                                                        <div class="col">
+                                                            <span class="h6"><%- ctx.stage.user.name %></span>
+                                                            <span class="text-muted ml-1"><%- ctx.stage.user.role %></span>
                                                         </div>
-                                                        <% if (auditor.order > 1 && auditor.aid !== auditors[0].aid) { %>
-                                                        <div class="form-check form-check-inline">
-                                                            <input class="form-check-input" type="radio" name="checkType"
-                                                                id="inlineRadio2"
-                                                                value="<%- auditConst.status.checkNoPre %>">
-                                                            <label class="form-check-label" for="inlineRadio2">退回上一审批人
-                                                                <%- auditors.find(item => item.sort === auditor.sort-1).name %>
-                                                            </label>
+                                                        <div class="col">
+                                                            <span class="pull-right text-success"><i class="fa fa-check-circle"></i></span>
                                                         </div>
-                                                        <% } %>
                                                     </div>
-                                                    <% } %>
-                                                    <% } else if(auditor.status === auditConst.status.checked){ %>
-                                                    <p style="margin: 0;"><%- auditor.opinion %></p>
-                                                    <% } %>
                                                 </div>
-                                                <% } %>
                                             </div>
                                         </div>
                                     </li>
-                                    <% } else {%>
-                                    <li class="timeline-list-item pb-2 <% if (auditor.status === auditConst.status.uncheck && idx === ctx.stage.auditHistory.length - 1 && ctx.stage.auditHistory.length !== 1) { %>is_uncheck<% } %>">
+                                    <% } %>
+                                    <li class="timeline-list-item pb-2 <% if (group.status === auditConst.status.uncheck && idx === ctx.stage.auditHistory.length - 1 && ctx.stage.auditHistory.length !== 1) { %>is_uncheck<% } %>">
+                                        <% if (his.endYear) { %>
                                         <div class="timeline-item-date">
-                                            <%- ctx.helper.formatDate(auditor.end_time) %>
+                                            <%- group.beginYear %>
+                                            <span title="<%- group.beginTime %>"><%- group.beginDate %></span>
                                         </div>
-                                        <% if(index < auditors.length - 1) { %>
+                                        <% } %>
+                                        <% if (index < his.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 || auditor.status === auditConst.status.checkCancel) {%>
-                                        <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>
+                                        <% if (group.status === auditConst.status.checked) { %>
+                                        <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) { %>
+                                        <div class="timeline-item-icon bg-warning text-light"><i class="fa fa-level-up"></i></div>
+                                        <% } else if (group.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-icon bg-secondary text-light"></div>
                                         <% } %>
                                         <div class="timeline-item-content">
+                                            <div class="py-1">
+                                                        <span class="text-black-50">
+                                                            <%- (!group.is_final ? group.audit_order : '终') %>审
+                                                            <% if (group.audit_type !== auditType.key.common) { %><span class="text-<%- auditType.info[group.audit_type].class %> "><%- auditType.info[group.audit_type].long %></span><% } %>
+                                                        </span>
+                                                <span class="pull-right <%- auditConst.statusClass[group.status] %>"><%- auditConst.statusString[group.status] %></span>
+                                            </div>
                                             <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 ? ctx.stage.user.name : '' %>
-                                                                <%- auditor.status === auditConst.status.checkNoPre && auditor.sort - 1 > 0 ? ctx.helper._.find(auditors, { sort: auditor.sort - 1 }).name : '' %>
-                                                            </span>
-                                                        </p>
-                                                        <p class="text-muted mb-0"><%- auditor.role %></p>
-                                                    </div>
-                                                </div>
-                                                <!--审批意见-->
-                                                <% if(auditor.times === ctx.stage.times && auditor.status !== auditConst.status.uncheck) { %>
-                                                <div class="card-body p-3 border-top">
-                                                    <% if (ctx.stage.times === idx + 1 && auditor.status === auditConst.status.checking) { %>
-                                                    <label>审批意见<b class="text-danger">*</b></label>
-                                                    <textarea class="form-control form-control-sm"
-                                                        name="opinion">不同意</textarea>
-                                                    <% if (ctx.stage.curAuditor.aid === auditor.aid) { %>
-                                                    <div id="reject-process" class="alert alert-warning"
-                                                        style="margin-top: 15px;">
-                                                        <div class="form-check form-check-inline">
-                                                            <input class="form-check-input" type="radio" name="checkType"
-                                                                id="inlineRadio1" value="<%- auditConst.status.checkNo %>">
-                                                            <label class="form-check-label" for="inlineRadio1">退回原报
-                                                                <%- ctx.stage.user.name %></label>
+                                                <div class="card-body px-3 py-0">
+                                                    <% for (const [i, auditor] of group.auditors.entries()) { %>
+                                                    <div class="card-text p-2 py-3 row <%- ( i > 0 ? 'border-top' : '') %>">
+                                                        <div class="col">
+                                                            <span class="h6"><%- auditor.name %></span>
+                                                            <span class="text-muted ml-1"><%- auditor.role %></span>
+                                                        </div>
+                                                        <div class="col">
+                                                            <% if (auditor.status === auditConst.status.checked) { %>
+                                                            <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) { %>
+                                                            <span class="pull-right text-warning"><i class="fa fa-share-square fa-rotate-270"></i></span>
+                                                            <% } else if (auditor.status === auditConst.status.checking) { %>
+                                                            <span class="pull-right text-warning"><i class="fa fa-commenting"></i></span>
+                                                            <% } %>
                                                         </div>
-                                                        <% if (auditor.order > 1 && auditor.aid !== auditors[0].aid) { %>
-                                                        <div class="form-check form-check-inline">
-                                                            <input class="form-check-input" type="radio" name="checkType"
-                                                                id="inlineRadio2"
-                                                                value="<%- auditConst.status.checkNoPre %>">
-                                                            <label class="form-check-label" for="inlineRadio2">退回上一审批人
-                                                                <%- auditors.find(item => item.sort === auditor.sort-1).name %>
-                                                            </label>
+                                                        <% if (auditor.status !== auditConst.status.uncheck && auditor.opinion) { %>
+                                                        <div class="col-12 py-1 bg-light"><i class="fa fa-commenting-o mr-1"></i><%- auditor.opinion%></div>
+                                                        <% } %>
+                                                        <% if (auditor.status === auditConst.status.checking && auditor.aid === ctx.session.sessionUser.accountId) { %>
+                                                        <div class="col-12 py-1 bg-light">
+                                                            <textarea class="form-control form-control-sm" name="opinion">不同意</textarea>
+                                                            <div class="alert alert-warning mt-1 mb-0 p-2">
+                                                                <div class="form-check form-check-inline">
+                                                                    <input class="form-check-input" type="radio" name="checkType" id="inlineRadio1" value="<%- auditConst.status.checkNo %>">
+                                                                    <label class="form-check-label" for="inlineRadio1">退回原报 <%- ctx.stage.user.name %></label>
+                                                                </div>
+                                                                <% if (auditor.audit_order > 1) { %>
+                                                                <div class="form-check form-check-inline">
+                                                                    <input class="form-check-input" type="radio" name="checkType" id="inlineRadio2" value="<%- auditConst.status.checkNoPre %>">
+                                                                    <label class="form-check-label" for="inlineRadio2">退回上一审批人
+                                                                        <% const pre = his.find(x => { return x.audit_order === auditor.audit_order - 1}); %>
+                                                                        <% (pre.audit_type === auditType.key.common ? pre.auditors[0].name : `${pre.audit_order}审`)%>
+                                                                    </label>
+                                                                </div>
+                                                                <% } %>
+                                                            </div>
                                                         </div>
                                                         <% } %>
                                                     </div>
                                                     <% } %>
-                                                    <% } else { %>
-                                                    <p style="margin: 0;"><%- auditor.opinion %></p>
-                                                    <% } %>
-
                                                 </div>
-                                                <% } %>
                                             </div>
                                         </div>
                                     </li>
-                                    <% } %>
                                     <% }) %>
                                 </ul>
                             </div>

+ 8 - 8
app/view/stage/compare_modal.ejs

@@ -9,18 +9,18 @@
             <div class="modal-body">
                 <table class="table table-sm">
                     <tr><th>审批人</th><th width="90">选择</th><th></th></tr>
-                    <% for (const [i, a] of compareAuditors.entries()) { %>
-                    <tr auditorId="<%- a.aid %>">
-                        <td><%- a.name %></td>
+                    <% for (const [i, group] of compareAuditorGroup.entries()) { %>
+                    <tr audit-order="<%- group.audit_order %>">
+                        <td><%- group.name %></td>
                         <td>
-                            <% if (a.status === auditConst.status.checked || a.status === auditConst.status.checkNo || a.status === auditConst.status.checkNoPre ||
-                                    (a.status === auditConst.status.checking && a.aid === ctx.session.sessionUser.accountId)) {%>
+                            <% if (group.status === auditConst.status.checked || group.status === auditConst.status.checkNo || group.status === auditConst.status.checkNoPre ||
+                                    (group.status === auditConst.status.checking && group.aid === ctx.session.sessionUser.accountId)) {%>
                             <input type="checkbox" audit-order="<%- i + 1 %>">
                             <% } %>
                         </td>
-                        <td class="text-center <%- auditConst.statusClass[a.status] %>" style="width: 80px">
-                            <% if (a.status !== auditConst.status.uncheck) {%>
-                            <%- auditConst.statusString[a.status] %>
+                        <td class="text-center <%- auditConst.statusClass[group.status] %>" style="width: 80px">
+                            <% if (group.status !== auditConst.status.uncheck) {%>
+                            <%- auditConst.statusString[group.status] %>
                             <% } %>
                         </td>
                     </tr>

+ 0 - 2
app/view/stage/index.ejs

@@ -548,8 +548,6 @@
     let attData = JSON.parse(unescape('<%- escape(JSON.stringify(attData)) %>'));
     const ckColSetting = 'stage-col-visible-1.0.3-<%- tender.id %>';
     const auditConst = JSON.parse('<%- JSON.stringify(auditConst) %>');
-    // const cur_uid = parseInt('<%- ctx.session.sessionUser.accountId %>');
-    const curAuditor = JSON.parse('<%- JSON.stringify(curAuditor) %>');
     const thirdParty = JSON.parse('<%- JSON.stringify(thirdParty) %>');
     const sfAttDelPower = <%- sfAttDelPower %>;
     let sfData = JSON.parse(unescape('<%- escape(JSON.stringify(sfData)) %>'));

+ 177 - 73
app/view/tender/detail.ejs

@@ -1179,41 +1179,119 @@
 </script>
 <script>
     const tenderId = parseInt('<%- tender.id %>');
-    $(document).ready(function () {
-        // 获取审批流程
-        $('a[data-target="#sp-list" ]').on('click', function () {
-            const type = $(this).attr('data-type');
-            const data = {
-                order: $(this).attr('data-order'),
-            };
-            let url = '';
-            let auditConst = '';
-            if (type === 'stage') {
-                url = '/tender/' + tenderId + '/measure/stage/auditors';
-                auditConst = JSON.parse('<%- JSON.stringify(audit.stage) %>');
-            } else if (type === 'ledger') {
-                url = '/tender/' + tenderId + '/measure/ledger/auditors';
-                auditConst = JSON.parse('<%- JSON.stringify(audit.ledger) %>');
-            } else if (type === 'material') {
-                url = '/tender/' + tenderId + '/measure/material/auditors';
-                auditConst = JSON.parse('<%- JSON.stringify(audit.material) %>');
+    const auditType = JSON.parse('<%- JSON.stringify(audit.auditType) %>');
+    const getAuditTypeText = function (type) {
+        if (type === auditType.key.common) return '';
+        return `<span class="text-${auditType.info[type].class}">${auditType.info[type].long}</span>`;
+    };
+    const loadStageHistory = function (result, auditConst) {
+        const { auditHistory, auditors2, user } = result;
+        let historyHTML = [];
+        const darkHTML = !dayMode ? 'bg-dark border-secondary text-white' : '';
+        const textClass = !dayMode ? 'text-light' : '';
+        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>`);
             }
-            postData(url, data, function (result) {
-                const { auditHistory, auditors, user } = result
-                let historyHTML = ''
-                const leftAuditors = auditors;
-                const darkHTML = !dayMode ? 'bg-dark border-secondary text-white' : '';
-                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>`
+            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 pr-1 ${ idx === auditHistory.length - 1 && auditHistory.length !== 1 ? 'last-auditor-list' : '' }">`);
+            his.forEach((group, index) => {
+                if (index === 0) {
+                    historyHTML.push(`<li class="timeline-list-item pb-2">
+                                            <div class="timeline-item-date">
+                                                ${group.beginYear}
+                                                <span class="${textClass}" title="${group.beginTime}">${group.beginDate}</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>原报</span>
+                                                    <span class="pull-right text-success">${idx !== 0 ? '重新' : '' }上报审批</span>
+                                                </div>
+                                                <div class="card ${darkHTML}">
+                                                    <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 ${textClass}">${group.beginYear}<span class="${textClass}"  title="${group.beginTime}">${group.beginDate}</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.push('<div class="timeline-item-icon bg-secondary text-light"></div>');
+                }
+
+                historyHTML.push('<div class="timeline-item-content">');
+                historyHTML.push(`<div class="py-1">
+                        <span class="">
+                        ${ !group.is_final ? group.audit_order + '' : '终' }审 ${getAuditTypeText(group.audit_type)}
+                        </span>
+                        <span class="pull-right ${auditConst.statusClass[group.status]}">${auditConst.statusString[group.status]}</span>
+                    </div>`);
+                historyHTML.push(`<div class="card ${darkHTML}"><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.push(`<div class="col-12 py-1"><i class="fa fa-commenting-o mr-1"></i>${auditor.opinion}</div>`);
                     }
-                    historyHTML += `<div class="${idx < auditHistory.length - 1 ? 'fold-card' : ''}">
+                    historyHTML.push('</div>');
+                }
+                historyHTML.push('</div></div>');
+                historyHTML.push('</div>');
+                historyHTML.push('</li>');
+            });
+            historyHTML.push('</div>');
+            historyHTML.push('</ul>');
+        });
+        $('#audit-list').empty();
+        $('#audit-list').append(historyHTML.join(''));
+    }
+    const loadHistory = function (result, auditConst) {
+        const { auditHistory, auditors, user } = result
+        let historyHTML = ''
+        const leftAuditors = auditors;
+        const darkHTML = !dayMode ? 'bg-dark border-secondary text-white' : '';
+        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>`
+            }
+            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) => {
-                        if (index === 0) {
-                            historyHTML += `<li class="timeline-list-item pb-2">
+            auditors.forEach((auditor, index) => {
+                if (index === 0) {
+                    historyHTML += `<li class="timeline-list-item pb-2">
                             <div class="timeline-item-date">
                                 ${formatDate(auditor.begin_time, !dayMode)}
                             </div>
@@ -1240,27 +1318,27 @@
                                 ${formatDate(auditor.end_time, !dayMode)}
                             </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">
+                    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">
+                    } 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">
+                    } 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>`
+                    } else {
+                        historyHTML += `<div class="timeline-item-icon bg-secondary text-light"></div>`
 
-                            }
-                            historyHTML += `<div class="timeline-item-content">
+                    }
+                    historyHTML += `<div class="timeline-item-content">
                                 <div class="card ${darkHTML}">
                                     <div class="card-body p-3">
                                         <div class="card-text">
@@ -1270,38 +1348,38 @@
                                             <p class="text-muted mb-0">${auditor.role}</p>
                                         </div>
                                     </div>`
-                            if (auditor.opinion) {
-                                historyHTML += `<div class="card-body p-3 border-top">
+                    if (auditor.opinion) {
+                        historyHTML += `<div class="card-body p-3 border-top">
                                     <p style="margin: 0;">${auditor.opinion}</p>
                                 </div>`
-                            }
-                            historyHTML += `</div></div></li>`
-                        } else {
-                            historyHTML += `<li class="timeline-list-item pb-2">
+                    }
+                    historyHTML += `</div></div></li>`
+                } else {
+                    historyHTML += `<li class="timeline-list-item pb-2">
                         <div class="timeline-item-date">
                             ${formatDate(auditor.end_time, !dayMode)}
                         </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">
+                    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">
+                    } 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">
+                    } 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">
+                    } else {
+                        historyHTML += `<div class="timeline-item-icon bg-secondary text-light"></div>`
+                    }
+                    historyHTML += `<div class="timeline-item-content">
                         <div class="card ${darkHTML}">
                             <div class="card-body p-3">
                                 <div class="card-text">
@@ -1317,19 +1395,45 @@
                                 </div>
                             </div>`
 
-                            if (auditor.opinion) {
-                                historyHTML += `<div class="card-body p-3 border-top">
+                    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 += '</ul></div>'
+                    }
+                    historyHTML += `</div></div></li>`
+                }
+            })
+            historyHTML += '</ul></div>'
 
-                })
-                $('#audit-list').empty()
-                $('#audit-list').append(historyHTML)
+        })
+        $('#audit-list').empty()
+        $('#audit-list').append(historyHTML);
+    };
+    $(document).ready(function () {
+        // 获取审批流程
+        $('a[data-target="#sp-list" ]').on('click', function () {
+            const type = $(this).attr('data-type');
+            const data = {
+                order: $(this).attr('data-order'),
+            };
+            let url = '';
+            let auditConst = '';
+            if (type === 'stage') {
+                url = '/tender/' + tenderId + '/measure/stage/auditors';
+                auditConst = JSON.parse('<%- JSON.stringify(audit.stage) %>');
+            } else if (type === 'ledger') {
+                url = '/tender/' + tenderId + '/measure/ledger/auditors';
+                auditConst = JSON.parse('<%- JSON.stringify(audit.ledger) %>');
+            } else if (type === 'material') {
+                url = '/tender/' + tenderId + '/measure/material/auditors';
+                auditConst = JSON.parse('<%- JSON.stringify(audit.material) %>');
+            }
+            postData(url, data, function (result) {
+                if (type === 'stage') {
+                    loadStageHistory(result, auditConst);
+                } else {
+                    loadHistory(result, auditConst);
+                }
             });
         });
 

+ 176 - 73
app/view/tender/modal.ejs

@@ -117,43 +117,120 @@
     </div>
 </div>
 <script>
+    const auditType = JSON.parse('<%- JSON.stringify(auditConst.auditType) %>');
     $(document).ready(function () {
-        // 获取审批流程
-        $('a[data-target="#sp-list" ]').on('click', function () {
-            const type = $(this).attr('data-type');
-            const data = {
-                order: $(this).attr('data-order'),
-            };
-            const tenderId = $(this).attr('data-tender');
-            let url = '';
-            let auditConst2 = '';
-            if (type === 'stage') {
-                url = '/tender/' + tenderId + '/measure/stage/auditors';
-                auditConst2 = JSON.parse('<%- JSON.stringify(auditConst.stage) %>');
-            } else if (type === 'ledger') {
-                url = '/tender/' + tenderId + '/measure/ledger/auditors';
-                auditConst2 = JSON.parse('<%- JSON.stringify(auditConst.ledger) %>');
-            } else if (type === 'material') {
-                url = '/tender/' + tenderId + '/measure/material/auditors';
-                auditConst2 = JSON.parse('<%- JSON.stringify(auditConst.material) %>');
-            }
+        const getAuditTypeText = function (type) {
+            if (type === auditType.key.common) return '';
+            return `<span class="text-${auditType.info[type].class}">${auditType.info[type].long}</span>`;
+        };
+        const loadStageHistory = function (result, auditConst2) {
+            const { auditHistory, auditors2, user } = result;
+            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.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 pr-1 ${ idx === auditHistory.length - 1 && auditHistory.length !== 1 ? 'last-auditor-list' : '' }">`);
+                his.forEach((group, index) => {
+                    console.log(group);
+                    if (index === 0) {
+                        historyHTML.push(`<li class="timeline-list-item pb-2">
+                                            <div class="timeline-item-date">
+                                                ${group.beginYear}
+                                                <span title="${group.beginTime}">${group.beginDate}</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 === auditConst2.status.uncheck && idx === auditHistory.length - 1 && auditHistory.length !== 1 ? 'is_uncheck' : ''}">`);
+                    if (group.endYear) {
+                        historyHTML.push(`<div class="timeline-item-date">${group.beginYear}<span title="${group.beginTime}">${group.beginDate}</span></div>`);
+                    }
+                    if (index < his.length - 1) {
+                        historyHTML.push('<div class="timeline-item-tail"></div>');
+                    }
+                    if (group.status === auditConst2.status.checked) {
+                        historyHTML.push('<div class="timeline-item-icon bg-success text-light"><i class="fa fa-check"></i></div>');
+                    } else if (group.status === auditConst2.status.checkNo || group.status === auditConst2.status.checkNoPre || group.status === auditConst2.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 === auditConst2.status.checking) {
+                        historyHTML.push('<div class="timeline-item-icon bg-warning text-light"><i class="fa fa-ellipsis-h"></i></div>');
+                    } else {
+                        historyHTML.push('<div class="timeline-item-icon bg-secondary text-light"></div>');
+                    }
+
+                    historyHTML.push('<div class="timeline-item-content">');
+                    historyHTML.push(`<div class="py-1">
+                        <span class="text-black-50">
+                        ${ !group.is_final ? group.audit_order + '' : '终' }审 ${getAuditTypeText(group.audit_type)}
+                        </span>
+                        <span class="pull-right ${auditConst2.statusClass[group.status]}">${auditConst2.statusString[group.status]}</span>
+                    </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 === auditConst2.status.checked) {
+                            historyHTML.push('<span class="pull-right text-success"><i class="fa fa-check-circle"></i></span>');
+                        } if (auditor.status === auditConst2.status.checkNo || auditor.status === auditConst2.status.checkNoPre || auditor.status === auditConst2.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.push(`<div class="col-12 py-1 bg-light"><i class="fa fa-commenting-o mr-1"></i>${auditor.opinion}</div>`);
+                        }
+                        historyHTML.push('</div>');
+                    }
+                    historyHTML.push('</div></div>');
+                    historyHTML.push('</div>');
+                    historyHTML.push('</li>');
+                });
+                historyHTML.push('</div>');
+                historyHTML.push('</ul>');
+            });
+            $('#audit-list').empty();
+            $('#audit-list').append(historyHTML.join(''));
+        }
+        const loadHistory = function (result, auditConst2) {
             const dayMode = true;
-            postData(url, data, function (result) {
-                const { auditHistory, auditors, user } = result
-                let historyHTML = ''
-                const leftAuditors = auditors;
-                const darkHTML = !dayMode ? 'bg-dark border-secondary text-white' : '';
-                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"
+            const { auditHistory, auditors, user } = result
+            let historyHTML = ''
+            const leftAuditors = auditors;
+            const darkHTML = !dayMode ? 'bg-dark border-secondary text-white' : '';
+            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>`
-                    }
-                    historyHTML += `<div class="${idx < auditHistory.length - 1 ? 'fold-card' : ''}">
+                }
+                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) => {
-                        if (index === 0) {
-                            historyHTML += `<li class="timeline-list-item pb-2">
+                auditors.forEach((auditor, index) => {
+                    if (index === 0) {
+                        historyHTML += `<li class="timeline-list-item pb-2">
                             <div class="timeline-item-date">
                                 ${formatDate(auditor.begin_time, !dayMode)}
                             </div>
@@ -180,27 +257,27 @@
                                 ${formatDate(auditor.end_time, !dayMode)}
                             </div>`
 
-                            if(index < auditors.length - 1) {
-                                historyHTML += `<div class="timeline-item-tail"></div>`
-                            }
-                            if(auditor.status === auditConst2.status.checked) {
-                                historyHTML += `<div class="timeline-item-icon bg-success text-light">
+                        if(index < auditors.length - 1) {
+                            historyHTML += `<div class="timeline-item-tail"></div>`
+                        }
+                        if(auditor.status === auditConst2.status.checked) {
+                            historyHTML += `<div class="timeline-item-icon bg-success text-light">
                                     <i class="fa fa-check"></i>
                                 </div>`
 
-                            } else if(auditor.status === auditConst2.status.checkNo || auditor.status === auditConst2.status.checkNoPre) {
-                                historyHTML += `<div class="timeline-item-icon bg-warning text-light">
+                        } else if(auditor.status === auditConst2.status.checkNo || auditor.status === auditConst2.status.checkNoPre) {
+                            historyHTML += `<div class="timeline-item-icon bg-warning text-light">
                                     <i class="fa fa-level-up"></i>
                                 </div>`
-                            } else if(auditor.status === auditConst2.status.checking) {
-                                historyHTML += `<div class="timeline-item-icon bg-warning text-light">
+                        } else if(auditor.status === auditConst2.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>`
+                        } else {
+                            historyHTML += `<div class="timeline-item-icon bg-secondary text-light"></div>`
 
-                            }
-                            historyHTML += `<div class="timeline-item-content">
+                        }
+                        historyHTML += `<div class="timeline-item-content">
                                 <div class="card ${darkHTML}">
                                     <div class="card-body p-3">
                                         <div class="card-text">
@@ -210,38 +287,38 @@
                                             <p class="text-muted mb-0">${auditor.role}</p>
                                         </div>
                                     </div>`
-                            if (auditor.opinion) {
-                                historyHTML += `<div class="card-body p-3 border-top">
+                        if (auditor.opinion) {
+                            historyHTML += `<div class="card-body p-3 border-top">
                                     <p style="margin: 0;">${auditor.opinion}</p>
                                 </div>`
-                            }
-                            historyHTML += `</div></div></li>`
-                        } else {
-                            historyHTML += `<li class="timeline-list-item pb-2">
+                        }
+                        historyHTML += `</div></div></li>`
+                    } else {
+                        historyHTML += `<li class="timeline-list-item pb-2">
                         <div class="timeline-item-date">
                             ${formatDate(auditor.end_time, !dayMode)}
                         </div>`
 
-                            if(index < auditors.length - 1) {
-                                historyHTML += `<div class="timeline-item-tail"></div>`
-                            }
-                            if(auditor.status === auditConst2.status.checked) {
-                                historyHTML += `<div class="timeline-item-icon bg-success text-light">
+                        if(index < auditors.length - 1) {
+                            historyHTML += `<div class="timeline-item-tail"></div>`
+                        }
+                        if(auditor.status === auditConst2.status.checked) {
+                            historyHTML += `<div class="timeline-item-icon bg-success text-light">
                                 <i class="fa fa-check"></i>
                             </div>`
-                            } else if(auditor.status === auditConst2.status.checkNo || auditor.status === auditConst2.status.checkNoPre) {
-                                historyHTML += `<div class="timeline-item-icon bg-warning text-light">
+                        } else if(auditor.status === auditConst2.status.checkNo || auditor.status === auditConst2.status.checkNoPre) {
+                            historyHTML += `<div class="timeline-item-icon bg-warning text-light">
                                 <i class="fa fa-level-up"></i>
                             </div>`
 
-                            } else if(auditor.status === auditConst2.status.checking) {
-                                historyHTML += `<div class="timeline-item-icon bg-warning text-light">
+                        } else if(auditor.status === auditConst2.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">
+                        } else {
+                            historyHTML += `<div class="timeline-item-icon bg-secondary text-light"></div>`
+                        }
+                        historyHTML += `<div class="timeline-item-content">
                         <div class="card ${darkHTML}">
                             <div class="card-body p-3">
                                 <div class="card-text">
@@ -257,19 +334,45 @@
                                 </div>
                             </div>`
 
-                            if (auditor.opinion) {
-                                historyHTML += `<div class="card-body p-3 border-top">
+                        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 += '</ul></div>'
-
+                        historyHTML += `</div></div></li>`
+                    }
                 })
-                $('#audit-list').empty()
-                $('#audit-list').append(historyHTML)
+                historyHTML += '</ul></div>'
+
+            })
+            $('#audit-list').empty()
+            $('#audit-list').append(historyHTML)
+        }
+        // 获取审批流程
+        $('a[data-target="#sp-list" ]').on('click', function () {
+            const type = $(this).attr('data-type');
+            const data = {
+                order: $(this).attr('data-order'),
+            };
+            const tenderId = $(this).attr('data-tender');
+            let url = '';
+            let auditConst2 = '';
+            if (type === 'stage') {
+                url = '/tender/' + tenderId + '/measure/stage/auditors';
+                auditConst2 = JSON.parse('<%- JSON.stringify(auditConst.stage) %>');
+            } else if (type === 'ledger') {
+                url = '/tender/' + tenderId + '/measure/ledger/auditors';
+                auditConst2 = JSON.parse('<%- JSON.stringify(auditConst.ledger) %>');
+            } else if (type === 'material') {
+                url = '/tender/' + tenderId + '/measure/material/auditors';
+                auditConst2 = JSON.parse('<%- JSON.stringify(auditConst.material) %>');
+            }
+            postData(url, data, function (result) {
+                if (type === 'stage') {
+                    loadStageHistory(result, auditConst2);
+                } else {
+                    loadHistory(result, auditConst2);
+                }
             });
         });
 

+ 37 - 1
app/view/tender/shenpi.ejs

@@ -40,7 +40,7 @@
                                             <span class="col-auto"><%- ctx.helper.transFormToChinese(i+1) %>审</span>
                                             <span class="col-7 spr-span">
                                                 <span class="d-inline-block">
-                                                    <select class="form-control form-control-sm" <% if (sp.code !== 'stage') { %> style="display: none;" <% } %>>
+                                                    <select class="form-control form-control-sm" data-type="<%- auditGroup[0].audit_type %>" <% if (sp.code !== 'stage') { %> style="display: none;" <% } %>>
                                                         <% for (const at of auditType.types) { %>
                                                         <option value="<%- at.value %>" <% if (auditGroup[0].audit_type === at.value) { %>selected<%} %>><%- at.name %></option>
                                                         <% } %>
@@ -59,6 +59,35 @@
                                                             </div>
                                                 </span></span></span>
                                                 <% } %>
+                                                <% if (auditGroup[0].audit_type !== auditType.key.common) { %>
+                                                <span class="d-inline-block">
+                                                <div class="dropdown text-right">
+                                                    <button class="btn btn-outline-primary btn-sm dropdown-toggle" type="button" id="<%- sp.code %>_dropdownMenuButton" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
+                                                        选择审批人
+                                                    </button>
+                                                    <div class="dropdown-menu dropdown-menu-right" id="<%- sp.code %>_dropdownMenu" aria-labelledby="<%- sp.code %>_dropdownMenuButton" style="width:220px">
+                                                        <div class="mb-2 p-2"><input class="form-control form-control-sm gr-search"
+                                                                                     placeholder="姓名/手机 检索" autocomplete="off" data-code="<%- sp.code %>"></div>
+                                                        <dl class="list-unstyled book-list">
+                                                            <% accountGroup.forEach((group, idx) => { %>
+                                                            <dt><a href="javascript: void(0);" class="acc-btn" data-groupid="<%- idx %>" data-type="hide"><i class="fa fa-plus-square"></i></a> <%- group.groupName %></dt>
+                                                                <div class="dd-content" data-toggleid="<%- idx %>">
+                                                                    <% group.groupList.forEach(item => { %>
+                                                                    <% if (item.id !== ctx.tender.data.user_id) { %>
+                                                                    <dd class="border-bottom p-2 mb-0 " data-id="<%- item.id %>" >
+                                                                    <p class="mb-0 d-flex"><span class="text-primary"><%- item.name %></span><span
+                                                                                class="ml-auto"><%- item.mobile %></span></p>
+                                                                    <span class="text-muted"><%- item.role %></span>
+                                                                    </dd>
+                                                                    <% } %>
+                                                                    <% });%>
+                                                                </div>
+                                                            <% }) %>
+                                                        </dl>
+                                                    </div>
+                                                </div>
+                                                </span>
+                                                <% } %>
                                             </span>
                                         </li>
                                         <% } %>
@@ -67,6 +96,13 @@
                                         <li class="d-flex justify-content-start mb-3">
                                             <span class="col-auto">一审</span>
                                             <span class="col-7 spr-span">
+                                                <span class="d-inline-block">
+                                                    <select class="form-control form-control-sm" data-type="<%- auditType.key.common %>" <% if (sp.code !== 'stage') { %> style="display: none;" <% } %>>
+                                                        <% for (const at of auditType.types) { %>
+                                                        <option value="<%- at.value %>" <% if (auditType.key.common === at.value) { %>selected<%} %>><%- at.name %></option>
+                                                        <% } %>
+                                                    </select>
+                                                </span>
                                             <span class="d-inline-block">
                                                 <div class="dropdown text-right">
                                                     <button class="btn btn-outline-primary btn-sm dropdown-toggle" type="button" id="<%- sp.code %>_dropdownMenuButton" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">

+ 0 - 16
app/view/tender/sub_menu.ejs

@@ -25,22 +25,6 @@
                         <input type="radio" name="options" id="option2" autocomplete="off"> 管理标段
                     </label>
                     <% } %>
-                    <% if (ctx.app.config.is_debug) { %>
-                    <label class="btn btn-sm btn-light <% if (ctx.url === '/listOrg') { %>active<% } %>" onclick="window.location.href='/listOrg'">
-                        <input type="radio" name="options" id="option1" autocomplete="off"> 标段列表(旧)
-                    </label>
-                    <label class="btn btn-sm btn-light <% if (ctx.url === '/list/infoOrg') { %>active<% } %>" onclick="window.location.href='/list/infoOrg'">
-                        <input type="radio" name="options" id="option1" autocomplete="off"> 金额概况(旧)
-                    </label>
-                    <label class="btn btn-sm btn-light  <% if (ctx.url === '/list/progressOrg') { %>active<% } %>" onclick="window.location.href='/list/progressOrg'">
-                        <input type="radio" name="options" id="option2" autocomplete="off"> 计量进度(旧)
-                    </label>
-                    <% if (userPermission !== null && userPermission.tender !== undefined && userPermission.tender.indexOf('1') !== -1) { %>
-                    <label class="btn btn-sm btn-light  <% if (ctx.url === '/list/manageOrg') { %>active<% } %>" onclick="window.location.href='/list/manageOrg'">
-                        <input type="radio" name="options" id="option2" autocomplete="off"> 管理标段(旧)
-                    </label>
-                    <% } %>
-                    <% } %>
                 </div>
             </div>
             <!--<div class="d-inline-block ml-3">-->

+ 2 - 1
sql/update.sql

@@ -8,4 +8,5 @@ ADD COLUMN `audit_type`  tinyint(4) UNSIGNED NOT NULL DEFAULT 1 COMMENT '审批
 ADD COLUMN `audit_order`  tinyint(4) UNSIGNED ZEROFILL NOT NULL DEFAULT 0 COMMENT '审批顺序' AFTER `audit_type`;
 
 ALTER TABLE `zh_stage_audit`
-ADD COLUMN `audit_type`  tinyint(4) UNSIGNED NOT NULL DEFAULT 1 COMMENT '审批类型(1个人,2会签,3或签)' AFTER `is_old`;
+ADD COLUMN `audit_type`  tinyint(4) UNSIGNED NOT NULL DEFAULT 1 COMMENT '审批类型(1个人,2会签,3或签)' AFTER `is_old`,
+ADD COLUMN `audit_order`  tinyint(4) UNSIGNED ZEROFILL NOT NULL DEFAULT 0 COMMENT '审批顺序' AFTER `audit_type`;