Преглед изворни кода

变更申请会签或签及审批组功能

ellisran пре 1 година
родитељ
комит
7c3dc9c6bd

+ 99 - 32
app/controller/change_controller.js

@@ -2536,7 +2536,7 @@ module.exports = app => {
             let page_total = 0;
             const tp = await ctx.service.changeApply.getTp(tender.id, status);
             for (const c of changes) {
-                c.curAuditor = await ctx.service.changeApplyAudit.getAuditorByStatus(c.id, c.status, c.times);
+                c.curAuditors = await ctx.service.changeApplyAudit.getAuditorsByStatus(c.id, c.status, c.times);
                 page_total = ctx.helper.add(page_total, c.total_price);
             }
             const tender_userInfo = await ctx.service.projectAccount.getDataById(ctx.tender.data.user_id);
@@ -2613,6 +2613,7 @@ module.exports = app => {
                 auditConst: audit.changeApply,
                 ruleConst: codeRuleConst.measure,
                 changeConst,
+                auditType,
                 changeProjectList,
                 pcLists,
                 tp,
@@ -2655,6 +2656,56 @@ module.exports = app => {
         }
 
         /**
+         * 审批流程(Get)
+         * @param ctx
+         * @return {Promise<void>}
+         */
+        async changeApplyAuditors(ctx) {
+            try {
+                const id = JSON.parse(ctx.request.body.data).id;
+                const tenderId = ctx.params.id;
+                const changeInfo = await ctx.service.changeApply.getDataById(id);
+                await ctx.service.changeApply.loadChangeUser(changeInfo);
+                await ctx.service.changeApply.loadChangeAuditViewData(changeInfo);
+                ctx.body = { err: 0, msg: '', data: changeInfo };
+            } catch (error) {
+                this.log(error);
+                ctx.body = { err: 1, msg: error.toString(), data: null };
+            }
+        }
+
+        async changeApplySpGroup(ctx) {
+            try {
+                const data = JSON.parse(ctx.request.body.data);
+                const result = await ctx.service.changeApplyAudit.changeSpGroup(ctx.change, data.sp_group);
+                if (!result) {
+                    throw '切换审批组失败';
+                }
+                const auditors = await ctx.service.changeApplyAudit.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 };
+            }
+        }
+
+        /**
+         * 撤回审批
+         * @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 };
+            }
+        }
+
+        /**
          * 新增变更申请 (Post)
          *
          * @param {Object} ctx - egg全局变量
@@ -2708,22 +2759,7 @@ module.exports = app => {
          * @private
          */
         async _getChangeApplyAuditViewData(ctx) {
-            const auditConst = audit.changeApply;
-            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) {
-                for (let i = 1; i <= times; i++) {
-                    ctx.change.auditHistory.push(await ctx.service.changeApplyAudit.getAuditors(ctx.change.id, i));
-                }
-            }
-            // 获取审批流程中左边列表
-            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 || ctx.change.status === auditConst.status.revise) {
-                ctx.change.auditorList = await ctx.service.changeApplyAudit.getAuditors(ctx.change.id, ctx.change.times);
-            }
+            await ctx.service.changeApply.loadChangeAuditViewData(ctx.change);
         }
 
         async applyInformation(ctx) {
@@ -2750,6 +2786,8 @@ module.exports = app => {
                     fileList,
                     whiteList,
                     authMobile: auth_mobile,
+                    auditType,
+                    shenpiConst,
                     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,
@@ -2758,19 +2796,23 @@ 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.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({
-                        where: { project_id: ctx.session.sessionProject.id, enable: 1 },
-                        columns: ['id', 'name', 'company', 'role', 'enable', 'is_admin', 'account_group', 'mobile'],
-                    });
-                    renderData.accountList = accountList;
-                    const unitList = await ctx.service.constructionUnit.getAllDataByCondition({ where: { pid: ctx.session.sessionProject.id } });
-                    renderData.accountGroup = unitList.map(item => {
-                        const groupList = accountList.filter(item1 => item1.company === item.name);
-                        return { groupName: item.name, groupList };
-                    });
+                // 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({
+                    where: { project_id: ctx.session.sessionProject.id, enable: 1 },
+                    columns: ['id', 'name', 'company', 'role', 'enable', 'is_admin', 'account_group', 'mobile'],
+                });
+                renderData.accountList = accountList;
+                const unitList = await ctx.service.constructionUnit.getAllDataByCondition({ where: { pid: ctx.session.sessionProject.id } });
+                renderData.accountGroup = unitList.map(item => {
+                    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, 'changeApply');
                 }
                 await this.layout('change/apply_information.ejs', renderData, 'change/apply_information_modal.ejs');
             } catch (err) {
@@ -2838,7 +2880,7 @@ module.exports = app => {
                     throw '添加审核人失败';
                 }
 
-                const auditors = await ctx.service.changeApplyAudit.getAuditorsWithOwner(ctx.change.id, ctx.change.times);
+                const auditors = await ctx.service.changeApplyAudit.getUserGroup(ctx.change.id, ctx.change.times);
                 ctx.body = { err: 0, msg: '', data: auditors };
             } catch (err) {
                 this.log(err);
@@ -2871,6 +2913,31 @@ module.exports = app => {
             }
         }
 
