浏览代码

3种变更撤回及修订功能

ellisran 1 年之前
父节点
当前提交
c854ab8d73

+ 72 - 0
app/const/audit.js

@@ -609,6 +609,10 @@ const changeProject = (function() {
         checked: 3, // 审批通过
         checkNo: 4, // 审批终止
         back: 5, // 退回到原报人重新上报
+        checkAgain: 6, // 终审退回  --该状态仅可用于,终审退回时,修改原终审的审批状态,并同时新增一条新的终审审批中记录
+        revise: 7, // 修订变更
+        cancelRevise: 8, // 撤销修订
+        checkCancel: 9, // 撤回
     };
     const statusString = [];
     statusString[status.uncheck] = '待上报';
@@ -616,6 +620,9 @@ const changeProject = (function() {
     statusString[status.checked] = '审批通过';
     statusString[status.checkNo] = '终止';
     statusString[status.back] = '审批退回';
+    statusString[status.revise] = '修订';
+    statusString[status.cancelRevise] = '撤销修订';
+    statusString[status.checkCancel] = '撤回';
 
     const statusClass = [];
     statusClass[status.uncheck] = '';
@@ -623,6 +630,9 @@ const changeProject = (function() {
     statusClass[status.checked] = 'text-success';
     statusClass[status.checkNo] = 'text-danger';
     statusClass[status.back] = 'text-warning';
+    statusClass[status.revise] = 'text-warning';
+    statusClass[status.cancelRevise] = 'text-success';
+    statusClass[status.checkCancel] = 'text-warning';
 
     // 标段概况页
     // 描述文本
@@ -632,6 +642,9 @@ const changeProject = (function() {
     auditString[status.checked] = '审批通过';
     auditString[status.checkNo] = '终止';
     auditString[status.back] = '审批退回';
+    auditString[status.revise] = '修订';
+    auditString[status.cancelRevise] = '撤销修订';
+    auditString[status.checkCancel] = '撤回';
     // 文字样式
     const auditStringClass = [];
     auditStringClass[status.uncheck] = '';
@@ -639,6 +652,9 @@ const changeProject = (function() {
     auditStringClass[status.checked] = 'text-success';
     auditStringClass[status.checkNo] = 'text-danger';
     auditStringClass[status.back] = 'text-warning';
+    auditStringClass[status.revise] = 'text-warning';
+    auditStringClass[status.cancelRevise] = 'text-success';
+    auditStringClass[status.checkCancel] = 'text-warning';
     // 描述文本
     const auditProgress = [];
     auditProgress[status.uncheck] = '草稿';
@@ -646,6 +662,9 @@ const changeProject = (function() {
     auditProgress[status.checked] = '审批通过';
     auditProgress[status.checkNo] = '终止';
     auditProgress[status.back] = '审批退回';
+    auditProgress[status.revise] = '修订中';
+    auditProgress[status.cancelRevise] = '撤销修订';
+    auditProgress[status.checkCancel] = '撤回';
     // 样式
     const auditProgressClass = [];
     auditProgressClass[status.uncheck] = '';
@@ -653,6 +672,9 @@ const changeProject = (function() {
     auditProgressClass[status.checked] = 'text-success';
     auditProgressClass[status.checkNo] = 'text-danger';
     auditProgressClass[status.back] = 'text-warning';
+    auditProgressClass[status.revise] = 'text-warning';
+    auditProgressClass[status.cancelRevise] = 'text-success';
+    auditProgressClass[status.checkCancel] = 'text-warning';
 
     const filter = {
         status: {
@@ -677,6 +699,7 @@ const changeProject = (function() {
     statusButton[status.checked] = '';
     statusButton[status.checkNo] = '';
     statusButton[status.back] = '重新上报';
+    statusButton[status.revise] = '修订';
 
     // 按钮样式
     const statusButtonClass = [];
@@ -685,6 +708,7 @@ const changeProject = (function() {
     statusButtonClass[status.checked] = '';
     statusButtonClass[status.checkNo] = '';
     statusButtonClass[status.back] = 'btn-warning';
+    statusButtonClass[status.revise] = 'btn-warning';
     return { status, statusString, statusClass, auditString, auditStringClass, auditProgress, auditProgressClass, filter, statusButton, statusButtonClass };
 })();
 
@@ -695,18 +719,28 @@ const changeApply = (function() {
         checking: 2, // 待审批|审批中
         checked: 3, // 审批通过
         checkNo: 4, // 退回到原报人重新上报
+        checkAgain: 6,
+        revise: 7, // 修订变更
+        cancelRevise: 8, // 撤销修订
+        checkCancel: 9, // 撤回
     };
     const statusString = [];
     statusString[status.uncheck] = '待上报';
     statusString[status.checking] = '审批中';
     statusString[status.checked] = '审批通过';
     statusString[status.checkNo] = '审批退回';
+    statusString[status.revise] = '修订';
+    statusString[status.cancelRevise] = '撤销修订';
+    statusString[status.checkCancel] = '撤回';
 
     const statusClass = [];
     statusClass[status.uncheck] = '';
     statusClass[status.checking] = 'text-warning';
     statusClass[status.checked] = 'text-success';
     statusClass[status.checkNo] = 'text-warning';
+    statusClass[status.revise] = 'text-warning';
+    statusClass[status.cancelRevise] = 'text-success';
+    statusClass[status.checkCancel] = 'text-warning';
 
     // 标段概况页
     // 描述文本
@@ -715,24 +749,36 @@ const changeApply = (function() {
     auditString[status.checking] = '审批中';
     auditString[status.checked] = '审批通过';
     auditString[status.checkNo] = '审批退回';
+    auditString[status.revise] = '修订';
+    auditString[status.cancelRevise] = '撤销修订';
+    auditString[status.checkCancel] = '撤回';
     // 文字样式
     const auditStringClass = [];
     auditStringClass[status.uncheck] = '';
     auditStringClass[status.checking] = 'text-warning';
     auditStringClass[status.checked] = 'text-success';
     auditStringClass[status.checkNo] = 'text-warning';
+    auditStringClass[status.revise] = 'text-warning';
+    auditStringClass[status.cancelRevise] = 'text-success';
+    auditStringClass[status.checkCancel] = 'text-warning';
     // 描述文本
     const auditProgress = [];
     auditProgress[status.uncheck] = '草稿';
     auditProgress[status.checking] = '审批中';
     auditProgress[status.checked] = '审批通过';
     auditProgress[status.checkNo] = '审批退回';
+    auditProgress[status.revise] = '修订中';
+    auditProgress[status.cancelRevise] = '撤销修订';
+    auditProgress[status.checkCancel] = '撤回';
     // 样式
     const auditProgressClass = [];
     auditProgressClass[status.uncheck] = '';
     auditProgressClass[status.checking] = 'text-warning';
     auditProgressClass[status.checked] = 'text-success';
     auditProgressClass[status.checkNo] = 'text-warning';
+    auditProgressClass[status.revise] = 'text-warning';
+    auditProgressClass[status.cancelRevise] = 'text-success';
+    auditProgressClass[status.checkCancel] = 'text-warning';
 
     const filter = {
         status: {
@@ -756,6 +802,7 @@ const changeApply = (function() {
     statusButton[status.checking] = '审批';
     statusButton[status.checked] = '';
     statusButton[status.checkNo] = '重新上报';
+    statusButton[status.revise] = '修订';
 
     // 按钮样式
     const statusButtonClass = [];
@@ -763,6 +810,7 @@ const changeApply = (function() {
     statusButtonClass[status.checking] = 'btn-success';
     statusButtonClass[status.checked] = '';
     statusButtonClass[status.checkNo] = 'btn-warning';
+    statusButtonClass[status.revise] = 'btn-warning';
     return { status, statusString, statusClass, auditString, auditStringClass, auditProgress, auditProgressClass, filter, statusButton, statusButtonClass };
 })();
 
@@ -773,18 +821,28 @@ const changePlan = (function() {
         checking: 2, // 待审批|审批中
         checked: 3, // 审批通过
         checkNo: 4, // 退回到原报人重新上报
+        checkAgain: 6,
+        revise: 7, // 修订变更
+        cancelRevise: 8, // 撤销修订
+        checkCancel: 9, // 撤回
     };
     const statusString = [];
     statusString[status.uncheck] = '待上报';
     statusString[status.checking] = '审批中';
     statusString[status.checked] = '审批通过';
     statusString[status.checkNo] = '审批退回';
+    statusString[status.revise] = '修订';
+    statusString[status.cancelRevise] = '撤销修订';
+    statusString[status.checkCancel] = '撤回';
 
     const statusClass = [];
     statusClass[status.uncheck] = '';
     statusClass[status.checking] = 'text-warning';
     statusClass[status.checked] = 'text-success';
     statusClass[status.checkNo] = 'text-warning';
+    statusClass[status.revise] = 'text-warning';
+    statusClass[status.cancelRevise] = 'text-success';
+    statusClass[status.checkCancel] = 'text-warning';
 
     // 标段概况页
     // 描述文本
@@ -793,24 +851,36 @@ const changePlan = (function() {
     auditString[status.checking] = '审批中';
     auditString[status.checked] = '审批通过';
     auditString[status.checkNo] = '审批退回';
+    auditString[status.revise] = '修订';
+    auditString[status.cancelRevise] = '撤销修订';
+    auditString[status.checkCancel] = '撤回';
     // 文字样式
     const auditStringClass = [];
     auditStringClass[status.uncheck] = '';
     auditStringClass[status.checking] = 'text-warning';
     auditStringClass[status.checked] = 'text-success';
     auditStringClass[status.checkNo] = 'text-warning';
+    auditStringClass[status.revise] = 'text-warning';
+    auditStringClass[status.cancelRevise] = 'text-success';
+    auditStringClass[status.checkCancel] = 'text-warning';
     // 描述文本
     const auditProgress = [];
     auditProgress[status.uncheck] = '草稿';
     auditProgress[status.checking] = '审批中';
     auditProgress[status.checked] = '审批通过';
     auditProgress[status.checkNo] = '审批退回';
+    auditProgress[status.revise] = '修订中';
+    auditProgress[status.cancelRevise] = '撤销修订';
+    auditProgress[status.checkCancel] = '撤回';
     // 样式
     const auditProgressClass = [];
     auditProgressClass[status.uncheck] = '';
     auditProgressClass[status.checking] = 'text-warning';
     auditProgressClass[status.checked] = 'text-success';
     auditProgressClass[status.checkNo] = 'text-warning';
+    auditProgressClass[status.revise] = 'text-warning';
+    auditProgressClass[status.cancelRevise] = 'text-success';
+    auditProgressClass[status.checkCancel] = 'text-warning';
 
     const filter = {
         status: {
@@ -834,6 +904,7 @@ const changePlan = (function() {
     statusButton[status.checking] = '审批';
     statusButton[status.checked] = '';
     statusButton[status.checkNo] = '重新上报';
+    statusButton[status.revise] = '修订';
 
     // 按钮样式
     const statusButtonClass = [];
@@ -841,6 +912,7 @@ const changePlan = (function() {
     statusButtonClass[status.checking] = 'btn-success';
     statusButtonClass[status.checked] = '';
     statusButtonClass[status.checkNo] = 'btn-warning';
+    statusButtonClass[status.revise] = 'btn-warning';
     return { status, statusString, statusClass, auditString, auditStringClass, auditProgress, auditProgressClass, filter, statusButton, statusButtonClass };
 })();
 

+ 333 - 11
app/controller/change_controller.js

@@ -1880,6 +1880,7 @@ module.exports = app => {
                 }
                 ctx.body = responseData;
             } catch (err) {
+                console.log(err);
                 this.log(err);
                 ctx.body = this.ajaxErrorBody(err, '数据错误');
             }
@@ -2192,7 +2193,7 @@ module.exports = app => {
          */
         async _getChangeProjectAuditViewData(ctx) {
             const auditConst = audit.changeProject;
-            const times = ctx.change.status === auditConst.status.back ? ctx.change.times - 1 : ctx.change.times;
+            const times = (ctx.change.status === auditConst.status.back || ctx.change.status === auditConst.status.revise) ? ctx.change.times - 1 : ctx.change.times;
             ctx.change.user = await ctx.service.projectAccount.getAccountInfoById(ctx.change.uid);
             ctx.change.auditHistory = [];
             if (times >= 1) {
@@ -2201,10 +2202,10 @@ module.exports = app => {
                 }
             }
             // 获取审批流程中左边列表
-            ctx.change.auditors2 = ctx.change.status === auditConst.status.back && ctx.change.uid !== ctx.session.sessionUser.accountId ?
+            ctx.change.auditors2 = (ctx.change.status === auditConst.status.back || ctx.change.status === auditConst.status.revise) && ctx.change.uid !== ctx.session.sessionUser.accountId ?
                 await ctx.service.changeProjectAudit.getAuditorsWithOwner(ctx.change.id, times) :
                 await ctx.service.changeProjectAudit.getAuditorsWithOwner(ctx.change.id, ctx.change.times);
-            if (ctx.change.status === auditConst.status.uncheck || ctx.change.status === auditConst.status.back) {
+            if (ctx.change.status === auditConst.status.uncheck || ctx.change.status === auditConst.status.back || ctx.change.status === auditConst.status.revise) {
                 ctx.change.auditorList = await ctx.service.changeProjectAudit.getAuditors(ctx.change.id, ctx.change.times);
             }
             ctx.change.xsAuditors = await ctx.service.changeProjectXsAudit.getAuditList(ctx.change.id);
@@ -2247,6 +2248,9 @@ module.exports = app => {
                 const fileList = await ctx.service.changeProjectAtt.getAllChangeProjectAtt(ctx.tender.id, ctx.change.id);
                 await this._getChangeProjectAuditViewData(ctx);
                 const changeClass = await this._getOrUpdateClass(ctx, 'changeProject');
+                // 获取用户人验证手机号
+                const pa = await ctx.service.projectAccount.getDataById(ctx.session.sessionUser.accountId);
+                const auth_mobile = pa.auth_mobile;
                 // 判断并更新
                 const renderData = {
                     tender,
@@ -2256,6 +2260,7 @@ module.exports = app => {
                     auditConst: audit.changeProject,
                     fileList,
                     whiteList,
+                    authMobile: auth_mobile,
                     returnUrl: this.app._.includes(ctx.request.headers.referer, '/tender/' + ctx.tender.id + '/change/project') ? ctx.request.headers.referer : null,
                     jsFiles: this.app.jsFiles.common.concat(this.app.jsFiles.change.project_information),
                     preUrl: '/tender/' + ctx.tender.id + '/change/project/' + ctx.change.id + '/information',
@@ -2636,6 +2641,109 @@ module.exports = app => {
             }
         }
 
+        /**
+         * 撤回审批
+         * @param ctx
+         * @return {Promise<void>}
+         */
+        async checkProjectAuditCancel(ctx) {
+            try {
+                if (!ctx.change.cancancel) throw '您无权进行该操作';
+
+                await ctx.service.changeProjectAudit.checkCancel(ctx.change);
+                ctx.body = { err: 0, url: ctx.request.header.referer, msg: '' };
+            } catch (err) {
+                this.log(err);
+                ctx.body = { err: 1, msg: err };
+            }
+        }
+
+        /**
+         * 变更立项修订重新上报
+         * @param {Object} ctx - egg全局变量
+         * @return {void}
+         */
+        async checkProjectRevise(ctx) {
+            try {
+                if (ctx.change.status !== audit.changeProject.status.checked || ctx.session.sessionUser.accountId !== ctx.change.uid) {
+                    throw '您无权进行该操作';
+                }
+                // 判断是否被变更申请调用了,是则无法发起修订
+                const projectInfo = await ctx.service.changeApply.getDataByCondition({ tid: ctx.tender.id, project_code: ctx.change.code });
+                if (projectInfo) {
+                    throw '该变更立项已被变更申请调用,无法发起修订';
+                }
+                if (ctx.session.sessionUser.loginStatus === 0) {
+                    const code = ctx.request.body.code;
+                    const pa = await ctx.service.projectAccount.getDataById(ctx.session.sessionUser.accountId);
+                    if (!pa.auth_mobile) {
+                        throw '未绑定手机号';
+                    }
+                    const cacheKey = 'smsCode:' + ctx.session.sessionUser.accountId;
+                    const cacheCode = await app.redis.get(cacheKey);
+                    // console.log(cacheCode);
+                    if (cacheCode === null || code === undefined || cacheCode !== (code + pa.auth_mobile)) {
+                        throw '验证码不正确!';
+                    }
+                }
+
+                // 获取是否已存在调用变更令
+                // const changeUsedData = await ctx.service.stageChange.getFinalUsedData(ctx.tender.id, changeData.cid);
+                // const stageChangeNum = this.ctx.helper.sum(changeUsedData.map(x => { return Math.abs(x.qty); }));
+                // if (stageChangeNum !== 0) {
+                //     throw '该变更令已被调用,无法重新审批';
+                // }
+                // 重新审批
+                const result = await ctx.service.changeProjectAudit.checkRevise(ctx.change);
+                if (!result) {
+                    throw '修订发起失败';
+                }
+                // ctx.redirect('/tender/' + changeData.tid + '/change/' + changeData.cid + '/info');
+                ctx.body = {
+                    err: 0,
+                    url: ctx.request.header.referer,
+                    msg: '',
+                };
+            } catch (err) {
+                console.log(err);
+                // ctx.redirect(ctx.request.header.referer);
+                ctx.body = {
+                    err: 1,
+                    // url: ctx.request.header.referer,
+                    msg: err,
+                };
+            }
+        }
+
+        /**
+         * 变更令撤销修订
+         * @param {Object} ctx - egg全局变量
+         * @return {void}
+         */
+        async cancelProjectRevise(ctx) {
+            try {
+                if (!(ctx.change.status === audit.changeProject.status.revise && (ctx.session.sessionUser.accountId === ctx.change.uid || ctx.session.sessionUser.accountId === ctx.session.sessionUser.is_admin))) {
+                    throw '您无权进行该操作';
+                }
+                // 重新审批
+                const result = await ctx.service.changeProjectAudit.cancelRevise(ctx.change);
+                if (!result) {
+                    throw '撤销修订失败';
+                }
+                ctx.body = {
+                    err: 0,
+                    url: ctx.request.header.referer,
+                    msg: '',
+                };
+            } catch (err) {
+                console.log(err);
+                ctx.body = {
+                    err: 1,
+                    msg: err,
+                };
+            }
+        }
+
         async _filterChangesApply(ctx, status = 0) {
             const tenderId = ctx.params.id;
             ctx.session.sessionUser.tenderId = tenderId;
@@ -2822,7 +2930,7 @@ module.exports = app => {
          */
         async _getChangeApplyAuditViewData(ctx) {
             const auditConst = audit.changeApply;
-            const times = ctx.change.status === auditConst.status.checkNo ? ctx.change.times - 1 : ctx.change.times;
+            const times = ctx.change.status === auditConst.status.checkNo || ctx.change.status === auditConst.status.revise ? ctx.change.times - 1 : ctx.change.times;
             ctx.change.user = await ctx.service.projectAccount.getAccountInfoById(ctx.change.uid);
             ctx.change.auditHistory = [];
             if (times >= 1) {
@@ -2831,10 +2939,10 @@ module.exports = app => {
                 }
             }
             // 获取审批流程中左边列表
-            ctx.change.auditors2 = ctx.change.status === auditConst.status.checkNo && ctx.change.uid !== ctx.session.sessionUser.accountId ?
+            ctx.change.auditors2 = (ctx.change.status === auditConst.status.checkNo || ctx.change.status === auditConst.status.revise) && ctx.change.uid !== ctx.session.sessionUser.accountId ?
                 await ctx.service.changeApplyAudit.getAuditorsWithOwner(ctx.change.id, times) :
                 await ctx.service.changeApplyAudit.getAuditorsWithOwner(ctx.change.id, ctx.change.times);
-            if (ctx.change.status === auditConst.status.uncheck || ctx.change.status === auditConst.status.checkNo) {
+            if (ctx.change.status === auditConst.status.uncheck || ctx.change.status === auditConst.status.checkNo || ctx.change.status === auditConst.status.revise) {
                 ctx.change.auditorList = await ctx.service.changeApplyAudit.getAuditors(ctx.change.id, ctx.change.times);
             }
         }
@@ -2849,6 +2957,9 @@ module.exports = app => {
                 // 获取附件列表
                 const fileList = await ctx.service.changeApplyAtt.getAllChangeApplyAtt(ctx.tender.id, ctx.change.id);
                 const changeClass = await this._getOrUpdateClass(ctx, 'changeApply');
+                // 获取用户人验证手机号
+                const pa = await ctx.service.projectAccount.getDataById(ctx.session.sessionUser.accountId);
+                const auth_mobile = pa.auth_mobile;
                 const renderData = {
                     tender,
                     change: ctx.change,
@@ -2859,6 +2970,7 @@ module.exports = app => {
                     auditConst: audit.changeApply,
                     fileList,
                     whiteList,
+                    authMobile: auth_mobile,
                     tpUnit: ctx.change.decimal ? ctx.change.decimal.tp : ctx.tender.info.decimal.tp,
                     upUnit: ctx.change.decimal ? ctx.change.decimal.up : ctx.tender.info.decimal.up,
                     changeUnits: changeConst.units,
@@ -2867,7 +2979,7 @@ module.exports = app => {
                     jsFiles: this.app.jsFiles.common.concat(this.app.jsFiles.change.apply_information),
                     preUrl: '/tender/' + ctx.tender.id + '/change/apply/' + ctx.change.id + '/information',
                 };
-                if ((ctx.change.status === audit.changeApply.status.uncheck || ctx.change.status === audit.changeApply.status.checkNo) && (ctx.session.sessionUser.accountId === ctx.change.uid || ctx.tender.isTourist)) {
+                if ((ctx.change.status === audit.changeApply.status.uncheck || ctx.change.status === audit.changeApply.status.checkNo || ctx.change.status === audit.changeApply.status.revise) && (ctx.session.sessionUser.accountId === ctx.change.uid || ctx.tender.isTourist)) {
                     // data.accountGroup = accountGroup;
                     // 获取所有项目参与者
                     const accountList = await ctx.service.projectAccount.getAllDataByCondition({
@@ -3256,6 +3368,109 @@ module.exports = app => {
             }
         }
 
+        /**
+         * 撤回审批
+         * @param ctx
+         * @return {Promise<void>}
+         */
+        async checkApplyAuditCancel(ctx) {
+            try {
+                if (!ctx.change.cancancel) throw '您无权进行该操作';
+
+                await ctx.service.changeApplyAudit.checkCancel(ctx.change);
+                ctx.body = { err: 0, url: ctx.request.header.referer, msg: '' };
+            } catch (err) {
+                this.log(err);
+                ctx.body = { err: 1, msg: err };
+            }
+        }
+
+        /**
+         * 变更申请修订重新上报
+         * @param {Object} ctx - egg全局变量
+         * @return {void}
+         */
+        async checkApplyRevise(ctx) {
+            try {
+                if (ctx.change.status !== audit.changeApply.status.checked || ctx.session.sessionUser.accountId !== ctx.change.uid) {
+                    throw '您无权进行该操作';
+                }
+                // 判断是否被变更申请调用了,是则无法发起修订
+                const projectInfo = await ctx.service.changePlan.getDataByCondition({ tid: ctx.tender.id, apply_code: ctx.change.code });
+                if (projectInfo) {
+                    throw '该变更申请已被变更方案调用,无法发起修订';
+                }
+                if (ctx.session.sessionUser.loginStatus === 0) {
+                    const code = ctx.request.body.code;
+                    const pa = await ctx.service.projectAccount.getDataById(ctx.session.sessionUser.accountId);
+                    if (!pa.auth_mobile) {
+                        throw '未绑定手机号';
+                    }
+                    const cacheKey = 'smsCode:' + ctx.session.sessionUser.accountId;
+                    const cacheCode = await app.redis.get(cacheKey);
+                    // console.log(cacheCode);
+                    if (cacheCode === null || code === undefined || cacheCode !== (code + pa.auth_mobile)) {
+                        throw '验证码不正确!';
+                    }
+                }
+
+                // 获取是否已存在调用变更令
+                // const changeUsedData = await ctx.service.stageChange.getFinalUsedData(ctx.tender.id, changeData.cid);
+                // const stageChangeNum = this.ctx.helper.sum(changeUsedData.map(x => { return Math.abs(x.qty); }));
+                // if (stageChangeNum !== 0) {
+                //     throw '该变更令已被调用,无法重新审批';
+                // }
+                // 重新审批
+                const result = await ctx.service.changeApplyAudit.checkRevise(ctx.change);
+                if (!result) {
+                    throw '修订发起失败';
+                }
+                // ctx.redirect('/tender/' + changeData.tid + '/change/' + changeData.cid + '/info');
+                ctx.body = {
+                    err: 0,
+                    url: ctx.request.header.referer,
+                    msg: '',
+                };
+            } catch (err) {
+                console.log(err);
+                // ctx.redirect(ctx.request.header.referer);
+                ctx.body = {
+                    err: 1,
+                    // url: ctx.request.header.referer,
+                    msg: err,
+                };
+            }
+        }
+
+        /**
+         * 变更令撤销修订
+         * @param {Object} ctx - egg全局变量
+         * @return {void}
+         */
+        async cancelApplyRevise(ctx) {
+            try {
+                if (!(ctx.change.status === audit.changeApply.status.revise && (ctx.session.sessionUser.accountId === ctx.change.uid || ctx.session.sessionUser.accountId === ctx.session.sessionUser.is_admin))) {
+                    throw '您无权进行该操作';
+                }
+                // 重新审批
+                const result = await ctx.service.changeApplyAudit.cancelRevise(ctx.change);
+                if (!result) {
+                    throw '撤销修订失败';
+                }
+                ctx.body = {
+                    err: 0,
+                    url: ctx.request.header.referer,
+                    msg: '',
+                };
+            } catch (err) {
+                console.log(err);
+                ctx.body = {
+                    err: 1,
+                    msg: err,
+                };
+            }
+        }
+
         // 变更方案
         async _filterChangesPlan(ctx, status = 0) {
             const tenderId = ctx.params.id;
@@ -3443,7 +3658,7 @@ module.exports = app => {
          */
         async _getChangePlanAuditViewData(ctx) {
             const auditConst = audit.changePlan;
-            const times = ctx.change.status === auditConst.status.checkNo ? ctx.change.times - 1 : ctx.change.times;
+            const times = ctx.change.status === auditConst.status.checkNo || ctx.change.status === auditConst.status.revise ? ctx.change.times - 1 : ctx.change.times;
             ctx.change.user = await ctx.service.projectAccount.getAccountInfoById(ctx.change.uid);
             ctx.change.auditHistory = [];
             if (times >= 1) {
@@ -3452,10 +3667,10 @@ module.exports = app => {
                 }
             }
             // 获取审批流程中左边列表
-            ctx.change.auditors2 = ctx.change.status === auditConst.status.checkNo && ctx.change.uid !== ctx.session.sessionUser.accountId ?
+            ctx.change.auditors2 = (ctx.change.status === auditConst.status.checkNo || ctx.change.status === auditConst.status.revise) && ctx.change.uid !== ctx.session.sessionUser.accountId ?
                 await ctx.service.changePlanAudit.getAuditorsWithOwner(ctx.change.id, times) :
                 await ctx.service.changePlanAudit.getAuditorsWithOwner(ctx.change.id, ctx.change.times);
-            if (ctx.change.status === auditConst.status.uncheck || ctx.change.status === auditConst.status.checkNo) {
+            if (ctx.change.status === auditConst.status.uncheck || ctx.change.status === auditConst.status.checkNo || ctx.change.status === auditConst.status.revise) {
                 ctx.change.auditorList = await ctx.service.changePlanAudit.getAuditors(ctx.change.id, ctx.change.times);
             }
         }
@@ -3480,6 +3695,9 @@ module.exports = app => {
                         }
                     }
                 }
+                // 获取用户人验证手机号
+                const pa = await ctx.service.projectAccount.getDataById(ctx.session.sessionUser.accountId);
+                const auth_mobile = pa.auth_mobile;
                 const changeClass = await this._getOrUpdateClass(ctx, 'changePlan');
                 const renderData = {
                     tender,
@@ -3491,6 +3709,7 @@ module.exports = app => {
                     auditConst: audit.changePlan,
                     fileList,
                     whiteList,
+                    authMobile: auth_mobile,
                     tpUnit: ctx.change.decimal ? ctx.change.decimal.tp : ctx.tender.info.decimal.tp,
                     upUnit: ctx.change.decimal ? ctx.change.decimal.up : ctx.tender.info.decimal.up,
                     changeUnits: changeConst.units,
@@ -3499,7 +3718,7 @@ module.exports = app => {
                     jsFiles: this.app.jsFiles.common.concat(this.app.jsFiles.change.plan_information),
                     preUrl: '/tender/' + ctx.tender.id + '/change/plan/' + ctx.change.id + '/information',
                 };
-                if ((ctx.change.status === audit.changePlan.status.uncheck || ctx.change.status === audit.changePlan.status.checkNo) && (ctx.session.sessionUser.accountId === ctx.change.uid || ctx.tender.isTourist)) {
+                if ((ctx.change.status === audit.changePlan.status.uncheck || ctx.change.status === audit.changePlan.status.checkNo || ctx.change.status === audit.changePlan.status.revise) && (ctx.session.sessionUser.accountId === ctx.change.uid || ctx.tender.isTourist)) {
                     // data.accountGroup = accountGroup;
                     // 获取所有项目参与者
                     const accountList = await ctx.service.projectAccount.getAllDataByCondition({
@@ -3881,6 +4100,109 @@ module.exports = app => {
                 ctx.body = { err: 1, msg: err.toString(), data: null };
             }
         }
+
+        /**
+         * 撤回审批
+         * @param ctx
+         * @return {Promise<void>}
+         */
+        async checkPlanAuditCancel(ctx) {
+            try {
+                if (!ctx.change.cancancel) throw '您无权进行该操作';
+
+                await ctx.service.changePlanAudit.checkCancel(ctx.change);
+                ctx.body = { err: 0, url: ctx.request.header.referer, msg: '' };
+            } catch (err) {
+                this.log(err);
+                ctx.body = { err: 1, msg: err };
+            }
+        }
+
+        /**
+         * 变更申请修订重新上报
+         * @param {Object} ctx - egg全局变量
+         * @return {void}
+         */
+        async checkPlanRevise(ctx) {
+            try {
+                if (ctx.change.status !== audit.changePlan.status.checked || ctx.session.sessionUser.accountId !== ctx.change.uid) {
+                    throw '您无权进行该操作';
+                }
+                // 判断是否被变更申请调用了,是则无法发起修订
+                const projectInfo = await ctx.service.change.getDataByCondition({ tid: ctx.tender.id, plan_code: ctx.change.code });
+                if (projectInfo) {
+                    throw '该变更方案已被变更令调用,无法发起修订';
+                }
+                if (ctx.session.sessionUser.loginStatus === 0) {
+                    const code = ctx.request.body.code;
+                    const pa = await ctx.service.projectAccount.getDataById(ctx.session.sessionUser.accountId);
+                    if (!pa.auth_mobile) {
+                        throw '未绑定手机号';
+                    }
+                    const cacheKey = 'smsCode:' + ctx.session.sessionUser.accountId;
+                    const cacheCode = await app.redis.get(cacheKey);
+                    // console.log(cacheCode);
+                    if (cacheCode === null || code === undefined || cacheCode !== (code + pa.auth_mobile)) {
+                        throw '验证码不正确!';
+                    }
+                }
+
+                // 获取是否已存在调用变更令
+                // const changeUsedData = await ctx.service.stageChange.getFinalUsedData(ctx.tender.id, changeData.cid);
+                // const stageChangeNum = this.ctx.helper.sum(changeUsedData.map(x => { return Math.abs(x.qty); }));
+                // if (stageChangeNum !== 0) {
+                //     throw '该变更令已被调用,无法重新审批';
+                // }
+                // 重新审批
+                const result = await ctx.service.changePlanAudit.checkRevise(ctx.change);
+                if (!result) {
+                    throw '修订发起失败';
+                }
+                // ctx.redirect('/tender/' + changeData.tid + '/change/' + changeData.cid + '/info');
+                ctx.body = {
+                    err: 0,
+                    url: ctx.request.header.referer,
+                    msg: '',
+                };
+            } catch (err) {
+                console.log(err);
+                // ctx.redirect(ctx.request.header.referer);
+                ctx.body = {
+                    err: 1,
+                    // url: ctx.request.header.referer,
+                    msg: err,
+                };
+            }
+        }
+
+        /**
+         * 变更令撤销修订
+         * @param {Object} ctx - egg全局变量
+         * @return {void}
+         */
+        async cancelPlanRevise(ctx) {
+            try {
+                if (!(ctx.change.status === audit.changePlan.status.revise && (ctx.session.sessionUser.accountId === ctx.change.uid || ctx.session.sessionUser.accountId === ctx.session.sessionUser.is_admin))) {
+                    throw '您无权进行该操作';
+                }
+                // 重新审批
+                const result = await ctx.service.changePlanAudit.cancelRevise(ctx.change);
+                if (!result) {
+                    throw '撤销修订失败';
+                }
+                ctx.body = {
+                    err: 0,
+                    url: ctx.request.header.referer,
+                    msg: '',
+                };
+            } catch (err) {
+                console.log(err);
+                ctx.body = {
+                    err: 1,
+                    msg: err,
+                };
+            }
+        }
     }
 
     return ChangeController;

+ 10 - 27
app/middleware/change_apply_check.js

@@ -50,39 +50,22 @@ module.exports = options => {
                 //     change.readOnly = change.status !== status.uncheck && change.status !== status.back;
                 // }
                 change.curTimes = change.times;
-                if (change.status === status.uncheck || change.status === status.checkNo) {
-                    change.curOrder = 0;
-                } else if (change.status === status.checked) {
-                    change.curOrder = _.max(_.map(change.auditors, 'order'));
-                } else {
-                    change.curOrder = change.curAuditor.aid === accountId ? change.curAuditor.order : change.curAuditor.order - 1;
-                }
                 change.filePermission = true;
             } else if (this.tender.isTourist) {
                 change.curTimes = change.times;
-                if (change.status === status.uncheck || change.status === status.checkNo) {
-                    change.curOrder = 0;
-                } else if (change.status === status.checked) {
-                    change.curOrder = _.max(_.map(change.auditors, 'order'));
-                } else {
-                    change.curOrder = change.curAuditor.order;
-                }
                 change.filePermission = this.tender.touristPermission.file || auditorIds.indexOf(accountId) !== -1;
             } else if (auditorIds.indexOf(accountId) !== -1) { // 审批人
                 if (change.status === status.uncheck) {
                     throw '您无权查看该数据';
                 }
                 // change.readOnly = change.status !== status.checking || accountId !== change.curAuditor.aid;
-                change.curTimes = change.status === status.checkNo ? change.times - 1 : change.times;
-                if (change.status === status.checked) {
-                    change.curOrder = _.max(_.map(change.auditors, 'order'));
-                } else if (change.status === status.checkNo) {
-                    const audit = this.service.changeApplyAudit.getDataByCondition({
-                        caid: change.id, times: change.times, status: status.checkNo,
-                    });
-                    change.curOrder = audit.order;
-                } else {
-                    change.curOrder = accountId === change.curAuditor.aid ? change.curAuditor.order : change.curAuditor.order - 1;
+                change.curTimes = change.status === status.checkNo || change.status === status.revise ? change.times - 1 : change.times;
+                change.filePermission = true;
+            } else if ((change.status === status.checkNo || change.status === status.revise) && change.uid !== accountId) {
+                const preAuditors = yield this.service.changeApplyAudit.getAuditors(change.id, change.times - 1);
+                const preAuditorIds = _.map(preAuditors, 'aid');
+                if (preAuditorIds.indexOf(accountId) === -1) {
+                    throw '您无权查看该数据';
                 }
                 change.filePermission = true;
             } else if (shareIds.indexOf(accountId) !== -1) { // 分享人
@@ -90,16 +73,16 @@ module.exports = options => {
                     throw '您无权查看该数据';
                 }
                 // change.readOnly = true;
-                change.curTimes = change.status === status.checkNo ? change.times - 1 : change.times;
-                change.curOrder = change.status === status.checked ? _.max(_.map(change.auditors, 'order')) : change.curAuditor.order - 1;
+                change.curTimes = change.status === status.checkNo || change.status === status.revise ? change.times - 1 : change.times;
                 change.filePermission = false;
             } else { // 其他不可见
                 throw '您无权查看该数据';
             }
             // 调差的readOnly 指表格和页面只能看不能改,和审批无关
-            change.readOnly = !((change.status === status.uncheck || change.status === status.checkNo) && accountId === change.uid);
+            change.readOnly = !((change.status === status.uncheck || change.status === status.checkNo || change.status === status.revise) && accountId === change.uid);
             change.shenpiPower = change.status === status.checking && change.curAuditor.aid === accountId;
             this.change = change;
+            yield this.service.changeApply.doCheckChangeCanCancel(this.change);
             yield next;
         } catch (err) {
             console.log(err);

+ 10 - 31
app/middleware/change_plan_check.js

@@ -44,29 +44,10 @@ module.exports = options => {
                 auditorIds = _.map(change.auditors, 'aid'),
                 shareIds = [];
             if (accountId === change.uid) { // 原报
-                // if (change.curAuditor) {
-                //     change.readOnly = change.status === status.checking && change.curAuditor.user_id === accountId;
-                // } else {
-                //     change.readOnly = change.status !== status.uncheck && change.status !== status.back;
-                // }
                 change.curTimes = change.times;
-                if (change.status === status.uncheck || change.status === status.checkNo) {
-                    change.curOrder = 0;
-                } else if (change.status === status.checked) {
-                    change.curOrder = _.max(_.map(change.auditors, 'order'));
-                } else {
-                    change.curOrder = change.curAuditor.aid === accountId ? change.curAuditor.order : change.curAuditor.order - 1;
-                }
                 change.filePermission = true;
             } else if (this.tender.isTourist) {
                 change.curTimes = change.times;
-                if (change.status === status.uncheck || change.status === status.checkNo) {
-                    change.curOrder = 0;
-                } else if (change.status === status.checked) {
-                    change.curOrder = _.max(_.map(change.auditors, 'order'));
-                } else {
-                    change.curOrder = change.curAuditor.order;
-                }
                 change.filePermission = this.tender.touristPermission.file || auditorIds.indexOf(accountId) !== -1;
             } else if (auditorIds.indexOf(accountId) !== -1) { // 审批人
                 if (change.status === status.uncheck) {
@@ -74,15 +55,12 @@ module.exports = options => {
                 }
                 // change.readOnly = change.status !== status.checking || accountId !== change.curAuditor.aid;
                 change.curTimes = change.status === status.checkNo ? change.times - 1 : change.times;
-                if (change.status === status.checked) {
-                    change.curOrder = _.max(_.map(change.auditors, 'order'));
-                } else if (change.status === status.checkNo) {
-                    const audit = this.service.changePlanAudit.getDataByCondition({
-                        cpid: change.id, times: change.times, status: status.checkNo,
-                    });
-                    change.curOrder = audit.order;
-                } else {
-                    change.curOrder = accountId === change.curAuditor.aid ? change.curAuditor.order : change.curAuditor.order - 1;
+                change.filePermission = true;
+            } else if ((change.status === status.checkNo || change.status === status.revise) && change.uid !== accountId) {
+                const preAuditors = yield this.service.changePlanAudit.getAuditors(change.id, change.times - 1);
+                const preAuditorIds = _.map(preAuditors, 'aid');
+                if (preAuditorIds.indexOf(accountId) === -1) {
+                    throw '您无权查看该数据';
                 }
                 change.filePermission = true;
             } else if (shareIds.indexOf(accountId) !== -1) { // 分享人
@@ -90,16 +68,17 @@ module.exports = options => {
                     throw '您无权查看该数据';
                 }
                 // change.readOnly = true;
-                change.curTimes = change.status === status.checkNo ? change.times - 1 : change.times;
-                change.curOrder = change.status === status.checked ? _.max(_.map(change.auditors, 'order')) : change.curAuditor.order - 1;
+                change.curTimes = change.status === status.checkNo || change.status === status.revise ? change.times - 1 : change.times;
                 change.filePermission = false;
             } else { // 其他不可见
                 throw '您无权查看该数据';
             }
             // 调差的readOnly 指表格和页面只能看不能改,和审批无关
-            change.readOnly = !((change.status === status.uncheck || change.status === status.checkNo) && accountId === change.uid);
+            change.readOnly = !((change.status === status.uncheck || change.status === status.checkNo || change.status === status.revise) && accountId === change.uid);
             change.shenpiPower = change.status === status.checking && change.curAuditor.aid === accountId;
             this.change = change;
+            yield this.service.changePlan.doCheckChangeCanCancel(this.change);
+            console.log(this.change.cancancel);
             yield next;
         } catch (err) {
             console.log(err);

+ 13 - 39
app/middleware/change_project_check.js

@@ -44,63 +44,37 @@ module.exports = options => {
                 xsAuditorIds = _.map(change.xsAuditors, 'aid'),
                 shareIds = [];
             if (accountId === change.uid) { // 原报
-                // if (change.curAuditor) {
-                //     change.readOnly = change.status === status.checking && change.curAuditor.user_id === accountId;
-                // } else {
-                //     change.readOnly = change.status !== status.uncheck && change.status !== status.back;
-                // }
                 change.curTimes = change.times;
-                if (change.status === status.uncheck || change.status === status.back || change.status === status.checkNo) {
-                    change.curOrder = 0;
-                } else if (change.status === status.checked) {
-                    change.curOrder = _.max(_.map(change.auditors, 'order'));
-                } else {
-                    change.curOrder = change.curAuditor.aid === accountId ? change.curAuditor.order : change.curAuditor.order - 1;
-                }
                 change.filePermission = true;
-            } else if (this.tender.isTourist) {
-                change.curTimes = change.times;
-                if (change.status === status.uncheck || change.status === status.back || change.status === status.checkNo) {
-                    change.curOrder = 0;
-                } else if (change.status === status.checked) {
-                    change.curOrder = _.max(_.map(change.auditors, 'order'));
-                } else {
-                    change.curOrder = change.curAuditor.order;
-                }
-                change.filePermission = this.tender.touristPermission.file || auditorIds.indexOf(accountId) !== -1;
             } else if (auditorIds.indexOf(accountId) !== -1 || xsAuditorIds.indexOf(accountId) !== -1) { // 审批人或者协审人
                 if (change.status === status.uncheck) {
                     throw '您无权查看该数据';
                 }
-                // change.readOnly = change.status !== status.checking || accountId !== change.curAuditor.aid;
-                change.curTimes = change.status === status.back ? change.times - 1 : change.times;
-                if (change.status === status.checked) {
-                    change.curOrder = _.max(_.map(change.auditors, 'order'));
-                } else if (change.status === status.back) {
-                    const audit = this.service.changeProjectAudit.getDataByCondition({
-                        cpid: change.id, times: change.times, status: status.back,
-                    });
-                    change.curOrder = audit.order;
-                } else if (change.status === status.checkNo) {
-                    change.curOrder = 0;
-                } else {
-                    change.curOrder = accountId === change.curAuditor.aid ? change.curAuditor.order : change.curAuditor.order - 1;
+                change.curTimes = change.status === status.back || change.status === status.revise ? change.times - 1 : change.times;
+                change.filePermission = true;
+            } else if ((change.status === status.back || change.status === status.revise) && change.uid !== accountId) {
+                const preAuditors = yield this.service.changeProjectAudit.getAuditors(change.id, change.times - 1);
+                const preAuditorIds = _.map(preAuditors, 'aid');
+                if (preAuditorIds.indexOf(accountId) === -1) {
+                    throw '您无权查看该数据';
                 }
                 change.filePermission = true;
             } else if (shareIds.indexOf(accountId) !== -1) { // 分享人
                 if (change.status === status.uncheck) {
                     throw '您无权查看该数据';
                 }
-                // change.readOnly = true;
-                change.curTimes = change.status === status.back ? change.times - 1 : change.times;
-                change.curOrder = change.status === status.checked ? _.max(_.map(change.auditors, 'order')) : (change.status !== status.checkNo ? change.curAuditor.order - 1 : 0);
+                change.curTimes = change.status === status.back || change.status === status.revise ? change.times - 1 : change.times;
                 change.filePermission = false;
+            } else if (this.tender.isTourist) {
+                change.curTimes = change.times;
+                change.filePermission = this.tender.touristPermission.file || auditorIds.indexOf(accountId) !== -1;
             } else { // 其他不可见
                 throw '您无权查看该数据';
             }
             // 调差的readOnly 指表格和页面只能看不能改,和审批无关
-            change.readOnly = !((change.status === status.uncheck || change.status === status.back) && accountId === change.uid);
+            change.readOnly = !((change.status === status.uncheck || change.status === status.back || change.status === status.revise) && accountId === change.uid);
             this.change = change;
+            yield this.service.changeProject.doCheckChangeCanCancel(this.change);
             yield next;
         } catch (err) {
             console.log(err);

+ 4 - 4
app/public/js/change_apply_information.js

@@ -320,7 +320,7 @@ $(document).ready(() => {
                 const select = type === 'update' ? SpreadJsObj.getSelectObject(info.sheet) : {unit: ''};
                 const col = info.sheet.zh_setting.cols[info.col];
                 // 未改变值则不提交
-                let validText = is_numeric(info.editingText) ? parseFloat(info.editingText) : (info.editingText ? trimInvalidChar(info.editingText) : '');
+                let validText = (col.type === 'Number' || col.field === 'new_up') && is_numeric(info.editingText) ? parseFloat(info.editingText) : (info.editingText ? trimInvalidChar(info.editingText) : '');
                 const orgValue = type === 'update' ? select[col.field] : '';
                 if (orgValue == validText || ((!orgValue || orgValue === '') && (validText === ''))) {
                     SpreadJsObj.reLoadRowData(info.sheet, info.row);
@@ -402,7 +402,7 @@ $(document).ready(() => {
                         // cLData[colSetting.field] = trimInvalidChar(info.sheet.getText(curRow, curCol));
 
                         let validText = info.sheet.getText(curRow, curCol);
-                        validText = is_numeric(validText) ? parseFloat(validText) : (validText ? trimInvalidChar(validText) : '');
+                        validText = (colSetting.type === 'Number' || colSetting.field === 'new_up') && is_numeric(validText) ? parseFloat(validText) : (validText ? trimInvalidChar(validText) : '');
                         const orgValue = curRow >= sortData.length ? '' : sortData[curRow][colSetting.field];
                         if (orgValue == validText || ((!orgValue || orgValue === '') && (validText === ''))) {
                             sameCol++;
@@ -664,7 +664,7 @@ $(document).ready(() => {
                 const select = SpreadJsObj.getSelectObject(info.sheet);
                 const col = info.sheet.zh_setting.cols[info.col];
                 // 未改变值则不提交
-                let validText = is_numeric(info.editingText) ? parseFloat(info.editingText) : (info.editingText ? trimInvalidChar(info.editingText) : '');
+                let validText = info.editingText ? trimInvalidChar(info.editingText) : '';
                 const orgValue = select[col.field];
                 if (orgValue == validText || ((!orgValue || orgValue === '') && (validText === ''))) {
                     SpreadJsObj.reLoadRowData(info.sheet, info.row);
@@ -713,7 +713,7 @@ $(document).ready(() => {
                     if (!colSetting) continue;
 
                     let validText = info.sheet.getText(curRow, curCol);
-                    validText = is_numeric(validText) ? parseFloat(validText) : (validText ? trimInvalidChar(validText) : null);
+                    validText = validText ? trimInvalidChar(validText) : null;
                     const orgValue = sortData[curRow][colSetting.field];
                     if (orgValue == validText || ((!orgValue || orgValue === '') && (validText === ''))) {
                         sameCol++;

+ 4 - 4
app/public/js/change_plan_information.js

@@ -383,7 +383,7 @@ $(document).ready(() => {
                     return;
                 }
                 // 未改变值则不提交
-                let validText = is_numeric(info.editingText) ? parseFloat(info.editingText) : (info.editingText ? trimInvalidChar(info.editingText) : '');
+                let validText = (col.type === 'Number' || col.field === 'new_up') && is_numeric(info.editingText) ? parseFloat(info.editingText) : (info.editingText ? trimInvalidChar(info.editingText) : '');
                 const orgValue = type === 'update' ? select[col.field] : '';
                 if (orgValue == validText || ((!orgValue || orgValue === '') && (validText === ''))) {
                     SpreadJsObj.reLoadRowData(info.sheet, info.row);
@@ -468,7 +468,7 @@ $(document).ready(() => {
                         // cLData[colSetting.field] = trimInvalidChar(info.sheet.getText(curRow, curCol));
 
                         let validText = info.sheet.getText(curRow, curCol);
-                        validText = is_numeric(validText) ? parseFloat(validText) : (validText ? trimInvalidChar(validText) : '');
+                        validText = (colSetting.type === 'Number' || colSetting.field === 'new_up') && is_numeric(validText) ? parseFloat(validText) : (validText ? trimInvalidChar(validText) : '');
                         const orgValue = curRow >= sortData.length ? '' : sortData[curRow][colSetting.field];
                         if (orgValue == validText || ((!orgValue || orgValue === '') && (validText === ''))) {
                             sameCol++;
@@ -765,7 +765,7 @@ $(document).ready(() => {
                 const select = SpreadJsObj.getSelectObject(info.sheet);
                 const col = info.sheet.zh_setting.cols[info.col];
                 // 未改变值则不提交
-                let validText = is_numeric(info.editingText) ? parseFloat(info.editingText) : (info.editingText ? trimInvalidChar(info.editingText) : '');
+                let validText = col.type === 'Number' && is_numeric(info.editingText) ? parseFloat(info.editingText) : (info.editingText ? trimInvalidChar(info.editingText) : '');
                 const orgValue = select[col.field];
                 if (orgValue == validText || ((!orgValue || orgValue === '') && (validText === ''))) {
                     SpreadJsObj.reLoadRowData(info.sheet, info.row);
@@ -833,7 +833,7 @@ $(document).ready(() => {
                     if (!colSetting) continue;
 
                     let validText = info.sheet.getText(curRow, curCol);
-                    validText = is_numeric(validText) ? parseFloat(validText) : (validText ? trimInvalidChar(validText) : null);
+                    validText = colSetting.type === 'Number' && is_numeric(validText) ? parseFloat(validText) : (validText ? trimInvalidChar(validText) : null);
                     const orgValue = sortData[curRow][colSetting.field];
                     if (orgValue == validText || ((!orgValue || orgValue === '') && (validText === ''))) {
                         sameCol++;

+ 9 - 0
app/router.js

@@ -544,6 +544,9 @@ module.exports = app => {
     app.post('/tender/:id/change/project/:cpid/information/audit/check', sessionAuth, tenderCheck, uncheckTenderCheck, changeProjectCheck, tenderBuildCheck, 'changeController.checkProjectAudit');
     app.post('/tender/:id/change/project/:cpid/information/xsaudit/add', sessionAuth, tenderCheck, uncheckTenderCheck, changeProjectCheck, 'changeController.addProjectXsAudit');
     app.post('/tender/:id/change/project/:cpid/information/xsaudit/delete', sessionAuth, tenderCheck, uncheckTenderCheck, changeProjectCheck, 'changeController.deleteProjectXsAudit');
+    app.post('/tender/:id/change/project/cancel/audit', sessionAuth, tenderCheck, uncheckTenderCheck, tenderBuildCheck, changeProjectCheck, 'changeController.checkProjectAuditCancel');
+    app.post('/tender/:id/change/project/check/revise', sessionAuth, tenderCheck, uncheckTenderCheck, tenderBuildCheck, changeProjectCheck, 'changeController.checkProjectRevise');
+    app.post('/tender/:id/change/project/cancel/revise', sessionAuth, tenderCheck, uncheckTenderCheck, tenderBuildCheck, changeProjectCheck, 'changeController.cancelProjectRevise');
     // 变更申请
     app.get('/tender/:id/change/apply', sessionAuth, tenderCheck, uncheckTenderCheck, 'changeController.apply');
     app.get('/tender/:id/change/apply/status/:status', sessionAuth, tenderCheck, uncheckTenderCheck, 'changeController.applyStatus');
@@ -560,6 +563,9 @@ module.exports = app => {
     app.post('/tender/:id/change/apply/:caid/information/audit/check', sessionAuth, tenderCheck, uncheckTenderCheck, changeApplyCheck, tenderBuildCheck, 'changeController.checkApplyAudit');
     app.get('/tender/:id/change/apply/:caid/information/notice', sessionAuth, tenderCheck, uncheckTenderCheck, changeApplyCheck, 'changeController.applyInformationNotice');
     app.post('/tender/:id/change/apply/:caid/information/list/save', sessionAuth, tenderCheck, uncheckTenderCheck, changeApplyCheck, 'changeController.saveApplyListsData');
+    app.post('/tender/:id/change/apply/cancel/audit', sessionAuth, tenderCheck, uncheckTenderCheck, tenderBuildCheck, changeApplyCheck, 'changeController.checkApplyAuditCancel');
+    app.post('/tender/:id/change/apply/check/revise', sessionAuth, tenderCheck, uncheckTenderCheck, tenderBuildCheck, changeApplyCheck, 'changeController.checkApplyRevise');
+    app.post('/tender/:id/change/apply/cancel/revise', sessionAuth, tenderCheck, uncheckTenderCheck, tenderBuildCheck, changeApplyCheck, 'changeController.cancelApplyRevise');
     // 变更方案
     app.get('/tender/:id/change/plan', sessionAuth, tenderCheck, uncheckTenderCheck, 'changeController.plan');
     app.get('/tender/:id/change/plan/status/:status', sessionAuth, tenderCheck, uncheckTenderCheck, 'changeController.planStatus');
@@ -576,6 +582,9 @@ module.exports = app => {
     app.post('/tender/:id/change/plan/:cpid/information/audit/start', sessionAuth, tenderCheck, uncheckTenderCheck, changePlanCheck, tenderBuildCheck, 'changeController.startPlanAudit');
     app.post('/tender/:id/change/plan/:cpid/information/audit/check', sessionAuth, tenderCheck, uncheckTenderCheck, changePlanCheck, tenderBuildCheck, 'changeController.checkPlanAudit');
     app.post('/tender/:id/change/plan/:cpid/information/list/save', sessionAuth, tenderCheck, uncheckTenderCheck, changePlanCheck, 'changeController.savePlanListsData');
+    app.post('/tender/:id/change/plan/cancel/audit', sessionAuth, tenderCheck, uncheckTenderCheck, tenderBuildCheck, changePlanCheck, 'changeController.checkPlanAuditCancel');
+    app.post('/tender/:id/change/plan/check/revise', sessionAuth, tenderCheck, uncheckTenderCheck, tenderBuildCheck, changePlanCheck, 'changeController.checkPlanRevise');
+    app.post('/tender/:id/change/plan/cancel/revise', sessionAuth, tenderCheck, uncheckTenderCheck, tenderBuildCheck, changePlanCheck, 'changeController.cancelPlanRevise');
     // 材料调差
     app.get('/tender/:id/measure/material', sessionAuth, tenderCheck, uncheckTenderCheck, 'materialController.index');
     app.post('/tender/:id/measure/material/add', sessionAuth, tenderCheck, uncheckTenderCheck, tenderBuildCheck, 'materialController.add');

+ 51 - 9
app/service/change_apply.js

@@ -133,14 +133,14 @@ module.exports = app => {
                         ];
                         break;
                     case 1: // 待处理(你的)
-                        sql = 'SELECT a.*, p.name as account_name FROM ?? as a LEFT JOIN ?? AS p On a.notice_uid = p.id WHERE a.tid = ? AND (a.id in(SELECT b.caid FROM ?? as b WHERE b.tid = ? AND b.aid = ? AND b.status = ?) OR (a.uid = ? AND (a.status = ? OR a.status = ?)))';
-                        sqlParam = [this.tableName, this.ctx.service.projectAccount.tableName, tenderId, this.ctx.service.changeApplyAudit.tableName, tenderId, this.ctx.session.sessionUser.accountId, audit.status.checking, this.ctx.session.sessionUser.accountId, audit.status.uncheck, audit.status.checkNo];
+                        sql = 'SELECT a.*, p.name as account_name FROM ?? as a LEFT JOIN ?? AS p On a.notice_uid = p.id WHERE a.tid = ? AND (a.id in(SELECT b.caid FROM ?? as b WHERE b.tid = ? AND b.aid = ? AND b.status = ?) OR (a.uid = ? AND (a.status = ? OR a.status = ? OR a.status = ?)))';
+                        sqlParam = [this.tableName, this.ctx.service.projectAccount.tableName, tenderId, this.ctx.service.changeApplyAudit.tableName, tenderId, this.ctx.session.sessionUser.accountId, audit.status.checking, this.ctx.session.sessionUser.accountId, audit.status.uncheck, audit.status.checkNo, audit.status.revise];
                         break;
                     case 5: // 待上报(所有的)PS:取未上报,退回,修订的变更令
                         sql =
                             'SELECT a.*, p.name as account_name FROM ?? AS a LEFT JOIN ?? AS p On a.notice_uid = p.id WHERE ' +
                             // 'a.id IN (SELECT b.caid FROM ?? AS b WHERE b.aid = ? AND a.times = b.times GROUP BY b.caid) AND ' +
-                            'a.uid = ? AND a.tid = ? AND (a.status = ? OR a.status = ?)';
+                            'a.uid = ? AND a.tid = ? AND (a.status = ? OR a.status = ? OR a.status = ?)';
                         sqlParam = [
                             this.tableName,
                             this.ctx.service.projectAccount.tableName,
@@ -149,6 +149,7 @@ module.exports = app => {
                             tenderId,
                             audit.status.uncheck,
                             audit.status.checkNo,
+                            audit.status.revise,
                         ];
                         break;
                     case 2: // 进行中(所有的)
@@ -220,15 +221,15 @@ module.exports = app => {
                     //     uid: this.ctx.session.sessionUser.accountId,
                     //     status: 2,
                     // });
-                    const sql6 = 'SELECT count(*) AS count FROM ?? as a WHERE a.tid = ? AND (a.id in(SELECT b.caid FROM ?? as b WHERE b.tid = ? AND b.aid = ? AND b.status = ?) OR (a.uid = ? AND (a.status = ? OR a.status = ?)))';
-                    const sqlParam6 = [this.tableName, tenderId, this.ctx.service.changeApplyAudit.tableName, tenderId, this.ctx.session.sessionUser.accountId, audit.status.checking, this.ctx.session.sessionUser.accountId, audit.status.uncheck, audit.status.checkNo];
+                    const sql6 = 'SELECT count(*) AS count FROM ?? as a WHERE a.tid = ? AND (a.id in(SELECT b.caid FROM ?? as b WHERE b.tid = ? AND b.aid = ? AND b.status = ?) OR (a.uid = ? AND (a.status = ? OR a.status = ? OR a.status = ?)))';
+                    const sqlParam6 = [this.tableName, tenderId, this.ctx.service.changeApplyAudit.tableName, tenderId, this.ctx.session.sessionUser.accountId, audit.status.checking, this.ctx.session.sessionUser.accountId, audit.status.uncheck, audit.status.checkNo, audit.status.revise];
                     const result6 = await this.db.query(sql6, sqlParam6);
                     return result6[0].count;
                 case 5: // 待上报(所有的)PS:取未上报,退回的变更立项
                     const sql2 =
                         'SELECT count(*) AS count FROM ?? AS a WHERE ' +
                         // 'a.id IN (SELECT b.caid FROM ?? AS b WHERE b.aid = ? AND a.times = b.times GROUP BY b.caid) ' +
-                        'a.uid = ? AND a.tid = ? AND (a.status = ? OR a.status = ?)';
+                        'a.uid = ? AND a.tid = ? AND (a.status = ? OR a.status = ? OR a.status = ?)';
                     const sqlParam2 = [
                         this.tableName,
                         // this.ctx.service.changeApplyAudit.tableName,
@@ -236,6 +237,7 @@ module.exports = app => {
                         tenderId,
                         audit.status.uncheck,
                         audit.status.checkNo,
+                        audit.status.revise,
                     ];
                     const result2 = await this.db.query(sql2, sqlParam2);
                     return result2[0].count;
@@ -292,15 +294,15 @@ module.exports = app => {
                     //     uid: this.ctx.session.sessionUser.accountId,
                     //     status: 2,
                     // });
-                    const sql6 = 'SELECT SUM(cast (a.total_price as decimal(18,6))) AS total_price FROM ?? as a WHERE a.tid = ? AND (a.id in(SELECT b.caid FROM ?? as b WHERE b.tid = ? AND b.aid = ? AND b.status = ?) OR (a.uid = ? AND (a.status = ? OR a.status = ?)))';
-                    const sqlParam6 = [this.tableName, tenderId, this.ctx.service.changeApplyAudit.tableName, tenderId, this.ctx.session.sessionUser.accountId, audit.status.checking, this.ctx.session.sessionUser.accountId, audit.status.uncheck, audit.status.checkNo];
+                    const sql6 = 'SELECT SUM(cast (a.total_price as decimal(18,6))) AS total_price FROM ?? as a WHERE a.tid = ? AND (a.id in(SELECT b.caid FROM ?? as b WHERE b.tid = ? AND b.aid = ? AND b.status = ?) OR (a.uid = ? AND (a.status = ? OR a.status = ? OR a.status = ?)))';
+                    const sqlParam6 = [this.tableName, tenderId, this.ctx.service.changeApplyAudit.tableName, tenderId, this.ctx.session.sessionUser.accountId, audit.status.checking, this.ctx.session.sessionUser.accountId, audit.status.uncheck, audit.status.checkNo, audit.status.revise];
                     const result6 = await this.db.query(sql6, sqlParam6);
                     return result6[0].total_price ? result6[0].total_price : 0;
                 case 5: // 待上报(所有的)PS:取未上报,退回的变更立项
                     const sql2 =
                         'SELECT SUM(cast (a.total_price as decimal(18,6))) AS total_price FROM ?? AS a WHERE ' +
                         // 'a.id IN (SELECT b.caid FROM ?? AS b WHERE b.aid = ? AND a.times = b.times GROUP BY b.caid) ' +
-                        'a.uid = ? AND a.tid = ? AND (a.status = ? OR a.status = ?)';
+                        'a.uid = ? AND a.tid = ? AND (a.status = ? OR a.status = ? OR a.status = ?)';
                     const sqlParam2 = [
                         this.tableName,
                         // this.ctx.service.changePlanAudit.tableName,
@@ -308,6 +310,7 @@ module.exports = app => {
                         tenderId,
                         audit.status.uncheck,
                         audit.status.checkNo,
+                        audit.status.revise,
                     ];
                     const result2 = await this.db.query(sql2, sqlParam2);
                     return result2[0].total_price ? result2[0].total_price : 0;
@@ -387,6 +390,8 @@ module.exports = app => {
                 await this.transaction.delete(this.ctx.service.changeApplyAtt.tableName, { caid: id });
                 // 最后删除变更令
                 await this.transaction.delete(this.tableName, { id });
+                // 删除history
+                await this.transaction.delete(this.ctx.service.changeApplyHistory.tableName, { caid: id });
                 // 记录删除日志
                 await this.ctx.service.projectLog.addProjectLog(this.transaction, projectLogConst.type.changeApply, projectLogConst.status.delete, changeInfo.code);
                 await this.transaction.commit();
@@ -397,6 +402,43 @@ module.exports = app => {
             }
             return result;
         }
+
+        async doCheckChangeCanCancel(change) {
+            // 获取当前审批人的上一个审批人,判断是否是当前登录人,并赋予撤回功能,(当审批人存在有审批过时,上一人不允许再撤回)
+            const status = audit.status;
+            const accountId = this.ctx.session.sessionUser.accountId;
+            const auditors = change.auditors;
+            change.cancancel = 0;
+            if (change.status !== status.checked && change.status !== status.uncheck && change.status !== status.revise) {
+                if (change.status !== status.checkNo) {
+                    // 找出当前操作人上一个审批人,包括审批完成的和退回上一个审批人的,同时当前操作人为第一人时,就是则为原报
+                    const onAuditor = this._.find(auditors, function(item) {
+                        return item.aid === change.curAuditor.aid && item.status === status.checking;
+                    });
+                    const preAudit = onAuditor.order !== 1 ? this._.find(auditors, { order: onAuditor.order - 1 }) : false;
+                    const preAid = preAudit ? (preAudit.status !== status.checkAgain ? preAudit.aid : false) : change.uid;// 已发起重审无法撤回
+                    if (onAuditor && onAuditor.aid === preAid && preAudit.status === status.checkCancel) {
+                        return;// 不可以多次撤回
+                    } else if (preAid === accountId && preAid !== change.uid) {
+                        if (preAudit.status === status.checked) {
+                            change.cancancel = 2;// 审批人撤回审批通过
+                        }
+                        // else if (preAudit.status === status.checkNoPre) {
+                        //     change.cancancel = 3;// 审批人撤回审批退回上一人
+                        // }
+                        change.preAudit = preAudit;
+                    } else if (preAid === accountId && preAid === change.uid) {
+                        change.cancancel = 1;// 原报撤回
+                    }
+                } else {
+                    const lastAuditors = await this.service.changeApplyAudit.getAuditors(change.id, change.times - 1);
+                    const onAuditor = this._.findLast(lastAuditors, { status: status.checkNo });
+                    if (onAuditor && onAuditor.aid === accountId) {
+                        change.cancancel = 4;// 审批人撤回退回原报
+                    }
+                }
+            }
+        }
     }
 
     return ChangeApply;

+ 346 - 3
app/service/change_apply_audit.js

@@ -75,8 +75,8 @@ module.exports = app => {
         async getAuditGroupByList(changeId, times) {
             const sql = 'SELECT la.`aid`, pa.`name`, pa.`company`, pa.`role`, la.`times`, la.`caid`, la.`aid`, la.`order` ' +
                 '  FROM ?? AS la Left Join ?? AS pa On la.`aid` = pa.`id`' +
-                '  WHERE la.`caid` = ? and la.`times` = ? GROUP BY la.`aid` ORDER BY la.`order`';
-            const sqlParam = [this.tableName, this.ctx.service.projectAccount.tableName, changeId, times];
+                '  WHERE la.`caid` = ? and la.`times` = ? and la.`status` != ? and la.`status` != ? GROUP BY la.`aid` ORDER BY la.`order`';
+            const sqlParam = [this.tableName, this.ctx.service.projectAccount.tableName, changeId, times, auditConst.status.revise, auditConst.status.cancelRevise];
             return await this.db.query(sql, sqlParam);
         }
 
@@ -95,6 +95,7 @@ module.exports = app => {
             switch (status) {
                 case auditConst.status.checking :
                 case auditConst.status.checked :
+                case auditConst.status.revise :
                     sql = 'SELECT la.`aid`, pa.`name`, pa.`company`, pa.`role`, la.`times`, la.`caid`, la.`aid`, la.`order` ' +
                         '  FROM ?? AS la Left Join ?? AS pa On la.`aid` = pa.`id` ' +
                         '  WHERE la.`caid` = ? and la.`status` = ? ' +
@@ -117,6 +118,12 @@ module.exports = app => {
             return auditor;
         }
 
+        async getLastAudit(caid, times, transaction = null) {
+            const sql = 'SELECT * FROM ?? WHERE `caid` = ? AND `times` = ? ORDER BY `order` DESC';
+            const sqlParam = [this.tableName, caid, times];
+            return transaction ? await transaction.queryOne(sql, sqlParam) : await this.db.queryOne(sql, sqlParam);
+        }
+
         /**
          * 获取审核人流程列表(包括原报)
          * @param {Number} materialId 调差id
@@ -279,7 +286,7 @@ module.exports = app => {
                     c_name: this.ctx.change.name,
                 };
                 await this.ctx.helper.sendWechat(audit.aid, smsTypeConst.const.BG, smsTypeConst.judge.approval.toString(), wxConst.template.change, wechatData);
-
+                await transaction.delete(this.ctx.service.changeProjectHistory.tableName, { caid: caId });
                 // todo 更新标段tender状态 ?
                 await transaction.commit();
             } catch (err) {
@@ -457,6 +464,7 @@ module.exports = app => {
 
         async _checkNo(pid, caId, checkData, times) {
             const time = new Date();
+            const changeData = await this.ctx.service.changeApply.getDataById(caId);
             // 整理当前流程审核人状态更新
             const audit = await this.getDataByCondition({ caid: caId, times, status: auditConst.status.checking });
             if (!audit) {
@@ -504,6 +512,12 @@ module.exports = app => {
                     c_name: this.ctx.change.name,
                 };
                 await this.ctx.helper.sendWechat(users, smsTypeConst.const.BG, smsTypeConst.judge.result.toString(), wxConst.template.change, wechatData);
+                // 生成内容保存表至zh_change_project_history中,用于撤回
+                // 回退spamount值数据
+                const changeList = await this.ctx.service.changeApplyList.getAllDataByCondition({
+                    where: { caid: caId },
+                });
+                await this.ctx.service.changeApplyHistory.saveHistory(transaction, changeData, changeList);
                 await transaction.commit();
             } catch (err) {
                 await transaction.rollback();
@@ -545,6 +559,335 @@ module.exports = app => {
             const sqlParam = [tenderId];
             return this.db.query(sql, sqlParam);
         }
+
+        /**
+         * 审批撤回
+         * @param {Number} stageId - 标段id
+         * @param {Number} times - 第几次审批
+         * @return {Promise<void>}
+         */
+        async checkCancel(change) {
+
+            // 分3种情况,根据ctx.cancancel值判断:
+            // 1.原报发起撤回,当前流程删除,并回到待上报
+            // 2.审批人撤回审批通过,增加流程,并回到它审批中
+            // 4.审批人撤回退回原报操作,删除新增的审批流,增加流程,回滚到它审批中
+            switch (change.cancancel) {
+                case 1: await this._userCheckCancel(change); break;
+                case 2: await this._auditCheckCancel(change); break;
+                case 4: await this._auditCheckCancelNo(change); break;
+                default: throw '不可撤回,请刷新页面重试';
+            }
+        }
+        /**
+         * 原报撤回,直接改动审批人状态
+         * 如果存在审批人数据,将其改为原报流程数据,但保留原提交人
+         *
+         * 一审 1 A checking  ->  A uncheck status改   pay/jl:删0(jl为增量数据,只删重复部分) 1->0 删1
+         * ...
+         *
+         * @param stage
+         * @returns {Promise<void>}
+         * @private
+         */
+        async _userCheckCancel(change) {
+            const transaction = await this.db.beginTransaction();
+            try {
+                // 整理当前流程审核人状态更新
+                const curAudit = await this.getDataByCondition({ caid: change.id, times: change.times, status: auditConst.status.checking });
+                // 审批人变成待审批状态
+                await transaction.update(this.tableName, {
+                    id: curAudit.id,
+                    status: auditConst.status.uncheck,
+                    begin_time: null,
+                    opinion: null,
+                });
+                // 变成待上报状态
+                await transaction.update(this.ctx.service.changeApply.tableName, {
+                    id: change.id,
+                    status: change.times === 1 ? auditConst.status.uncheck : auditConst.status.checkNo,
+                });
+                await transaction.commit();
+            } catch (err) {
+                await transaction.rollback();
+                throw err;
+            }
+
+        }
+        /**
+         * 审批人撤回审批通过,插入两条数据
+         *
+         * 一审 1 A checked             一审 1 A checked
+         * 二审 2 B checked   pre ->    二审 2 B checked
+         * 三审 3 C checking  cur       二审 3 B checkCancel  增                增extra_his     增tp_his
+         * 四审 4 D uncheck             二审 4 B checking     增                增pay_cur
+         *                             三审 5 C uncheck      order、status改
+         *                             四审 6 D uncheck      order改
+         *
+         * @param stage
+         * @returns {Promise<void>}
+         * @private
+         */
+        async _auditCheckCancel(change) {
+            const time = new Date();
+
+            const transaction = await this.db.beginTransaction();
+            try {
+                // 整理当前流程审核人状态更新
+                const curAudit = await this.getDataByCondition({ caid: change.id, times: change.times, status: auditConst.status.checking });
+                const preAudit = change.preAudit;
+                if (!curAudit || curAudit.order <= 1 || !preAudit) {
+                    throw '撤回用户数据错误';
+                }
+                // 顺移其后审核人流程顺序
+                const sql = 'UPDATE ' + this.tableName + ' SET `order` = `order` + 2 WHERE caid = ? AND times = ? AND `order` > ?';
+                await transaction.query(sql, [change.id, change.times, curAudit.order]);
+                // 当前审批人2次添加至流程中
+                const newAuditors = [];
+                // 先入撤回记录
+                newAuditors.push({
+                    tid: change.tid,
+                    caid: change.id,
+                    aid: preAudit.aid,
+                    times: change.times,
+                    order: curAudit.order,
+                    status: auditConst.status.checkCancel,
+                    begin_time: time,
+                    end_time: time,
+                    opinion: '',
+                });
+                newAuditors.push({
+                    tid: change.tid,
+                    caid: change.id,
+                    aid: preAudit.aid,
+                    times: change.times,
+                    order: curAudit.order + 1,
+                    status: auditConst.status.checking,
+                    begin_time: time,
+                });
+                await transaction.insert(this.tableName, newAuditors);
+                // 当前审批人变成待审批
+                await transaction.update(this.tableName, { id: curAudit.id, order: curAudit.order + 2, begin_time: null, status: auditConst.status.uncheck });
+                await transaction.commit();
+            } catch (err) {
+                await transaction.rollback();
+                throw err;
+            }
+        }
+
+        /**
+         * 审批人撤回审批退回原报
+         *
+         * 1# 一审 1 A checked              1# 一审 1 A checked
+         *    二审 2 B checkNo   pre   ->      二审 2 B checkNo
+         *    三审 3 C uncheck                 二审 3 B checkCancel    增       pay: 2#0 -> 1#3   jl: 2#0 -> 1#3   增tp_his   增extra_his
+         *                                    二审 4 B checking       增       pay: 2#0 -> 1#4
+         *                                    三审 5 C uncheck        order改
+         *
+         * 2# 一审 1 A uncheck              2#                        删       pay: 2#0删   jl: 2#0删
+         *    二审 2 B uncheck
+         *    三审 3 C uncheck
+         *
+         * @param stage
+         * @returns {Promise<void>}
+         * @private
+         */
+        async _auditCheckCancelNo(change) {
+
+            const time = new Date();
+
+            const transaction = await this.db.beginTransaction();
+            try {
+                // const curAudit = await this.getDataByCondition({ cpid: change.id, times: change.times - 1, status: auditConst.status.back });
+                const curAudit = await this.getAuditorByStatus(change.id, auditConst.status.checkNo, change.times);
+                // 整理上一个流程审核人状态更新
+                // 顺移其后审核人流程顺序
+                const sql = 'UPDATE ' + this.tableName + ' SET `order` = `order` + 2 WHERE caid = ? AND times = ? AND `order` > ?';
+                await transaction.query(sql, [change.id, change.times - 1, curAudit.order]);
+                // 当前审批人2次添加至流程中
+                const newAuditors = [];
+                newAuditors.push({
+                    tid: change.tid,
+                    caid: change.id,
+                    aid: curAudit.aid,
+                    times: curAudit.times,
+                    order: curAudit.order + 1,
+                    status: auditConst.status.checkCancel,
+                    begin_time: time,
+                    end_time: time,
+                    opinion: '',
+                });
+                newAuditors.push({
+                    tid: change.tid,
+                    caid: change.id,
+                    aid: curAudit.aid,
+                    times: curAudit.times,
+                    order: curAudit.order + 2,
+                    status: auditConst.status.checking,
+                    begin_time: time,
+                });
+                await transaction.insert(this.tableName, newAuditors);
+                // 删除当前次审批流
+                await transaction.delete(this.tableName, { caid: change.id, times: change.times });
+                // 回退数据
+                await this.ctx.service.changeApplyHistory.returnHistory(transaction, change.id);
+                await transaction.delete(this.ctx.service.changeApplyHistory.tableName, { caid: change.id });
+                // // 设置变更立项为审批中
+                // await transaction.update(this.ctx.service.changeProject.tableName, {
+                //     id: change.id,
+                //     time: change.times - 1,
+                //     status: auditConst.status.checking,
+                // });
+                await transaction.commit();
+            } catch (err) {
+                await transaction.rollback();
+                throw err;
+            }
+        }
+
+        /**
+         * 重新审批变更令
+         * @param { string } cid - 查询的清单
+         * @return {Promise<*>} - 可用的变更令列表
+         */
+        async checkRevise(change) {
+            const time = new Date();
+            // 初始化事务
+            const transaction = await this.db.beginTransaction();
+            let result = false;
+            try {
+                const pid = this.ctx.session.sessionProject.id;
+                // 获取审核人列表
+                const sql = 'SELECT `tid`, `caid`, `aid`, `order` FROM ?? WHERE `caid` = ? and `times` = ? GROUP BY `aid` ORDER BY `id` ASC';
+                const sqlParam = [this.tableName, change.id, change.times];
+                const auditors = await this.db.query(sql, sqlParam);
+                // 添加到消息推送表
+                const noticeContent = await this.getNoticeContent(pid, change.tid, change.id, this.ctx.session.sessionUser.accountId, '发起修订');
+                const records = [];
+                auditors.forEach(auditor => {
+                    records.push({
+                        pid,
+                        type: pushType.changeApply,
+                        uid: auditor.aid,
+                        status: auditConst.status.revise,
+                        content: noticeContent,
+                    });
+                });
+                await transaction.insert('zh_notice', records);
+
+                // 获取当前次数审批人列表
+                const auditList = await this.getAuditGroupByList(change.id, change.times);
+                const lastAudit = await this.getLastAudit(change.id, change.times);
+                const insert_audit_array = [];
+                // 新增一个发起修订状态到审批流程中
+                const revise_audit = {
+                    tid: change.tid,
+                    caid: change.id,
+                    aid: change.uid,
+                    times: change.times,
+                    order: lastAudit.order + 1,
+                    status: auditConst.status.revise,
+                    begin_time: time,
+                    end_time: time,
+                    opinion: '',
+                };
+                insert_audit_array.push(revise_audit);
+                // 新增新一次的审批人列表
+                let order = 1;
+                for (const al of auditList) {
+                    const insert_audit = {
+                        tid: change.tid,
+                        caid: change.id,
+                        aid: al.aid,
+                        times: change.times + 1,
+                        order,
+                        status: auditConst.status.uncheck,
+                    };
+                    insert_audit_array.push(insert_audit);
+                    order++;
+                }
+                await transaction.insert(this.tableName, insert_audit_array);
+                // 生成内容保存表至zh_change_history中,用于撤销修订回退
+                const changeData = await transaction.get(this.ctx.service.changeApply.tableName, { id: change.id });
+                const changeList = await this.ctx.service.changeApplyList.getAllDataByCondition({
+                    where: { caid: change.id },
+                });
+                await this.ctx.service.changeApplyHistory.saveHistory(transaction, changeData, changeList);
+                // 设置变更立项修订状态
+                await transaction.update(this.ctx.service.changeApply.tableName, {
+                    id: change.id,
+                    notice_code: null,
+                    notice_uid: null,
+                    decimal: null,
+                    status: auditConst.status.revise,
+                    times: change.times + 1,
+                });
+
+                await transaction.commit();
+                result = true;
+            } catch (error) {
+                console.log(error);
+                await transaction.rollback();
+                result = false;
+            }
+            return result;
+        }
+
+        /**
+         * 撤销修订变更令
+         * @param { string } cid - 查询的清单
+         * @return {Promise<*>} - 可用的变更令列表
+         */
+        async cancelRevise(change) {
+            const time = new Date();
+            // 初始化事务
+            const transaction = await this.db.beginTransaction();
+            let result = false;
+            try {
+                const pid = this.ctx.session.sessionProject.id;
+                // 获取审核人列表
+                const sql = 'SELECT `tid`, `caid`, `aid`, `order` FROM ?? WHERE `caid` = ? and `times` = ? GROUP BY `aid` ORDER BY `id` ASC';
+                const sqlParam = [this.tableName, change.id, change.times - 1];
+                const auditors = await this.db.query(sql, sqlParam);
+                // 添加到消息推送表
+                const noticeContent = await this.getNoticeContent(pid, change.tid, change.id, this.ctx.session.sessionUser.accountId, '撤销修订');
+                const records = [];
+                auditors.forEach(auditor => {
+                    records.push({
+                        pid,
+                        type: pushType.changeApply,
+                        uid: auditor.aid,
+                        status: auditConst.status.cancelRevise,
+                        content: noticeContent,
+                    });
+                });
+                await transaction.insert('zh_notice', records);
+                const lastAudit = await this.getLastAudit(change.id, change.times - 1);
+                // 新增一个撤销修订状态到审批流程中
+                const revise_audit = {
+                    tid: change.tid,
+                    caid: change.id,
+                    aid: this.ctx.session.sessionUser.accountId,
+                    times: change.times - 1,
+                    order: lastAudit.order + 1,
+                    status: auditConst.status.cancelRevise,
+                    begin_time: time,
+                    end_time: time,
+                    opinion: '',
+                };
+                await transaction.insert(this.ctx.service.changeApplyAudit.tableName, revise_audit);
+                await transaction.delete(this.ctx.service.changeApplyAudit.tableName, { caid: change.id, times: change.times });
+                await this.ctx.service.changeApplyHistory.returnHistory(transaction, change.id);
+                await transaction.delete(this.ctx.service.changeApplyHistory.tableName, { caid: change.id });
+                await transaction.commit();
+                result = true;
+            } catch (error) {
+                console.log(error);
+                await transaction.rollback();
+                result = false;
+            }
+            return result;
+        }
     }
 
     return ChangeApplyAudit;

+ 53 - 0
app/service/change_apply_history.js

@@ -0,0 +1,53 @@
+'use strict';
+
+/**
+ * 变更新增部位插入记录表
+ *
+ * @author Mai
+ * @date
+ * @version
+ */
+
+module.exports = app => {
+
+    class ChangeApplyHistory extends app.BaseService {
+        /**
+         * 构造函数
+         *
+         * @param {Object} ctx - egg全局变量
+         * @return {void}
+         */
+        constructor(ctx) {
+            super(ctx);
+            this.tableName = 'change_apply_history';
+        }
+
+        async saveHistory(transaction, data, list) {
+            await transaction.insert(this.tableName, {
+                tid: data.tid,
+                caid: data.id,
+                info_json: JSON.stringify(data),
+                list_json: JSON.stringify(list),
+            });
+        }
+
+        async returnHistory(transaction, caid) {
+            const data = await transaction.get(this.tableName, { caid });
+            if (!data) throw '撤销前数据不存在,无法撤销';
+            const change_update = {};
+            const oldInfo = JSON.parse(data.info_json);
+            for (const key in oldInfo) {
+                if (key !== 'in_time') {
+                    change_update[key] = oldInfo[key];
+                }
+            }
+            await transaction.update(this.ctx.service.changeApply.tableName, change_update);
+            const oldList = JSON.parse(data.list_json);
+            // 先删后插
+            await transaction.delete(this.ctx.service.changeApplyList.tableName, { caid });
+            await transaction.insert(this.ctx.service.changeApplyList.tableName, oldList);
+        }
+    }
+
+    return ChangeApplyHistory;
+};

+ 51 - 9
app/service/change_plan.js

@@ -154,14 +154,14 @@ module.exports = app => {
                         ];
                         break;
                     case 1: // 待处理(你的)
-                        sql = 'SELECT a.*, p.name as account_name FROM ?? as a LEFT JOIN ?? AS p On a.uid = p.id WHERE a.tid = ? AND (a.id in(SELECT b.cpid FROM ?? as b WHERE b.tid = ? AND b.aid = ? AND b.status = ?) OR (a.uid = ? AND (a.status = ? OR a.status = ?)))';
-                        sqlParam = [this.tableName, this.ctx.service.projectAccount.tableName, tenderId, this.ctx.service.changePlanAudit.tableName, tenderId, this.ctx.session.sessionUser.accountId, audit.status.checking, this.ctx.session.sessionUser.accountId, audit.status.uncheck, audit.status.checkNo];
+                        sql = 'SELECT a.*, p.name as account_name FROM ?? as a LEFT JOIN ?? AS p On a.uid = p.id WHERE a.tid = ? AND (a.id in(SELECT b.cpid FROM ?? as b WHERE b.tid = ? AND b.aid = ? AND b.status = ?) OR (a.uid = ? AND (a.status = ? OR a.status = ? OR a.status = ?)))';
+                        sqlParam = [this.tableName, this.ctx.service.projectAccount.tableName, tenderId, this.ctx.service.changePlanAudit.tableName, tenderId, this.ctx.session.sessionUser.accountId, audit.status.checking, this.ctx.session.sessionUser.accountId, audit.status.uncheck, audit.status.checkNo, audit.status.revise];
                         break;
                     case 5: // 待上报(所有的)PS:取未上报,退回,修订的变更令
                         sql =
                             'SELECT a.*, p.name as account_name FROM ?? AS a LEFT JOIN ?? AS p On a.uid = p.id WHERE ' +
                             // 'a.id IN (SELECT b.cpid FROM ?? AS b WHERE b.aid = ? AND a.times = b.times GROUP BY b.cpid) AND ' +
-                            'a.uid = ? AND a.tid = ? AND (a.status = ? OR a.status = ?)';
+                            'a.uid = ? AND a.tid = ? AND (a.status = ? OR a.status = ? OR a.status = ?)';
                         sqlParam = [
                             this.tableName,
                             this.ctx.service.projectAccount.tableName,
@@ -170,6 +170,7 @@ module.exports = app => {
                             tenderId,
                             audit.status.uncheck,
                             audit.status.checkNo,
+                            audit.status.revise,
                         ];
                         break;
                     case 2: // 进行中(所有的)
@@ -241,15 +242,15 @@ module.exports = app => {
                     //     uid: this.ctx.session.sessionUser.accountId,
                     //     status: 2,
                     // });
-                    const sql6 = 'SELECT count(*) AS count FROM ?? as a WHERE a.tid = ? AND (a.id in(SELECT b.cpid FROM ?? as b WHERE b.tid = ? AND b.aid = ? AND b.status = ?) OR (a.uid = ? AND (a.status = ? OR a.status = ?)))';
-                    const sqlParam6 = [this.tableName, tenderId, this.ctx.service.changePlanAudit.tableName, tenderId, this.ctx.session.sessionUser.accountId, audit.status.checking, this.ctx.session.sessionUser.accountId, audit.status.uncheck, audit.status.checkNo];
+                    const sql6 = 'SELECT count(*) AS count FROM ?? as a WHERE a.tid = ? AND (a.id in(SELECT b.cpid FROM ?? as b WHERE b.tid = ? AND b.aid = ? AND b.status = ?) OR (a.uid = ? AND (a.status = ? OR a.status = ? OR a.status = ?)))';
+                    const sqlParam6 = [this.tableName, tenderId, this.ctx.service.changePlanAudit.tableName, tenderId, this.ctx.session.sessionUser.accountId, audit.status.checking, this.ctx.session.sessionUser.accountId, audit.status.uncheck, audit.status.checkNo, audit.status.revise];
                     const result6 = await this.db.query(sql6, sqlParam6);
                     return result6[0].count;
                 case 5: // 待上报(所有的)PS:取未上报,退回的变更立项
                     const sql2 =
                         'SELECT count(*) AS count FROM ?? AS a WHERE ' +
                         // 'a.id IN (SELECT b.cpid FROM ?? AS b WHERE b.aid = ? AND a.times = b.times GROUP BY b.cpid) ' +
-                        'a.uid = ? AND a.tid = ? AND (a.status = ? OR a.status = ?)';
+                        'a.uid = ? AND a.tid = ? AND (a.status = ? OR a.status = ? OR a.status = ?)';
                     const sqlParam2 = [
                         this.tableName,
                         // this.ctx.service.changePlanAudit.tableName,
@@ -257,6 +258,7 @@ module.exports = app => {
                         tenderId,
                         audit.status.uncheck,
                         audit.status.checkNo,
+                        audit.status.revise,
                     ];
                     const result2 = await this.db.query(sql2, sqlParam2);
                     return result2[0].count;
@@ -313,15 +315,15 @@ module.exports = app => {
                     //     uid: this.ctx.session.sessionUser.accountId,
                     //     status: 2,
                     // });
-                    const sql6 = 'SELECT SUM(cast (a.total_price as decimal(18,6))) AS total_price FROM ?? as a WHERE a.tid = ? AND (a.id in(SELECT b.cpid FROM ?? as b WHERE b.tid = ? AND b.aid = ? AND b.status = ?) OR (a.uid = ? AND (a.status = ? OR a.status = ?)))';
-                    const sqlParam6 = [this.tableName, tenderId, this.ctx.service.changePlanAudit.tableName, tenderId, this.ctx.session.sessionUser.accountId, audit.status.checking, this.ctx.session.sessionUser.accountId, audit.status.uncheck, audit.status.checkNo];
+                    const sql6 = 'SELECT SUM(cast (a.total_price as decimal(18,6))) AS total_price FROM ?? as a WHERE a.tid = ? AND (a.id in(SELECT b.cpid FROM ?? as b WHERE b.tid = ? AND b.aid = ? AND b.status = ?) OR (a.uid = ? AND (a.status = ? OR a.status = ? OR a.status = ?)))';
+                    const sqlParam6 = [this.tableName, tenderId, this.ctx.service.changePlanAudit.tableName, tenderId, this.ctx.session.sessionUser.accountId, audit.status.checking, this.ctx.session.sessionUser.accountId, audit.status.uncheck, audit.status.checkNo, audit.status.revise];
                     const result6 = await this.db.query(sql6, sqlParam6);
                     return result6[0].total_price ? result6[0].total_price : 0;
                 case 5: // 待上报(所有的)PS:取未上报,退回的变更立项
                     const sql2 =
                         'SELECT SUM(cast (a.total_price as decimal(18,6))) AS total_price FROM ?? AS a WHERE ' +
                         // 'a.id IN (SELECT b.cpid FROM ?? AS b WHERE b.aid = ? AND a.times = b.times GROUP BY b.cpid) ' +
-                        'a.uid = ? AND a.tid = ? AND (a.status = ? OR a.status = ?)';
+                        'a.uid = ? AND a.tid = ? AND (a.status = ? OR a.status = ? OR a.status = ?)';
                     const sqlParam2 = [
                         this.tableName,
                         // this.ctx.service.changePlanAudit.tableName,
@@ -329,6 +331,7 @@ module.exports = app => {
                         tenderId,
                         audit.status.uncheck,
                         audit.status.checkNo,
+                        audit.status.revise,
                     ];
                     const result2 = await this.db.query(sql2, sqlParam2);
                     return result2[0].total_price ? result2[0].total_price : 0;
@@ -410,6 +413,8 @@ module.exports = app => {
                 await this.transaction.delete(this.ctx.service.changePlanAtt.tableName, { cpid: id });
                 // 最后删除变更令
                 await this.transaction.delete(this.tableName, { id });
+                // 删除history
+                await this.transaction.delete(this.ctx.service.changePlanHistory.tableName, { cpid: id });
                 // 记录删除日志
                 await this.ctx.service.projectLog.addProjectLog(this.transaction, projectLogConst.type.changePlan, projectLogConst.status.delete, changeInfo.code);
                 await this.transaction.commit();
@@ -420,6 +425,43 @@ module.exports = app => {
             }
             return result;
         }
+
+        async doCheckChangeCanCancel(change) {
+            // 获取当前审批人的上一个审批人,判断是否是当前登录人,并赋予撤回功能,(当审批人存在有审批过时,上一人不允许再撤回)
+            const status = audit.status;
+            const accountId = this.ctx.session.sessionUser.accountId;
+            const auditors = change.auditors;
+            change.cancancel = 0;
+            if (change.status !== status.checked && change.status !== status.uncheck && change.status !== status.revise) {
+                if (change.status !== status.checkNo) {
+                    // 找出当前操作人上一个审批人,包括审批完成的和退回上一个审批人的,同时当前操作人为第一人时,就是则为原报
+                    const onAuditor = this._.find(auditors, function(item) {
+                        return item.aid === change.curAuditor.aid && item.status === status.checking;
+                    });
+                    const preAudit = onAuditor.order !== 1 ? this._.find(auditors, { order: onAuditor.order - 1 }) : false;
+                    const preAid = preAudit ? (preAudit.status !== status.checkAgain ? preAudit.aid : false) : change.uid;// 已发起重审无法撤回
+                    if (onAuditor && onAuditor.aid === preAid && preAudit.status === status.checkCancel) {
+                        return;// 不可以多次撤回
+                    } else if (preAid === accountId && preAid !== change.uid) {
+                        if (preAudit.status === status.checked) {
+                            change.cancancel = 2;// 审批人撤回审批通过
+                        }
+                        // else if (preAudit.status === status.checkNoPre) {
+                        //     change.cancancel = 3;// 审批人撤回审批退回上一人
+                        // }
+                        change.preAudit = preAudit;
+                    } else if (preAid === accountId && preAid === change.uid) {
+                        change.cancancel = 1;// 原报撤回
+                    }
+                } else {
+                    const lastAuditors = await this.service.changePlanAudit.getAuditors(change.id, change.times - 1);
+                    const onAuditor = this._.findLast(lastAuditors, { status: status.checkNo });
+                    if (onAuditor && onAuditor.aid === accountId) {
+                        change.cancancel = 4;// 审批人撤回退回原报
+                    }
+                }
+            }
+        }
     }
 
     return ChangePlan;

+ 367 - 3
app/service/change_plan_audit.js

@@ -75,8 +75,8 @@ module.exports = app => {
         async getAuditGroupByList(changeId, times) {
             const sql = 'SELECT la.`aid`, pa.`name`, pa.`company`, pa.`role`, la.`times`, la.`cpid`, la.`aid`, la.`order` ' +
                 '  FROM ?? AS la Left Join ?? AS pa On la.`aid` = pa.`id`' +
-                '  WHERE la.`cpid` = ? and la.`times` = ? GROUP BY la.`aid` ORDER BY la.`order`';
-            const sqlParam = [this.tableName, this.ctx.service.projectAccount.tableName, changeId, times];
+                '  WHERE la.`cpid` = ? and la.`times` = ? and la.`status` != ? and la.`status` != ? GROUP BY la.`aid` ORDER BY la.`order`';
+            const sqlParam = [this.tableName, this.ctx.service.projectAccount.tableName, changeId, times, auditConst.status.revise, auditConst.status.cancelRevise];
             return await this.db.query(sql, sqlParam);
         }
 
@@ -95,6 +95,7 @@ module.exports = app => {
             switch (status) {
                 case auditConst.status.checking :
                 case auditConst.status.checked :
+                case auditConst.status.revise :
                     sql = 'SELECT la.`aid`, pa.`name`, pa.`company`, pa.`role`, la.`times`, la.`cpid`, la.`aid`, la.`order`, la.`status` ' +
                         '  FROM ?? AS la Left Join ?? AS pa On la.`aid` = pa.`id` ' +
                         '  WHERE la.`cpid` = ? and la.`status` = ? ' +
@@ -117,6 +118,12 @@ module.exports = app => {
             return auditor;
         }
 
+        async getLastAudit(cpid, times, transaction = null) {
+            const sql = 'SELECT * FROM ?? WHERE `cpid` = ? AND `times` = ? ORDER BY `order` DESC';
+            const sqlParam = [this.tableName, cpid, times];
+            return transaction ? await transaction.queryOne(sql, sqlParam) : await this.db.queryOne(sql, sqlParam);
+        }
+
         /**
          * 获取审核人流程列表(包括原报)
          * @param {Number} materialId 调差id
@@ -283,7 +290,7 @@ module.exports = app => {
                     c_name: this.ctx.change.name,
                 };
                 await this.ctx.helper.sendWechat(audit.aid, smsTypeConst.const.BG, smsTypeConst.judge.approval.toString(), wxConst.template.change, wechatData);
-
+                await transaction.delete(this.ctx.service.changeProjectHistory.tableName, { cpid: cpId });
                 // todo 更新标段tender状态 ?
                 await transaction.commit();
             } catch (err) {
@@ -470,6 +477,7 @@ module.exports = app => {
 
         async _checkNo(pid, cpId, checkData, times) {
             const time = new Date();
+            const changeData = await this.ctx.service.changePlan.getDataById(cpId);
             // 整理当前流程审核人状态更新
             const audit = await this.getDataByCondition({ cpid: cpId, times, status: auditConst.status.checking });
             if (!audit) {
@@ -518,6 +526,12 @@ module.exports = app => {
                     c_name: this.ctx.change.name,
                 };
                 await this.ctx.helper.sendWechat(users, smsTypeConst.const.BG, smsTypeConst.judge.result.toString(), wxConst.template.change, wechatData);
+                // 生成内容保存表至zh_change_project_history中,用于撤回
+                // 回退spamount值数据
+                const changeList = await this.ctx.service.changePlanList.getAllDataByCondition({
+                    where: { cpid: cpId },
+                });
+                await this.ctx.service.changePlanHistory.saveHistory(transaction, changeData, changeList);
                 await transaction.commit();
             } catch (err) {
                 await transaction.rollback();
@@ -587,6 +601,356 @@ module.exports = app => {
             ];
             return await this.db.query(sql, sqlParam);
         }
+
+        /**
+         * 审批撤回
+         * @param {Number} stageId - 标段id
+         * @param {Number} times - 第几次审批
+         * @return {Promise<void>}
+         */
+        async checkCancel(change) {
+
+            // 分3种情况,根据ctx.cancancel值判断:
+            // 1.原报发起撤回,当前流程删除,并回到待上报
+            // 2.审批人撤回审批通过,增加流程,并回到它审批中
+            // 4.审批人撤回退回原报操作,删除新增的审批流,增加流程,回滚到它审批中
+            switch (change.cancancel) {
+                case 1: await this._userCheckCancel(change); break;
+                case 2: await this._auditCheckCancel(change); break;
+                case 4: await this._auditCheckCancelNo(change); break;
+                default: throw '不可撤回,请刷新页面重试';
+            }
+        }
+        /**
+         * 原报撤回,直接改动审批人状态
+         * 如果存在审批人数据,将其改为原报流程数据,但保留原提交人
+         *
+         * 一审 1 A checking  ->  A uncheck status改   pay/jl:删0(jl为增量数据,只删重复部分) 1->0 删1
+         * ...
+         *
+         * @param stage
+         * @returns {Promise<void>}
+         * @private
+         */
+        async _userCheckCancel(change) {
+            const transaction = await this.db.beginTransaction();
+            try {
+                // 整理当前流程审核人状态更新
+                const curAudit = await this.getDataByCondition({ cpid: change.id, times: change.times, status: auditConst.status.checking });
+                // 审批人变成待审批状态
+                await transaction.update(this.tableName, {
+                    id: curAudit.id,
+                    status: auditConst.status.uncheck,
+                    begin_time: null,
+                    opinion: null,
+                });
+                // 清单审批值删除并重算变更金额
+                await this.ctx.service.changePlanList.delAuditAmount(transaction, change.id);
+                // 变成待上报状态
+                await transaction.update(this.ctx.service.changePlan.tableName, {
+                    id: change.id,
+                    status: change.times === 1 ? auditConst.status.uncheck : auditConst.status.checkNo,
+                });
+                await transaction.commit();
+            } catch (err) {
+                await transaction.rollback();
+                throw err;
+            }
+
+        }
+        /**
+         * 审批人撤回审批通过,插入两条数据
+         *
+         * 一审 1 A checked             一审 1 A checked
+         * 二审 2 B checked   pre ->    二审 2 B checked
+         * 三审 3 C checking  cur       二审 3 B checkCancel  增                增extra_his     增tp_his
+         * 四审 4 D uncheck             二审 4 B checking     增                增pay_cur
+         *                             三审 5 C uncheck      order、status改
+         *                             四审 6 D uncheck      order改
+         *
+         * @param stage
+         * @returns {Promise<void>}
+         * @private
+         */
+        async _auditCheckCancel(change) {
+            const time = new Date();
+
+            const transaction = await this.db.beginTransaction();
+            try {
+                // 整理当前流程审核人状态更新
+                const curAudit = await this.getDataByCondition({ cpid: change.id, times: change.times, status: auditConst.status.checking });
+                const preAudit = change.preAudit;
+                if (!curAudit || curAudit.order <= 1 || !preAudit) {
+                    throw '撤回用户数据错误';
+                }
+                // 顺移其后审核人流程顺序
+                const sql = 'UPDATE ' + this.tableName + ' SET `order` = `order` + 2 WHERE cpid = ? AND times = ? AND `order` > ?';
+                await transaction.query(sql, [change.id, change.times, curAudit.order]);
+                // 当前审批人2次添加至流程中
+                const newAuditors = [];
+                // 先入撤回记录
+                newAuditors.push({
+                    tid: change.tid,
+                    cpid: change.id,
+                    aid: preAudit.aid,
+                    times: change.times,
+                    order: curAudit.order,
+                    status: auditConst.status.checkCancel,
+                    begin_time: time,
+                    end_time: time,
+                    opinion: '',
+                });
+                newAuditors.push({
+                    tid: change.tid,
+                    cpid: change.id,
+                    aid: preAudit.aid,
+                    times: change.times,
+                    order: curAudit.order + 1,
+                    status: auditConst.status.checking,
+                    begin_time: time,
+                });
+                await transaction.insert(this.tableName, newAuditors);
+                // 当前审批人变成待审批
+                await transaction.update(this.tableName, { id: curAudit.id, order: curAudit.order + 2, begin_time: null, status: auditConst.status.uncheck });
+                // 清除上一人的值并调整spamount值
+                const updateList = [];
+                const changeList = await this.ctx.service.changePlanList.getAllDataByCondition({
+                    where: { cpid: change.id },
+                });
+                for (const cl of changeList) {
+                    const audit_amount = cl.audit_amount.split(',');
+                    const last_amount = audit_amount[audit_amount.length - 1] ? audit_amount[audit_amount.length - 1] : 0;
+                    audit_amount.splice(-1, 1);
+                    const list_update = {
+                        id: cl.id,
+                        audit_amount: audit_amount.join(','),
+                        spamount: parseFloat(last_amount),
+                    };
+                    updateList.push(list_update);
+                }
+                if (updateList.length > 0) await transaction.updateRows(this.ctx.service.changePlanList.tableName, updateList);
+                // 更新total_price
+                await this.ctx.service.changePlanList.calcCamountSum(transaction);
+                await transaction.commit();
+            } catch (err) {
+                await transaction.rollback();
+                throw err;
+            }
+        }
+
+        /**
+         * 审批人撤回审批退回原报
+         *
+         * 1# 一审 1 A checked              1# 一审 1 A checked
+         *    二审 2 B checkNo   pre   ->      二审 2 B checkNo
+         *    三审 3 C uncheck                 二审 3 B checkCancel    增       pay: 2#0 -> 1#3   jl: 2#0 -> 1#3   增tp_his   增extra_his
+         *                                    二审 4 B checking       增       pay: 2#0 -> 1#4
+         *                                    三审 5 C uncheck        order改
+         *
+         * 2# 一审 1 A uncheck              2#                        删       pay: 2#0删   jl: 2#0删
+         *    二审 2 B uncheck
+         *    三审 3 C uncheck
+         *
+         * @param stage
+         * @returns {Promise<void>}
+         * @private
+         */
+        async _auditCheckCancelNo(change) {
+
+            const time = new Date();
+
+            const transaction = await this.db.beginTransaction();
+            try {
+                // const curAudit = await this.getDataByCondition({ cpid: change.id, times: change.times - 1, status: auditConst.status.back });
+                const curAudit = await this.getAuditorByStatus(change.id, auditConst.status.checkNo, change.times);
+                // 整理上一个流程审核人状态更新
+                // 顺移其后审核人流程顺序
+                const sql = 'UPDATE ' + this.tableName + ' SET `order` = `order` + 2 WHERE cpid = ? AND times = ? AND `order` > ?';
+                await transaction.query(sql, [change.id, change.times - 1, curAudit.order]);
+                // 当前审批人2次添加至流程中
+                const newAuditors = [];
+                newAuditors.push({
+                    tid: change.tid,
+                    cpid: change.id,
+                    aid: curAudit.aid,
+                    times: curAudit.times,
+                    order: curAudit.order + 1,
+                    status: auditConst.status.checkCancel,
+                    begin_time: time,
+                    end_time: time,
+                    opinion: '',
+                });
+                newAuditors.push({
+                    tid: change.tid,
+                    cpid: change.id,
+                    aid: curAudit.aid,
+                    times: curAudit.times,
+                    order: curAudit.order + 2,
+                    status: auditConst.status.checking,
+                    begin_time: time,
+                });
+                await transaction.insert(this.tableName, newAuditors);
+                // 删除当前次审批流
+                await transaction.delete(this.tableName, { cpid: change.id, times: change.times });
+                // 回退数据
+                await this.ctx.service.changePlanHistory.returnHistory(transaction, change.id);
+                await transaction.delete(this.ctx.service.changePlanHistory.tableName, { cpid: change.id });
+                // // 设置变更立项为审批中
+                // await transaction.update(this.ctx.service.changeProject.tableName, {
+                //     id: change.id,
+                //     time: change.times - 1,
+                //     status: auditConst.status.checking,
+                // });
+                await transaction.commit();
+            } catch (err) {
+                await transaction.rollback();
+                throw err;
+            }
+        }
+
+        /**
+         * 重新审批变更令
+         * @param { string } cid - 查询的清单
+         * @return {Promise<*>} - 可用的变更令列表
+         */
+        async checkRevise(change) {
+            const time = new Date();
+            // 初始化事务
+            const transaction = await this.db.beginTransaction();
+            let result = false;
+            try {
+                const pid = this.ctx.session.sessionProject.id;
+                // 获取审核人列表
+                const sql = 'SELECT `tid`, `cpid`, `aid`, `order` FROM ?? WHERE `cpid` = ? and `times` = ? GROUP BY `aid` ORDER BY `id` ASC';
+                const sqlParam = [this.tableName, change.id, change.times];
+                const auditors = await this.db.query(sql, sqlParam);
+                // 添加到消息推送表
+                const noticeContent = await this.getNoticeContent(pid, change.tid, change.id, this.ctx.session.sessionUser.accountId, '发起修订');
+                const records = [];
+                auditors.forEach(auditor => {
+                    records.push({
+                        pid,
+                        type: pushType.changePlan,
+                        uid: auditor.aid,
+                        status: auditConst.status.revise,
+                        content: noticeContent,
+                    });
+                });
+                await transaction.insert('zh_notice', records);
+
+                // 获取当前次数审批人列表
+                const auditList = await this.getAuditGroupByList(change.id, change.times);
+                const lastAudit = await this.getLastAudit(change.id, change.times);
+                const insert_audit_array = [];
+                // 新增一个发起修订状态到审批流程中
+                const revise_audit = {
+                    tid: change.tid,
+                    cpid: change.id,
+                    aid: change.uid,
+                    times: change.times,
+                    order: lastAudit.order + 1,
+                    status: auditConst.status.revise,
+                    begin_time: time,
+                    end_time: time,
+                    opinion: '',
+                };
+                insert_audit_array.push(revise_audit);
+                // 新增新一次的审批人列表
+                let order = 1;
+                for (const al of auditList) {
+                    const insert_audit = {
+                        tid: change.tid,
+                        cpid: change.id,
+                        aid: al.aid,
+                        times: change.times + 1,
+                        order,
+                        status: auditConst.status.uncheck,
+                    };
+                    insert_audit_array.push(insert_audit);
+                    order++;
+                }
+                await transaction.insert(this.tableName, insert_audit_array);
+                // 生成内容保存表至zh_change_history中,用于撤销修订回退
+                const changeData = await transaction.get(this.ctx.service.changePlan.tableName, { id: change.id });
+                const changeList = await this.ctx.service.changePlanList.getAllDataByCondition({
+                    where: { cpid: change.id },
+                });
+                await this.ctx.service.changePlanHistory.saveHistory(transaction, changeData, changeList);
+                // 清单审批值删除并重算变更金额
+                await this.ctx.service.changePlanList.delAuditAmount(transaction, change.id);
+                // 设置变更立项修订状态
+                await transaction.update(this.ctx.service.changePlan.tableName, {
+                    id: change.id,
+                    decimal: null,
+                    status: auditConst.status.revise,
+                    times: change.times + 1,
+                });
+
+                await transaction.commit();
+                result = true;
+            } catch (error) {
+                console.log(error);
+                await transaction.rollback();
+                result = false;
+            }
+            return result;
+        }
+
+        /**
+         * 撤销修订变更令
+         * @param { string } cid - 查询的清单
+         * @return {Promise<*>} - 可用的变更令列表
+         */
+        async cancelRevise(change) {
+            const time = new Date();
+            // 初始化事务
+            const transaction = await this.db.beginTransaction();
+            let result = false;
+            try {
+                const pid = this.ctx.session.sessionProject.id;
+                // 获取审核人列表
+                const sql = 'SELECT `tid`, `cpid`, `aid`, `order` FROM ?? WHERE `cpid` = ? and `times` = ? GROUP BY `aid` ORDER BY `id` ASC';
+                const sqlParam = [this.tableName, change.id, change.times - 1];
+                const auditors = await this.db.query(sql, sqlParam);
+                // 添加到消息推送表
+                const noticeContent = await this.getNoticeContent(pid, change.tid, change.id, this.ctx.session.sessionUser.accountId, '撤销修订');
+                const records = [];
+                auditors.forEach(auditor => {
+                    records.push({
+                        pid,
+                        type: pushType.changePlan,
+                        uid: auditor.aid,
+                        status: auditConst.status.cancelRevise,
+                        content: noticeContent,
+                    });
+                });
+                await transaction.insert('zh_notice', records);
+                const lastAudit = await this.getLastAudit(change.id, change.times - 1);
+                // 新增一个撤销修订状态到审批流程中
+                const revise_audit = {
+                    tid: change.tid,
+                    cpid: change.id,
+                    aid: this.ctx.session.sessionUser.accountId,
+                    times: change.times - 1,
+                    order: lastAudit.order + 1,
+                    status: auditConst.status.cancelRevise,
+                    begin_time: time,
+                    end_time: time,
+                    opinion: '',
+                };
+                await transaction.insert(this.ctx.service.changePlanAudit.tableName, revise_audit);
+                await transaction.delete(this.ctx.service.changePlanAudit.tableName, { cpid: change.id, times: change.times });
+                await this.ctx.service.changePlanHistory.returnHistory(transaction, change.id);
+                await transaction.delete(this.ctx.service.changePlanHistory.tableName, { cpid: change.id });
+                await transaction.commit();
+                result = true;
+            } catch (error) {
+                console.log(error);
+                await transaction.rollback();
+                result = false;
+            }
+            return result;
+        }
     }
     return ChangePlanAudit;
 };

+ 53 - 0
app/service/change_plan_history.js

@@ -0,0 +1,53 @@
+'use strict';
+
+/**
+ * 变更新增部位插入记录表
+ *
+ * @author Mai
+ * @date
+ * @version
+ */
+
+module.exports = app => {
+
+    class ChangePlanHistory extends app.BaseService {
+        /**
+         * 构造函数
+         *
+         * @param {Object} ctx - egg全局变量
+         * @return {void}
+         */
+        constructor(ctx) {
+            super(ctx);
+            this.tableName = 'change_plan_history';
+        }
+
+        async saveHistory(transaction, data, list) {
+            await transaction.insert(this.tableName, {
+                tid: data.tid,
+                cpid: data.id,
+                info_json: JSON.stringify(data),
+                list_json: JSON.stringify(list),
+            });
+        }
+
+        async returnHistory(transaction, cpid) {
+            const data = await transaction.get(this.tableName, { cpid });
+            if (!data) throw '撤销前数据不存在,无法撤销';
+            const change_update = {};
+            const oldInfo = JSON.parse(data.info_json);
+            for (const key in oldInfo) {
+                if (key !== 'in_time') {
+                    change_update[key] = oldInfo[key];
+                }
+            }
+            await transaction.update(this.ctx.service.changePlan.tableName, change_update);
+            const oldList = JSON.parse(data.list_json);
+            // 先删后插
+            await transaction.delete(this.ctx.service.changePlanList.tableName, { cpid });
+            await transaction.insert(this.ctx.service.changePlanList.tableName, oldList);
+        }
+    }
+
+    return ChangePlanHistory;
+};

+ 65 - 8
app/service/change_project.js

@@ -110,8 +110,10 @@ module.exports = app => {
                 switch (status) {
                     case 0: // 包含你的所有变更立项
                         sql =
-                            'SELECT a.*, p.name as account_name FROM ?? AS a LEFT JOIN ?? AS p On a.uid = p.id WHERE a.tid = ? AND ' +
-                            '(a.uid = ? OR (a.status != ? AND (a.id IN (SELECT b.cpid FROM ?? AS b WHERE b.aid = ? AND a.times = b.times GROUP BY b.cpid) OR a.id IN (SELECT c.cpid FROM ?? AS c WHERE c.aid = ? AND c.tid = ?))) OR a.status = ? )';
+                            'SELECT a.*, p.name as account_name FROM ?? AS a LEFT JOIN ?? AS p On a.uid = p.id WHERE a.tid = ? AND' +
+                            ' (a.uid = ? OR (a.status != ? AND (a.id IN (SELECT b.cpid FROM ?? AS b WHERE b.aid = ? AND a.times = b.times GROUP BY b.cpid)' +
+                            ' OR a.id IN (SELECT c.cpid FROM ?? AS c WHERE c.aid = ? AND c.tid = ?)))' +
+                            ' OR a.status = ? )';
                         sqlParam = [
                             this.tableName,
                             this.ctx.service.projectAccount.tableName,
@@ -127,14 +129,22 @@ module.exports = app => {
                         ];
                         break;
                     case 1: // 待处理(你的)
-                        sql = 'SELECT a.*, p.name as account_name FROM ?? as a LEFT JOIN ?? AS p On a.uid = p.id WHERE a.tid = ? AND (a.id in(SELECT b.cpid FROM ?? as b WHERE b.tid = ? AND b.aid = ? AND b.status = ?) OR (a.uid = ? AND (a.status = ? OR a.status = ?)))';
-                        sqlParam = [this.tableName, this.ctx.service.projectAccount.tableName, tenderId, this.ctx.service.changeProjectAudit.tableName, tenderId, this.ctx.session.sessionUser.accountId, audit.status.checking, this.ctx.session.sessionUser.accountId, audit.status.uncheck, audit.status.back];
+                        sql = 'SELECT a.*, p.name as account_name FROM ?? as a LEFT JOIN ?? AS p On a.uid = p.id WHERE' +
+                            ' a.tid = ? AND (a.id in(SELECT b.cpid FROM ?? as b WHERE b.tid = ? AND b.aid = ? AND b.status = ?)' +
+                            ' OR (a.uid = ? AND (a.status = ? OR a.status = ? OR a.status = ?)))';
+                        sqlParam = [this.tableName,
+                            this.ctx.service.projectAccount.tableName,
+                            tenderId, this.ctx.service.changeProjectAudit.tableName,
+                            tenderId, this.ctx.session.sessionUser.accountId,
+                            audit.status.checking, this.ctx.session.sessionUser.accountId,
+                            audit.status.uncheck, audit.status.back, audit.status.revise,
+                        ];
                         break;
                     case 5: // 待上报(所有的)PS:取未上报,退回,修订的变更令
                         sql =
                             'SELECT a.*, p.name as account_name FROM ?? AS a LEFT JOIN ?? AS p On a.uid = p.id WHERE ' +
                             // 'a.id IN (SELECT b.cpid FROM ?? AS b WHERE b.aid = ? AND a.times = b.times GROUP BY b.cpid) AND ' +
-                            'a.uid = ? AND a.tid = ? AND (a.status = ? OR a.status = ?)';
+                            'a.uid = ? AND a.tid = ? AND (a.status = ? OR a.status = ? OR a.status = ?)';
                         sqlParam = [
                             this.tableName,
                             this.ctx.service.projectAccount.tableName,
@@ -143,6 +153,7 @@ module.exports = app => {
                             tenderId,
                             audit.status.uncheck,
                             audit.status.back,
+                            audit.status.revise,
                         ];
                         break;
                     case 2: // 进行中(所有的)
@@ -245,15 +256,15 @@ module.exports = app => {
                     //     uid: this.ctx.session.sessionUser.accountId,
                     //     status: 2,
                     // });
-                    const sql6 = 'SELECT count(*) AS count FROM ?? as a WHERE a.tid = ? AND (a.id in(SELECT b.cpid FROM ?? as b WHERE b.tid = ? AND b.aid = ? AND b.status = ?) OR (a.uid = ? AND (a.status = ? OR a.status = ?)))';
-                    const sqlParam6 = [this.tableName, tenderId, this.ctx.service.changeProjectAudit.tableName, tenderId, this.ctx.session.sessionUser.accountId, audit.status.checking, this.ctx.session.sessionUser.accountId, audit.status.uncheck, audit.status.back];
+                    const sql6 = 'SELECT count(*) AS count FROM ?? as a WHERE a.tid = ? AND (a.id in(SELECT b.cpid FROM ?? as b WHERE b.tid = ? AND b.aid = ? AND b.status = ?) OR (a.uid = ? AND (a.status = ? OR a.status = ? OR a.status = ?)))';
+                    const sqlParam6 = [this.tableName, tenderId, this.ctx.service.changeProjectAudit.tableName, tenderId, this.ctx.session.sessionUser.accountId, audit.status.checking, this.ctx.session.sessionUser.accountId, audit.status.uncheck, audit.status.back, audit.status.revise];
                     const result6 = await this.db.query(sql6, sqlParam6);
                     return result6[0].count;
                 case 5: // 待上报(所有的)PS:取未上报,退回的变更立项
                     const sql2 =
                         'SELECT count(*) AS count FROM ?? AS a WHERE ' +
                         // 'a.id IN (SELECT b.cpid FROM ?? AS b WHERE b.aid = ? AND a.times = b.times GROUP BY b.cpid) ' +
-                        'a.uid = ? AND a.tid = ? AND (a.status = ? OR a.status = ?)';
+                        'a.uid = ? AND a.tid = ? AND (a.status = ? OR a.status = ? OR a.status = ?)';
                     const sqlParam2 = [
                         this.tableName,
                         // this.ctx.service.changeProjectAudit.tableName,
@@ -261,6 +272,7 @@ module.exports = app => {
                         tenderId,
                         audit.status.uncheck,
                         audit.status.back,
+                        audit.status.revise,
                     ];
                     const result2 = await this.db.query(sql2, sqlParam2);
                     return result2[0].count;
@@ -346,6 +358,8 @@ module.exports = app => {
                 // }
                 // 最后删除变更令
                 await this.transaction.delete(this.tableName, { id });
+                // 删除history
+                await this.transaction.delete(this.ctx.service.changeProjectHistory.tableName, { cpid: id });
                 // 记录删除日志
                 await this.ctx.service.projectLog.addProjectLog(this.transaction, projectLogConst.type.changeProject, projectLogConst.status.delete, changeInfo.code);
                 await this.transaction.commit();
@@ -356,6 +370,49 @@ module.exports = app => {
             }
             return result;
         }
+
+        async doCheckChangeCanCancel(change) {
+            // 获取当前审批人的上一个审批人,判断是否是当前登录人,并赋予撤回功能,(当审批人存在有审批过时,上一人不允许再撤回)
+            const status = audit.status;
+            const accountId = this.ctx.session.sessionUser.accountId;
+            const auditors = change.auditors;
+            change.cancancel = 0;
+            if (change.status !== status.checked && change.status !== status.uncheck && change.status !== status.revise) {
+                if (change.status === status.back) {
+                    const lastAuditors = await this.service.changeProjectAudit.getAuditors(change.id, change.times - 1);
+                    const onAuditor = this._.findLast(lastAuditors, { status: status.back });
+                    if (onAuditor && onAuditor.aid === accountId) {
+                        change.cancancel = 4;// 审批人撤回退回原报
+                    }
+                } else if (change.status === status.checkNo) {
+                    const onAuditor = this._.find(auditors, function(item) {
+                        return item.status === status.checkNo;
+                    });
+                    if (onAuditor && onAuditor.aid === accountId) {
+                        change.cancancel = 3; // 审批人撤回审批终止
+                    }
+                } else {
+                    // 找出当前操作人上一个审批人,包括审批完成的和退回上一个审批人的,同时当前操作人为第一人时,就是则为原报
+                    const onAuditor = this._.find(auditors, function(item) {
+                        return item.aid === change.curAuditor.aid && item.status === status.checking;
+                    });
+
+                    const preAudit = onAuditor.order > 1 ? this._.find(auditors, { order: onAuditor.order - 1 }) : false;
+                    const preAid = preAudit ? (preAudit.status !== status.checkAgain ? preAudit.aid : false) : change.uid;
+                    // console.log(onAuditor, preAudit, auditors);
+                    if (onAuditor.aid === preAid && preAudit.status === status.checkCancel) {
+                        return;// 不可以多次撤回
+                    } else if (preAid === accountId && preAid !== change.uid) {
+                        if (preAudit.status === status.checked) {
+                            change.cancancel = 2;// 审批人撤回审批通过
+                        }
+                        change.preAudit = preAudit;
+                    } else if (preAid === accountId && preAid === change.uid) {
+                        change.cancancel = 1;// 原报撤回
+                    }
+                }
+            }
+        }
     }
 
     return ChangeProject;

+ 395 - 3
app/service/change_project_audit.js

@@ -75,8 +75,8 @@ module.exports = app => {
         async getAuditGroupByList(changeId, times) {
             const sql = 'SELECT la.`aid`, pa.`name`, pa.`company`, pa.`role`, la.`times`, la.`cpid`, la.`aid`, la.`order` ' +
                 '  FROM ?? AS la Left Join ?? AS pa On la.`aid` = pa.`id`' +
-                '  WHERE la.`cpid` = ? and la.`times` = ? GROUP BY la.`aid` ORDER BY la.`order`';
-            const sqlParam = [this.tableName, this.ctx.service.projectAccount.tableName, changeId, times];
+                '  WHERE la.`cpid` = ? and la.`times` = ? and la.`status` != ? and la.`status` != ? GROUP BY la.`aid` ORDER BY la.`order`';
+            const sqlParam = [this.tableName, this.ctx.service.projectAccount.tableName, changeId, times, auditConst.status.revise, auditConst.status.cancelRevise];
             return await this.db.query(sql, sqlParam);
         }
 
@@ -96,6 +96,8 @@ module.exports = app => {
                 case auditConst.status.checking :
                 case auditConst.status.checked :
                 case auditConst.status.checkNo :
+                case auditConst.status.revise :
+                case auditConst.status.cancelRevise :
                     sql = 'SELECT la.`aid`, pa.`name`, pa.`company`, pa.`role`, la.`times`, la.`cpid`, la.`aid`, la.`order` ' +
                         '  FROM ?? AS la Left Join ?? AS pa On la.`aid` = pa.`id` ' +
                         '  WHERE la.`cpid` = ? and la.`status` = ? ' +
@@ -118,6 +120,12 @@ module.exports = app => {
             return auditor;
         }
 
+        async getLastAudit(cpid, times, transaction = null) {
+            const sql = 'SELECT * FROM ?? WHERE `cpid` = ? AND `times` = ? ORDER BY `order` DESC';
+            const sqlParam = [this.tableName, cpid, times];
+            return transaction ? await transaction.queryOne(sql, sqlParam) : await this.db.queryOne(sql, sqlParam);
+        }
+
         /**
          * 获取审核人流程列表(包括原报)
          * @param {Number} materialId 调差id
@@ -280,7 +288,7 @@ module.exports = app => {
                     c_name: this.ctx.change.name,
                 };
                 await this.ctx.helper.sendWechat(audit.aid, smsTypeConst.const.BG, smsTypeConst.judge.approval.toString(), wxConst.template.change, wechatData);
-
+                await transaction.delete(this.ctx.service.changeProjectHistory.tableName, { cpid: cpId });
                 // todo 更新标段tender状态 ?
                 await transaction.commit();
             } catch (err) {
@@ -458,6 +466,7 @@ module.exports = app => {
 
         async _back(pid, cpId, checkData, times) {
             const time = new Date();
+            const changeData = await this.ctx.service.changeProject.getDataById(cpId);
             // 整理当前流程审核人状态更新
             const audit = await this.getDataByCondition({ cpid: cpId, times, status: auditConst.status.checking });
             if (!audit) {
@@ -500,6 +509,8 @@ module.exports = app => {
                     c_name: this.ctx.change.name,
                 };
                 await this.ctx.helper.sendWechat(users, smsTypeConst.const.BG, smsTypeConst.judge.result.toString(), wxConst.template.change, wechatData);
+                // 生成内容保存表至zh_change_project_history中,用于撤回
+                await this.ctx.service.changeProjectHistory.saveHistory(transaction, changeData);
                 await transaction.commit();
             } catch (err) {
                 await transaction.rollback();
@@ -590,6 +601,387 @@ module.exports = app => {
             const sqlParam = [tenderId];
             return this.db.query(sql, sqlParam);
         }
+
+        /**
+         * 审批撤回
+         * @param {Number} stageId - 标段id
+         * @param {Number} times - 第几次审批
+         * @return {Promise<void>}
+         */
+        async checkCancel(change) {
+
+            // 分4种情况,根据ctx.cancancel值判断:
+            // 1.原报发起撤回,当前流程删除,并回到待上报
+            // 2.审批人撤回审批通过,增加流程,并回到它审批中
+            // 3.审批人撤回审批终止,增加流程,并回到它审批中,并更新立项状态为审批中
+            // 4.审批人撤回退回原报操作,删除新增的审批流,增加流程,回滚到它审批中
+            switch (change.cancancel) {
+                case 1: await this._userCheckCancel(change); break;
+                case 2: await this._auditCheckCancel(change); break;
+                case 3: await this._auditCheckCancelStop(change); break;
+                case 4: await this._auditCheckCancelNo(change); break;
+                default: throw '不可撤回,请刷新页面重试';
+            }
+        }
+        /**
+         * 原报撤回,直接改动审批人状态
+         * 如果存在审批人数据,将其改为原报流程数据,但保留原提交人
+         *
+         * 一审 1 A checking  ->  A uncheck status改   pay/jl:删0(jl为增量数据,只删重复部分) 1->0 删1
+         * ...
+         *
+         * @param stage
+         * @returns {Promise<void>}
+         * @private
+         */
+        async _userCheckCancel(change) {
+            const transaction = await this.db.beginTransaction();
+            try {
+                // 整理当前流程审核人状态更新
+                const curAudit = await this.getDataByCondition({ cpid: change.id, times: change.times, status: auditConst.status.checking });
+                // 审批人变成待审批状态
+                await transaction.update(this.tableName, {
+                    id: curAudit.id,
+                    status: auditConst.status.uncheck,
+                    begin_time: null,
+                    opinion: null,
+                });
+                // 变成待上报状态
+                await transaction.update(this.ctx.service.changeProject.tableName, {
+                    id: change.id,
+                    status: change.times === 1 ? auditConst.status.uncheck : auditConst.status.back,
+                });
+                await transaction.commit();
+            } catch (err) {
+                await transaction.rollback();
+                throw err;
+            }
+
+        }
+        /**
+         * 审批人撤回审批通过,插入两条数据
+         *
+         * 一审 1 A checked             一审 1 A checked
+         * 二审 2 B checked   pre ->    二审 2 B checked
+         * 三审 3 C checking  cur       二审 3 B checkCancel  增                增extra_his     增tp_his
+         * 四审 4 D uncheck             二审 4 B checking     增                增pay_cur
+         *                             三审 5 C uncheck      order、status改
+         *                             四审 6 D uncheck      order改
+         *
+         * @param stage
+         * @returns {Promise<void>}
+         * @private
+         */
+        async _auditCheckCancel(change) {
+            const time = new Date();
+
+            const transaction = await this.db.beginTransaction();
+            try {
+                // 整理当前流程审核人状态更新
+                const curAudit = await this.getDataByCondition({ cpid: change.id, times: change.times, status: auditConst.status.checking });
+                const preAudit = change.preAudit;
+                if (!curAudit || curAudit.order <= 1 || !preAudit) {
+                    throw '撤回用户数据错误';
+                }
+                // 顺移其后审核人流程顺序
+                const sql = 'UPDATE ' + this.tableName + ' SET `order` = `order` + 2 WHERE cpid = ? AND times = ? AND `order` > ?';
+                await transaction.query(sql, [change.id, change.times, curAudit.order]);
+                // 当前审批人2次添加至流程中
+                const newAuditors = [];
+                // 先入撤回记录
+                newAuditors.push({
+                    tid: change.tid,
+                    cpid: change.id,
+                    aid: preAudit.aid,
+                    times: change.times,
+                    order: curAudit.order,
+                    status: auditConst.status.checkCancel,
+                    begin_time: time,
+                    end_time: time,
+                    opinion: '',
+                });
+                newAuditors.push({
+                    tid: change.tid,
+                    cpid: change.id,
+                    aid: preAudit.aid,
+                    times: change.times,
+                    order: curAudit.order + 1,
+                    status: auditConst.status.checking,
+                    begin_time: time,
+                });
+                await transaction.insert(this.tableName, newAuditors);
+                // 当前审批人变成待审批
+                await transaction.update(this.tableName, { id: curAudit.id, order: curAudit.order + 2, begin_time: null, status: auditConst.status.uncheck });
+                await transaction.commit();
+            } catch (err) {
+                await transaction.rollback();
+                throw err;
+            }
+        }
+
+        /**
+         * 审批人撤回审批终止,插入两条数据
+         *
+         * @param stage
+         * @returns {Promise<void>}
+         * @private
+         */
+        async _auditCheckCancelStop(change) {
+            const time = new Date();
+
+            const transaction = await this.db.beginTransaction();
+            try {
+                // const curAudit = await this.getDataByCondition({ cpid: change.id, times: change.times, status: auditConst.status.checkNo });
+                const curAudit = await this.getAuditorByStatus(change.id, auditConst.status.checkNo);
+                if (!curAudit) {
+                    throw '撤回用户数据错误';
+                }
+                // 顺移其后审核人流程顺序
+                const sql = 'UPDATE ' + this.tableName + ' SET `order` = `order` + 2 WHERE cpid = ? AND times = ? AND `order` > ?';
+                await transaction.query(sql, [change.id, change.times, curAudit.order]);
+                // 当前审批人2次添加至流程中
+                const newAuditors = [];
+                // 先入撤回记录
+                newAuditors.push({
+                    tid: change.tid,
+                    cpid: change.id,
+                    aid: curAudit.aid,
+                    times: change.times,
+                    order: curAudit.order + 1,
+                    status: auditConst.status.checkCancel,
+                    begin_time: time,
+                    end_time: time,
+                    opinion: '',
+                });
+                newAuditors.push({
+                    tid: change.tid,
+                    cpid: change.id,
+                    aid: curAudit.aid,
+                    times: change.times,
+                    order: curAudit.order + 2,
+                    status: auditConst.status.checking,
+                    begin_time: time,
+                });
+                await transaction.insert(this.tableName, newAuditors);
+                await transaction.update(this.ctx.service.changeProject.tableName, {
+                    id: change.id,
+                    status: auditConst.status.checking,
+                });
+
+                await transaction.commit();
+            } catch (err) {
+                await transaction.rollback();
+                throw err;
+            }
+        }
+
+        /**
+         * 审批人撤回审批退回原报
+         *
+         * 1# 一审 1 A checked              1# 一审 1 A checked
+         *    二审 2 B checkNo   pre   ->      二审 2 B checkNo
+         *    三审 3 C uncheck                 二审 3 B checkCancel    增       pay: 2#0 -> 1#3   jl: 2#0 -> 1#3   增tp_his   增extra_his
+         *                                    二审 4 B checking       增       pay: 2#0 -> 1#4
+         *                                    三审 5 C uncheck        order改
+         *
+         * 2# 一审 1 A uncheck              2#                        删       pay: 2#0删   jl: 2#0删
+         *    二审 2 B uncheck
+         *    三审 3 C uncheck
+         *
+         * @param stage
+         * @returns {Promise<void>}
+         * @private
+         */
+        async _auditCheckCancelNo(change) {
+
+            const time = new Date();
+
+            const transaction = await this.db.beginTransaction();
+            try {
+                // const curAudit = await this.getDataByCondition({ cpid: change.id, times: change.times - 1, status: auditConst.status.back });
+                const curAudit = await this.getAuditorByStatus(change.id, auditConst.status.back, change.times);
+                // 整理上一个流程审核人状态更新
+                // 顺移其后审核人流程顺序
+                const sql = 'UPDATE ' + this.tableName + ' SET `order` = `order` + 2 WHERE cpid = ? AND times = ? AND `order` > ?';
+                await transaction.query(sql, [change.id, change.times - 1, curAudit.order]);
+                // 当前审批人2次添加至流程中
+                const newAuditors = [];
+                newAuditors.push({
+                    tid: change.tid,
+                    cpid: change.id,
+                    aid: curAudit.aid,
+                    times: curAudit.times,
+                    order: curAudit.order + 1,
+                    status: auditConst.status.checkCancel,
+                    begin_time: time,
+                    end_time: time,
+                    opinion: '',
+                });
+                newAuditors.push({
+                    tid: change.tid,
+                    cpid: change.id,
+                    aid: curAudit.aid,
+                    times: curAudit.times,
+                    order: curAudit.order + 2,
+                    status: auditConst.status.checking,
+                    begin_time: time,
+                });
+                await transaction.insert(this.tableName, newAuditors);
+                // 删除当前次审批流
+                await transaction.delete(this.tableName, { cpid: change.id, times: change.times });
+                // 回退数据
+                await this.ctx.service.changeProjectHistory.returnHistory(transaction, change.id);
+                await transaction.delete(this.ctx.service.changeProjectHistory.tableName, { cpid: change.id });
+                // // 设置变更立项为审批中
+                // await transaction.update(this.ctx.service.changeProject.tableName, {
+                //     id: change.id,
+                //     time: change.times - 1,
+                //     status: auditConst.status.checking,
+                // });
+                await transaction.commit();
+            } catch (err) {
+                await transaction.rollback();
+                throw err;
+            }
+        }
+
+        /**
+         * 重新审批变更令
+         * @param { string } cid - 查询的清单
+         * @return {Promise<*>} - 可用的变更令列表
+         */
+        async checkRevise(change) {
+            const time = new Date();
+            // 初始化事务
+            const transaction = await this.db.beginTransaction();
+            let result = false;
+            try {
+                const pid = this.ctx.session.sessionProject.id;
+                // 获取审核人列表
+                const sql = 'SELECT `tid`, `cpid`, `aid`, `order` FROM ?? WHERE `cpid` = ? and `times` = ? GROUP BY `aid` ORDER BY `id` ASC';
+                const sqlParam = [this.tableName, change.id, change.times];
+                const auditors = await this.db.query(sql, sqlParam);
+                // 添加到消息推送表
+                const noticeContent = await this.getNoticeContent(pid, change.tid, change.id, this.ctx.session.sessionUser.accountId, '发起修订');
+                const records = [];
+                auditors.forEach(auditor => {
+                    records.push({
+                        pid,
+                        type: pushType.changeProject,
+                        uid: auditor.aid,
+                        status: auditConst.status.revise,
+                        content: noticeContent,
+                    });
+                });
+                await transaction.insert('zh_notice', records);
+
+                // 获取当前次数审批人列表
+                const auditList = await this.getAuditGroupByList(change.id, change.times);
+                const lastAudit = await this.getLastAudit(change.id, change.times);
+                const insert_audit_array = [];
+                // 新增一个发起修订状态到审批流程中
+                const revise_audit = {
+                    tid: change.tid,
+                    cpid: change.id,
+                    aid: change.uid,
+                    times: change.times,
+                    order: lastAudit.order + 1,
+                    status: auditConst.status.revise,
+                    begin_time: time,
+                    end_time: time,
+                    opinion: '',
+                };
+                insert_audit_array.push(revise_audit);
+                // 新增新一次的审批人列表
+                let order = 1;
+                for (const al of auditList) {
+                    const insert_audit = {
+                        tid: change.tid,
+                        cpid: change.id,
+                        aid: al.aid,
+                        times: change.times + 1,
+                        order,
+                        status: auditConst.status.uncheck,
+                    };
+                    insert_audit_array.push(insert_audit);
+                    order++;
+                }
+                await transaction.insert(this.tableName, insert_audit_array);
+                // 生成内容保存表至zh_change_history中,用于撤销修订回退
+                const changeData = await transaction.get(this.ctx.service.changeProject.tableName, { id: change.id });
+                await this.ctx.service.changeProjectHistory.saveHistory(transaction, changeData);
+                // 设置变更立项修订状态
+                await transaction.update(this.ctx.service.changeProject.tableName, {
+                    id: change.id,
+                    status: auditConst.status.revise,
+                    times: change.times + 1,
+                });
+
+                await transaction.commit();
+                result = true;
+            } catch (error) {
+                console.log(error);
+                await transaction.rollback();
+                result = false;
+            }
+            return result;
+        }
+
+        /**
+         * 撤销修订变更令
+         * @param { string } cid - 查询的清单
+         * @return {Promise<*>} - 可用的变更令列表
+         */
+        async cancelRevise(change) {
+            const time = new Date();
+            // 初始化事务
+            const transaction = await this.db.beginTransaction();
+            let result = false;
+            try {
+                const pid = this.ctx.session.sessionProject.id;
+                // 获取审核人列表
+                const sql = 'SELECT `tid`, `cpid`, `aid`, `order` FROM ?? WHERE `cpid` = ? and `times` = ? GROUP BY `aid` ORDER BY `id` ASC';
+                const sqlParam = [this.tableName, change.id, change.times - 1];
+                const auditors = await this.db.query(sql, sqlParam);
+                // 添加到消息推送表
+                const noticeContent = await this.getNoticeContent(pid, change.tid, change.id, this.ctx.session.sessionUser.accountId, '撤销修订');
+                const records = [];
+                auditors.forEach(auditor => {
+                    records.push({
+                        pid,
+                        type: pushType.changeProject,
+                        uid: auditor.aid,
+                        status: auditConst.status.cancelRevise,
+                        content: noticeContent,
+                    });
+                });
+                await transaction.insert('zh_notice', records);
+                const lastAudit = await this.getLastAudit(change.id, change.times - 1);
+                // 新增一个撤销修订状态到审批流程中
+                const revise_audit = {
+                    tid: change.tid,
+                    cpid: change.id,
+                    aid: this.ctx.session.sessionUser.accountId,
+                    times: change.times - 1,
+                    order: lastAudit.order + 1,
+                    status: auditConst.status.cancelRevise,
+                    begin_time: time,
+                    end_time: time,
+                    opinion: '',
+                };
+                await transaction.insert(this.ctx.service.changeProjectAudit.tableName, revise_audit);
+                await transaction.delete(this.ctx.service.changeProjectAudit.tableName, { cpid: change.id, times: change.times });
+                await this.ctx.service.changeProjectHistory.returnHistory(transaction, change.id);
+                await transaction.delete(this.ctx.service.changeProjectHistory.tableName, { cpid: change.id });
+                await transaction.commit();
+                result = true;
+            } catch (error) {
+                console.log(error);
+                await transaction.rollback();
+                result = false;
+            }
+            return result;
+        }
     }
 
     return ChangeProjectAudit;

+ 48 - 0
app/service/change_project_history.js

@@ -0,0 +1,48 @@
+'use strict';
+
+/**
+ * 变更新增部位插入记录表
+ *
+ * @author Mai
+ * @date
+ * @version
+ */
+
+module.exports = app => {
+
+    class ChangeProjectHistory extends app.BaseService {
+        /**
+         * 构造函数
+         *
+         * @param {Object} ctx - egg全局变量
+         * @return {void}
+         */
+        constructor(ctx) {
+            super(ctx);
+            this.tableName = 'change_project_history';
+        }
+
+        async saveHistory(transaction, data) {
+            await transaction.insert(this.tableName, {
+                tid: data.tid,
+                cpid: data.id,
+                info_json: JSON.stringify(data),
+            });
+        }
+
+        async returnHistory(transaction, cpid) {
+            const data = await transaction.get(this.tableName, { cpid });
+            if (!data) throw '撤销前数据不存在,无法撤销';
+            const change_update = {};
+            const oldInfo = JSON.parse(data.info_json);
+            for (const key in oldInfo) {
+                if (key !== 'in_time') {
+                    change_update[key] = oldInfo[key];
+                }
+            }
+            await transaction.update(this.ctx.service.changeProject.tableName, change_update);
+        }
+    }
+
+    return ChangeProjectHistory;
+};

+ 5 - 6
app/view/change/apply.ejs

@@ -82,7 +82,7 @@
                             <td><% if (c.notice_code) { %><a href="/tender/<%- tender.id %>/change/apply/<%- c.id %>/information/notice"><%- c.notice_code %></a><% } %></td>
                             <td><%- c.account_name %></td>
                             <td>
-                                <% if ((c.status === auditConst.status.uncheck || c.status === auditConst.status.checkNo) && c.uid === ctx.session.sessionUser.accountId) { %>
+                                <% if ((c.status === auditConst.status.uncheck || c.status === auditConst.status.checkNo || c.status === auditConst.status.revise) && c.uid === ctx.session.sessionUser.accountId) { %>
                                     <a href="/tender/<%- tender.id %>/change/apply/<%- c.id %>/information" class="btn <%- auditConst.statusButtonClass[c.status] %> btn-sm"><%- auditConst.statusButton[c.status] %></a>
                                 <% } else if (c.status === auditConst.status.checking && c.curAuditor && c.curAuditor.aid === ctx.session.sessionUser.accountId) { %>
                                     <a href="/tender/<%- tender.id %>/change/apply/<%- c.id %>/information" class="btn <%- auditConst.statusButtonClass[c.status] %> btn-sm"><%- auditConst.statusButton[c.status] %></a>
@@ -90,13 +90,12 @@
                                     <span class="<%- auditConst.auditProgressClass[c.status] %>"><%- auditConst.auditProgress[c.status] %></span>
                                 <% } %>
                             </td>
-                            <% if (c.status === auditConst.status.uncheck) { %>
                             <td>
-                                待上报
+                                <% if (c.status !== auditConst.status.uncheck) { %>
+                                    <%- c.curAuditor ? c.curAuditor.name : '' %>-<%- c.curAuditor ? c.curAuditor.role : '' %>
+                                <% } %>
+                                <span class="<%- auditConst.statusClass[c.status] %>"><%- auditConst.statusString[c.status] %></span>
                             </td>
-                            <% } else { %>
-                            <td><%- c.curAuditor.name %>-<%- c.curAuditor.role %> <span class="<%- auditConst.statusClass[c.status] %>"><%- auditConst.statusString[c.status] %></span></td>
-                            <% } %>
                             <td>
                                 <% if (c.uid === uid && (c.status === auditConst.status.uncheck || c.status === auditConst.status.checkNo)) { %><a href="#del-bg" caid="<%= c.id %>" data-toggle="modal" data-target="#del-bg" class="btn btn-outline-danger btn-sm delete-caid-modal">删除</a><% } %>
                             </td>

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

@@ -17,6 +17,9 @@
                 <% } %>
             </div>
             <div class="ml-auto" id="sp-btn">
+                <% if (ctx.change.cancancel) { %>
+                    <a href="javascript: void(0);" data-toggle="modal" data-target="#sp-down-cancel" class="btn btn-danger btn-sm mr-2">撤回</a>
+                <% } %>
                 <% if (ctx.change.status === auditConst.status.uncheck) { %>
                     <% if (ctx.session.sessionUser.accountId === ctx.change.uid) { %>
                         <a id="sub-sp-btn" href="javascript: void(0);" data-toggle="modal" data-target="#sub-sp" class="btn btn-primary btn-sm">上报审批</a>
@@ -31,9 +34,15 @@
                         <a href="#sp-list" data-toggle="modal" data-target="#sp-list" class="btn btn-outline-secondary btn-sm">审批中</a>
                     <% } %>
                 <% } else if (ctx.change.status === auditConst.status.checked) { %>
+                    <% if (ctx.session.sessionUser.accountId === ctx.change.uid) { %>
+                        <a href="javascript: void(0);" data-toggle="modal" data-target="#sp-down-revise" class="btn btn-warning btn-sm mr-2">修订变更</a>
+                    <% } %>
                     <a href="#sp-list" data-toggle="modal" data-target="#sp-list" class="btn btn-outline-success btn-sm">审批完成</a>
-                <% } else if (ctx.change.status === auditConst.status.checkNo) { %>
-                    <a href="#sp-list" data-type="hide" data-toggle="modal" data-target="#sp-list" class="btn btn-outline-warning btn-sm text-muted sp-list-btn">审批退回</a>
+                <% } else if (ctx.change.status === auditConst.status.checkNo || ctx.change.status === auditConst.status.revise) { %>
+                    <% if (ctx.change.status === auditConst.status.revise && (ctx.session.sessionUser.accountId === ctx.change.uid || ctx.session.sessionUser.is_admin)) { %>
+                        <a href="#sub-revoke" data-toggle="modal" data-target="#sub-revoke" class="btn btn-warning btn-sm mr-2">撤销修订</a>
+                    <% } %>
+                    <a href="#sp-list" data-type="hide" data-toggle="modal" data-target="#sp-list" class="btn btn-outline-warning btn-sm text-muted sp-list-btn">审批<% if (ctx.change.status === auditConst.status.checkNo) { %>退回<% } else { %>修订<% } %></a>
                     <% if (ctx.session.sessionUser.accountId === ctx.change.uid) { %>
                         <a href="#sp-list" data-type="show" data-toggle="modal" data-target="#sp-list"  class="btn btn-primary btn-sm sp-list-btn">重新上报</a>
                     <% } %>

+ 248 - 72
app/view/change/apply_information_modal.ejs

@@ -30,7 +30,7 @@
         </div>
     </div>
 </div>
-<% if ((ctx.change.status === auditConst.status.uncheck || ctx.change.status === auditConst.status.checkNo) && (ctx.session.sessionUser.accountId === ctx.change.uid || ctx.tender.isTourist)) { %>
+<% if ((ctx.change.status === auditConst.status.uncheck || ctx.change.status === auditConst.status.checkNo || ctx.change.status === auditConst.status.revise) && (ctx.session.sessionUser.accountId === ctx.change.uid || ctx.tender.isTourist)) { %>
     <!--上报审批-->
     <div class="modal fade" id="sub-sp" data-backdrop="static">
         <div class="modal-dialog" role="document">
@@ -146,6 +146,31 @@
             </div>
         </div>
     </div>
+    <% if (ctx.change.status === auditConst.status.revise && (ctx.session.sessionUser.accountId === ctx.change.uid || ctx.session.sessionUser.is_admin)) { %>
+        <!--撤销修订-->
+        <div class="modal fade" id="sub-revoke"  role="dialog" aria-labelledby="myModalLabel">
+            <div class="modal-dialog" role="document">
+                <form id="reviseForm" class="modal-content" method="post" action="/tender/<%- tender.id %>/change/apply/cancel/revise" onsubmit="return false;">
+                    <div class="modal-header">
+                        <h5 class="modal-title" id="myModalLabel">撤销修订</h5>
+                        <button type="button" class="close" data-dismiss="modal" aria-label="Close">
+                            <span aria-hidden="true">&times;</span>
+                        </button>
+                    </div>
+                    <div class="modal-body">
+                        <h5>撤销修订,所有修改的数据将全部会被还原。</h5>
+                        <h5>确认撤销修订?</h5>
+                    </div>
+                    <div class="modal-footer">
+                        <input type="hidden" name="caid" value="<%= ctx.change.id %>">
+                        <input type="hidden" name="_csrf_j" value="<%= ctx.csrf %>" />
+                        <button type="button" class="btn btn-secondary btn-sm" data-dismiss="modal">关闭</button>
+                        <button type="button" id="cancel-revise-btn" class="btn btn-primary btn-sm">确认</button>
+                    </div>
+                </form>
+            </div>
+        </div>
+    <% } %>
 <% } %>
 
 <!--审批流程/结果-->
@@ -153,12 +178,12 @@
     <div class="modal-dialog modal-lg" role="document">
         <div class="modal-content">
             <div class="modal-header">
-                <h5 class="modal-title"><%- ctx.change.status !== auditConst.status.checkNo ? '审批流程' : '重新上报' %></h5>
+                <h5 class="modal-title"><%- ctx.change.status === auditConst.status.checkNo || ctx.change.status === auditConst.status.revise ? '重新上报' : '审批流程' %></h5>
             </div>
             <div class="modal-body">
                 <div class="row">
                     <div class="col-4">
-                        <% if(ctx.change.status === auditConst.status.checkNo) { %>
+                        <% if(ctx.change.status === auditConst.status.checkNo || ctx.change.status === auditConst.status.revise) { %>
                             <a class="sp-list-item" href="#sub-sp" data-toggle="modal" data-target="#sub-sp" id="hideSp">修改审批流程</a>
                         <% } %>
                         <div class="card mt-3">
@@ -229,11 +254,11 @@
                                                 <% if(index < auditors.length - 1) { %>
                                                     <div class="timeline-item-tail"></div>
                                                 <% } %>
-                                                <% if(auditor.status === auditConst.status.checked) { %>
+                                                <% if(auditor.status === auditConst.status.checked || auditor.status === auditConst.status.cancelRevise) { %>
                                                     <div class="timeline-item-icon bg-success text-light">
                                                         <i class="fa fa-check"></i>
                                                     </div>
-                                                <% } else if(auditor.status === auditConst.status.checkNo) {%>
+                                                <% } else if(auditor.status === auditConst.status.checkNo || auditor.status === auditConst.status.revise || auditor.status === auditConst.status.checkCancel) {%>
                                                     <div class="timeline-item-icon bg-warning text-light">
                                                         <i class="fa fa-level-up"></i>
                                                     </div>
@@ -250,7 +275,7 @@
                                                         <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>
+                                                                            class="pull-right <%- auditConst.auditStringClass[auditor.status] %>"><%- auditConst.auditString[auditor.status] %></span>
                                                                 </p>
                                                                 <p class="text-muted mb-0"><%- auditor.role %></p>
                                                             </div>
@@ -273,11 +298,11 @@
                                                 <% if(index < auditors.length - 1) { %>
                                                     <div class="timeline-item-tail"></div>
                                                 <% } %>
-                                                <% if(auditor.status === auditConst.status.checked) { %>
+                                                <% if(auditor.status === auditConst.status.checked || auditor.status === auditConst.status.cancelRevise) { %>
                                                     <div class="timeline-item-icon bg-success text-light">
                                                         <i class="fa fa-check"></i>
                                                     </div>
-                                                <% } else if(auditor.status === auditConst.status.checkNo) {%>
+                                                <% } else if(auditor.status === auditConst.status.checkNo || auditor.status === auditConst.status.revise || auditor.status === auditConst.status.checkCancel) {%>
                                                     <div class="timeline-item-icon bg-warning text-light">
                                                         <i class="fa fa-level-up"></i>
                                                     </div>
@@ -296,7 +321,7 @@
                                                                 <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] : ''%>
+                                                                            <%- auditConst.auditStringClass[auditor.status] %>"><%- auditor.status !== auditConst.status.uncheck ? auditConst.auditString[auditor.status] : ''%>
                                                                         <%- auditor.status === auditConst.status.checkNo ? ctx.change.user.name : '' %>
                                                         </span>
                                                                 </p>
@@ -411,11 +436,11 @@
                                                         <% if(index < auditors.length - 1) { %>
                                                             <div class="timeline-item-tail"></div>
                                                         <% } %>
-                                                        <% if(auditor.status === auditConst.status.checked) { %>
+                                                        <% if(auditor.status === auditConst.status.checked || auditor.status === auditConst.status.cancelRevise) { %>
                                                             <div class="timeline-item-icon bg-success text-light">
                                                                 <i class="fa fa-check"></i>
                                                             </div>
-                                                        <% } else if(auditor.status === auditConst.status.checkNo) {%>
+                                                        <% } else if(auditor.status === auditConst.status.checkNo || auditor.status === auditConst.status.revise || auditor.status === auditConst.status.checkCancel) {%>
                                                             <div class="timeline-item-icon bg-warning text-light">
                                                                 <i class="fa fa-level-up"></i>
                                                             </div>
@@ -432,30 +457,32 @@
                                                                 <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>
+                                                                                    class="pull-right <%- auditConst.auditStringClass[auditor.status] %>"><%- auditConst.auditString[auditor.status] %></span>
                                                                         </p>
                                                                         <p class="text-muted mb-0"><%- auditor.role %></p>
                                                                     </div>
                                                                 </div>
                                                                 <!--审批意见-->
                                                                 <% if(auditor.status !== auditConst.status.uncheck) { %>
+                                                                    <% if (ctx.change.times === idx + 1 && auditor.status === auditConst.status.checking) { %>
                                                                     <div class="card-body p-3 border-top">
-                                                                        <% if (ctx.change.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 (auditors[auditors.length - 1].aid === auditor.aid) { %>
-                                                                                <!--终审填写批复编号-->
-                                                                                <div class="form-group mt-3">
-                                                                                    <label>变更通知书<b class="text-danger">*&nbsp;</b></label>
-                                                                                    <input class="form-control form-control-sm" value="BGTZ-<%- change.code %>" name="notice_code" type="text">
-                                                                                    <input type="hidden" name="notice_uid" value="<%- auditor.aid %>">
-                                                                                </div>
-                                                                            <% } %>
-                                                                        <% } else { %>
-                                                                            <p style="margin: 0;"><%- auditor.opinion %></p>
+                                                                        <label>审批意见<b class="text-danger">*</b></label>
+                                                                        <textarea class="form-control form-control-sm"
+                                                                                  name="opinion">同意</textarea>
+                                                                        <% if (auditors[auditors.length - 1].aid === auditor.aid) { %>
+                                                                            <!--终审填写批复编号-->
+                                                                            <div class="form-group mt-3">
+                                                                                <label>变更通知书<b class="text-danger">*&nbsp;</b></label>
+                                                                                <input class="form-control form-control-sm" value="BGTZ-<%- change.code %>" name="notice_code" type="text">
+                                                                                <input type="hidden" name="notice_uid" value="<%- auditor.aid %>">
+                                                                            </div>
                                                                         <% } %>
                                                                     </div>
+                                                                    <% } else if (auditor.opinion) { %>
+                                                                        <div class="card-body p-3 border-top">
+                                                                            <p style="margin: 0;"><%- auditor.opinion %></p>
+                                                                        </div>
+                                                                    <% } %>
                                                                 <% } %>
                                                             </div>
                                                         </div>
@@ -468,11 +495,11 @@
                                                         <% if(index < auditors.length - 1) { %>
                                                             <div class="timeline-item-tail"></div>
                                                         <% } %>
-                                                        <% if(auditor.status === auditConst.status.checked) { %>
+                                                        <% if(auditor.status === auditConst.status.checked || auditor.status === auditConst.status.cancelRevise) { %>
                                                             <div class="timeline-item-icon bg-success text-light">
                                                                 <i class="fa fa-check"></i>
                                                             </div>
-                                                        <% } else if(auditor.status === auditConst.status.checkNo) {%>
+                                                        <% } else if(auditor.status === auditConst.status.checkNo || auditor.status === auditConst.status.revise || auditor.status === auditConst.status.checkCancel) {%>
                                                             <div class="timeline-item-icon bg-warning text-light">
                                                                 <i class="fa fa-level-up"></i>
                                                             </div>
@@ -491,7 +518,7 @@
                                                                         <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] : ''%>
+                                                                                    <%- auditConst.auditStringClass[auditor.status] %>"><%- auditor.status !== auditConst.status.uncheck ? auditConst.auditString[auditor.status] : ''%>
                                                                                 <%- auditor.status === auditConst.status.checkNo ? ctx.change.user.name : '' %>
                                                                 </span>
                                                                         </p>
@@ -500,23 +527,25 @@
                                                                 </div>
                                                                 <!--审批意见-->
                                                                 <% if(auditor.status !== auditConst.status.uncheck) { %>
+                                                                    <% if (ctx.change.times === idx + 1 && auditor.status === auditConst.status.checking) { %>
                                                                     <div class="card-body p-3 border-top">
-                                                                        <% if (ctx.change.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 (auditors[auditors.length - 1].aid === auditor.aid) { %>
-                                                                            <!--终审填写批复编号-->
-                                                                            <div class="form-group mt-3">
-                                                                                <label>变更通知书<b class="text-danger">*&nbsp;</b></label>
-                                                                                <input class="form-control form-control-sm" value="BGTZ-<%- change.code %>" name="notice_code" type="text">
-                                                                                <input type="hidden" name="notice_uid" value="<%- auditor.aid %>">
-                                                                            </div>
-                                                                            <% } %>
-                                                                        <% } else { %>
-                                                                            <p style="margin: 0;"><%- auditor.opinion %></p>
+                                                                        <label>审批意见<b class="text-danger">*</b></label>
+                                                                        <textarea class="form-control form-control-sm"
+                                                                                  name="opinion">同意</textarea>
+                                                                        <% if (auditors[auditors.length - 1].aid === auditor.aid) { %>
+                                                                        <!--终审填写批复编号-->
+                                                                        <div class="form-group mt-3">
+                                                                            <label>变更通知书<b class="text-danger">*&nbsp;</b></label>
+                                                                            <input class="form-control form-control-sm" value="BGTZ-<%- change.code %>" name="notice_code" type="text">
+                                                                            <input type="hidden" name="notice_uid" value="<%- auditor.aid %>">
+                                                                        </div>
                                                                         <% } %>
                                                                     </div>
+                                                                    <% } else if (auditor.opinion) { %>
+                                                                        <div class="card-body p-3 border-top">
+                                                                            <p style="margin: 0;"><%- auditor.opinion %></p>
+                                                                        </div>
+                                                                    <% } %>
                                                                 <% } %>
                                                             </div>
                                                         </div>
@@ -617,11 +646,11 @@
                                                         <% if(index < auditors.length - 1) { %>
                                                             <div class="timeline-item-tail"></div>
                                                         <% } %>
-                                                        <% if(auditor.status === auditConst.status.checked) { %>
+                                                        <% if(auditor.status === auditConst.status.checked || auditor.status === auditConst.status.cancelRevise) { %>
                                                             <div class="timeline-item-icon bg-success text-light">
                                                                 <i class="fa fa-check"></i>
                                                             </div>
-                                                        <% } else if(auditor.status === auditConst.status.checkNo) {%>
+                                                        <% } else if(auditor.status === auditConst.status.checkNo || auditor.status === auditConst.status.revise || auditor.status === auditConst.status.checkCancel) {%>
                                                             <div class="timeline-item-icon bg-warning text-light">
                                                                 <i class="fa fa-level-up"></i>
                                                             </div>
@@ -638,7 +667,7 @@
                                                                 <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>
+                                                                                    class="pull-right <%- auditConst.auditStringClass[auditor.status] %>"><%- auditConst.auditString[auditor.status] %></span>
                                                                         </p>
                                                                         <p class="text-muted mb-0"><%- auditor.role %></p>
                                                                     </div>
@@ -646,8 +675,8 @@
 
                                                                 <!--审批意见-->
                                                                 <% if(auditor.times === ctx.change.times && auditor.status !== auditConst.status.uncheck) { %>
+                                                                    <% if (ctx.change.times === idx + 1 && auditor.status === auditConst.status.checking) { %>
                                                                     <div class="card-body p-3 border-top">
-                                                                        <% if (ctx.change.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>
@@ -662,10 +691,12 @@
                                                                                     </div>
                                                                                 </div>
                                                                             <% } %>
-                                                                        <% } else if(auditor.status === auditConst.status.checked){ %>
-                                                                            <p style="margin: 0;"><%- auditor.opinion %></p>
-                                                                        <% } %>
                                                                     </div>
+                                                                    <% } else if(auditor.opinion) { %>
+                                                                        <div class="card-body p-3 border-top">
+                                                                            <p style="margin: 0;"><%- auditor.opinion %></p>
+                                                                        </div>
+                                                                    <% } %>
                                                                 <% } %>
                                                             </div>
                                                         </div>
@@ -678,11 +709,11 @@
                                                         <% if(index < auditors.length - 1) { %>
                                                             <div class="timeline-item-tail"></div>
                                                         <% } %>
-                                                        <% if(auditor.status === auditConst.status.checked) { %>
+                                                        <% if(auditor.status === auditConst.status.checked || auditor.status === auditConst.status.cancelRevise) { %>
                                                             <div class="timeline-item-icon bg-success text-light">
                                                                 <i class="fa fa-check"></i>
                                                             </div>
-                                                        <% } else if(auditor.status === auditConst.status.checkNo) {%>
+                                                        <% } else if(auditor.status === auditConst.status.checkNo || auditor.status === auditConst.status.revise || auditor.status === auditConst.status.checkCancel) {%>
                                                             <div class="timeline-item-icon bg-warning text-light">
                                                                 <i class="fa fa-level-up"></i>
                                                             </div>
@@ -701,7 +732,7 @@
                                                                         <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] : ''%>
+                                                                                    <%- auditConst.auditStringClass[auditor.status] %>"><%- auditor.status !== auditConst.status.uncheck ? auditConst.auditString[auditor.status] : ''%>
                                                                                 <%- auditor.status === auditConst.status.checkNo ? ctx.change.user.name : '' %>
                                                                 </span>
                                                                         </p>
@@ -710,28 +741,28 @@
                                                                 </div>
                                                                 <!--审批意见-->
                                                                 <% if(auditor.times === ctx.change.times && auditor.status !== auditConst.status.uncheck) { %>
+                                                                    <% if (ctx.change.times === idx + 1 && auditor.status === auditConst.status.checking) { %>
                                                                     <div class="card-body p-3 border-top">
-                                                                        <% if (ctx.change.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.change.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 %>" checked>
-                                                                                        <label class="form-check-label" for="inlineRadio1">退回原报
-                                                                                            <%- ctx.change.user.name %></label>
-                                                                                    </div>
+                                                                        <label>审批意见<b class="text-danger">*</b></label>
+                                                                        <textarea class="form-control form-control-sm"
+                                                                                  name="opinion">不同意</textarea>
+                                                                        <% if (ctx.change.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 %>" checked>
+                                                                                    <label class="form-check-label" for="inlineRadio1">退回原报
+                                                                                        <%- ctx.change.user.name %></label>
                                                                                 </div>
-                                                                            <% } %>
-                                                                        <% } else { %>
-                                                                            <p style="margin: 0;"><%- auditor.opinion %></p>
+                                                                            </div>
                                                                         <% } %>
-
                                                                     </div>
-
+                                                                    <% } else { %>
+                                                                        <div class="card-body p-3 border-top">
+                                                                            <p style="margin: 0;"><%- auditor.opinion %></p>
+                                                                        </div>
+                                                                    <% } %>
                                                                 <% } %>
                                                             </div>
                                                         </div>
@@ -755,13 +786,87 @@
         </div>
     <% } %>
 <% } %>
+<% if (ctx.change.status === auditConst.status.checked && ctx.session.sessionUser.accountId === ctx.change.uid) { %>
+    <% if (!authMobile && ctx.session.sessionUser.loginStatus === 0) { %>
+        <!--原报修订变更-->
+        <div class="modal fade" id="sp-down-revise" data-backdrop="static">
+            <div class="modal-dialog" role="document">
+                <div class="modal-content">
+                    <div class="modal-header">
+                        <h5 class="modal-title">修订变更</h5>
+                    </div>
+                    <div class="modal-body">
+                        <h5>修订变更需要您的手机短信验证</h5>
+                        <h5>您目前还没设置认证手机,请先设置。</h5>
+                    </div>
+                    <div class="modal-footer">
+                        <button type="button" class="btn btn-sm btn-secondary" data-dismiss="modal">取消</button>
+                        <a href="/profile/sms" class="btn btn-sm btn-primary">去设置</a>
+                    </div>
+                </div>
+            </div>
+        </div>
+    <% } else { %>
+        <!--修订变更-->
+        <div class="modal fade" id="sp-down-revise" data-backdrop="static">
+            <div class="modal-dialog" role="document">
+                <form id="reviseForm" class="modal-content" method="post" action="/tender/<%- tender.id %>/change/apply/check/revise" onsubmit="return false;">
+                    <div class="modal-header">
+                        <h5 class="modal-title">修订变更</h5>
+                    </div>
+                    <div class="modal-body">
+                        <h5>确认需要修订变更「<%= change.code %>」?</h5>
+                        <% if (ctx.session.sessionUser.loginStatus === 0) { %>
+                            <div class="form-group">
+                                <label>修订需要验证码确认,验证码将发送至尾号<%- authMobile.slice(-4) %>的手机</label>
+                                <div class="input-group input-group-sm mb-3">
+                                    <input class="form-control" type="text" readonly="readonly" name="code" placeholder="输入短信中的6位验证码" />
+                                    <div class="input-group-append">
+                                        <button class="btn btn-outline-secondary" type="button" id="get-code">获取验证码</button>
+                                    </div>
+                                </div>
+                            </div>
+                        <% } %>
+                    </div>
+                    <div class="modal-footer">
+                        <input type="hidden" name="cpid" value="<%= ctx.change.id %>">
+                        <input type="hidden" name="_csrf_j" value="<%= ctx.csrf %>" />
+                        <button type="button" class="btn btn-secondary btn-sm" data-dismiss="modal">关闭</button>
+                        <button type="button" id="re-shenpi-btn2" class="btn btn-warning btn-sm" <% if (ctx.session.sessionUser.loginStatus === 0) { %>disabled<% } %>>确定修订</button>
+                    </div>
+                </form>
+            </div>
+        </div>
+    <% } %>
+<% } %>
+<% if (ctx.change.cancancel) { %>
+    <div class="modal fade" id="sp-down-cancel" data-backdrop="static">
+        <div class="modal-dialog" role="document">
+            <div class="modal-content">
+                <div class="modal-header">
+                    <h5 class="modal-title">撤回</h5>
+                </div>
+                <div class="modal-body">
+                    <h5>撤回后将回退到你的操作状态,确定撤回?</h5>
+                </div>
+                <div class="modal-footer">
+                    <button type="button" class="btn btn-secondary btn-sm" data-dismiss="modal">取消</button>
+                    <button type="submit" class="btn btn-danger btn-sm" id="cancel-shenpi-btn">确定撤回</button>
+                </div>
+            </div>
+        </div>
+    </div>
+<% } %>
 <% if (ctx.session.sessionUser.accountId === ctx.change.uid && (ctx.change.status === auditConst.status.uncheck || ctx.change.status === auditConst.status.checkNo)) { %>
     <script>
         const accountGroup = JSON.parse(unescape('<%- escape(JSON.stringify(accountGroup)) %>'));
         const accountList = JSON.parse(unescape('<%- escape(JSON.stringify(accountList)) %>'));
     </script>
 <% } %>
-<script>const cur_uid = parseInt('<%- ctx.session.sessionUser.accountId %>');</script>
+<script>
+    const cur_uid = parseInt('<%- ctx.session.sessionUser.accountId %>');
+    const csrf = '<%= ctx.csrf %>';
+</script>
 <script>
     $('.sp-location-list').on('shown.bs.modal', function () {
         const scrollBox = $(this).find('div[class="col-8 modal-height-500"]');
@@ -807,4 +912,75 @@
             $('.modal-title').text('重新上报')
         }
     });
+
+    $('#re-shenpi-btn2').click(function () {
+        const data = {
+            caid: parseInt('<%- ctx.change.id %>'),
+        };
+        <% if (ctx.session.sessionUser.loginStatus === 0) { %>
+        const code = $("#reviseForm input[name='code']").val();
+        if ($(this).hasClass('disabled')) {
+            return false;
+        }
+        if (code.length < 6) {
+            // alert('请填写正确的验证码');
+            toast('请填写正确的验证码', 'error');
+            return false;
+        }
+        data.code = code;
+        <% } %>
+        $.ajax({
+            url: '/tender/<%- tender.id %>/change/apply/check/revise?_csrf_j=' + csrf,
+            type: 'post',
+            data: data,
+            dataTye: 'json',
+            success: function(response) {
+                if (response.err === 0) {
+                    window.location.href = response.url;
+                } else {
+                    toastr.error(response.msg);
+                }
+            }
+        });
+    })
+
+    $('#cancel-revise-btn').click(function () {
+        const data = {
+            caid: parseInt('<%- ctx.change.id %>'),
+        };
+        $.ajax({
+            url: '/tender/<%- tender.id %>/change/apply/cancel/revise?_csrf_j=' + csrf,
+            type: 'post',
+            data: data,
+            dataTye: 'json',
+            success: function(response) {
+                if (response.err === 0) {
+                    window.location.href = response.url;
+                } else {
+                    toastr.error(response.msg);
+                }
+            }
+        });
+    })
+
+    <% if (ctx.change && ctx.change.cancancel) { %>
+    $("#cancel-shenpi-btn").click(function () {
+        const data = {
+            caid: parseInt('<%- ctx.change.id %>'),
+        };
+        $.ajax({
+            url: '/tender/<%- tender.id %>/change/apply/cancel/audit?_csrf_j=' + csrf,
+            type: 'post',
+            data: data,
+            dataTye: 'json',
+            success: function (response) {
+                if (response.err === 0) {
+                    window.location.href = response.url;
+                } else {
+                    toastr.error(response.msg);
+                }
+            }
+        });
+    });
+    <% } %>
 </script>

+ 5 - 6
app/view/change/plan.ejs

@@ -81,7 +81,7 @@
                             <td style="text-align: right"><%- c.total_price %></td>
                             <td><%- c.apply_code %></td>
                             <td>
-                                <% if ((c.status === auditConst.status.uncheck || c.status === auditConst.status.checkNo) && c.uid === ctx.session.sessionUser.accountId) { %>
+                                <% if ((c.status === auditConst.status.uncheck || c.status === auditConst.status.checkNo || c.status === auditConst.status.revise) && c.uid === ctx.session.sessionUser.accountId) { %>
                                     <a href="/tender/<%- tender.id %>/change/plan/<%- c.id %>/information" class="btn <%- auditConst.statusButtonClass[c.status] %> btn-sm"><%- auditConst.statusButton[c.status] %></a>
                                 <% } else if (c.status === auditConst.status.checking && c.curAuditor && c.curAuditor.aid === ctx.session.sessionUser.accountId) { %>
                                     <a href="/tender/<%- tender.id %>/change/plan/<%- c.id %>/information" class="btn <%- auditConst.statusButtonClass[c.status] %> btn-sm"><%- auditConst.statusButton[c.status] %></a>
@@ -89,13 +89,12 @@
                                     <span class="<%- auditConst.auditProgressClass[c.status] %>"><%- auditConst.auditProgress[c.status] %></span>
                                 <% } %>
                             </td>
-                            <% if (c.status === auditConst.status.uncheck) { %>
                             <td>
-                                待上报
+                                <% if (c.status !== auditConst.status.uncheck) { %>
+                                    <%- c.curAuditor.name %>-<%- c.curAuditor.role %>
+                                <% } %>
+                                <span class="<%- auditConst.statusClass[c.status] %>"><%- auditConst.statusString[c.status] %></span>
                             </td>
-                            <% } else { %>
-                            <td><%- c.curAuditor.name %>-<%- c.curAuditor.role %> <span class="<%- auditConst.statusClass[c.status] %>"><%- auditConst.statusString[c.status] %></span></td>
-                            <% } %>
                             <td>
                                 <% if (c.uid === uid && (c.status === auditConst.status.uncheck || c.status === auditConst.status.checkNo)) { %><a href="#del-bg" cpid="<%= c.id %>" data-toggle="modal" data-target="#del-bg" class="btn btn-outline-danger btn-sm delete-cpid-modal">删除</a><% } %>
                             </td>

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

@@ -17,6 +17,9 @@
                 <% } %>
             </div>
             <div class="ml-auto" id="sp-btn">
+                <% if (ctx.change.cancancel) { %>
+                    <a href="javascript: void(0);" data-toggle="modal" data-target="#sp-down-cancel" class="btn btn-danger btn-sm mr-2">撤回</a>
+                <% } %>
                 <% if (ctx.change.status === auditConst.status.uncheck) { %>
                     <% if (ctx.session.sessionUser.accountId === ctx.change.uid) { %>
                         <a id="sub-sp-btn" href="javascript: void(0);" data-toggle="modal" data-target="#sub-sp" class="btn btn-primary btn-sm">上报审批</a>
@@ -31,9 +34,15 @@
                         <a href="#sp-list" data-toggle="modal" data-target="#sp-list" class="btn btn-outline-secondary btn-sm">审批中</a>
                     <% } %>
                 <% } else if (ctx.change.status === auditConst.status.checked) { %>
+                    <% if (ctx.session.sessionUser.accountId === ctx.change.uid) { %>
+                        <a href="javascript: void(0);" data-toggle="modal" data-target="#sp-down-revise" class="btn btn-warning btn-sm mr-2">修订变更</a>
+                    <% } %>
                     <a href="#sp-list" data-toggle="modal" data-target="#sp-list" class="btn btn-outline-success btn-sm">审批完成</a>
-                <% } else if (ctx.change.status === auditConst.status.checkNo) { %>
-                    <a href="#sp-list" data-type="hide" data-toggle="modal" data-target="#sp-list" class="btn btn-outline-warning btn-sm text-muted sp-list-btn">审批退回</a>
+                <% } else if (ctx.change.status === auditConst.status.checkNo || ctx.change.status === auditConst.status.revise) { %>
+                    <% if (ctx.change.status === auditConst.status.revise && (ctx.session.sessionUser.accountId === ctx.change.uid || ctx.session.sessionUser.is_admin)) { %>
+                        <a href="#sub-revoke" data-toggle="modal" data-target="#sub-revoke" class="btn btn-warning btn-sm mr-2">撤销修订</a>
+                    <% } %>
+                    <a href="#sp-list" data-type="hide" data-toggle="modal" data-target="#sp-list" class="btn btn-outline-warning btn-sm text-muted sp-list-btn">审批<% if (ctx.change.status === auditConst.status.checkNo) { %>退回<% } else { %>修订<% } %></a>
                     <% if (ctx.session.sessionUser.accountId === ctx.change.uid) { %>
                         <a href="#sp-list" data-type="show" data-toggle="modal" data-target="#sp-list"  class="btn btn-primary btn-sm sp-list-btn">重新上报</a>
                     <% } %>

+ 215 - 39
app/view/change/plan_information_modal.ejs

@@ -22,7 +22,7 @@
         </div>
     </div>
 </div>
-<% if ((ctx.change.status === auditConst.status.uncheck || ctx.change.status === auditConst.status.checkNo) && (ctx.session.sessionUser.accountId === ctx.change.uid || ctx.tender.isTourist)) { %>
+<% if ((ctx.change.status === auditConst.status.uncheck || ctx.change.status === auditConst.status.checkNo || ctx.change.status === auditConst.status.revise) && (ctx.session.sessionUser.accountId === ctx.change.uid || ctx.tender.isTourist)) { %>
     <!--上报审批-->
     <div class="modal fade" id="sub-sp" data-backdrop="static">
         <div class="modal-dialog" role="document">
@@ -138,6 +138,31 @@
             </div>
         </div>
     </div>
+    <% if (ctx.change.status === auditConst.status.revise && (ctx.session.sessionUser.accountId === ctx.change.uid || ctx.session.sessionUser.is_admin)) { %>
+        <!--撤销修订-->
+        <div class="modal fade" id="sub-revoke"  role="dialog" aria-labelledby="myModalLabel">
+            <div class="modal-dialog" role="document">
+                <form id="reviseForm" class="modal-content" method="post" action="/tender/<%- tender.id %>/change/plan/cancel/revise" onsubmit="return false;">
+                    <div class="modal-header">
+                        <h5 class="modal-title" id="myModalLabel">撤销修订</h5>
+                        <button type="button" class="close" data-dismiss="modal" aria-label="Close">
+                            <span aria-hidden="true">&times;</span>
+                        </button>
+                    </div>
+                    <div class="modal-body">
+                        <h5>撤销修订,所有修改的数据将全部会被还原。</h5>
+                        <h5>确认撤销修订?</h5>
+                    </div>
+                    <div class="modal-footer">
+                        <input type="hidden" name="cpid" value="<%= ctx.change.id %>">
+                        <input type="hidden" name="_csrf_j" value="<%= ctx.csrf %>" />
+                        <button type="button" class="btn btn-secondary btn-sm" data-dismiss="modal">关闭</button>
+                        <button type="button" id="cancel-revise-btn" class="btn btn-primary btn-sm">确认</button>
+                    </div>
+                </form>
+            </div>
+        </div>
+    <% } %>
 <% } %>
 
 <!--审批流程/结果-->
@@ -145,12 +170,12 @@
     <div class="modal-dialog modal-lg" role="document">
         <div class="modal-content">
             <div class="modal-header">
-                <h5 class="modal-title"><%- ctx.change.status !== auditConst.status.checkNo ? '审批流程' : '重新上报' %></h5>
+                <h5 class="modal-title"><%- ctx.change.status === auditConst.status.checkNo || ctx.change.status === auditConst.status.revise ? '重新上报' : '审批流程' %></h5>
             </div>
             <div class="modal-body">
                 <div class="row">
                     <div class="col-4">
-                        <% if(ctx.change.status === auditConst.status.checkNo) { %>
+                        <% if(ctx.change.status === auditConst.status.checkNo || ctx.change.status === auditConst.status.revise) { %>
                             <a class="sp-list-item" href="#sub-sp" data-toggle="modal" data-target="#sub-sp" id="hideSp">修改审批流程</a>
                         <% } %>
                         <div class="card mt-3">
@@ -221,11 +246,11 @@
                                                 <% if(index < auditors.length - 1) { %>
                                                     <div class="timeline-item-tail"></div>
                                                 <% } %>
-                                                <% if(auditor.status === auditConst.status.checked) { %>
+                                                <% if(auditor.status === auditConst.status.checked || auditor.status === auditConst.status.cancelRevise) { %>
                                                     <div class="timeline-item-icon bg-success text-light">
                                                         <i class="fa fa-check"></i>
                                                     </div>
-                                                <% } else if(auditor.status === auditConst.status.checkNo) {%>
+                                                <% } else if(auditor.status === auditConst.status.checkNo || auditor.status === auditConst.status.revise || auditor.status === auditConst.status.checkCancel) {%>
                                                     <div class="timeline-item-icon bg-warning text-light">
                                                         <i class="fa fa-level-up"></i>
                                                     </div>
@@ -242,7 +267,7 @@
                                                         <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>
+                                                                            class="pull-right <%- auditConst.auditStringClass[auditor.status] %>"><%- auditConst.auditString[auditor.status] %></span>
                                                                 </p>
                                                                 <p class="text-muted mb-0"><%- auditor.role %></p>
                                                             </div>
@@ -265,11 +290,11 @@
                                                 <% if(index < auditors.length - 1) { %>
                                                     <div class="timeline-item-tail"></div>
                                                 <% } %>
-                                                <% if(auditor.status === auditConst.status.checked) { %>
+                                                <% if(auditor.status === auditConst.status.checked || auditor.status === auditConst.status.cancelRevise) { %>
                                                     <div class="timeline-item-icon bg-success text-light">
                                                         <i class="fa fa-check"></i>
                                                     </div>
-                                                <% } else if(auditor.status === auditConst.status.checkNo) {%>
+                                                <% } else if(auditor.status === auditConst.status.checkNo || auditor.status === auditConst.status.revise || auditor.status === auditConst.status.checkCancel) {%>
                                                     <div class="timeline-item-icon bg-warning text-light">
                                                         <i class="fa fa-level-up"></i>
                                                     </div>
@@ -288,7 +313,7 @@
                                                                 <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] : ''%>
+                                                                            <%- auditConst.auditStringClass[auditor.status] %>"><%- auditor.status !== auditConst.status.uncheck ? auditConst.auditString[auditor.status] : ''%>
                                                                         <%- auditor.status === auditConst.status.checkNo ? ctx.change.user.name : '' %>
                                                         </span>
                                                                 </p>
@@ -316,7 +341,7 @@
             <form class="modal-footer" method="post" action="<%- preUrl %>/audit/start" onsubmit="return checkAuditorFrom()">
                 <input type="hidden" name="_csrf_j" value="<%= ctx.csrf %>">
                 <button type="button" class="btn btn-secondary btn-sm" data-dismiss="modal">关闭</button>
-                <% if(ctx.change.status === auditConst.status.checkNo && ctx.session.sessionUser.accountId === ctx.change.uid) { %>
+                <% if((ctx.change.status === auditConst.status.checkNo || ctx.change.status === auditConst.status.revise) && ctx.session.sessionUser.accountId === ctx.change.uid) { %>
                     <button class="btn btn-primary btn-sm sp-list-item" type="submit">确认上报</button>
                 <% } %>
             </form>
@@ -403,11 +428,11 @@
                                                         <% if(index < auditors.length - 1) { %>
                                                             <div class="timeline-item-tail"></div>
                                                         <% } %>
-                                                        <% if(auditor.status === auditConst.status.checked) { %>
+                                                        <% if(auditor.status === auditConst.status.checked || auditor.status === auditConst.status.cancelRevise) { %>
                                                             <div class="timeline-item-icon bg-success text-light">
                                                                 <i class="fa fa-check"></i>
                                                             </div>
-                                                        <% } else if(auditor.status === auditConst.status.checkNo) {%>
+                                                        <% } else if(auditor.status === auditConst.status.checkNo || auditor.status === auditConst.status.revise || auditor.status === auditConst.status.checkCancel) {%>
                                                             <div class="timeline-item-icon bg-warning text-light">
                                                                 <i class="fa fa-level-up"></i>
                                                             </div>
@@ -424,22 +449,24 @@
                                                                 <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>
+                                                                                    class="pull-right <%- auditConst.auditStringClass[auditor.status] %>"><%- auditConst.auditString[auditor.status] %></span>
                                                                         </p>
                                                                         <p class="text-muted mb-0"><%- auditor.role %></p>
                                                                     </div>
                                                                 </div>
                                                                 <!--审批意见-->
                                                                 <% if(auditor.status !== auditConst.status.uncheck) { %>
+                                                                    <% if (ctx.change.times === idx + 1 && auditor.status === auditConst.status.checking) { %>
                                                                     <div class="card-body p-3 border-top">
-                                                                        <% if (ctx.change.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>
+                                                                    <% } else if (auditor.opinion) { %>
+                                                                        <div class="card-body p-3 border-top">
+                                                                            <p style="margin: 0;"><%- auditor.opinion %></p>
+                                                                        </div>
+                                                                    <% } %>
                                                                 <% } %>
                                                             </div>
                                                         </div>
@@ -452,11 +479,11 @@
                                                         <% if(index < auditors.length - 1) { %>
                                                             <div class="timeline-item-tail"></div>
                                                         <% } %>
-                                                        <% if(auditor.status === auditConst.status.checked) { %>
+                                                        <% if(auditor.status === auditConst.status.checked || auditor.status === auditConst.status.cancelRevise) { %>
                                                             <div class="timeline-item-icon bg-success text-light">
                                                                 <i class="fa fa-check"></i>
                                                             </div>
-                                                        <% } else if(auditor.status === auditConst.status.checkNo) {%>
+                                                        <% } else if(auditor.status === auditConst.status.checkNo || auditor.status === auditConst.status.revise || auditor.status === auditConst.status.checkCancel) {%>
                                                             <div class="timeline-item-icon bg-warning text-light">
                                                                 <i class="fa fa-level-up"></i>
                                                             </div>
@@ -475,7 +502,7 @@
                                                                         <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] : ''%>
+                                                                                    <%- auditConst.auditStringClass[auditor.status] %>"><%- auditor.status !== auditConst.status.uncheck ? auditConst.auditString[auditor.status] : ''%>
                                                                                 <%- auditor.status === auditConst.status.checkNo ? ctx.change.user.name : '' %>
                                                                 </span>
                                                                         </p>
@@ -484,15 +511,17 @@
                                                                 </div>
                                                                 <!--审批意见-->
                                                                 <% if(auditor.status !== auditConst.status.uncheck) { %>
+                                                                    <% if (ctx.change.times === idx + 1 && auditor.status === auditConst.status.checking) { %>
                                                                     <div class="card-body p-3 border-top">
-                                                                        <% if (ctx.change.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>
+                                                                    <% } else if (auditor.opinion) { %>
+                                                                    <div class="card-body p-3 border-top">
+                                                                        <p style="margin: 0;"><%- auditor.opinion %></p>
+                                                                    </div>
+                                                                    <% } %>
                                                                 <% } %>
                                                             </div>
                                                         </div>
@@ -593,11 +622,11 @@
                                                         <% if(index < auditors.length - 1) { %>
                                                             <div class="timeline-item-tail"></div>
                                                         <% } %>
-                                                        <% if(auditor.status === auditConst.status.checked) { %>
+                                                        <% if(auditor.status === auditConst.status.checked || auditor.status === auditConst.status.cancelRevise) { %>
                                                             <div class="timeline-item-icon bg-success text-light">
                                                                 <i class="fa fa-check"></i>
                                                             </div>
-                                                        <% } else if(auditor.status === auditConst.status.checkNo) {%>
+                                                        <% } else if(auditor.status === auditConst.status.checkNo || auditor.status === auditConst.status.revise || auditor.status === auditConst.status.checkCancel) {%>
                                                             <div class="timeline-item-icon bg-warning text-light">
                                                                 <i class="fa fa-level-up"></i>
                                                             </div>
@@ -622,8 +651,8 @@
 
                                                                 <!--审批意见-->
                                                                 <% if(auditor.times === ctx.change.times && auditor.status !== auditConst.status.uncheck) { %>
+                                                                    <% if (ctx.change.times === idx + 1 && auditor.status === auditConst.status.checking) { %>
                                                                     <div class="card-body p-3 border-top">
-                                                                        <% if (ctx.change.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>
@@ -638,10 +667,12 @@
                                                                                     </div>
                                                                                 </div>
                                                                             <% } %>
-                                                                        <% } else if(auditor.status === auditConst.status.checked){ %>
-                                                                            <p style="margin: 0;"><%- auditor.opinion %></p>
-                                                                        <% } %>
                                                                     </div>
+                                                                    <% } else if(auditor.opinion) { %>
+                                                                    <div class="card-body p-3 border-top">
+                                                                        <p style="margin: 0;"><%- auditor.opinion %></p>
+                                                                    </div>
+                                                                    <% } %>
                                                                 <% } %>
                                                             </div>
                                                         </div>
@@ -654,11 +685,11 @@
                                                         <% if(index < auditors.length - 1) { %>
                                                             <div class="timeline-item-tail"></div>
                                                         <% } %>
-                                                        <% if(auditor.status === auditConst.status.checked) { %>
+                                                        <% if(auditor.status === auditConst.status.checked || auditor.status === auditConst.status.cancelRevise) { %>
                                                             <div class="timeline-item-icon bg-success text-light">
                                                                 <i class="fa fa-check"></i>
                                                             </div>
-                                                        <% } else if(auditor.status === auditConst.status.checkNo) {%>
+                                                        <% } else if(auditor.status === auditConst.status.checkNo || auditor.status === auditConst.status.revise || auditor.status === auditConst.status.checkCancel) {%>
                                                             <div class="timeline-item-icon bg-warning text-light">
                                                                 <i class="fa fa-level-up"></i>
                                                             </div>
@@ -686,8 +717,8 @@
                                                                 </div>
                                                                 <!--审批意见-->
                                                                 <% if(auditor.times === ctx.change.times && auditor.status !== auditConst.status.uncheck) { %>
+                                                                    <% if (ctx.change.times === idx + 1 && auditor.status === auditConst.status.checking) { %>
                                                                     <div class="card-body p-3 border-top">
-                                                                        <% if (ctx.change.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>
@@ -702,12 +733,12 @@
                                                                                     </div>
                                                                                 </div>
                                                                             <% } %>
-                                                                        <% } else { %>
-                                                                            <p style="margin: 0;"><%- auditor.opinion %></p>
-                                                                        <% } %>
-
                                                                     </div>
-
+                                                                    <% } else if(auditor.opinion) { %>
+                                                                    <div class="card-body p-3 border-top">
+                                                                        <p style="margin: 0;"><%- auditor.opinion %></p>
+                                                                    </div>
+                                                                    <% } %>
                                                                 <% } %>
                                                             </div>
                                                         </div>
@@ -731,13 +762,87 @@
         </div>
     <% } %>
 <% } %>
+<% if (ctx.change.status === auditConst.status.checked && ctx.session.sessionUser.accountId === ctx.change.uid) { %>
+    <% if (!authMobile && ctx.session.sessionUser.loginStatus === 0) { %>
+        <!--原报修订变更-->
+        <div class="modal fade" id="sp-down-revise" data-backdrop="static">
+            <div class="modal-dialog" role="document">
+                <div class="modal-content">
+                    <div class="modal-header">
+                        <h5 class="modal-title">修订变更</h5>
+                    </div>
+                    <div class="modal-body">
+                        <h5>修订变更需要您的手机短信验证</h5>
+                        <h5>您目前还没设置认证手机,请先设置。</h5>
+                    </div>
+                    <div class="modal-footer">
+                        <button type="button" class="btn btn-sm btn-secondary" data-dismiss="modal">取消</button>
+                        <a href="/profile/sms" class="btn btn-sm btn-primary">去设置</a>
+                    </div>
+                </div>
+            </div>
+        </div>
+    <% } else { %>
+        <!--修订变更-->
+        <div class="modal fade" id="sp-down-revise" data-backdrop="static">
+            <div class="modal-dialog" role="document">
+                <form id="reviseForm" class="modal-content" method="post" action="/tender/<%- tender.id %>/change/plan/check/revise" onsubmit="return false;">
+                    <div class="modal-header">
+                        <h5 class="modal-title">修订变更</h5>
+                    </div>
+                    <div class="modal-body">
+                        <h5>确认需要修订变更「<%= change.code %>」?</h5>
+                        <% if (ctx.session.sessionUser.loginStatus === 0) { %>
+                            <div class="form-group">
+                                <label>修订需要验证码确认,验证码将发送至尾号<%- authMobile.slice(-4) %>的手机</label>
+                                <div class="input-group input-group-sm mb-3">
+                                    <input class="form-control" type="text" readonly="readonly" name="code" placeholder="输入短信中的6位验证码" />
+                                    <div class="input-group-append">
+                                        <button class="btn btn-outline-secondary" type="button" id="get-code">获取验证码</button>
+                                    </div>
+                                </div>
+                            </div>
+                        <% } %>
+                    </div>
+                    <div class="modal-footer">
+                        <input type="hidden" name="cpid" value="<%= ctx.change.id %>">
+                        <input type="hidden" name="_csrf_j" value="<%= ctx.csrf %>" />
+                        <button type="button" class="btn btn-secondary btn-sm" data-dismiss="modal">关闭</button>
+                        <button type="button" id="re-shenpi-btn2" class="btn btn-warning btn-sm" <% if (ctx.session.sessionUser.loginStatus === 0) { %>disabled<% } %>>确定修订</button>
+                    </div>
+                </form>
+            </div>
+        </div>
+    <% } %>
+<% } %>
+<% if (ctx.change.cancancel) { %>
+    <div class="modal fade" id="sp-down-cancel" data-backdrop="static">
+        <div class="modal-dialog" role="document">
+            <div class="modal-content">
+                <div class="modal-header">
+                    <h5 class="modal-title">撤回</h5>
+                </div>
+                <div class="modal-body">
+                    <h5>撤回后将回退到你的操作状态,确定撤回?</h5>
+                </div>
+                <div class="modal-footer">
+                    <button type="button" class="btn btn-secondary btn-sm" data-dismiss="modal">取消</button>
+                    <button type="submit" class="btn btn-danger btn-sm" id="cancel-shenpi-btn">确定撤回</button>
+                </div>
+            </div>
+        </div>
+    </div>
+<% } %>
 <% if (ctx.session.sessionUser.accountId === ctx.change.uid && (ctx.change.status === auditConst.status.uncheck || ctx.change.status === auditConst.status.checkNo)) { %>
     <script>
         const accountGroup = JSON.parse(unescape('<%- escape(JSON.stringify(accountGroup)) %>'));
         const accountList = JSON.parse(unescape('<%- escape(JSON.stringify(accountList)) %>'));
     </script>
 <% } %>
-<script>const cur_uid = parseInt('<%- ctx.session.sessionUser.accountId %>');</script>
+<script>
+    const cur_uid = parseInt('<%- ctx.session.sessionUser.accountId %>');
+    const csrf = '<%= ctx.csrf %>';
+</script>
 <script>
     $('.sp-location-list').on('shown.bs.modal', function () {
         const scrollBox = $(this).find('div[class="col-8 modal-height-500"]');
@@ -783,4 +888,75 @@
             $('.modal-title').text('重新上报')
         }
     });
+
+    $('#re-shenpi-btn2').click(function () {
+        const data = {
+            cpid: parseInt('<%- ctx.change.id %>'),
+        };
+        <% if (ctx.session.sessionUser.loginStatus === 0) { %>
+        const code = $("#reviseForm input[name='code']").val();
+        if ($(this).hasClass('disabled')) {
+            return false;
+        }
+        if (code.length < 6) {
+            // alert('请填写正确的验证码');
+            toast('请填写正确的验证码', 'error');
+            return false;
+        }
+        data.code = code;
+        <% } %>
+        $.ajax({
+            url: '/tender/<%- tender.id %>/change/plan/check/revise?_csrf_j=' + csrf,
+            type: 'post',
+            data: data,
+            dataTye: 'json',
+            success: function(response) {
+                if (response.err === 0) {
+                    window.location.href = response.url;
+                } else {
+                    toastr.error(response.msg);
+                }
+            }
+        });
+    })
+
+    $('#cancel-revise-btn').click(function () {
+        const data = {
+            cpid: parseInt('<%- ctx.change.id %>'),
+        };
+        $.ajax({
+            url: '/tender/<%- tender.id %>/change/plan/cancel/revise?_csrf_j=' + csrf,
+            type: 'post',
+            data: data,
+            dataTye: 'json',
+            success: function(response) {
+                if (response.err === 0) {
+                    window.location.href = response.url;
+                } else {
+                    toastr.error(response.msg);
+                }
+            }
+        });
+    })
+
+    <% if (ctx.change && ctx.change.cancancel) { %>
+    $("#cancel-shenpi-btn").click(function () {
+        const data = {
+            cpid: parseInt('<%- ctx.change.id %>'),
+        };
+        $.ajax({
+            url: '/tender/<%- tender.id %>/change/plan/cancel/audit?_csrf_j=' + csrf,
+            type: 'post',
+            data: data,
+            dataTye: 'json',
+            success: function (response) {
+                if (response.err === 0) {
+                    window.location.href = response.url;
+                } else {
+                    toastr.error(response.msg);
+                }
+            }
+        });
+    });
+    <% } %>
 </script>

+ 1 - 1
app/view/change/project.ejs

@@ -76,7 +76,7 @@
                         <tr><td><a href="/tender/<%- tender.id %>/change/project/<%- c.id %>/information"><%- c.code %></a></td>
                             <td><%- c.name %></td><td><%- c.account_name %></td><td><%- changeConst.project_type[c.type] %></td><td><%- ctx.helper.formatFullDate(c.in_time) %></td>
                             <td>
-                                <% if ((c.status === auditConst.status.uncheck || c.status === auditConst.status.back) && c.uid === ctx.session.sessionUser.accountId) { %>
+                                <% if ((c.status === auditConst.status.uncheck || c.status === auditConst.status.back || c.status === auditConst.status.revise) && c.uid === ctx.session.sessionUser.accountId) { %>
                                     <a href="/tender/<%- tender.id %>/change/project/<%- c.id %>/information" class="btn <%- auditConst.statusButtonClass[c.status] %> btn-sm"><%- auditConst.statusButton[c.status] %></a>
                                 <% } else if (c.status === auditConst.status.checking && c.curAuditor && c.curAuditor.aid === ctx.session.sessionUser.accountId) { %>
                                     <a href="/tender/<%- tender.id %>/change/project/<%- c.id %>/information" class="btn <%- auditConst.statusButtonClass[c.status] %> btn-sm"><%- auditConst.statusButton[c.status] %></a>

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

@@ -13,6 +13,9 @@
             </div>
             <div class="ml-auto" id="sp-btn">
                 <a href="#xieshen" data-toggle="modal" data-target="#xieshen" class="btn btn-sm btn-primary mr-2">添加协审</a>
+                <% if (ctx.change.cancancel) { %>
+                    <a href="javascript: void(0);" data-toggle="modal" data-target="#sp-down-cancel" class="btn btn-danger btn-sm mr-2">撤回</a>
+                <% } %>
                 <% if (ctx.change.status === auditConst.status.uncheck) { %>
                     <% if (ctx.session.sessionUser.accountId === ctx.change.uid) { %>
                         <a id="sub-sp-btn" href="javascript: void(0);" data-toggle="modal" data-target="#sub-sp" class="btn btn-primary btn-sm">上报审批</a>
@@ -28,9 +31,15 @@
                         <a href="#sp-list" data-toggle="modal" data-target="#sp-list" class="btn btn-outline-secondary btn-sm">审批中</a>
                     <% } %>
                 <% } else if (ctx.change.status === auditConst.status.checked) { %>
+                    <% if (ctx.session.sessionUser.accountId === ctx.change.uid) { %>
+                        <a href="javascript: void(0);" data-toggle="modal" data-target="#sp-down-revise" class="btn btn-warning btn-sm mr-2">修订变更</a>
+                    <% } %>
                     <a href="#sp-list" data-toggle="modal" data-target="#sp-list" class="btn btn-outline-secondary btn-sm">审批完成</a>
-                <% } else if (ctx.change.status === auditConst.status.back) { %>
-                    <a href="#sp-list" data-type="hide" data-toggle="modal" data-target="#sp-list" class="btn btn-outline-warning btn-sm text-muted sp-list-btn">审批退回</a>
+                <% } else if (ctx.change.status === auditConst.status.back || ctx.change.status === auditConst.status.revise) { %>
+                    <% if (ctx.change.status === auditConst.status.revise && (ctx.session.sessionUser.accountId === ctx.change.uid || ctx.session.sessionUser.is_admin)) { %>
+                        <a href="#sub-revoke" data-toggle="modal" data-target="#sub-revoke" class="btn btn-warning btn-sm mr-2">撤销修订</a>
+                    <% } %>
+                    <a href="#sp-list" data-type="hide" data-toggle="modal" data-target="#sp-list" class="btn btn-outline-warning btn-sm text-muted sp-list-btn">审批<% if (ctx.change.status === auditConst.status.back) { %>退回<% } else { %>修订<% } %></a>
                     <% if (ctx.session.sessionUser.accountId === ctx.change.uid) { %>
                         <a href="#sp-list" data-type="show" data-toggle="modal" data-target="#sp-list"  class="btn btn-primary btn-sm sp-list-btn">重新上报</a>
                     <% } %>

+ 240 - 46
app/view/change/project_information_modal.ejs

@@ -20,7 +20,7 @@
         </div>
     </div>
 </div>
-<% if ((ctx.change.status === auditConst.status.uncheck || ctx.change.status === auditConst.status.back) && (ctx.session.sessionUser.accountId === ctx.change.uid || ctx.tender.isTourist)) { %>
+<% if ((ctx.change.status === auditConst.status.uncheck || ctx.change.status === auditConst.status.back || ctx.change.status === auditConst.status.revise) && (ctx.session.sessionUser.accountId === ctx.change.uid || ctx.tender.isTourist)) { %>
     <!--上报审批-->
     <div class="modal fade" id="sub-sp" data-backdrop="static">
         <div class="modal-dialog" role="document">
@@ -86,6 +86,31 @@
             </div>
         </div>
     </div>
+    <% if (ctx.change.status === auditConst.status.revise && (ctx.session.sessionUser.accountId === ctx.change.uid || ctx.session.sessionUser.is_admin)) { %>
+        <!--撤销修订-->
+        <div class="modal fade" id="sub-revoke"  role="dialog" aria-labelledby="myModalLabel">
+            <div class="modal-dialog" role="document">
+                <form id="reviseForm" class="modal-content" method="post" action="/tender/<%- tender.id %>/change/project/cancel/revise" onsubmit="return false;">
+                    <div class="modal-header">
+                        <h5 class="modal-title" id="myModalLabel">撤销修订</h5>
+                        <button type="button" class="close" data-dismiss="modal" aria-label="Close">
+                            <span aria-hidden="true">&times;</span>
+                        </button>
+                    </div>
+                    <div class="modal-body">
+                        <h5>撤销修订,所有修改的数据将全部会被还原。</h5>
+                        <h5>确认撤销修订?</h5>
+                    </div>
+                    <div class="modal-footer">
+                        <input type="hidden" name="cpid" value="<%= ctx.change.id %>">
+                        <input type="hidden" name="_csrf_j" value="<%= ctx.csrf %>" />
+                        <button type="button" class="btn btn-secondary btn-sm" data-dismiss="modal">关闭</button>
+                        <button type="button" id="cancel-revise-btn" class="btn btn-primary btn-sm">确认</button>
+                    </div>
+                </form>
+            </div>
+        </div>
+    <% } %>
 <% } %>
 
 <!--审批流程/结果-->
@@ -93,12 +118,12 @@
     <div class="modal-dialog modal-lg" role="document">
         <div class="modal-content">
             <div class="modal-header">
-                <h5 class="modal-title"><%- ctx.change.status !== auditConst.status.back ? '审批流程' : '重新上报' %></h5>
+                <h5 class="modal-title"><%- ctx.change.status === auditConst.status.back || ctx.change.status === auditConst.status.revise ? '重新上报' : '审批流程' %></h5>
             </div>
             <div class="modal-body">
                 <div class="row">
                     <div class="col-4">
-                        <% if(ctx.change.status === auditConst.status.back) { %>
+                        <% if(ctx.change.status === auditConst.status.back || ctx.change.status === auditConst.status.revise) { %>
                             <a class="sp-list-item" href="#sub-sp" data-toggle="modal" data-target="#sub-sp" id="hideSp">修改审批流程</a>
                         <% } %>
                         <div class="card mt-3">
@@ -169,11 +194,11 @@
                                                 <% if(index < auditors.length - 1) { %>
                                                     <div class="timeline-item-tail"></div>
                                                 <% } %>
-                                                <% if(auditor.status === auditConst.status.checked) { %>
+                                                <% if(auditor.status === auditConst.status.checked || auditor.status === auditConst.status.cancelRevise) { %>
                                                     <div class="timeline-item-icon bg-success text-light">
                                                         <i class="fa fa-check"></i>
                                                     </div>
-                                                <% } else if(auditor.status === auditConst.status.back) {%>
+                                                <% } else if(auditor.status === auditConst.status.back || auditor.status === auditConst.status.revise || auditor.status === auditConst.status.checkCancel) {%>
                                                     <div class="timeline-item-icon bg-warning text-light">
                                                         <i class="fa fa-level-up"></i>
                                                     </div>
@@ -183,7 +208,7 @@
                                                     </div>
                                                 <% } else if(auditor.status === auditConst.status.checkNo) { %>
                                                     <div class="timeline-item-icon bg-danger text-light">
-                                                        <i class="fa fa-ellipsis-h"></i>
+                                                        <i class="fa fa-stop"></i>
                                                     </div>
                                                 <% } else {%>
                                                     <div class="timeline-item-icon bg-secondary text-light">
@@ -194,7 +219,7 @@
                                                         <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>
+                                                                            class="pull-right <%- auditConst.auditStringClass[auditor.status] %>"><%- auditConst.auditString[auditor.status] %></span>
                                                                 </p>
                                                                 <p class="text-muted mb-0"><%- auditor.role %></p>
                                                             </div>
@@ -217,11 +242,11 @@
                                                 <% if(index < auditors.length - 1) { %>
                                                     <div class="timeline-item-tail"></div>
                                                 <% } %>
-                                                <% if(auditor.status === auditConst.status.checked) { %>
+                                                <% if(auditor.status === auditConst.status.checked || auditor.status === auditConst.status.cancelRevise) { %>
                                                     <div class="timeline-item-icon bg-success text-light">
                                                         <i class="fa fa-check"></i>
                                                     </div>
-                                                <% } else if(auditor.status === auditConst.status.back) {%>
+                                                <% } else if(auditor.status === auditConst.status.back || auditor.status === auditConst.status.revise || auditor.status === auditConst.status.checkCancel) {%>
                                                     <div class="timeline-item-icon bg-warning text-light">
                                                         <i class="fa fa-level-up"></i>
                                                     </div>
@@ -231,7 +256,7 @@
                                                     </div>
                                                 <% } else if(auditor.status === auditConst.status.checkNo) { %>
                                                     <div class="timeline-item-icon bg-danger text-light">
-                                                        <i class="fa fa-ellipsis-h"></i>
+                                                        <i class="fa fa-stop"></i>
                                                     </div>
                                                 <% } else { %>
                                                     <div class="timeline-item-icon bg-secondary text-light">
@@ -244,7 +269,7 @@
                                                                 <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] : ''%>
+                                                                            <%- auditConst.auditStringClass[auditor.status] %>"><%- auditor.status !== auditConst.status.uncheck ? auditConst.auditString[auditor.status] : ''%>
                                                                         <%- auditor.status === auditConst.status.back ? ctx.change.user.name : '' %>
                                                         </span>
                                                                 </p>
@@ -272,7 +297,7 @@
             <form class="modal-footer" method="post" action="<%- preUrl %>/audit/start" onsubmit="return checkAuditorFrom()">
                 <input type="hidden" name="_csrf_j" value="<%= ctx.csrf %>">
                 <button type="button" class="btn btn-secondary btn-sm" data-dismiss="modal">关闭</button>
-                <% if(ctx.change.status === auditConst.status.back && ctx.session.sessionUser.accountId === ctx.change.uid) { %>
+                <% if((ctx.change.status === auditConst.status.back || ctx.change.status === auditConst.status.revise) && ctx.session.sessionUser.accountId === ctx.change.uid) { %>
                     <button class="btn btn-primary btn-sm sp-list-item" type="submit">确认上报</button>
                 <% } %>
             </form>
@@ -359,11 +384,11 @@
                                                         <% if(index < auditors.length - 1) { %>
                                                             <div class="timeline-item-tail"></div>
                                                         <% } %>
-                                                        <% if(auditor.status === auditConst.status.checked) { %>
+                                                        <% if(auditor.status === auditConst.status.checked || auditor.status === auditConst.status.cancelRevise) { %>
                                                             <div class="timeline-item-icon bg-success text-light">
                                                                 <i class="fa fa-check"></i>
                                                             </div>
-                                                        <% } else if(auditor.status === auditConst.status.back) {%>
+                                                        <% } else if(auditor.status === auditConst.status.back || auditor.status === auditConst.status.revise || auditor.status === auditConst.status.checkCancel) {%>
                                                             <div class="timeline-item-icon bg-warning text-light">
                                                                 <i class="fa fa-level-up"></i>
                                                             </div>
@@ -371,6 +396,10 @@
                                                             <div class="timeline-item-icon bg-warning text-light">
                                                                 <i class="fa fa-ellipsis-h"></i>
                                                             </div>
+                                                        <% } else if(auditor.status === auditConst.status.checkNo) { %>
+                                                            <div class="timeline-item-icon bg-danger text-light">
+                                                                <i class="fa fa-stop"></i>
+                                                            </div>
                                                         <% } else {%>
                                                             <div class="timeline-item-icon bg-secondary text-light">
                                                             </div>
@@ -380,22 +409,26 @@
                                                                 <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>
+                                                                                    class="pull-right <%- auditConst.auditString[auditor.status] %>"><%- auditConst.auditString[auditor.status] %></span>
                                                                         </p>
                                                                         <p class="text-muted mb-0"><%- auditor.role %></p>
                                                                     </div>
                                                                 </div>
                                                                 <!--审批意见-->
                                                                 <% if(auditor.status !== auditConst.status.uncheck) { %>
+                                                                    <% if (ctx.change.times === idx + 1 && auditor.status === auditConst.status.checking) { %>
                                                                     <div class="card-body p-3 border-top">
                                                                         <% if (ctx.change.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>
+                                                                    <% } else if (auditor.opinion) { %>
+                                                                    <div class="card-body p-3 border-top">
+                                                                        <p style="margin: 0;"><%- auditor.opinion %></p>
+                                                                    </div>
+                                                                    <% } %>
                                                                 <% } %>
                                                             </div>
                                                         </div>
@@ -408,11 +441,11 @@
                                                         <% if(index < auditors.length - 1) { %>
                                                             <div class="timeline-item-tail"></div>
                                                         <% } %>
-                                                        <% if(auditor.status === auditConst.status.checked) { %>
+                                                        <% if(auditor.status === auditConst.status.checked || auditor.status === auditConst.status.cancelRevise) { %>
                                                             <div class="timeline-item-icon bg-success text-light">
                                                                 <i class="fa fa-check"></i>
                                                             </div>
-                                                        <% } else if(auditor.status === auditConst.status.back) {%>
+                                                        <% } else if(auditor.status === auditConst.status.back || auditor.status === auditConst.status.revise || auditor.status === auditConst.status.checkCancel) {%>
                                                             <div class="timeline-item-icon bg-warning text-light">
                                                                 <i class="fa fa-level-up"></i>
                                                             </div>
@@ -420,6 +453,10 @@
                                                             <div class="timeline-item-icon bg-warning text-light">
                                                                 <i class="fa fa-ellipsis-h"></i>
                                                             </div>
+                                                        <% } else if(auditor.status === auditConst.status.checkNo) { %>
+                                                            <div class="timeline-item-icon bg-danger text-light">
+                                                                <i class="fa fa-stop"></i>
+                                                            </div>
                                                         <% } else { %>
                                                             <div class="timeline-item-icon bg-secondary text-light">
                                                             </div>
@@ -431,7 +468,7 @@
                                                                         <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] : ''%>
+                                                                                    <%- auditConst.auditStringClass[auditor.status] %>"><%- auditor.status !== auditConst.status.uncheck ? auditConst.auditString[auditor.status] : ''%>
                                                                                 <%- auditor.status === auditConst.status.back ? ctx.change.user.name : '' %>
                                                                 </span>
                                                                         </p>
@@ -440,15 +477,19 @@
                                                                 </div>
                                                                 <!--审批意见-->
                                                                 <% if(auditor.status !== auditConst.status.uncheck) { %>
-                                                                    <div class="card-body p-3 border-top">
-                                                                        <% if (ctx.change.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 { %>
+                                                                    <% if (ctx.change.times === idx + 1 && auditor.status === auditConst.status.checking) { %>
+                                                                        <div class="card-body p-3 border-top">
+                                                                            <% if (ctx.change.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>
+                                                                            <% } %>
+                                                                        </div>
+                                                                    <% } else if (auditor.opinion) { %>
+                                                                        <div class="card-body p-3 border-top">
                                                                             <p style="margin: 0;"><%- auditor.opinion %></p>
-                                                                        <% } %>
-                                                                    </div>
+                                                                        </div>
+                                                                    <% } %>
                                                                 <% } %>
                                                             </div>
                                                         </div>
@@ -549,11 +590,11 @@
                                                         <% if(index < auditors.length - 1) { %>
                                                             <div class="timeline-item-tail"></div>
                                                         <% } %>
-                                                        <% if(auditor.status === auditConst.status.checked) { %>
+                                                        <% if(auditor.status === auditConst.status.checked || auditor.status === auditConst.status.cancelRevise) { %>
                                                             <div class="timeline-item-icon bg-success text-light">
                                                                 <i class="fa fa-check"></i>
                                                             </div>
-                                                        <% } else if(auditor.status === auditConst.status.back) {%>
+                                                        <% } else if(auditor.status === auditConst.status.back || auditor.status === auditConst.status.revise || auditor.status === auditConst.status.checkCancel) {%>
                                                             <div class="timeline-item-icon bg-warning text-light">
                                                                 <i class="fa fa-level-up"></i>
                                                             </div>
@@ -561,6 +602,10 @@
                                                             <div class="timeline-item-icon bg-warning text-light">
                                                                 <i class="fa fa-ellipsis-h"></i>
                                                             </div>
+                                                        <% } else if(auditor.status === auditConst.status.checkNo) { %>
+                                                            <div class="timeline-item-icon bg-danger text-light">
+                                                                <i class="fa fa-stop"></i>
+                                                            </div>
                                                         <% } else {%>
                                                             <div class="timeline-item-icon bg-secondary text-light">
                                                             </div>
@@ -570,7 +615,7 @@
                                                                 <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>
+                                                                                    class="pull-right <%- auditConst.auditStringClass[auditor.status] %>"><%- auditConst.auditString[auditor.status] %></span>
                                                                         </p>
                                                                         <p class="text-muted mb-0"><%- auditor.role %></p>
                                                                     </div>
@@ -578,8 +623,8 @@
 
                                                                 <!--审批意见-->
                                                                 <% if(auditor.times === ctx.change.times && auditor.status !== auditConst.status.uncheck) { %>
+                                                                    <% if (ctx.change.times === idx + 1 && auditor.status === auditConst.status.checking) { %>
                                                                     <div class="card-body p-3 border-top">
-                                                                        <% if (ctx.change.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>
@@ -594,10 +639,12 @@
                                                                                     </div>
                                                                                 </div>
                                                                             <% } %>
-                                                                        <% } else if(auditor.status === auditConst.status.checked){ %>
-                                                                            <p style="margin: 0;"><%- auditor.opinion %></p>
-                                                                        <% } %>
                                                                     </div>
+                                                                    <% } else if(auditor.opinion) { %>
+                                                                    <div class="card-body p-3 border-top">
+                                                                        <p style="margin: 0;"><%- auditor.opinion %></p>
+                                                                    </div>
+                                                                    <% } %>
                                                                 <% } %>
                                                             </div>
                                                         </div>
@@ -610,11 +657,11 @@
                                                         <% if(index < auditors.length - 1) { %>
                                                             <div class="timeline-item-tail"></div>
                                                         <% } %>
-                                                        <% if(auditor.status === auditConst.status.checked) { %>
+                                                        <% if(auditor.status === auditConst.status.checked || auditor.status === auditConst.status.cancelRevise) { %>
                                                             <div class="timeline-item-icon bg-success text-light">
                                                                 <i class="fa fa-check"></i>
                                                             </div>
-                                                        <% } else if(auditor.status === auditConst.status.back) {%>
+                                                        <% } else if(auditor.status === auditConst.status.back || auditor.status === auditConst.status.revise || auditor.status === auditConst.status.checkCancel) {%>
                                                             <div class="timeline-item-icon bg-warning text-light">
                                                                 <i class="fa fa-level-up"></i>
                                                             </div>
@@ -622,6 +669,10 @@
                                                             <div class="timeline-item-icon bg-warning text-light">
                                                                 <i class="fa fa-ellipsis-h"></i>
                                                             </div>
+                                                        <% } else if(auditor.status === auditConst.status.checkNo) { %>
+                                                            <div class="timeline-item-icon bg-danger text-light">
+                                                                <i class="fa fa-stop"></i>
+                                                            </div>
                                                         <% } else { %>
                                                             <div class="timeline-item-icon bg-secondary text-light">
                                                             </div>
@@ -633,7 +684,7 @@
                                                                         <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] : ''%>
+                                                                                    <%- auditConst.auditStringClass[auditor.status] %>"><%- auditor.status !== auditConst.status.uncheck ? auditConst.auditString[auditor.status] : ''%>
                                                                                 <%- auditor.status === auditConst.status.back ? ctx.change.user.name : '' %>
                                                                 </span>
                                                                         </p>
@@ -642,12 +693,12 @@
                                                                 </div>
                                                                 <!--审批意见-->
                                                                 <% if(auditor.times === ctx.change.times && auditor.status !== auditConst.status.uncheck) { %>
-                                                                    <div class="card-body p-3 border-top">
-                                                                        <% if (ctx.change.times === idx + 1 && auditor.status === auditConst.status.checking) { %>
+                                                                    <% if (ctx.change.times === idx + 1 && auditor.status === auditConst.status.checking) { %>
+                                                                        <div class="card-body p-3 border-top">
                                                                             <label>审批意见<b class="text-danger">*</b></label>
                                                                             <textarea class="form-control form-control-sm"
                                                                                       name="opinion">不同意</textarea>
-                                                                            <% if (ctx.change.curAuditor.aid === auditor.aid ) { %>
+                                                                            <% if (ctx.change.curAuditor.aid === auditor.aid) { %>
                                                                                 <div id="reject-process" class="alert alert-warning"
                                                                                      style="margin-top: 15px;">
                                                                                     <div class="form-check form-check-inline">
@@ -658,12 +709,12 @@
                                                                                     </div>
                                                                                 </div>
                                                                             <% } %>
-                                                                        <% } else { %>
+                                                                        </div>
+                                                                    <% } else if(auditor.opinion) { %>
+                                                                        <div class="card-body p-3 border-top">
                                                                             <p style="margin: 0;"><%- auditor.opinion %></p>
-                                                                        <% } %>
-
-                                                                    </div>
-
+                                                                        </div>
+                                                                    <% } %>
                                                                 <% } %>
                                                             </div>
                                                         </div>
@@ -776,10 +827,82 @@
         </div>
     </div>
 </div>
+<% if (ctx.change.status === auditConst.status.checked && ctx.session.sessionUser.accountId === ctx.change.uid) { %>
+    <% if (!authMobile && ctx.session.sessionUser.loginStatus === 0) { %>
+        <!--原报修订变更-->
+        <div class="modal fade" id="sp-down-revise" data-backdrop="static">
+            <div class="modal-dialog" role="document">
+                <div class="modal-content">
+                    <div class="modal-header">
+                        <h5 class="modal-title">修订变更</h5>
+                    </div>
+                    <div class="modal-body">
+                        <h5>修订变更需要您的手机短信验证</h5>
+                        <h5>您目前还没设置认证手机,请先设置。</h5>
+                    </div>
+                    <div class="modal-footer">
+                        <button type="button" class="btn btn-sm btn-secondary" data-dismiss="modal">取消</button>
+                        <a href="/profile/sms" class="btn btn-sm btn-primary">去设置</a>
+                    </div>
+                </div>
+            </div>
+        </div>
+    <% } else { %>
+        <!--修订变更-->
+        <div class="modal fade" id="sp-down-revise" data-backdrop="static">
+            <div class="modal-dialog" role="document">
+                <form id="reviseForm" class="modal-content" method="post" action="/tender/<%- tender.id %>/change/project/check/revise" onsubmit="return false;">
+                    <div class="modal-header">
+                        <h5 class="modal-title">修订变更</h5>
+                    </div>
+                    <div class="modal-body">
+                        <h5>确认需要修订变更「<%= change.code %>」?</h5>
+                        <% if (ctx.session.sessionUser.loginStatus === 0) { %>
+                            <div class="form-group">
+                                <label>修订需要验证码确认,验证码将发送至尾号<%- authMobile.slice(-4) %>的手机</label>
+                                <div class="input-group input-group-sm mb-3">
+                                    <input class="form-control" type="text" readonly="readonly" name="code" placeholder="输入短信中的6位验证码" />
+                                    <div class="input-group-append">
+                                        <button class="btn btn-outline-secondary" type="button" id="get-code">获取验证码</button>
+                                    </div>
+                                </div>
+                            </div>
+                        <% } %>
+                    </div>
+                    <div class="modal-footer">
+                        <input type="hidden" name="cpid" value="<%= ctx.change.id %>">
+                        <input type="hidden" name="_csrf_j" value="<%= ctx.csrf %>" />
+                        <button type="button" class="btn btn-secondary btn-sm" data-dismiss="modal">关闭</button>
+                        <button type="button" id="re-shenpi-btn2" class="btn btn-warning btn-sm" <% if (ctx.session.sessionUser.loginStatus === 0) { %>disabled<% } %>>确定修订</button>
+                    </div>
+                </form>
+            </div>
+        </div>
+    <% } %>
+<% } %>
+<% if (ctx.change.cancancel) { %>
+    <div class="modal fade" id="sp-down-cancel" data-backdrop="static">
+        <div class="modal-dialog" role="document">
+            <div class="modal-content">
+                <div class="modal-header">
+                    <h5 class="modal-title">撤回</h5>
+                </div>
+                <div class="modal-body">
+                    <h5>撤回后将回退到你的操作状态,确定撤回?</h5>
+                </div>
+                <div class="modal-footer">
+                    <button type="button" class="btn btn-secondary btn-sm" data-dismiss="modal">取消</button>
+                    <button type="submit" class="btn btn-danger btn-sm" id="cancel-shenpi-btn">确定撤回</button>
+                </div>
+            </div>
+        </div>
+    </div>
+<% } %>
 <script>
     const accountGroup = JSON.parse(unescape('<%- escape(JSON.stringify(accountGroup)) %>'));
     const accountList = JSON.parse(unescape('<%- escape(JSON.stringify(accountList)) %>'));
     const cur_uid = parseInt('<%- ctx.session.sessionUser.accountId %>');
+    const csrf = '<%= ctx.csrf %>';
     $('.sp-location-list').on('shown.bs.modal', function () {
         const scrollBox = $(this).find('div[class="col-8 modal-height-500"]');
         const bdiv = (scrollBox.offset() && scrollBox.offset().top) || 0;
@@ -824,4 +947,75 @@
             $('.modal-title').text('重新上报')
         }
     });
+
+    $('#re-shenpi-btn2').click(function () {
+        const data = {
+            cpid: parseInt('<%- ctx.change.id %>'),
+        };
+        <% if (ctx.session.sessionUser.loginStatus === 0) { %>
+        const code = $("#reviseForm input[name='code']").val();
+        if ($(this).hasClass('disabled')) {
+            return false;
+        }
+        if (code.length < 6) {
+            // alert('请填写正确的验证码');
+            toast('请填写正确的验证码', 'error');
+            return false;
+        }
+        data.code = code;
+        <% } %>
+        $.ajax({
+            url: '/tender/<%- tender.id %>/change/project/check/revise?_csrf_j=' + csrf,
+            type: 'post',
+            data: data,
+            dataTye: 'json',
+            success: function(response) {
+                if (response.err === 0) {
+                    window.location.href = response.url;
+                } else {
+                    toastr.error(response.msg);
+                }
+            }
+        });
+    })
+
+    $('#cancel-revise-btn').click(function () {
+        const data = {
+            cpid: parseInt('<%- ctx.change.id %>'),
+        };
+        $.ajax({
+            url: '/tender/<%- tender.id %>/change/project/cancel/revise?_csrf_j=' + csrf,
+            type: 'post',
+            data: data,
+            dataTye: 'json',
+            success: function(response) {
+                if (response.err === 0) {
+                    window.location.href = response.url;
+                } else {
+                    toastr.error(response.msg);
+                }
+            }
+        });
+    })
+
+    <% if (ctx.change && ctx.change.cancancel) { %>
+    $("#cancel-shenpi-btn").click(function () {
+        const data = {
+            cpid: parseInt('<%- ctx.change.id %>'),
+        };
+        $.ajax({
+            url: '/tender/<%- tender.id %>/change/project/cancel/audit?_csrf_j=' + csrf,
+            type: 'post',
+            data: data,
+            dataTye: 'json',
+            success: function (response) {
+                if (response.err === 0) {
+                    window.location.href = response.url;
+                } else {
+                    toastr.error(response.msg);
+                }
+            }
+        });
+    });
+    <% } %>
 </script>

+ 23 - 11
sql/update.sql

@@ -1,13 +1,25 @@
-ALTER TABLE `zh_change_audit_list`
-ADD COLUMN `checked_amount` decimal(30, 8) NULL DEFAULT 0 COMMENT '审批变更后数量(整型)' AFTER `samount`,
-ADD COLUMN `checked_price` decimal(30, 8) NULL DEFAULT 0 COMMENT '审批变更后金额(整型)' AFTER `checked_amount`;
+CREATE TABLE `zh_change_project_history` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `tid` int(11) NOT NULL COMMENT '标段id',
+  `cpid` int(11) NOT NULL COMMENT '变更立项id',
+  `info_json` json DEFAULT NULL COMMENT '内容json值',
+  PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='变更立项内容临时保存表,用于修订撤销与撤回';
 
-UPDATE `zh_change_audit_list` SET `checked_amount` = CONVERT(`samount`, DECIMAL) WHERE `samount` != '';
+CREATE TABLE `zh_change_apply_history` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `tid` int(11) NOT NULL COMMENT '标段id',
+  `caid` int(11) NOT NULL COMMENT '变更申请id',
+  `info_json` json DEFAULT NULL COMMENT '内容json值',
+  `list_json` json DEFAULT NULL COMMENT '清单json值',
+  PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='变更申请内容临时保存表,用于修订撤销与撤回';
 
-ALTER TABLE `zh_sub_project_info`
-ADD COLUMN `reply_land`  varchar(20) NOT NULL DEFAULT '' COMMENT '批复用地' AFTER `quake_peak_value`,
-ADD COLUMN `occupy_land`  varchar(20) NOT NULL DEFAULT '' COMMENT '永久占用土地' AFTER `reply_land`,
-ADD COLUMN `demolish_building`  varchar(20) NOT NULL DEFAULT '' COMMENT '实际拆迁房屋' AFTER `occupy_land`;
-
-ALTER TABLE `zh_payment_detail`
-ADD COLUMN `bills_decimal`  varchar(100) NOT NULL DEFAULT '{"tp":2, "up": 2, "qty": 3}' COMMENT '小数位数' AFTER `in_time`;
+CREATE TABLE `zh_change_plan_history` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `tid` int(11) NOT NULL COMMENT '标段id',
+  `cpid` int(11) NOT NULL COMMENT '变更方案id',
+  `info_json` json DEFAULT NULL COMMENT '内容json值',
+  `list_json` json DEFAULT NULL COMMENT '清单json值',
+  PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='变更方案内容临时保存表,用于修订撤销与撤回';

+ 13 - 0
sql/update20231218.sql

@@ -0,0 +1,13 @@
+ALTER TABLE `zh_change_audit_list`
+ADD COLUMN `checked_amount` decimal(30, 8) NULL DEFAULT 0 COMMENT '审批变更后数量(整型)' AFTER `samount`,
+ADD COLUMN `checked_price` decimal(30, 8) NULL DEFAULT 0 COMMENT '审批变更后金额(整型)' AFTER `checked_amount`;
+
+UPDATE `zh_change_audit_list` SET `checked_amount` = CONVERT(`samount`, DECIMAL) WHERE `samount` != '';
+
+ALTER TABLE `zh_sub_project_info`
+ADD COLUMN `reply_land`  varchar(20) NOT NULL DEFAULT '' COMMENT '批复用地' AFTER `quake_peak_value`,
+ADD COLUMN `occupy_land`  varchar(20) NOT NULL DEFAULT '' COMMENT '永久占用土地' AFTER `reply_land`,
+ADD COLUMN `demolish_building`  varchar(20) NOT NULL DEFAULT '' COMMENT '实际拆迁房屋' AFTER `occupy_land`;
+
+ALTER TABLE `zh_payment_detail`
+ADD COLUMN `decimal`  varchar(100) NOT NULL DEFAULT '{"tp":2, "up": 2, "qty": 3}' COMMENT '小数位数' AFTER `in_time`;