فهرست منبع

变更立项会签或签及审批组功能

ellisran 1 سال پیش
والد
کامیت
533af8f686

+ 14 - 3
app/const/audit.js

@@ -734,6 +734,7 @@ const changeProject = (function() {
         revise: 7, // 修订变更
         cancelRevise: 8, // 撤销修订
         checkCancel: 9, // 撤回
+        checkSkip: 10, // 跳过
     };
     const statusString = [];
     statusString[status.uncheck] = '待上报';
@@ -769,6 +770,7 @@ const changeProject = (function() {
     auditString[status.revise] = '修订';
     auditString[status.cancelRevise] = '撤销修订';
     auditString[status.checkCancel] = '撤回';
+    auditString[status.checkSkip] = '审批通过';
     // 文字样式
     const auditStringClass = [];
     auditStringClass[status.uncheck] = '';
@@ -780,9 +782,10 @@ const changeProject = (function() {
     auditStringClass[status.revise] = 'text-warning';
     auditStringClass[status.cancelRevise] = 'text-success';
     auditStringClass[status.checkCancel] = 'text-warning';
+    auditStringClass[status.checkSkip] = 'text-success';
     // 描述文本
     const auditProgress = [];
-    auditProgress[status.uncheck] = '草稿';
+    auditProgress[status.uncheck] = '待上报';
     auditProgress[status.checking] = '审批中';
     auditProgress[status.checked] = '审批通过';
     auditProgress[status.checkNo] = '终止';
@@ -791,6 +794,7 @@ const changeProject = (function() {
     auditProgress[status.revise] = '修订中';
     auditProgress[status.cancelRevise] = '撤销修订';
     auditProgress[status.checkCancel] = '撤回';
+    auditProgress[status.checkSkip] = '审批通过';
     // 样式
     const auditProgressClass = [];
     auditProgressClass[status.uncheck] = '';
@@ -802,6 +806,7 @@ const changeProject = (function() {
     auditProgressClass[status.revise] = 'text-warning';
     auditProgressClass[status.cancelRevise] = 'text-success';
     auditProgressClass[status.checkCancel] = 'text-warning';
+    auditProgressClass[status.checkSkip] = 'text-success';
 
     const filter = {
         status: {
@@ -850,6 +855,7 @@ const changeApply = (function() {
         revise: 7, // 修订变更
         cancelRevise: 8, // 撤销修订
         checkCancel: 9, // 撤回
+        checkSkip: 10, // 跳过
     };
     const statusString = [];
     statusString[status.uncheck] = '待上报';
@@ -882,6 +888,7 @@ const changeApply = (function() {
     auditString[status.revise] = '修订';
     auditString[status.cancelRevise] = '撤销修订';
     auditString[status.checkCancel] = '撤回';
+    auditString[status.checkSkip] = '审批通过';
     // 文字样式
     const auditStringClass = [];
     auditStringClass[status.uncheck] = '';
@@ -892,9 +899,10 @@ const changeApply = (function() {
     auditStringClass[status.revise] = 'text-warning';
     auditStringClass[status.cancelRevise] = 'text-success';
     auditStringClass[status.checkCancel] = 'text-warning';
+    auditStringClass[status.checkSkip] = 'text-success';
     // 描述文本
     const auditProgress = [];
-    auditProgress[status.uncheck] = '草稿';
+    auditProgress[status.uncheck] = '待上报';
     auditProgress[status.checking] = '审批中';
     auditProgress[status.checked] = '审批通过';
     auditProgress[status.checkNo] = '审批退回';
@@ -958,6 +966,7 @@ const changePlan = (function() {
         revise: 7, // 修订变更
         cancelRevise: 8, // 撤销修订
         checkCancel: 9, // 撤回
+        checkSkip: 10, // 跳过
     };
     const statusString = [];
     statusString[status.uncheck] = '待上报';
@@ -990,6 +999,7 @@ const changePlan = (function() {
     auditString[status.revise] = '修订';
     auditString[status.cancelRevise] = '撤销修订';
     auditString[status.checkCancel] = '撤回';
+    auditString[status.checkSkip] = '审批通过';
     // 文字样式
     const auditStringClass = [];
     auditStringClass[status.uncheck] = '';
@@ -1000,9 +1010,10 @@ const changePlan = (function() {
     auditStringClass[status.revise] = 'text-warning';
     auditStringClass[status.cancelRevise] = 'text-success';
     auditStringClass[status.checkCancel] = 'text-warning';
+    auditStringClass[status.checkSkip] = 'text-success';
     // 描述文本
     const auditProgress = [];
-    auditProgress[status.uncheck] = '草稿';
+    auditProgress[status.uncheck] = '待上报';
     auditProgress[status.checking] = '审批中';
     auditProgress[status.checked] = '审批通过';
     auditProgress[status.checkNo] = '审批退回';

+ 78 - 28
app/controller/change_controller.js

@@ -1353,7 +1353,7 @@ module.exports = app => {
                 // }
                 const data = JSON.parse(ctx.request.body.data);
                 if (ctx.session.sessionUser.is_admin && ctx.change.status !== audit.change.status.checked) {
-                    await ctx.service.changeAudit.saveAudit(ctx.change.cid, ctx.change.times, data);
+                    await ctx.service.changeAudit.saveAudit(ctx.change.cid, ctx.change.times, ctx.change.sp_group, data);
                     const auditors = await ctx.service.changeAudit.getUniqUserGroup(ctx.change.cid, ctx.change.times);
                     ctx.body = { err: 0, msg: '', data: auditors };
                 } else {
@@ -1711,7 +1711,7 @@ module.exports = app => {
             const changes = await ctx.service.changeProject.getListByStatus(tender.id, status, 1, sorts, orders);
             const total = await ctx.service.changeProject.getCountByStatus(tender.id, status);
             for (const c of changes) {
-                c.curAuditor = await ctx.service.changeProjectAudit.getAuditorByStatus(c.id, c.status, c.times);
+                c.curAuditors = await ctx.service.changeProjectAudit.getAuditorsByStatus(c.id, c.status, c.times);
             }
             const accountInfo = await this.ctx.service.projectAccount.getDataById(this.ctx.session.sessionUser.accountId);
             const userPermission = accountInfo !== undefined && accountInfo.permission !== ''
@@ -1781,6 +1781,7 @@ module.exports = app => {
                 auditConst: audit.changeProject,
                 ruleConst: codeRuleConst.measure,
                 changeConst,
+                auditType,
                 tenderMenu: this.menu.tenderMenu,
                 preUrl: '/tender/' + tender.id,
                 jsFiles: this.app.jsFiles.common.concat(this.app.jsFiles.change.project),
@@ -1804,6 +1805,25 @@ module.exports = app => {
         }
 
         /**
+         * 审批流程(Get)
+         * @param ctx
+         * @return {Promise<void>}
+         */
+        async changeProjectAuditors(ctx) {
+            try {
+                const id = JSON.parse(ctx.request.body.data).id;
+                const tenderId = ctx.params.id;
+                const changeInfo = await ctx.service.changeProject.getDataById(id);
+                await ctx.service.changeProject.loadChangeUser(changeInfo);
+                await ctx.service.changeProject.loadChangeAuditViewData(changeInfo);
+                ctx.body = { err: 0, msg: '', data: changeInfo };
+            } catch (error) {
+                this.log(error);
+                ctx.body = { err: 1, msg: error.toString(), data: null };
+            }
+        }
+
+        /**
          * 新增变更立项 (Post)
          *
          * @param {Object} ctx - egg全局变量
@@ -1872,23 +1892,7 @@ module.exports = app => {
          * @private
          */
         async _getChangeProjectAuditViewData(ctx) {
-            const auditConst = audit.changeProject;
-            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) {
-                for (let i = 1; i <= times; i++) {
-                    ctx.change.auditHistory.push(await ctx.service.changeProjectAudit.getAuditors(ctx.change.id, i));
-                }
-            }
-            // 获取审批流程中左边列表
-            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 || 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);
+            await ctx.service.changeProject.loadChangeAuditViewData(ctx.change);
         }
 
         // 变更类别获取及更新
@@ -1923,7 +1927,7 @@ module.exports = app => {
         async projectInformation(ctx) {
             try {
                 const whiteList = this.ctx.app.config.multipart.whitelist;
-                const tender = await ctx.service.tender.getDataById(ctx.tender.id);
+                // const tender = await ctx.service.tender.getDataById(ctx.tender.id);
                 // 获取附件列表
                 const fileList = await ctx.service.changeProjectAtt.getAllChangeProjectAtt(ctx.tender.id, ctx.change.id);
                 await this._getChangeProjectAuditViewData(ctx);
@@ -1933,7 +1937,7 @@ module.exports = app => {
                 const auth_mobile = pa.auth_mobile;
                 // 判断并更新
                 const renderData = {
-                    tender,
+                    tender: ctx.tender,
                     change: ctx.change,
                     changeConst,
                     changeClass,
@@ -1941,6 +1945,8 @@ module.exports = app => {
                     fileList,
                     whiteList,
                     authMobile: auth_mobile,
+                    auditType,
+                    shenpiConst,
                     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',
@@ -1958,6 +1964,10 @@ module.exports = app => {
                     const groupList = accountList.filter(item1 => item1.company === item.name);
                     return { groupName: item.name, groupList };
                 });
+                // 获取固定审批流列表
+                if (ctx.tender.info.shenpi.change === shenpiConst.sp_status.gdspl) {
+                    renderData.spGroupList = await ctx.service.shenpiGroup.getGroupListByChangeType(ctx.tender.id, shenpiConst.sp_type.change, 'changeProject');
+                }
                 // }
                 await this.layout('change/project_information.ejs', renderData, 'change/project_information_modal.ejs');
             } catch (err) {
@@ -1966,6 +1976,20 @@ module.exports = app => {
             }
         }
 
+        async changeProjectSpGroup(ctx) {
+            try {
+                const data = JSON.parse(ctx.request.body.data);
+                const result = await ctx.service.changeProjectAudit.changeSpGroup(ctx.change, data.sp_group);
+                if (!result) {
+                    throw '切换审批组失败';
+                }
+                const auditors = await ctx.service.changeProjectAudit.getUserGroup(ctx.change.id, ctx.change.times);
+                ctx.body = { err: 0, msg: '', data: auditors };
+            } catch (err) {
+                ctx.body = { err: 1, msg: err.toString(), data: null };
+            }
+        }
+
         // 审批相关
         /**
          * 添加审批人
@@ -2000,14 +2024,14 @@ module.exports = app => {
                 if (exist) {
                     throw '该审核人已存在,请勿重复添加';
                 }
-                // const shenpiInfo = await ctx.service.shenpiAudit.getDataByCondition({ tid: ctx.tender.id, sp_type: shenpiConst.sp_type.material, sp_status: shenpiConst.sp_status.gdzs });
-                // const is_gdzs = shenpiInfo && ctx.tender.info.shenpi.material === shenpiConst.sp_status.gdzs ? 1 : 0;
-                const result = await ctx.service.changeProjectAudit.addAuditor(ctx.change.id, id, ctx.change.times);
+                const shenpiInfo = await ctx.service.shenpiAudit.getDataByCondition({ tid: ctx.tender.id, sp_type: shenpiConst.sp_type.change, sp_status: shenpiConst.sp_status.gdzs });
+                const is_gdzs = shenpiInfo && ctx.tender.info.shenpi.change === shenpiConst.sp_status.gdzs ? 1 : 0;
+                const result = await ctx.service.changeProjectAudit.addAuditor(ctx.change.id, id, ctx.change.times, is_gdzs);
                 if (!result) {
                     throw '添加审核人失败';
                 }
 
-                const auditors = await ctx.service.changeProjectAudit.getAuditorsWithOwner(ctx.change.id, ctx.change.times);
+                const auditors = await ctx.service.changeProjectAudit.getUserGroup(ctx.change.id, ctx.change.times);
                 ctx.body = { err: 0, msg: '', data: auditors };
             } catch (err) {
                 this.log(err);
@@ -2109,6 +2133,31 @@ module.exports = app => {
             }
         }
 
+        async saveProjectAudit(ctx) {
+            try {
+                // if (ctx.change.revising) {
+                //     throw '台账修订中,请勿修改提交期数据';
+                // }
+                const data = JSON.parse(ctx.request.body.data);
+                if (ctx.session.sessionUser.is_admin && ctx.change.status !== audit.change.status.checked) {
+                    await ctx.service.changeProjectAudit.saveAudit(ctx.change.id, ctx.change.times, ctx.change.sp_group, data);
+                    const auditors = await ctx.service.changeProjectAudit.getUniqUserGroup(ctx.change.id, ctx.change.times);
+                    ctx.body = { err: 0, msg: '', data: auditors };
+                } else {
+                    throw '您无权进行该操作';
+                }
+            } catch (err) {
+                this.log(err);
+                // ctx.session.postError = err.toString();
+                // ctx.redirect(ctx.request.header.referer);
+                ctx.body = {
+                    err: 1,
+                    // url: ctx.request.header.referer,
+                    msg: err,
+                };
+            }
+        }
+
         /**
          * 上传附件
          * @param {*} ctx 上下文
@@ -2296,7 +2345,7 @@ module.exports = app => {
                 if (!ctx.change || ctx.change.status !== auditConst.status.checking) {
                     throw '当前材料调差期数据有误';
                 }
-                if (!ctx.change.curAuditor || ctx.change.curAuditor.aid !== ctx.session.sessionUser.accountId) {
+                if (ctx.change.curAuditorIds.length === 0 || ctx.change.curAuditorIds.indexOf(ctx.session.sessionUser.accountId) === -1) {
                     throw '您无权进行该操作';
                 }
                 const data = {
@@ -2346,8 +2395,9 @@ module.exports = app => {
         async checkProjectAgain(ctx) {
             try {
                 // 获取终审
-                const auditInfo = await this.ctx.service.changeProjectAudit.getAuditorByStatus(ctx.change.id, audit.changeProject.status.checked);
-                if (ctx.change.status !== audit.changeProject.status.checked || ctx.session.sessionUser.accountId !== auditInfo.aid) {
+                // const auditInfo = await this.ctx.service.changeProjectAudit.getAuditorByStatus(ctx.change.id, audit.changeProject.status.checked);
+                // if (ctx.change.status !== audit.changeProject.status.checked || ctx.session.sessionUser.accountId !== auditInfo.aid) {
+                if (ctx.change.status !== audit.changeProject.status.checked) {
                     throw '您无权进行该操作';
                 }
                 // 判断是否被变更申请调用了,是则无法发起修订

+ 1 - 1
app/controller/tender_controller.js

@@ -1000,7 +1000,7 @@ module.exports = app => {
                 let auditList = [];
                 if (data.status === shenpiConst.sp_status.gdspl) {
                     const group = await ctx.service.shenpiGroup.getGroupBySelect(ctx.tender.id, shenpiConst.sp_type[data.code]);
-                    auditList = await ctx.service.shenpiAudit.getAuditGroupList(ctx.tender.id, shenpiConst.sp_type[data.code], data.status, group ? group.id : null);
+                    auditList = await ctx.service.shenpiAudit.getAuditGroupList(ctx.tender.id, shenpiConst.sp_type[data.code], data.status, group ? group.id : 0);
                 } else if (data.status === shenpiConst.sp_status.gdzs) {
                     auditList = await ctx.service.shenpiAudit.getAudit(ctx.tender.id, shenpiConst.sp_type[data.code], data.status);
                 }

+ 3 - 2
app/extend/helper.js

@@ -1588,12 +1588,13 @@ module.exports = {
         return result;
     },
 
-    groupAuditors(auditors, orderField = 'order') {
+    groupAuditors(auditors, orderField = 'order', noYb = false) {
         auditors.sort((x, y) => {
             return x[orderField] - y[orderField];
         });
+        const newAuditors = noYb ? auditors.filter(x => { return x.audit_order !== 0; }) : auditors;
         const Group = [];
-        for (const a of auditors) {
+        for (const a of newAuditors) {
             if (a[orderField] !== undefined) {
                 if (Group.length === 0) {
                     Group.push([a])

+ 101 - 0
app/middleware/change_project_audit_check.js

@@ -0,0 +1,101 @@
+'use strict';
+
+/**
+ *
+ *
+ * @author Ellisran
+ * @date 2020/10/15
+ * @version
+ */
+
+const status = require('../const/audit').changeProject.status;
+const shenpiConst = require('../const/shenpi');
+const _ = require('lodash');
+
+module.exports = options => {
+    /**
+     * 标段校验 中间件
+     * 1. 读取标段数据(包括属性)
+     * 2. 检验用户是否可见标段(不校验具体权限)
+     *
+     * @param {function} next - 中间件继续执行的方法
+     * @return {void}
+     */
+    return function* changeProjectAuditCheck(next) {
+        try {
+            // 获取revise
+            const id = this.params.cpid || this.request.body.cpid;
+            if (!id) {
+                throw '您访问的变更立项不存在';
+            }
+            // const change = yield this.service.change.getDataByCondition({ cid });
+            if (!this.change) {
+                const change = yield this.service.changeProject.getDataById(id);
+                if (!change) throw '变更立项数据有误';
+                yield this.service.changeProject.loadChangeUser(change);
+                this.change = change;
+            }
+            const change = this.change;
+            if ((change.status === status.uncheck || change.status === status.back || change.status === status.revise) && this.tender.info.shenpi.change !== shenpiConst.sp_status.sqspr) {
+                const shenpi_status = this.tender.info.shenpi.change;
+                // 进一步比较审批流是否与审批流程设置的相同,不同则替换为固定审批流或固定的终审
+                const auditList = yield this.service.changeProjectAudit.getAllDataByCondition({ where: { cpid: change.id, times: change.times }, orders: [['order', 'asc']] });
+                if (shenpi_status === shenpiConst.sp_status.gdspl) {
+                    // 判断并获取审批组
+                    const group = yield this.service.shenpiGroup.getSelectGroupByChangeType(this.tender.id, shenpiConst.sp_type.change, 'changeProject', change.sp_group);
+                    if ((group && change.sp_group !== group.id) || (!group && change.sp_group !== 0)) {
+                        change.sp_group = group ? group.id : 0;
+                        yield this.service.changeProject.defaultUpdate({ id: change.id, sp_group: change.sp_group });
+                    }
+                    const condition = { tid: this.tender.id, sp_type: shenpiConst.sp_type.change, sp_status: shenpi_status, sp_group: change.sp_group };
+                    const shenpiList = yield this.service.shenpiAudit.getAllDataByCondition({ where: condition, orders: [['audit_order', 'asc']] });
+                    yield this.service.shenpiAudit.noYbShenpiList(change.uid, shenpiList);
+                    // 判断2个id数组是否相同,不同则删除原审批流,切换成固定的审批流
+                    let sameAudit = auditList.length === shenpiList.length;
+                    if (sameAudit) {
+                        for (const audit of auditList) {
+                            const shenpi = shenpiList.find(x => { return x.audit_id === audit.aid; });
+                            if (!shenpi || shenpi.audit_order !== audit.audit_order || shenpi.audit_type !== audit.audit_type) {
+                                sameAudit = false;
+                                break;
+                            }
+                        }
+                    }
+                    if (!sameAudit) {
+                        yield this.service.changeProjectAudit.updateNewAuditList(change, shenpiList);
+                        yield this.service.changeProject.loadChangeUser(change);
+                        yield this.service.changeProject.doCheckChangeCanCancel(change);
+                    }
+                } else if (shenpi_status === shenpiConst.sp_status.gdzs) {
+                    const shenpiInfo = yield this.service.shenpiAudit.getDataByCondition({ tid: this.tender.id, sp_type: shenpiConst.sp_type.change, sp_status: shenpi_status });
+                    // 判断最后一个id是否与固定终审id相同,不同则删除原审批流中如果存在的id和添加终审
+                    const lastAuditors = auditList.filter(x => { return x.order === auditList.length - 1; });
+                    if (shenpiInfo && (lastAuditors.length === 0 || (lastAuditors.length > 1 || shenpiInfo.audit_id !== lastAuditors[0].aid))) {
+                        yield this.service.changeProjectAudit.updateLastAudit(change, auditList, shenpiInfo.audit_id);
+                        yield this.service.changeProject.loadChangeUser(change);
+                        yield this.service.changeProject.doCheckChangeCanCancel(change);
+                    } else if (!shenpiInfo) {
+                        // 不存在终审人的状态下这里恢复为授权审批人
+                        this.tender.info.shenpi.change = shenpiConst.sp_status.sqspr;
+                    }
+                }
+            }
+            yield next;
+        } catch (err) {
+            console.log(err);
+            // 输出错误到日志
+            if (err.stack) {
+                this.logger.error(err);
+            } else {
+                this.getLogger('fail').info(JSON.stringify({
+                    error: err,
+                    project: this.session.sessionProject,
+                    user: this.session.sessionUser,
+                    body: this.session.body,
+                }));
+            }
+            // 重定向值标段管理
+            this.redirect(this.request.headers.referer);
+        }
+    };
+};

+ 8 - 8
app/middleware/change_project_check.js

@@ -31,22 +31,22 @@ module.exports = options => {
                 throw '您访问的变更立项不存在';
             }
             const change = yield this.service.changeProject.getDataById(cpid);
+            if (!change) throw '变更立项数据有误';
             // 读取原报、审核人数据
-            change.auditors = yield this.service.changeProjectAudit.getAuditors(change.id, change.times);
-            change.curAuditor = yield this.service.changeProjectAudit.getCurAuditor(change.id, change.times);
+            yield this.service.changeProject.loadChangeUser(change);
+            // change.auditors = yield this.service.changeProjectAudit.getAuditors(change.id, change.times);
+            // change.curAuditor = yield this.service.changeProjectAudit.getCurAuditor(change.id, change.times);
             change.xsAuditors = yield this.service.changeProjectXsAudit.getAuditList(change.id);
-
-            if (!change) throw '变更令数据有误';
             // 权限相关
             // todo 校验权限 (标段参与人、分享)
             const accountId = this.session.sessionUser.accountId,
-                auditorIds = _.map(change.auditors, 'aid'),
                 xsAuditorIds = _.map(change.xsAuditors, 'aid'),
                 shareIds = [];
+            const permission = this.session.sessionUser.permission;
             if (accountId === change.uid) { // 原报
                 change.curTimes = change.times;
                 change.filePermission = true;
-            } else if (auditorIds.indexOf(accountId) !== -1 || xsAuditorIds.indexOf(accountId) !== -1) { // 审批人或者协审人
+            } else if (change.auditorIds.indexOf(accountId) !== -1 || xsAuditorIds.indexOf(accountId) !== -1) { // 审批人或者协审人
                 if (change.status === status.uncheck) {
                     throw '您无权查看该数据';
                 }
@@ -59,7 +59,7 @@ module.exports = options => {
                     throw '您无权查看该数据';
                 }
                 change.filePermission = true;
-            } else if (shareIds.indexOf(accountId) !== -1) { // 分享人
+            } else if (shareIds.indexOf(accountId) !== -1 || (permission !== null && permission.tender !== undefined && permission.tender.indexOf('2') !== -1)) { // 分享人
                 if (change.status === status.uncheck) {
                     throw '您无权查看该数据';
                 }
@@ -67,7 +67,7 @@ module.exports = options => {
                 change.filePermission = false;
             } else if (this.tender.isTourist) {
                 change.curTimes = change.times;
-                change.filePermission = this.tender.touristPermission.file || auditorIds.indexOf(accountId) !== -1;
+                change.filePermission = this.tender.touristPermission.file || change.auditorIds.indexOf(accountId) !== -1;
             } else { // 其他不可见
                 throw '您无权查看该数据';
             }

+ 182 - 0
app/public/js/change_project.js

@@ -135,7 +135,189 @@ class codeRuleSet {
     }
 }
 
+/**
+ * 期计量 - 期列表页面 js
+ *
+ * @author Mai
+ * @date 2018/12/7
+ * @version
+ */
+const getGroupAuditHtml = function (group) {
+    return group.map(u => { return `<small class="d-inline-block text-dark mx-1" title="${u.role}" data-auditorId="${u.aid}">${u.name}</small>`; }).join('');
+};
+
+const getAuditTypeHtml = function (type) {
+    if (type === auditType.key.common) return '';
+    return `<div class="li-subscript"><span class="badge badge-pill badge-${auditType.info[type].class} p-1 badge-bg-small"><small>${auditType.info[type].short}</small></span></div>`;
+};
+
+const getAuditTypeText = function (type) {
+    if (type === auditType.key.common) return '';
+    return `<span class="text-${auditType.info[type].class}">${auditType.info[type].long}</span>`;
+};
+
 $(document).ready(() => {
+
+    $('#audit-list').on('click', 'a', function() {
+        const type = $(this).data('target')
+        const auditCard = $(this).parent().parent()
+        if (type === 'show') {
+            $(this).data('target', 'hide')
+            auditCard.find('.fold-card').slideDown('swing', () => {
+                auditCard.find('#end-target').text($(this).data('idx') + '#')
+                auditCard.find('#fold-btn').text('收起历史审核记录')
+            })
+        } else {
+            $(this).data('target', 'show')
+            auditCard.find('.fold-card').slideUp('swing', () => {
+                auditCard.find('#end-target').text('1#')
+                auditCard.find('#fold-btn').text('展开历史审核记录')
+            })
+        }
+    });
+
+    // 获取审批流程
+    $('a[data-target="#sp-list" ]').on('click', function () {
+        const data = {
+            id: $(this).attr('c-id'),
+        };
+        postData('/tender/' + tenderId + '/change/project/auditors', data, function (result) {
+            const { auditHistory, auditors2, user } = result;
+            let auditorsHTML = [];
+            auditors2.forEach((group, idx) => {
+                if (idx === 0) {
+                    auditorsHTML.push(`<li class="list-group-item d-flex justify-content-between align-items-center">
+                        <span class="mr-1"><i class="fa fa fa-play-circle fa-rotate-90"></i></span>
+                    <span class="text-muted">${getGroupAuditHtml(group)}</span>
+                    <span class="badge badge-light badge-pill ml-auto"><small>原报</small></span>
+                    </li>`);
+                } else if(idx === auditors2.length -1 && idx !== 0) {
+                    auditorsHTML.push(`<li class="list-group-item d-flex justify-content-between align-items-center">
+                        <span class="mr-1"><i class="fa fa fa-stop-circle fa-rotate-90"></i></span>
+                    <span class="text-muted">${getGroupAuditHtml(group)}</span>
+                    <div class="d-flex ml-auto">
+                    ${getAuditTypeHtml(group[0].audit_type)}
+                    <span class="badge badge-light badge-pill ml-auto"><small>终审</small></span>
+                    </div>
+                    </li>`);
+                } else {
+                    auditorsHTML.push(`<li class="list-group-item d-flex justify-content-between align-items-center">
+                        <span class="mr-1"><i class="fa fa fa-chevron-circle-down"></i></span>
+                    <span class="text-muted">${getGroupAuditHtml(group)}</span>
+                    <div class="d-flex ml-auto">
+                    ${getAuditTypeHtml(group[0].audit_type)}
+                    <span class="badge badge-light badge-pill"><small>${transFormToChinese(idx)}审</small></span>
+                    </div>
+                    </li>`);
+                }
+            });
+            $('#auditor-list').empty();
+            $('#auditor-list').append(auditorsHTML.join(''));
+
+            let historyHTML = [];
+            auditHistory.forEach((his, idx) => {
+                if (idx === auditHistory.length - 1 && auditHistory.length !== 1) {
+                    historyHTML.push(`<div class="text-right"><a href="javascript: void(0);" id="fold-btn" data-target="show">展开历史审批流程</a></div>`);
+                }
+                historyHTML.push(`<div class="${idx < auditHistory.length - 1 ? 'fold-card' : ''}">`);
+                historyHTML.push(`<div class="text-center text-muted">${idx+1}#</div>`);
+                historyHTML.push(`<ul class="timeline-list list-unstyled mt-2 ${ idx === auditHistory.length - 1 && auditHistory.length !== 1 ? 'last-auditor-list' : '' }">`);
+                his.forEach((group, index) => {
+                    if (index === 0) {
+                        historyHTML.push(`<li class="timeline-list-item pb-2">
+                                            <div class="timeline-item-date">
+                                                ${group.beginYear}
+                                                <span>${group.beginDate}</span>
+                                                <span>${group.beginTime}</span>
+                                            </div>
+                                            <div class="timeline-item-tail"></div>
+                                            <div class="timeline-item-icon bg-success text-light"><i class="fa fa-caret-down"></i></div>
+                                            <div class="timeline-item-content">
+                                                <div class="py-1">
+                                                    <span class="text-black-50">原报</span>
+                                                    <span class="pull-right text-success">${idx !== 0 ? '重新' : '' }上报审批</span>
+                                                </div>
+                                                <div class="card">
+                                                    <div class="card-body px-3 py-0">
+                                                        <div class="card-text p-2 py-3 row">
+                                                            <div class="col">
+                                                                <span class="h6">${user.name}</span>
+                                                                <span class="text-muted ml-1">${user.role}</span>
+                                                            </div>
+                                                            <div class="col">
+                                                                <span class="pull-right text-success"><i class="fa fa-check-circle"></i></span>
+                                                            </div>
+                                                        </div>
+                                                    </div>
+                                                </div>
+                                            </div>
+                                        </li>`);
+                    }
+                    historyHTML.push(`<li class="timeline-list-item pb-2 ${ group.status === auditConst.status.uncheck && idx === auditHistory.length - 1 && auditHistory.length !== 1 ? 'is_uncheck' : ''}">`);
+                    if (group.endYear) {
+                        historyHTML.push(`<div class="timeline-item-date">${group.endYear}<span>${group.endDate}</span><span>${group.endTime}</span></div>`);
+                    }
+                    if (index < his.length - 1) {
+                        historyHTML.push('<div class="timeline-item-tail"></div>');
+                    }
+                    if (group.status === auditConst.status.checked || group.status === auditConst.status.cancelRevise) {
+                        historyHTML.push('<div class="timeline-item-icon bg-success text-light"><i class="fa fa-check"></i></div>');
+                    } else if (group.status === auditConst.status.back || group.status === auditConst.status.revise || group.status === auditConst.status.checkCancel) {
+                        historyHTML.push('<div class="timeline-item-icon bg-warning text-light"><i class="fa fa-level-up"></i></div>');
+                    } else if (group.status === auditConst.status.checking) {
+                        historyHTML.push('<div class="timeline-item-icon bg-warning text-light"><i class="fa fa-ellipsis-h"></i></div>');
+                    } else if (group.status === auditConst.status.checkNo) {
+                        historyHTML.push('<div class="timeline-item-icon bg-danger text-light"><i class="fa fa-stop"></i></div>');
+                    } else if(group.status === auditConst.status.checkAgain) {
+                        historyHTML.push('<div class="timeline-item-icon bg-warning text-light"><i class="fa fa-check"></i></div>');
+                    } else {
+                        historyHTML.push('<div class="timeline-item-icon bg-secondary text-light"></div>');
+                    }
+
+                    historyHTML.push('<div class="timeline-item-content">');
+                    historyHTML.push('<div class="py-1">');
+                    const statuStr = group.status !== auditConst.status.uncheck ?
+                        `<span class="pull-right ${auditConst.statusClass[group.status]}">${auditConst.statusString[group.status]}</span>` : '';
+                    historyHTML.push(`
+                    <span class="text-black-50">
+                    ${ group.audit_order === 0 ? '原报' : !group.is_final ? group.audit_order + '审' : '终审' } ${getAuditTypeText(group.audit_type)}
+                    </span>
+                    ${statuStr}`);
+                    historyHTML.push('</div>');
+                    historyHTML.push('<div class="card"><div class="card-body px-3 py-0">');
+                    for (const [i, auditor] of group.auditors.entries()) {
+                        historyHTML.push(`<div class="card-text p-2 py-3 row ${ ( i > 0 ? 'border-top' : '') }">`);
+                        historyHTML.push(`<div class="col"><span class="h6">${auditor.name}</span><span class="text-muted ml-1">${auditor.role}</span></div>`);
+                        historyHTML.push('<div class="col">');
+                        if (auditor.status === auditConst.status.checked || group.status === auditConst.status.cancelRevise) {
+                            historyHTML.push('<span class="pull-right text-success"><i class="fa fa-check-circle"></i></span>');
+                        } else if (auditor.status === auditConst.status.back || group.status === auditConst.status.revise || auditor.status === auditConst.status.checkCancel) {
+                            historyHTML.push('<span class="pull-right text-warning"><i class="fa fa-share-square fa-rotate-270"></i></span>');
+                        } else if (auditor.status === auditConst.status.checking) {
+                            historyHTML.push('<span class="pull-right text-warning"><i class="fa fa-commenting"></i></span>');
+                        } else if (auditor.status === auditConst.status.checkNo) {
+                            historyHTML.push('<span class="pull-right text-danger"><i class="fa fa-stop-circle"></i></span>');
+                        } else if (auditor.status === auditConst.status.checkAgain) {
+                            historyHTML.push('<span class="pull-right text-warning"><i class="fa fa-check"></i></span>');
+                        }
+                        historyHTML.push('</div>');
+                        if (auditor.opinion) {
+                            historyHTML.push(`<div class="col-12 py-1 bg-light"><i class="fa fa-commenting-o mr-1"></i>${auditor.opinion}</div>`);
+                        }
+                        historyHTML.push('</div>');
+                    }
+                    historyHTML.push('</div></div>');
+                    historyHTML.push('</div>');
+                    historyHTML.push('</li>');
+                });
+                historyHTML.push('</div>');
+                historyHTML.push('</ul>');
+            });
+            $('#audit-list').empty();
+            $('#audit-list').append(historyHTML.join(''));
+        });
+    });
+
     // 首次进入设置
     let showNoNeed = false;
     if (cRuleFirst) {

+ 131 - 70
app/public/js/change_project_audit.js

@@ -79,87 +79,35 @@ $(document).ready(function () {
         const id = parseInt($(this).data('id'));
         if (id) {
             postData(preUrl + '/audit/add', { auditorId: id }, (datas) => {
-                const html = [];
-                // 如果是重新上报,添加到重新上报列表中
-                const auditorshtml = [];
-                for (const [index,data] of datas.entries()) {
-                    if (index !== 0) {
-                        html.push('<li class="list-group-item" auditorId="'+ data.aid +'">');
-                        html.push('<a href="javascript: void(0)" class="text-danger pull-right">移除</a>');
-                        html.push('<span>');
-                        html.push(data.order + ' ');
-                        html.push(data.name + ' ');
-                        html.push('</span>');
-                        html.push('<small class="text-muted">');
-                        html.push(data.role);
-                        html.push('</small></li>');
-                    }
-                    // 添加新审批人流程修改
-                    auditorshtml.push('<li class="list-group-item" data-auditorid="' + data.aid + '">');
-                    auditorshtml.push('<i class="fa ' + (index+1 === datas.length ? 'fa-stop-circle' : 'fa-chevron-circle-down') + '"></i> ');
-                    auditorshtml.push(data.name + ' <small class="text-muted">' + data.role + '</small>');
-                    if (index === 0) {
-                        auditorshtml.push('<span class="pull-right">原报</span>');
-                    } else if (index+1 === datas.length) {
-                        auditorshtml.push('<span class="pull-right">终审</span>');
-                    } else {
-                        auditorshtml.push('<span class="pull-right">'+ transFormToChinese(index) +'审</span>');
-                    }
-                    auditorshtml.push('</li>');
-                }
-                $('#auditors').html(html.join(''));
-
-
-                // 重新上报时。令其它的审批人流程图标转换
-                // $('#auditors-list li i').removeClass('fa-stop-circle').addClass('fa-chevron-circle-down');
-                // for (let i = 0; i < $('#auditors-list li').length; i++) {
-                //     $('#auditors-list li').eq(i).find('.pull-right').text(transFormToChinese(i+1) + '审');
-                //     $('#auditors-list2 li').eq(i).find('.pull-right').text(transFormToChinese(i+1) + '审');
-                // }
-
-                $('#auditors-list').html(auditorshtml.join(''));
-
-                // const auditorshtml2 = [];
-                // // 重新上报时。令其它的审批人流程图标转换
-                // $('#auditors-list2 li i').removeClass('fa-stop-circle').addClass('fa-chevron-circle-down');
-                // // 添加新审批人
-                // auditorshtml2.push('<li class="list-group-item" data-auditid="' + data.aid + '">');
-                // auditorshtml2.push('<h5 class="card-title"><i class="fa fa-stop-circle"></i> ');
-                // auditorshtml2.push(data.name + ' <small class="text-muted">' + data.role + '</small>');
-                // auditorshtml2.push('<span class="pull-right">终审</span>');
-                // auditorshtml2.push('</h5></li>');
-                // $('#auditors-list2').append(auditorshtml2.join(''));
+                makeSpHtml(datas);
             });
         }
     });
     // 删除审批人
-    $('body').on('click', '#auditors li>a', function () {
-        const li = $(this).parent();
+    $('body').on('click', '#auditors li a', function () {
+        const li = $(this).parents('li');
         const data = {
             auditorId: parseInt(li.attr('auditorId')),
         };
+        console.log(data);
         postData(preUrl +  '/audit/delete', data, (result) => {
             li.remove();
-            for (const rst of result) {
-                const aLi = $('li[auditorId=' + rst.aid + ']');
-                $('span', aLi).text(rst.order + ' ' + rst.name + ' ');
-            }
+            let index = 1;
+            $('#auditors li').each(function () {
+                $(this).children('.col-auto').eq(0).text(index);
+                index++;
+            });
 
             // 如果是重新上报
             // 令最后一个图标转换
-            $('#auditors-list li[data-auditorid="' + data.auditorId + '"]').remove();
+            $('#auditors-list li[data-auditorId="' + data.auditorId + '"]').remove();
             if ($('#auditors-list li').length !== 0 && !$('#auditors-list li i').hasClass('fa-stop-circle')) {
-                $('#auditors-list li').eq($('#auditors-list li').length-1).children('i')
+                console.log($('#auditors-list li').length-1, $('#auditors-list li').eq($('#auditors-list li').length-1).find('i'));
+                $('#auditors-list li').eq($('#auditors-list li').length-1).find('i')
                     .removeClass('fa-chevron-circle-down').addClass('fa-stop-circle');
             }
-            // $('#auditors-list2 li[data-auditid="' + data.auditorId + '"]').remove();
-            // if ($('#auditors-list2 li').length !== 0 && !$('#auditors-list2 li i').hasClass('fa-stop-circle')) {
-            //     $('#auditors-list2 li').eq($('#auditors-list2 li').length-1).children('i')
-            //         .removeClass('fa-chevron-circle-down').addClass('fa-stop-circle');
-            // }
             for (let i = 0; i < $('#auditors-list li').length; i++) {
-                $('#auditors-list li').eq(i).find('.pull-right').text(i === 0 ? '原报' : (i+1 === $('#auditors-list li').length ? '终' : transFormToChinese(i)) + '审');
-                // $('#auditors-list2').eq(i).find('.pull-right').text((i+1 === $('#auditors-list2').length ? '终' : transFormToChinese(i+1)) + '审');
+                $('#auditors-list li').eq(i).find('.badge-pill').children('small').text(i === 0 ? '原报' : (i+1 === $('#auditors-list li').length ? '终' : transFormToChinese(i)) + '审');
             }
         });
     });
@@ -188,11 +136,13 @@ $(document).ready(function () {
                         </a> ${group.groupName}</dt>
                         <div class="dd-content" data-toggleid="${idx}">`
                         group.groupList.forEach(item => {
-                            html += `<dd class="border-bottom p-2 mb-0 " data-id="${item.id}" >
-                                <p class="mb-0 d-flex"><span class="text-primary">${item.name}</span><span
-                                        class="ml-auto">${item.mobile || ''}</span></p>
-                                <span class="text-muted">${item.role || ''}</span>
-                            </dd>`
+                            if (item.id !== change.uid) {
+                                html += `<dd class="border-bottom p-2 mb-0 " data-id="${item.id}" >
+                                    <p class="mb-0 d-flex"><span class="text-primary">${item.name}</span><span
+                                            class="ml-auto">${item.mobile || ''}</span></p>
+                                    <span class="text-muted">${item.role || ''}</span>
+                                </dd>`
+                            }
                         });
                         html += '</div>'
                     })
@@ -261,6 +211,22 @@ $(document).ready(function () {
         });
     });
 
+    // 切换审批组
+    $('#change-sp-group').change(function () {
+        const data = {
+            type: 'change_sp_group',
+            sp_group: parseInt($(this).val()),
+        }
+        if (!data.sp_group) {
+            toastr.error('请选择固定审批组');
+            return false;
+        }
+        console.log(data);
+        postData(preUrl + '/audit/spgroup', data, (datas) => {
+            makeSpHtml(datas);
+        });
+    });
+
     // 退回选择修改审批人流程
     $('#hideSp').click(function () {
         $('#sp-list').modal('hide');
@@ -273,7 +239,102 @@ $(document).ready(function () {
     $('#sp-list').on('hidden.bs.modal', function (e) {
         $(document.body).addClass('modal-open');
     });
+
+    // 管理员更改审批流程js部分
+    let timer3 = null;
+    let oldSearchVal3 = null;
+    $('body').on('input propertychange', '#admin-edit-shenpi .gr-search', function(e) {
+        oldSearchVal3 = e.target.value;
+        timer3 && clearTimeout(timer3);
+        timer3 = setTimeout(() => {
+            const newVal = $(this).val();
+            const code = $(this).attr('data-code');
+            let html = '';
+            if (newVal && newVal === oldSearchVal3) {
+                accountList.filter(item => item && item.id !== change.uid && (item.name.indexOf(newVal) !== -1 || (item.mobile && item.mobile.indexOf(newVal) !== -1))).forEach(item => {
+                    html += `<dd class="border-bottom p-2 mb-0 " data-id="${item.id}" >
+                        <p class="mb-0 d-flex"><span class="text-primary">${item.name}</span><span
+                                class="ml-auto">${item.mobile || ''}</span></p>
+                        <span class="text-muted">${item.role || ''}</span>
+                    </dd>`
+                });
+                $('#' + code + '_dropdownMenu .book-list').empty();
+                $('#' + code + '_dropdownMenu .book-list').append(html);
+            } else {
+                if (!$('#' + code + '_dropdownMenu .acc-btn').length) {
+                    accountGroup.forEach((group, idx) => {
+                        if (!group) return;
+                        html += `<dt><a href="javascript: void(0);" class="acc-btn" data-groupid="${idx}" data-type="hide"><i class="fa fa-plus-square"></i>
+                        </a> ${group.groupName}</dt>
+                        <div class="dd-content" data-toggleid="${idx}">`;
+                        group.groupList.forEach(item => {
+                            if (item.id !== change.uid) {
+                                html += `<dd class="border-bottom p-2 mb-0 " data-id="${item.id}" >
+                                    <p class="mb-0 d-flex"><span class="text-primary">${item.name}</span><span
+                                            class="ml-auto">${item.mobile || ''}</span></p>
+                                    <span class="text-muted">${item.role || ''}</span>
+                                </dd>`;
+                            }
+                        });
+                        html += '</div>';
+                    });
+                    $('#' + code + '_dropdownMenu .book-list').empty();
+                    $('#' + code + '_dropdownMenu .book-list').append(html);
+                }
+            }
+        }, 400);
+    });
 });
+function makeSpHtml(datas) {
+    const html = [];
+    // 如果是重新上报,添加到重新上报列表中
+    const auditorshtml = [];
+    for (const [index,data] of datas.entries()) {
+        if (index !== 0) {
+            html.push('<li class="list-group-item d-flex" auditorId="'+ data[0].aid +'">');
+            html.push(`<div class="col-auto">${index}</div>`);
+            html.push('<div class="col">');
+            for (const auditor of data) {
+                html.push(`<div class="d-inline-block mx-1" auditorId="${auditor.uid}"><i class="fa fa-user text-muted"></i> ${auditor.name} <small class="text-muted">${auditor.role}</small></div>`);
+            }
+            html.push('</div>');
+            html.push('<div class="col-auto">');
+            // todo 添加会签或签时
+            if (data[0].audit_type !== auditType.key.common) {
+                html.push(`<span class="badge badge-pill badge-${auditType.info[data[0].audit_type].class} badge-bg-small"><small>${auditType.info[data[0].audit_type].long}</small></span>`);
+            }
+            // html.push('<span class="badge badge-pill badge-primary badge-bg-small"><small></small></span>');
+            if (shenpi_status === shenpiConst.sp_status.sqspr || (shenpi_status === shenpiConst.sp_status.gdzs && index+1 !== datas.length)) {
+                html.push('<a href="javascript: void(0)" class="text-danger pull-right">移除</a>');
+            }
+            html.push('</div>');
+            html.push('</li>');
+        }
+        // 添加新审批人流程修改
+        auditorshtml.push('<li class="list-group-item d-flex justify-content-between align-items-center" data-auditorid="' + data[0].aid + '">');
+        auditorshtml.push('<span class="mr-1"><i class="fa ' + (index === 0 ? 'fa-play-circle fa-rotate-90' : index+1 === datas.length ? 'fa-stop-circle' : 'fa-chevron-circle-down') + '"></i></span>');
+        auditorshtml.push('<span class="text-muted">');
+        for (const auditor of data) {
+            auditorshtml.push(`<small class="d-inline-block text-dark mx-1" title="${auditor.role}" data-auditorId="${auditor.uid}">${auditor.name}</small>`);
+        }
+        auditorshtml.push('</span>');
+        auditorshtml.push('<div class="d-flex ml-auto">');
+        if (data[0].audit_type !== auditType.key.common) {
+            auditorshtml.push(`<span class="badge badge-pill badge-${auditType.info[data[0].audit_type].class} p-1"><small>${auditType.info[data[0].audit_type].short}</small></span>`);
+        }
+        if (index === 0) {
+            auditorshtml.push('<span class="badge badge-light badge-pill ml-auto"><small>原报</small></span>');
+        } else if (index+1 === datas.length) {
+            auditorshtml.push('<span class="badge badge-light badge-pill"><small>终审</small></span>');
+        } else {
+            auditorshtml.push('<span class="badge badge-light badge-pill"><small>'+ transFormToChinese(index) +'审</small></span>');
+        }
+        auditorshtml.push('</div>');
+        auditorshtml.push('</li>');
+    }
+    $('#auditors').html(html.join(''));
+    $('#auditors-list').html(auditorshtml.join(''));
+}
 // 检查上报情况
 function checkAuditorFrom () {
     if ($('#auditors li').length === 0) {

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

@@ -317,7 +317,7 @@ $(document).ready(function () {
         },
         getGroupHtml: function (flow, this_code) {
             let html = '';
-            if (flow && flow.groupList.length > 0) {
+            if (flow && flow.groupList && flow.groupList.length > 0) {
                 let groupSelectHtml = '';
                 for (const group of flow.groupList) {
                     groupSelectHtml += `<option value="${group.id}" ${group.is_select === 1 ? 'selected' : ''}>${group.name}</option>`;

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

@@ -425,7 +425,7 @@ $(document).ready(function () {
                 lastAuditorHtml.push('<div class="timeline-item-content">');
                 lastAuditorHtml.push(`<div class="py-1">
                         <span class="text-black-50">
-                        ${ !index === datas.length - 1 ? data[0].audit_order + '' : '终' }审 ${getAuditTypeText(data[0].audit_type)}
+                        ${ index !== datas.length - 1 ? data[0].audit_order + '' : '终' }审 ${getAuditTypeText(data[0].audit_type)}
                         </span>
                     </div>`);
                 lastAuditorHtml.push('<div class="card"><div class="card-body px-3 py-0">');

+ 6 - 2
app/router.js

@@ -13,6 +13,7 @@ module.exports = app => {
     const ledgerAuditCheck = app.middlewares.ledgerAuditCheck();
     const reviseAuditCheck = app.middlewares.reviseAuditCheck();
     const changeAuditCheck = app.middlewares.changeAuditCheck();
+    const changeProjectAuditCheck = app.middlewares.changeProjectAuditCheck();
     const uncheckTenderCheck = app.middlewares.uncheckTenderCheck();
     // 期读取中间件
     const stageCheck = app.middlewares.stageCheck();
@@ -564,14 +565,14 @@ module.exports = app => {
     app.get('/tender/:id/change/project/status/:status', sessionAuth, tenderCheck, uncheckTenderCheck, 'changeController.projectStatus');
     app.post('/tender/:id/change/project/add', sessionAuth, tenderCheck, uncheckTenderCheck, tenderBuildCheck, 'changeController.projectAdd');
     app.post('/tender/:id/change/project/delete', sessionAuth, tenderCheck, uncheckTenderCheck, tenderBuildCheck, 'changeController.projectDelete');
-    app.get('/tender/:id/change/project/:cpid/information', sessionAuth, tenderCheck, uncheckTenderCheck, changeProjectCheck, 'changeController.projectInformation');
+    app.get('/tender/:id/change/project/:cpid/information', sessionAuth, tenderCheck, uncheckTenderCheck, changeProjectCheck, changeProjectAuditCheck, 'changeController.projectInformation');
     app.post('/tender/:id/change/project/:cpid/information/save', sessionAuth, tenderCheck, uncheckTenderCheck, changeProjectCheck, 'changeController.projectInformationSave');
     app.post('/tender/:id/change/project/:cpid/information/file/upload', sessionAuth, tenderCheck, uncheckTenderCheck, changeProjectCheck, 'changeController.uploadProjectFile');
     app.post('/tender/:id/change/project/:cpid/information/file/delete', sessionAuth, tenderCheck, uncheckTenderCheck, changeProjectCheck, 'changeController.deleteProjectFile');
     app.get('/tender/:id/change/project/:cpid/information/file/:fid/download', sessionAuth, tenderCheck, uncheckTenderCheck, changeProjectCheck, 'changeController.downloadProjectFile');
     app.post('/tender/:id/change/project/:cpid/information/audit/add', sessionAuth, tenderCheck, uncheckTenderCheck, changeProjectCheck, 'changeController.addProjectAudit');
     app.post('/tender/:id/change/project/:cpid/information/audit/delete', sessionAuth, tenderCheck, uncheckTenderCheck, changeProjectCheck, 'changeController.deleteProjectAudit');
-    app.post('/tender/:id/change/project/:cpid/information/audit/start', sessionAuth, tenderCheck, uncheckTenderCheck, changeProjectCheck, tenderBuildCheck, 'changeController.startProjectAudit');
+    app.post('/tender/:id/change/project/:cpid/information/audit/start', sessionAuth, tenderCheck, uncheckTenderCheck, changeProjectCheck, changeProjectAuditCheck, tenderBuildCheck, 'changeController.startProjectAudit');
     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');
@@ -579,6 +580,9 @@ module.exports = app => {
     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.post('/tender/:id/change/project/check/again', sessionAuth, tenderCheck, uncheckTenderCheck, tenderBuildCheck, changeProjectCheck, 'changeController.checkProjectAgain');
+    app.post('/tender/:id/change/project/:cpid/information/audit/save', sessionAuth, tenderCheck, uncheckTenderCheck, changeProjectCheck, 'changeController.saveProjectAudit');
+    app.post('/tender/:id/change/project/:cpid/information/audit/spgroup', sessionAuth, tenderCheck, uncheckTenderCheck, changeProjectCheck, 'changeController.changeProjectSpGroup');
+    app.post('/tender/:id/change/project/auditors', sessionAuth, tenderCheck, uncheckTenderCheck, 'changeController.changeProjectAuditors');
     // 变更申请
     app.get('/tender/:id/change/apply', sessionAuth, tenderCheck, uncheckTenderCheck, 'changeController.apply');
     app.get('/tender/:id/change/apply/status/:status', sessionAuth, tenderCheck, uncheckTenderCheck, 'changeController.applyStatus');

+ 1 - 1
app/service/change.js

@@ -1659,7 +1659,7 @@ module.exports = app => {
                 // 获取所有审核人列表
                 const auditors = await this.ctx.service.changeAudit.getUniqAuditor(cid, times);
                 // 添加到消息推送表
-                const noticeContent = await this.getNoticeContent(pid, changeData.tid, changeData.cid, this.ctx.session.sessionUser.accountId, '撤销重新审批');
+                const noticeContent = await this.getNoticeContent(pid, changeData.tid, changeData.cid, this.ctx.session.sessionUser.accountId, '撤销修订');
                 const records = [];
                 auditors.forEach(auditor => {
                     records.push({

+ 70 - 15
app/service/change_project.js

@@ -9,6 +9,7 @@
  */
 
 const audit = require('../const/audit').changeProject;
+const auditType = require('../const/audit').auditType;
 // const smsTypeConst = require('../const/sms_type');
 // const SMS = require('../lib/sms');
 // const SmsAliConst = require('../const/sms_alitemplate');
@@ -31,6 +32,50 @@ module.exports = app => {
             this.tableName = 'change_project';
         }
 
+        async loadChangeAuditViewData(change) {
+            const times = change.status === audit.status.back || change.status === audit.status.revise ? change.times - 1 : change.times;
+
+            if (!change.user) change.user = await this.ctx.service.projectAccount.getAccountInfoById(change.uid);
+            change.auditHistory = await this.ctx.service.changeProjectAudit.getAuditorHistory(change.id, times);
+            // 获取审批流程中左边列表
+            if ((change.status === audit.status.back || change.status === audit.status.revise) && change.uid !== this.ctx.session.sessionUser.accountId && !this.ctx.session.sessionUser.is_admin) {
+                const auditors = await this.ctx.service.changeProjectAudit.getAuditors(change.id, times); // 全部参与的审批人
+                const auditorGroups = this.ctx.helper.groupAuditors(auditors, 'order', true);
+                change.auditors2 = this.ctx.helper.groupAuditorsUniq(auditorGroups);
+                change.auditors2.unshift([{
+                    aid: change.user.id, order: 0, times: change.times - 1, audit_order: 0, audit_type: auditType.key.common,
+                    name: change.user.name, role: change.user.role, company: change.user.company,
+                }]);
+            } else {
+                change.auditors2 = change.userGroups;
+            }
+            if (change.status === audit.status.uncheck || change.status === audit.status.back || change.status === audit.status.revise) {
+                change.auditorList = await this.ctx.service.changeProjectAudit.getAuditors(change.id, change.times);
+            }
+        }
+
+        async loadChangeUser(change) {
+            const status = audit.status;
+            const accountId = this.ctx.session.sessionUser.accountId;
+
+            change.user = await this.ctx.service.projectAccount.getAccountInfoById(change.uid);
+            change.auditors = await this.ctx.service.changeProjectAudit.getAuditors(change.id, change.times); // 全部参与的审批人
+            change.auditorIds = this._.map(change.auditors, 'aid');
+            change.curAuditors = change.auditors.filter(x => { return x.status === status.checking; }); // 当前流程中审批中的审批人
+            change.curAuditorIds = this._.map(change.curAuditors, 'aid');
+            change.flowAuditors = change.curAuditors.length > 0 ? change.auditors.filter(x => { return x.order === change.curAuditors[0].order; }) : []; // 当前流程中参与的审批人(包含会签时,审批通过的人)
+            change.flowAuditorIds = this._.map(change.flowAuditors, 'aid');
+            change.nextAuditors = change.curAuditors.length > 0 ? change.auditors.filter(x => { return x.order === change.curAuditors[0].order + 1; }) : [];
+            change.nextAuditorIds = this._.map(change.nextAuditors, 'aid');
+            change.auditorGroups = this.ctx.helper.groupAuditors(change.auditors, 'order', true);
+            change.userGroups = this.ctx.helper.groupAuditorsUniq(change.auditorGroups);
+            change.userGroups.unshift([{
+                aid: change.user.id, order: 0, times: change.times, audit_order: 0, audit_type: auditType.key.common,
+                name: change.user.name, role: change.user.role, company: change.user.company,
+            }]);
+            change.finalAuditorIds = change.userGroups[change.userGroups.length - 1].map(x => { return x.aid; });
+        }
+
         async add(tenderId, userId, code, name) {
             const type = userId === this.ctx.tender.data.user_id ? codeRuleConst.ruleType.suggestion : codeRuleConst.ruleType.will;
             const sql = 'SELECT COUNT(*) as count FROM ?? WHERE `tid` = ? AND `code` = ? AND type = ?';
@@ -383,7 +428,6 @@ module.exports = app => {
             // 获取当前审批人的上一个审批人,判断是否是当前登录人,并赋予撤回功能,(当审批人存在有审批过时,上一人不允许再撤回)
             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) {
@@ -391,31 +435,42 @@ module.exports = app => {
                     const onAuditor = this._.findLast(lastAuditors, { status: status.back });
                     if (onAuditor && onAuditor.aid === accountId) {
                         change.cancancel = 4;// 审批人撤回退回原报
+                        change.preAuditors = lastAuditors.filter(x => { return x.order === onAuditor.order; });
                     }
                 } else if (change.status === status.checkNo) {
-                    const onAuditor = this._.findLast(auditors, function(item) {
+                    const lastAuditors = await this.service.changeProjectAudit.getAuditors(change.id, change.times);
+                    const onAuditor = this._.findLast(change.auditors, function(item) {
                         return item.status === status.checkNo;
                     });
                     if (onAuditor && onAuditor.aid === accountId) {
                         change.cancancel = 3; // 审批人撤回审批终止
+                        change.preAuditors = lastAuditors.filter(x => { return x.order === onAuditor.order; });
                     }
                 } 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) {
+                    if (change.flowAuditors.find(x => { return x.status !== status.checking; }) && change.flowAuditorIds.indexOf(accountId) < 0) return; // 当前流程存在审批人审批通过时,不可撤回
+                    if (change.curAuditorIds.indexOf(accountId) < 0 && change.flowAuditorIds.indexOf(accountId) >= 0) {
+                        change.cancancel = 5; // 会签未全部审批通过时,审批人撤回审批通过
+                        return;
+                    }
+                    const preAuditors = change.curAuditors[0].order !== 1 ? change.auditors.filter(x => { return x.order === change.curAuditors[0].order - 1; }) : [];
+                    const preAuditorCheckAgain = preAuditors.find(pa => { return pa.status === status.checkAgain; });
+                    const preAuditorCheckCancel = preAuditors.find(pa => { return pa.status === status.checkCancel; });
+                    const preAuditorIds = preAuditorCheckAgain ? [] : preAuditors.map(x => { return x.aid; }); // 重审不可撤回
+                    if ((this._.isEqual(change.flowAuditorIds, preAuditorIds) && preAuditorCheckCancel)) {
+                        return; // 不可以多次撤回
+                    }
+                    const preAuditChecked = preAuditors.find(pa => { return pa.status === status.checked && pa.aid === accountId; });
+                    // const preAuditCheckNoPre = preAuditors.find(pa => { return pa.status === status.checkNoPre && pa.uid === accountId; });
+                    if (preAuditorIds.indexOf(accountId) >= 0) {
+                        if (preAuditChecked && change.status === status.checking) {
                             change.cancancel = 2;// 审批人撤回审批通过
                         }
-                        change.preAudit = preAudit;
-                    } else if (preAid === accountId && preAid === change.uid) {
+                        // else if (preAuditCheckNoPre && change.status === status.checkNoPre) {
+                        //     change.cancancel = 3;// 审批人撤回审批退回上一人
+                        // }
+                        change.preAuditors = preAuditors;
+                    } else if (preAuditors.length === 0 && accountId === change.uid) {
                         change.cancancel = 1;// 原报撤回
                     }
                 }

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 707 - 309
app/service/change_project_audit.js


+ 26 - 3
app/service/shenpi_audit.js

@@ -8,6 +8,7 @@
  * @version
  */
 const shenpiConst = require('../const/shenpi');
+const auditType = require('../const/audit').auditType;
 
 module.exports = app => {
 
@@ -396,8 +397,8 @@ module.exports = app => {
             }
         }
 
-        async updateAuditListWithAuditType(transaction, tenderId, sp_status, sp_type, auditGroup) {
-            const auditList = await this.getAuditList(tenderId, sp_type, sp_status);
+        async updateAuditListWithAuditType(transaction, tenderId, sp_status, sp_type, auditGroup, sp_group = 0) {
+            const auditList = await this.getAuditList(tenderId, sp_type, sp_status, sp_group);
             const newAuditList = [];
             for (const group of auditGroup) {
                 newAuditList.push(...group);
@@ -415,7 +416,7 @@ module.exports = app => {
             if (sameAudit) return;
 
             // 更新固定审批流
-            await transaction.delete(this.tableName, { tid: tenderId, sp_type, sp_status });
+            await transaction.delete(this.tableName, { tid: tenderId, sp_type, sp_status, sp_group });
             const insertDatas = [];
             for (const a of newAuditList) {
                 insertDatas.push({
@@ -425,6 +426,7 @@ module.exports = app => {
                     audit_id: a.aid || a.uid,
                     audit_order: a.audit_order,
                     audit_type: a.audit_type,
+                    sp_group,
                 });
             }
             await transaction.insert(this.tableName, insertDatas);
@@ -456,6 +458,27 @@ module.exports = app => {
             }
             return removeTenders;
         }
+
+        async noYbShenpiList(uid, shenpiList) {
+            // 剔除原报及分析审批流中的审批人
+            const yBIndex = shenpiList.findIndex(x => { return x.audit_id === uid; });
+            if (yBIndex !== -1) {
+                const yB = shenpiList[yBIndex];
+                if (yB.audit_type !== auditType.key.common) {
+                    // 如果是会签或签且人数只有2人,则audit_type变为common
+                    const curSps = shenpiList.filter(x => { return x.audit_order === yB.audit_order; });
+                    if (curSps.length === 1) {
+                        shenpiList.forEach(x => { if (x.audit_order > yB.audit_order) x.audit_order--; });
+                    } else if (curSps.length === 2) {
+                        curSps.forEach(x => { x.audit_type = auditType.key.common; });
+                    }
+                } else {
+                    // 比它大的audit_order都要-1;
+                    shenpiList.forEach(x => { if (x.audit_order > yB.audit_order) x.audit_order--; });
+                }
+                shenpiList.splice(yBIndex, 1);
+            }
+        }
     }
 
     return ShenpiAudit;

+ 6 - 4
app/service/shenpi_group.js

@@ -146,13 +146,15 @@ module.exports = app => {
         }
 
         async getSelectGroupByChangeType(tid, sp_type, change_type, group_id = 0) {
-            if (group_id !== 0) {
-                const group = await this.getDataById(group_id);
-                if (group) return group;
-            }
             // 查找变更选中的审批组id,没有这为默认审批组或null
             const result = await this.getGroupListByChangeType(tid, sp_type, change_type);
             if (result && result.length > 0) {
+                if (group_id !== 0) {
+                    const group = await this._.find(result, function(item) {
+                        return item.id === group_id;
+                    });
+                    if (group) return group;
+                }
                 const hadSelect = this._.find(result, function(item) {
                     return item.is_select === 1;
                 });

+ 2 - 2
app/service/stage_audit.js

@@ -2042,7 +2042,7 @@ module.exports = app => {
          * @param {Object} data - 更改参数
          * @return {Promise<void>}
          */
-        async saveAudit(stageId, times, data) {
+        async saveAudit(stageId, times, sp_group, data) {
             const transaction = await this.db.beginTransaction();
             try {
                 const auditors = await this.getAuditGroupByList(stageId, times);
@@ -2119,7 +2119,7 @@ module.exports = app => {
                     const newAuditors = await transaction.select(this.tableName, { where: { sid: stageId, times } });
                     const newAuditorGroup = this.ctx.helper.groupAuditors(newAuditors);
                     const uniqNewAuditorGroup = this.ctx.helper.groupAuditorsUniq(newAuditorGroup);
-                    await this.ctx.service.shenpiAudit.updateAuditListWithAuditType(transaction, this.ctx.tender.id, this.ctx.tender.info.shenpi.stage, shenpiConst.sp_type.stage, uniqNewAuditorGroup);
+                    await this.ctx.service.shenpiAudit.updateAuditListWithAuditType(transaction, this.ctx.tender.id, this.ctx.tender.info.shenpi.stage, shenpiConst.sp_type.stage, uniqNewAuditorGroup, sp_group);
                 } else if (this.ctx.tender.info.shenpi.stage === shenpiConst.sp_status.gdzs) {
                     const newAuditors = await this.getAuditGroupByList(stageId, times, transaction);
                     await this.ctx.service.shenpiAudit.updateAuditList(transaction, this.ctx.tender.id, this.ctx.tender.info.shenpi.stage, shenpiConst.sp_type.stage, this._.map(newAuditors, 'aid'));

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

@@ -508,7 +508,7 @@
                                                             </div>
                                                         <% } else { %>
                                                 <span class="text-black-50">
-                                                    <%- (!group.is_final ? group.audit_order : '终') %>
+                                                    <%- (group.audit_order === 0 ? '原报' : !group.is_final ? group.audit_order + '审' : '终') %>
                                                     <% if (group.audit_type !== auditType.key.common) { %><span class="text-<%- auditType.info[group.audit_type].class %> "><%- auditType.info[group.audit_type].long %></span><% } %>
                                                 </span>
                                                         <% if (group.status !== auditConst.status.uncheck) { %>

+ 32 - 15
app/view/change/project.ejs

@@ -64,37 +64,52 @@
         <div class="c-body">
             <div class="sjs-height-0">
                 <table class="table table-bordered">
-                    <thead>
-                    <tr>
-                        <th width="20%" id="sort_change">变更立项书编号</th><th width="30%">变更工程名称</th>
+                    <thead class="text-center">
+                    <tr><th width="4%">序号</th>
+                        <th width="18%" id="sort_change">变更立项书编号</th><th width="27%">变更工程名称</th>
                         <th width="6%">发起人</th><th width="7%">发起类型</th><th width="12%">发起时间</th>
-                        <th width="7%">审批状态</th><th width="13%">审批进度</th><th width="5%">操作</th>
+                        <th width="7%">审批状态</th><th width="14%">审批进度</th><th width="5%">操作</th>
                     </tr>
                     </thead>
                     <tbody id="changeList">
-                    <% for (const c of changes) { %>
-                        <tr><td><a href="/tender/<%- tender.id %>/change/project/<%- c.id %>/information"><%- c.code %></a></td>
+                    <% if (changes.length > 0) { %>
+                    <% for (const [index, c] of changes.entries()) { %>
+                        <tr>
+                            <td class="text-center"><%- (pageInfo.page-1)*pageInfo.pageSize + index+1 %></td>
+                            <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.status === auditConst.status.revise) && c.uid === ctx.session.sessionUser.accountId) { %>
+                            <td class="text-center">
+                                <% if (c.status === auditConst.status.uncheck && 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) { %>
+                                <% } else if ((c.status === auditConst.status.back || c.status === auditConst.status.revise) && c.curAuditors && c.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>
+                                <% } else if (c.status === auditConst.status.checking && c.curAuditors && c.curAuditors.findIndex(x => { return x.aid === ctx.session.sessionUser.accountId; }) >= 0) { %>
+                                    <% const curAudit = c.curAuditors.find(x => { return x.aid === ctx.session.sessionUser.accountId; }); %>
+                                    <% if (curAudit.status === auditConst.status.checking) { %>
+                                        <a href="/tender/<%- tender.id %>/change/project/<%- c.id %>/information" class="btn <%- auditConst.statusButtonClass[c.status] %> btn-sm"><%- auditConst.statusButton[c.status] %></a>
+                                    <% } else { %>
+                                        <span class="<%- auditConst.auditStringClass[curAudit.status] %>"><%- auditConst.auditString[curAudit.status] %></span>
+                                    <% } %>
                                 <% } else { %>
-                                    <span class="<%- auditConst.auditProgressClass[c.status] %>"><%- auditConst.auditProgress[c.status] %></span>
+                                    <span class="<%- auditConst.auditStringClass[c.status] %>"><%- auditConst.auditString[c.status] %></span>
                                 <% } %>
                             </td>
-                            <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 class="<%- auditConst.auditProgressClass[c.status] %>">
+                                <% if (c.curAuditors && c.curAuditors.length > 0) { %>
+                                    <% if (c.curAuditors[0].audit_type === auditType.key.common) { %>
+                                        <a href="#sp-list" data-toggle="modal" data-target="#sp-list" c-id="<%- c.id %>"><%- c.curAuditors[0].name %><%if (c.curAuditors[0].role !== '' && c.curAuditors[0].role !== null) { %>-<%- c.curAuditors[0].role %><% } %></a>
+                                    <% } else { %>
+                                        <a href="#sp-list" data-toggle="modal" data-target="#sp-list" c-id="<%- c.id %>"><%- ctx.helper.transFormToChinese(c.curAuditors[0].audit_order) + '审' %></a>
+                                    <% } %>
+                                <% } %>
+                                <%- auditConst.auditProgress[c.status] %>
                             </td>
                             <td>
                                 <% if (c.uid === uid && (c.status === auditConst.status.uncheck || c.status === auditConst.status.back)) { %><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>
                         </tr>
                     <% } %>
+                    <% } %>
                     </tbody>
                 </table>
                 <!--翻页-->
@@ -114,4 +129,6 @@
     const cRuleFirst = parseInt('<%- c_rule_first %>');
     const ruleType = parseInt('<%- ruleType %>');
     const rulesType = '<%- rule_type %>';
+    const auditType = JSON.parse(unescape('<%- escape(JSON.stringify(auditType)) %>'));
+    const auditConst = JSON.parse(unescape('<%- escape(JSON.stringify(auditConst)) %>'));
 </script>

+ 3 - 3
app/view/change/project_information.ejs

@@ -29,7 +29,7 @@
                         <a id="sub-sp-btn" href="javascript: void(0);" data-toggle="modal" data-target="#sub-sp" class="btn btn-outline-secondary btn-sm">上报中</a>
                     <% } %>
                 <% } else if (ctx.change.status === auditConst.status.checking) { %>
-                    <% if (ctx.change.curAuditor && ctx.change.curAuditor.aid === ctx.session.sessionUser.accountId) { %>
+                    <% if (ctx.change.curAuditorIds.indexOf(ctx.session.sessionUser.accountId) >= 0) { %>
                         <a href="#stop" data-toggle="modal" data-target="#stop" class="btn btn-sm btn-danger mr-2">终止</a>
                         <a id="sp-done-btn" href="javascript: void(0);" data-toggle="modal" data-target="#sp-done" class="btn btn-success btn-sm">审批通过</a>
                         <a href="#sp-back" data-toggle="modal" data-target="#sp-back" class="btn btn-warning btn-sm">审批退回</a>
@@ -37,8 +37,8 @@
                         <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) { %>
-                    <a href="#sp-list" data-toggle="modal" data-target="#sp-list" class="btn btn-outline-secondary btn-sm">审批完成</a>
-                    <% if (ctx.session.sessionUser.accountId === ctx.change.auditors2[ctx.change.auditors2.length-1].aid) { %>
+                    <a href="#sp-list" data-toggle="modal" data-target="#sp-list" class="btn btn-outline-success btn-sm">审批完成</a>
+                    <% if (ctx.change.auditors !== undefined && ctx.change.finalAuditorIds.indexOf(ctx.session.sessionUser.accountId) >= 0) { %>
                         <!--重新审批-->
                         <a href="#sp-down-back" data-toggle="modal" data-target="#sp-down-back" class="btn btn-warning btn-sm ml-2">重新审批</a>
                     <% } %>

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 906 - 482
app/view/change/project_information_modal.ejs


+ 25 - 1
app/view/change/project_modal.ejs

@@ -17,7 +17,31 @@
         </div>
     </div>
 </div>
-
+<!--审批流程/结果-->
+<div class="modal fade" id="sp-list" data-backdrop="static">
+    <div class="modal-dialog modal-lg" role="document">
+        <div class="modal-content">
+            <div class="modal-header">
+                <h5 class="modal-title">审批流程</h5>
+            </div>
+            <div class="modal-body">
+                <div class="row">
+                    <div class="col-4 modal-height-500" style="overflow: auto">
+                        <div class="card mt-3">
+                            <ul class="list-group list-group-flush" id="auditor-list">
+                            </ul>
+                        </div>
+                    </div>
+                    <div class="col-8 modal-height-500" style="overflow: auto" id="audit-list">
+                    </div>
+                </div>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-secondary btn-sm" data-dismiss="modal">关闭</button>
+            </div>
+        </div>
+    </div>
+</div>
 <% if (tender.user_id === uid || (userPermission !== null && ((userPermission.tender !== undefined && userPermission.tender.indexOf('5') !== -1) || (userPermission.change !== undefined && userPermission.change.indexOf('1') !== -1)))) { %>
 <!--弹出添加变更令-->
 <div class="modal fade" id="add-bj-modal" data-backdrop="static">

+ 1 - 1
app/view/stage/audit_modal.ejs

@@ -229,7 +229,7 @@
                                                             <div class="col">
                                                                 <% if (auditor.status === auditConst.status.checked) { %>
                                                                 <span class="pull-right text-success"><i class="fa fa-check-circle"></i></span>
-                                                                <% } if (auditor.status === auditConst.status.checkNo || auditor.status === auditConst.status.checkNoPre || auditor.status === auditConst.status.checkCancel) { %>
+                                                                <% } else if (auditor.status === auditConst.status.checkNo || auditor.status === auditConst.status.checkNoPre || auditor.status === auditConst.status.checkCancel) { %>
                                                                 <span class="pull-right text-warning"><i class="fa fa-share-square fa-rotate-270"></i></span>
                                                                 <% } %>
                                                             </div>

+ 109 - 0
db_script/change_audit_order.js

@@ -0,0 +1,109 @@
+const BaseUtil = require('./baseUtils');
+const querySql = BaseUtil.querySql;
+
+const change_type = {
+    changeProject: {
+        table_name: 'zh_change_project',
+        audit_table_name: 'zh_change_project_audit',
+        tb_id: 'cpid',
+    },
+    changeApply: {
+        table_name: 'zh_change_apply',
+        audit_table_name: 'zh_change_apply_audit',
+        tb_id: 'caid',
+    },
+    changePlan: {
+        table_name: 'zh_change_plan',
+        audit_table_name: 'zh_change_plan_audit',
+        tb_id: 'cpid',
+    },
+};
+
+const errorChangeTimes = [];
+
+const updateChangeTimes = async function(change, times, type) {
+    const auditData = await querySql('SELECT * FROM ?? WHERE ' + change_type[type].tb_id + ' = ? AND times = ? ORDER BY `order`', [change_type[type].audit_table_name, change.id, times]);
+    if (auditData.length === 0) return;
+
+    const auditIds = [], filterIds = [];
+    for (const ad of auditData) {
+        if (ad.audit_order) {
+            if (filterIds.indexOf(ad.aid) < 0) filterIds.push(ad.aid);
+        } else {
+            const auditId = auditIds.find(x => { return x.aid === ad.aid; });
+            if (auditId) {
+                auditId.ids.push(ad.id);
+            } else {
+                auditIds.push({ aid: ad.aid, ids: [ad.id] });
+            }
+        }
+    }
+    if (auditIds.length === 0) return;
+    if (filterIds.length > 0 && auditIds.length > 0) {
+        const object = { tid: change.tid, times };
+        object[change_type[type].tb_id] = change.id;
+        errorChangeTimes.push(object);
+    }
+    for (const [i, aid] of auditIds.entries()) {
+        for (const id of aid.ids) {
+            await querySql('UPDATE ?? SET audit_order = ? WHERE id = ?', [change_type[type].audit_table_name, i + 1, id]);
+        }
+    }
+};
+
+const updateChange = async function(change, type) {
+    for (let i = 1; i <= change.times; i++) {
+        await updateChangeTimes(change, i, type);
+    }
+};
+
+const doCompleteTest = async function(id) {
+    try {
+        const tender = await querySql('Select * From zh_tender where id > ?', [id]);
+        for (const t of tender) {
+            console.log(`Update Tender ${t.name}(${t.id}):`);
+            for (const type in change_type) {
+                const changes = await querySql('Select * From ?? where tid = ?', [change_type[type].table_name, t.id]);
+                for (const c of changes) {
+                    await updateChange(c, type);
+                }
+            }
+        }
+    } catch (err) {
+        console.log(err);
+    }
+    BaseUtil.closePool();
+    if (errorChangeTimes.length > 0) {
+        console.log('ERROR change info: ');
+        console.log(errorChangeTimes);
+    }
+};
+
+const doComplete = async function() {
+    try {
+        const tenders = await querySql('Select * From zh_tender');
+        for (const t of tenders) {
+            console.log(`Update Tender ${t.name}(${t.id}):`);
+            for (const type in change_type) {
+                const changes = await querySql('Select * From ?? where tid = ?', [change_type[type].table_name, t.id]);
+                for (const c of changes) {
+                    await updateChange(c, type);
+                }
+            }
+        }
+    } catch (err) {
+        console.log(err);
+    }
+    BaseUtil.closePool();
+    if (errorChangeTimes.length > 0) {
+        console.log('ERROR change info: ');
+        console.log(errorChangeTimes);
+    }
+};
+
+const tenderId = process.argv[3];
+if (tenderId) {
+    doCompleteTest(tenderId);
+} else {
+    doComplete();
+}

+ 21 - 0
sql/update.sql

@@ -15,5 +15,26 @@ ADD COLUMN `sp_group` int(11) NULL DEFAULT 0 COMMENT '审批组id' AFTER `audit_
 ALTER TABLE `zh_change`
 ADD COLUMN `sp_group` int(11) NULL DEFAULT 0 COMMENT '固定审批组id' AFTER `delimit`;
 
+ALTER TABLE `zh_change_project_audit`
+ADD COLUMN `audit_type`  tinyint(4) UNSIGNED NOT NULL DEFAULT 1 COMMENT '审批类型(1个人,2会签,3或签)' AFTER `opinion`,
+ADD COLUMN `audit_order`  tinyint(4) UNSIGNED NOT NULL DEFAULT 0 COMMENT '审批顺序' AFTER `audit_type`;
+
+ALTER TABLE `zh_change_project`
+ADD COLUMN `sp_group` int(11) NULL DEFAULT 0 COMMENT '固定审批组id' AFTER `content`;
+
+ALTER TABLE `zh_change_apply_audit`
+ADD COLUMN `audit_type`  tinyint(4) UNSIGNED NOT NULL DEFAULT 1 COMMENT '审批类型(1个人,2会签,3或签)' AFTER `opinion`,
+ADD COLUMN `audit_order`  tinyint(4) UNSIGNED NOT NULL DEFAULT 0 COMMENT '审批顺序' AFTER `audit_type`;
+
+ALTER TABLE `zh_change_apply`
+ADD COLUMN `sp_group` int(11) NULL DEFAULT 0 COMMENT '固定审批组id' AFTER `decimal`;
+
+ALTER TABLE `zh_change_plan_audit`
+ADD COLUMN `audit_type`  tinyint(4) UNSIGNED NOT NULL DEFAULT 1 COMMENT '审批类型(1个人,2会签,3或签)' AFTER `opinion`,
+ADD COLUMN `audit_order`  tinyint(4) UNSIGNED NOT NULL DEFAULT 0 COMMENT '审批顺序' AFTER `audit_type`;
+
+ALTER TABLE `zh_change_plan`
+ADD COLUMN `sp_group` int(11) NULL DEFAULT 0 COMMENT '固定审批组id' AFTER `decimal`;
+
 ALTER TABLE `calculation`.`zh_stage`
 ADD COLUMN `final_auditor_str` varchar(50) NOT NULL DEFAULT '' COMMENT '终审人相关' AFTER `pre_negative_qc_tp`;