+        async saveApplyAudit(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.changeApplyAudit.saveAudit(ctx.change.id, ctx.change.times, ctx.change.sp_group, data);
+                    const auditors = await ctx.service.changeApplyAudit.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 上下文
@@ -3059,7 +3126,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 = {

+ 101 - 0
app/middleware/change_apply_audit_check.js

@@ -0,0 +1,101 @@
+'use strict';
+
+/**
+ *
+ *
+ * @author Ellisran
+ * @date 2020/10/15
+ * @version
+ */
+
+const status = require('../const/audit').changeApply.status;
+const shenpiConst = require('../const/shenpi');
+const _ = require('lodash');
+
+module.exports = options => {
+    /**
+     * 标段校验 中间件
+     * 1. 读取标段数据(包括属性)
+     * 2. 检验用户是否可见标段(不校验具体权限)
+     *
+     * @param {function} next - 中间件继续执行的方法
+     * @return {void}
+     */
+    return function* changeApplyAuditCheck(next) {
+        try {
+            // 获取revise
+            const id = this.params.caid || this.request.body.caid;
+            if (!id) {
+                throw '您访问的变更申请不存在';
+            }
+            // const change = yield this.service.change.getDataByCondition({ cid });
+            if (!this.change) {
+                const change = yield this.service.changeApply.getDataById(id);
+                if (!change) throw '变更申请数据有误';
+                yield this.service.changeApply.loadChangeUser(change);
+                this.change = change;
+            }
+            const change = this.change;
+            if ((change.status === status.uncheck || change.status === status.checkNo || 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.changeApplyAudit.getAllDataByCondition({ where: { caid: 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, 'changeApply', 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.changeApply.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.changeApplyAudit.updateNewAuditList(change, shenpiList);
+                        yield this.service.changeApply.loadChangeUser(change);
+                        yield this.service.changeApply.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.changeApplyAudit.updateLastAudit(change, auditList, shenpiInfo.audit_id);
+                        yield this.service.changeApply.loadChangeUser(change);
+                        yield this.service.changeApply.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 - 16
app/middleware/change_apply_check.js

@@ -31,34 +31,24 @@ module.exports = options => {
                 throw '您访问的变更立项不存在';
             }
             const change = yield this.service.changeApply.getDataById(caid);
+            if (!change) throw '变更申请数据有误';
             // 读取原报、审核人数据
-            change.auditors = yield this.service.changeApplyAudit.getAuditors(change.id, change.times);
-            change.curAuditor = yield this.service.changeApplyAudit.getCurAuditor(change.id, change.times);
+            yield this.service.changeApply.loadChangeUser(change);
             // decimal小数位设置
             change.decimal = change.decimal ? JSON.parse(change.decimal) : { tp: this.tender.info.decimal.tp, up: this.tender.info.decimal.up, precision: this.tender.info.precision };
-
-            if (!change) throw '变更令数据有误';
             // 权限相关
             // todo 校验权限 (标段参与人、分享)
             const accountId = this.session.sessionUser.accountId,
                 auditorIds = _.map(change.auditors, 'aid'),
                 shareIds = [];
+            const permission = this.session.sessionUser.permission;
             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;
                 change.filePermission = true;
-            } else if (this.tender.isTourist) {
-                change.curTimes = change.times;
-                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.status === status.revise ? change.times - 1 : change.times;
                 change.filePermission = true;
             } else if ((change.status === status.checkNo || change.status === status.revise) && change.uid !== accountId) {
@@ -68,19 +58,21 @@ 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 '您无权查看该数据';
                 }
-                // change.readOnly = true;
                 change.curTimes = change.status === status.checkNo || 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.checkNo || change.status === status.revise) && accountId === change.uid);
-            change.shenpiPower = change.status === status.checking && change.curAuditor.aid === accountId;
+            change.shenpiPower = change.status === status.checking && change.curAuditorIds.indexOf(accountId) !== -1;
             this.change = change;
             yield this.service.changeApply.doCheckChangeCanCancel(this.change);
             yield next;

+ 177 - 0
app/public/js/change_apply.js

@@ -134,8 +134,185 @@ 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/apply/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.checkNo || 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.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.checkNo || 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.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) {

+ 80 - 66
app/public/js/change_apply_audit.js

@@ -54,7 +54,7 @@ $(document).ready(function () {
     })
 
     // 添加审批流程按钮逻辑
-    $('.book-list').on('click', 'dt', function () {
+    $('body').on('click', '.book-list dt', function () {
         const idx = $(this).find('.acc-btn').attr('data-groupid')
         const type = $(this).find('.acc-btn').attr('data-type')
         if (type === 'hide') {
@@ -73,94 +73,58 @@ $(document).ready(function () {
     })
 
     // 添加到审批流程中
-    $('dl').on('click', 'dd', function () {
+    $('.search-user-list').on('click', 'dd', 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 () {
+    $('body').on('click', '#auditors li a', function () {
         const li = $(this).parent();
         const data = {
             auditorId: parseInt(li.attr('auditorId')),
         };
         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)) + '审');
             }
         });
     });
+
+    // 切换审批组
+    $('#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');
@@ -174,6 +138,56 @@ $(document).ready(function () {
         $(document.body).addClass('modal-open');
     });
 });
+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) {

+ 0 - 45
app/public/js/change_project_audit.js

@@ -239,51 +239,6 @@ $(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 = [];

+ 6 - 2
app/router.js

@@ -14,6 +14,7 @@ module.exports = app => {
     const reviseAuditCheck = app.middlewares.reviseAuditCheck();
     const changeAuditCheck = app.middlewares.changeAuditCheck();
     const changeProjectAuditCheck = app.middlewares.changeProjectAuditCheck();
+    const changeApplyAuditCheck = app.middlewares.changeApplyAuditCheck();
     const uncheckTenderCheck = app.middlewares.uncheckTenderCheck();
     // 期读取中间件
     const stageCheck = app.middlewares.stageCheck();
@@ -588,14 +589,14 @@ module.exports = app => {
     app.get('/tender/:id/change/apply/status/:status', sessionAuth, tenderCheck, uncheckTenderCheck, 'changeController.applyStatus');
     app.post('/tender/:id/change/apply/add', sessionAuth, tenderCheck, uncheckTenderCheck, tenderBuildCheck, 'changeController.applyAdd');
     app.post('/tender/:id/change/apply/delete', sessionAuth, tenderCheck, uncheckTenderCheck, tenderBuildCheck, 'changeController.applyDelete');
-    app.get('/tender/:id/change/apply/:caid/information', sessionAuth, tenderCheck, uncheckTenderCheck, changeApplyCheck, 'changeController.applyInformation');
+    app.get('/tender/:id/change/apply/:caid/information', sessionAuth, tenderCheck, uncheckTenderCheck, changeApplyCheck, changeApplyAuditCheck, 'changeController.applyInformation');
     app.post('/tender/:id/change/apply/:caid/information/save', sessionAuth, tenderCheck, uncheckTenderCheck, changeApplyCheck, 'changeController.applyInformationSave');
     app.post('/tender/:id/change/apply/:caid/information/file/upload', sessionAuth, tenderCheck, uncheckTenderCheck, changeApplyCheck, 'changeController.uploadApplyFile');
     app.post('/tender/:id/change/apply/:caid/information/file/delete', sessionAuth, tenderCheck, uncheckTenderCheck, changeApplyCheck, 'changeController.deleteApplyFile');
     app.get('/tender/:id/change/apply/:caid/information/file/:fid/download', sessionAuth, tenderCheck, uncheckTenderCheck, changeApplyCheck, 'changeController.downloadApplyFile');
     app.post('/tender/:id/change/apply/:caid/information/audit/add', sessionAuth, tenderCheck, uncheckTenderCheck, changeApplyCheck, 'changeController.addApplyAudit');
     app.post('/tender/:id/change/apply/:caid/information/audit/delete', sessionAuth, tenderCheck, uncheckTenderCheck, changeApplyCheck, 'changeController.deleteApplyAudit');
-    app.post('/tender/:id/change/apply/:caid/information/audit/start', sessionAuth, tenderCheck, uncheckTenderCheck, changeApplyCheck, tenderBuildCheck, 'changeController.startApplyAudit');
+    app.post('/tender/:id/change/apply/:caid/information/audit/start', sessionAuth, tenderCheck, uncheckTenderCheck, changeApplyCheck, changeApplyAuditCheck, tenderBuildCheck, 'changeController.startApplyAudit');
     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');
@@ -603,6 +604,9 @@ module.exports = app => {
     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.post('/tender/:id/change/apply/check/again', sessionAuth, tenderCheck, uncheckTenderCheck, tenderBuildCheck, changeApplyCheck, 'changeController.checkApplyAgain');
+    app.post('/tender/:id/change/apply/:caid/information/audit/save', sessionAuth, tenderCheck, uncheckTenderCheck, changeApplyCheck, 'changeController.saveApplyAudit');
+    app.post('/tender/:id/change/apply/:caid/information/audit/spgroup', sessionAuth, tenderCheck, uncheckTenderCheck, changeApplyCheck, 'changeController.changeApplySpGroup');
+    app.post('/tender/:id/change/apply/auditors', sessionAuth, tenderCheck, uncheckTenderCheck, 'changeController.changeApplyAuditors');
     // 变更方案
     app.get('/tender/:id/change/plan', sessionAuth, tenderCheck, uncheckTenderCheck, 'changeController.plan');
     app.get('/tender/:id/change/plan/status/:status', sessionAuth, tenderCheck, uncheckTenderCheck, 'changeController.planStatus');

+ 72 - 20
app/service/change_apply.js

@@ -9,6 +9,7 @@
  */
 
 const audit = require('../const/audit').changeApply;
+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_apply';
         }
 
+        async loadChangeAuditViewData(change) {
+            const times = change.status === audit.status.checkNo || 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.changeApplyAudit.getAuditorHistory(change.id, times);
+            // 获取审批流程中左边列表
+            if ((change.status === audit.status.checkNo || change.status === audit.status.revise) && change.uid !== this.ctx.session.sessionUser.accountId && !this.ctx.session.sessionUser.is_admin) {
+                const auditors = await this.ctx.service.changeApplyAudit.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.checkNo || change.status === audit.status.revise) {
+                change.auditorList = await this.ctx.service.changeApplyAudit.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.changeApplyAudit.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, project_code, name) {
             const sql = 'SELECT COUNT(*) as count FROM ?? WHERE `tid` = ? AND `code` = ?';
             const sqlParam = [this.tableName, tenderId, code];
@@ -418,32 +463,39 @@ module.exports = app => {
             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) {
+                if (change.status === status.checkNo) {
+                    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;// 审批人撤回退回原报
+                        change.preAuditors = lastAuditors.filter(x => { return x.order === onAuditor.order; });
+                    }
+                } else {
+                    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;// 审批人撤回审批通过
                         }
-                        // else if (preAudit.status === status.checkNoPre) {
+                        // else if (preAuditCheckNoPre && change.status === status.checkNoPre) {
                         //     change.cancancel = 3;// 审批人撤回审批退回上一人
                         // }
-                        change.preAudit = preAudit;
-                    } else if (preAid === accountId && preAid === change.uid) {
+                        change.preAuditors = preAuditors;
+                    } else if (preAuditors.length === 0 && accountId === 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;// 审批人撤回退回原报
-                    }
                 }
             }
         }

Разлика између датотеке није приказан због своје велике величине
+ 647 - 291
app/service/change_apply_audit.js


+ 4 - 4
app/service/change_project_audit.js

@@ -163,7 +163,7 @@ module.exports = app => {
                     cur = await this.db.queryOne('SELECT * From ?? where cpid = ? AND times = ? AND status = ? ORDER By times DESC, `order` DESC', [this.tableName, cpId, times, status]);
                     if (!cur) return [];
 
-                    sql = 'SELECT la.`aid`, pa.`name`, pa.`company`, pa.`role`, la.`times`, la.`cpid`, la.`order`, la.audit_order, la.audit_type ' +
+                    sql = 'SELECT la.`aid`, pa.`name`, pa.`company`, pa.`role`, la.`times`, la.`cpid`, la.`order`, la.`status`, la.`audit_order`, la.`audit_type` ' +
                         '  FROM ?? AS la Left Join ?? AS pa On la.`aid` = pa.`id` ' +
                         '  WHERE la.`cpid` = ? and la.`order` = ? and times = ?';
                     sqlParam = [this.tableName, this.ctx.service.projectAccount.tableName, cpId, cur.order, times];
@@ -173,7 +173,7 @@ module.exports = app => {
                     cur = await this.db.queryOne('SELECT * From ?? where cpid = ? AND times = ? AND status = ? ORDER By times DESC, `order` DESC', [this.tableName, cpId, parseInt(times) - 1, status]);
                     if (!cur) return [];
 
-                    sql = 'SELECT la.`aid`, pa.`name`, pa.`company`, pa.`role`, la.`times`, la.`cpid`, la.`order`, la.audit_order, la.audit_type ' +
+                    sql = 'SELECT la.`aid`, pa.`name`, pa.`company`, pa.`role`, la.`times`, la.`cpid`, la.`order`, la.`status`, la.`audit_order`, la.`audit_type` ' +
                         '  FROM ?? AS la Left Join ?? AS pa On la.`aid` = pa.`id` ' +
                         '  WHERE la.`cpid` = ? and la.`order` = ? and la.`times` = ?';
                     sqlParam = [this.tableName, this.ctx.service.projectAccount.tableName, cpId, cur.order, parseInt(times) - 1];
@@ -862,12 +862,10 @@ module.exports = app => {
 
             const transaction = await this.db.beginTransaction();
             try {
-                // 整理当前流程审核人状态更新
                 // 顺移其后审核人流程顺序
                 const sql = 'UPDATE ' + this.tableName + ' SET `order` = `order` + 2 WHERE cpid = ? AND times = ? AND `order` > ?';
                 await transaction.query(sql, [change.id, selfAuditor.times, change.curAuditors[0].order]);
                 // 当前审批人2次添加至流程中
-                // 当前审批人2次添加至流程中
                 const checkCancelAuditors = [],
                     checkingAuditors = [];
                 change.preAuditors.forEach(x => {
@@ -1215,6 +1213,8 @@ module.exports = app => {
                     id: change.id,
                     status: auditConst.status.checking,
                 });
+                //  检查三方特殊推送
+                await this.ctx.service.specMsg.addChangeProjectMsg(transaction, this.ctx.session.sessionProject.id, this.ctx.change, pushOperate.change_project.flow);
                 await transaction.commit();
                 result = true;
             } catch (error) {

+ 22 - 9
app/view/change/apply.ejs

@@ -67,7 +67,7 @@
         <div class="c-body">
             <div class="sjs-height-0">
                 <table class="table table-bordered">
-                    <thead>
+                    <thead class="text-center">
                     <tr>
                         <th width="13%" id="sort_change">变更申请编号</th><th width="15%">变更工程名称</th>
                         <th width="10%">变更金额</th><th width="15%">变更立项书</th><th width="13%">变更通知书</th>
@@ -81,20 +81,31 @@
                             <td><%- c.project_code %></td>
                             <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.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/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) { %>
+                                <% } else if ((c.status === auditConst.status.checkNo || c.status === auditConst.status.revise) && c.curAuditors && c.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>
+                                <% } 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/apply/<%- 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 ? c.curAuditor.name : '' %>-<%- c.curAuditor ? c.curAuditor.role : '' %>
+                            <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>
+                                    <% } %>
                                 <% } %>
-                                <span class="<%- auditConst.statusClass[c.status] %>"><%- auditConst.statusString[c.status] %></span>
+                                <%- auditConst.auditProgress[c.status] %>
                             </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><% } %>
@@ -121,5 +132,7 @@
     const ruleType = parseInt('<%- ruleType %>');
     const openChangeProject = parseInt('<%- ctx.session.sessionProject.page_show.openChangeProject %>');
     const rulesType = '<%- rule_type %>';
+    const auditType = JSON.parse(unescape('<%- escape(JSON.stringify(auditType)) %>'));
+    const auditConst = JSON.parse(unescape('<%- escape(JSON.stringify(auditConst)) %>'));
     const changeProjectList = JSON.parse(unescape('<%- escape(JSON.stringify(changeProjectList)) %>'));
 </script>

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

@@ -33,7 +33,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 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>
                     <% } else { %>
@@ -41,7 +41,7 @@
                     <% } %>
                 <% } else if (ctx.change.status === auditConst.status.checked) { %>
                     <a href="#sp-list" data-toggle="modal" data-target="#sp-list" class="btn btn-outline-success btn-sm">审批完成</a>
-                    <% if (ctx.session.sessionUser.accountId === ctx.change.auditors2[ctx.change.auditors2.length-1].aid) { %>
+                    <% 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>
                     <% } %>

Разлика између датотеке није приказан због своје велике величине
+ 886 - 468
app/view/change/apply_information_modal.ejs


+ 25 - 1
app/view/change/apply_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) { %>
 <!--弹出添加变更令-->
 <div class="modal fade" id="add-bj-modal" data-backdrop="static">

+ 6 - 6
app/view/change/project_information_modal.ejs

@@ -1362,16 +1362,16 @@
             })
 
             // 管理员更改审批流程js部分
-            let timer2 = null;
-            let oldSearchVal2 = null;
+            let timer3 = null;
+            let oldSearchVal3 = null;
             $('body').on('input propertychange', '#admin-edit-shenpi .gr-search', function(e) {
-                oldSearchVal2 = e.target.value;
-                timer2 && clearTimeout(timer2);
-                timer2 = setTimeout(() => {
+                oldSearchVal3 = e.target.value;
+                timer3 && clearTimeout(timer3);
+                timer3 = setTimeout(() => {
                     const newVal = $(this).val();
                     const code = $(this).attr('data-code');
                     let html = '';
-                    if (newVal && newVal === oldSearchVal2) {
+                    if (newVal && newVal === oldSearchVal3) {
                         accountList.filter(item => item && (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