瀏覽代碼

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

MaiXinRong 11 月之前
父節點
當前提交
a6bd2a767f

+ 74 - 13
app/controller/change_controller.js

@@ -146,6 +146,7 @@ module.exports = app => {
                 dealCode: ctx.tender.info.deal_info.dealCode,
                 auditConst: audit.change,
                 changeConst,
+                shenpiConst,
                 state,
                 ruleType: codeRuleConst.ruleType.change,
                 ruleConst: codeRuleConst.measure,
@@ -321,6 +322,46 @@ module.exports = app => {
             }
         }
 
+        async batchChangeFun(ctx) {
+            try {
+                const data = JSON.parse(ctx.request.body.data);
+                const responseData = {
+                    err: 0,
+                    msg: '',
+                    data: {},
+                };
+                switch (data.type) {
+                    case 'get_report_list':
+                        responseData.data.shenpi_status = ctx.tender.info.shenpi.change;
+                        responseData.data.uncheckList = await ctx.service.change.getUncheckList(ctx.tender.id, ctx.tender.info.shenpi.change);
+                        if (ctx.tender.info.shenpi.change === shenpiConst.sp_status.gdspl) {
+                            responseData.data.spGroupList = await ctx.service.shenpiGroup.getGroupListByChangeType(ctx.tender.id, shenpiConst.sp_type.change, 'change');
+                        } else {
+                            // 获取所有项目参与者
+                            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'],
+                            });
+                            responseData.data.accountList = accountList;
+                            const unitList = await ctx.service.constructionUnit.getAllDataByCondition({ where: { pid: ctx.session.sessionProject.id } });
+                            responseData.data.accountGroup = unitList.map(item => {
+                                const groupList = accountList.filter(item1 => item1.company === item.name);
+                                return { groupName: item.name, groupList };
+                            });
+                        }
+                        break;
+                    case 'get_shenpi_list':
+                        responseData.data.checkingList = await ctx.service.change.getCheckingList(ctx.tender.id, ctx.session.sessionUser.accountId);
+                        break;
+                    default: throw '参数有误';
+                }
+                ctx.body = responseData;
+            } catch (err) {
+                this.log(err);
+                ctx.body = { err: 1, msg: err.toString(), data: null };
+            }
+        }
+
         /**
          * 获取审批界面所需的 原报、审批人数据等
          * @param ctx
@@ -622,6 +663,11 @@ module.exports = app => {
          * @return {Promise<void>}
          */
         async startAudit(ctx) {
+            const responseData = {
+                err: 0,
+                msg: '',
+                data: {},
+            };
             try {
                 // 检查权限等
                 if (!ctx.change) {
@@ -654,11 +700,13 @@ module.exports = app => {
                     }
                 }
                 await ctx.service.changeAudit.start(ctx.change.cid, ctx.change.times);
-                ctx.redirect(ctx.request.header.referer);
+                // ctx.redirect(ctx.request.header.referer);
+                ctx.body = responseData;
             } catch (err) {
                 this.log(err);
-                ctx.session.postError = err.toString();
-                ctx.redirect(ctx.request.header.referer);
+                // ctx.session.postError = err.toString();
+                // ctx.redirect(ctx.request.header.referer);
+                ctx.body = { err: 1, msg: err.toString(), data: null };
             }
         }
 
@@ -721,7 +769,7 @@ module.exports = app => {
                     throw '移除审核人失败';
                 }
 
-                const auditors = await ctx.service.changeAudit.getAuditorsNew(ctx.change.cid, ctx.change.times);
+                const auditors = await ctx.service.changeAudit.getUserGroup(ctx.change.cid, ctx.change.times);
                 ctx.body = { err: 0, msg: '', data: auditors };
             } catch (err) {
                 ctx.body = { err: 1, msg: err.toString(), data: null };
@@ -807,8 +855,14 @@ module.exports = app => {
                         }
                     }
                 }
-                ctx.body = { err: 0, msg: '', data: { bills: ctx.helper._.concat(ledgerData, changeLedgerData), pos: ctx.helper._.concat(posData, changePosData), dealBills } };
+                const bodyData = { bills: ctx.helper._.concat(ledgerData, changeLedgerData), pos: ctx.helper._.concat(posData, changePosData), dealBills };
+                if (data.from === 'batch') {
+                    bodyData.changeLedgerList = changeLedgerData;
+                    bodyData.changePosList = changePosData;
+                }
+                ctx.body = { err: 0, msg: '', data: bodyData };
             } catch (err) {
+                console.log(err);
                 this.log(err);
                 ctx.body = { err: 1, msg: err.toString(), data: [] };
             }
@@ -848,12 +902,18 @@ module.exports = app => {
          * @return {void}
          */
         async approval(ctx) {
+            const data = JSON.parse(ctx.request.body.data);
+            const responseData = {
+                err: 0,
+                msg: '',
+                data: {},
+            };
             try {
-                const changeData = await ctx.service.change.getDataByCondition({ cid: ctx.request.body.change_id });
+                const changeData = await ctx.service.change.getDataByCondition({ cid: data.change_id });
                 if (!changeData) {
                     throw '变更令数据错误';
                 }
-                const status = parseInt(ctx.request.body.status);
+                const status = parseInt(data.status);
                 // 判断是否到你审批,如果不是则无法审批
                 const curAuditor = await ctx.service.changeAudit.getCurAuditors(changeData.cid, changeData.times);
                 if (!curAuditor || (curAuditor && ctx.helper._.findIndex(curAuditor, { uid: ctx.session.sessionUser.accountId }) === -1)) {
@@ -867,27 +927,28 @@ module.exports = app => {
                 const pid = this.ctx.session.sessionProject.id;
                 switch (status) {
                     case 3:// 审批通过
-                        result = await ctx.service.change.approvalSuccess(pid, ctx.request.body, changeData);
+                        result = await ctx.service.change.approvalSuccess(pid, data, changeData);
                         break;
                     // case 4:// 审批终止
                     //     result = await ctx.service.change.approvalStop(ctx.request.body);
                     //     break;
                     case 5:// 审批退回到原报人
-                        result = await ctx.service.change.approvalCheckNo(pid, ctx.request.body, changeData);
+                        result = await ctx.service.change.approvalCheckNo(pid, data, changeData);
                         break;
                     case 6:// 审批退回到上一个审批人
-                        result = await ctx.service.change.approvalCheckNoPre(pid, ctx.request.body, changeData);
+                        result = await ctx.service.change.approvalCheckNoPre(pid, data, changeData);
                         break;
                     default:break;
                 }
                 if (!result) {
                     throw '审批失败';
                 }
-                ctx.redirect(ctx.request.header.referer);
+                ctx.body = responseData;
             } catch (err) {
                 console.log(err);
-                ctx.session.postError = err.toString();
-                ctx.redirect(ctx.request.header.referer);
+                // ctx.session.postError = err.toString();
+                // ctx.redirect(ctx.request.header.referer);
+                ctx.body = { err: 1, msg: err.toString(), data: null };
             }
         }
 

+ 17 - 0
app/public/js/change_information.js

@@ -637,6 +637,8 @@ $(document).ready(() => {
 function calcChangePrice() {
     let positive_tp = 0;
     let negative_tp = 0;
+    let valuation_tp = 0;
+    let unvaluation_tp = 0;
     let new_tp = 0;
     for (const c of changeList) {
         if (c.spamount) {
@@ -647,6 +649,11 @@ function calcChangePrice() {
             } else {
                 negative_tp = ZhCalc.add(negative_tp, price);
             }
+            if (c.is_valuation) {
+                valuation_tp = ZhCalc.add(valuation_tp, price);
+            } else {
+                unvaluation_tp = ZhCalc.add(unvaluation_tp, price);
+            }
         }
     }
     const updateTpList = {};
@@ -663,11 +670,21 @@ function calcChangePrice() {
         updateTpList.negative_tp = negative_tp;
         updateFlag = true;
     }
+    if (valuation_tp !== changeVp) {
+        updateTpList.valuation_tp = valuation_tp;
+        updateFlag = true;
+    }
+    if (unvaluation_tp !== changeUp) {
+        updateTpList.unvaluation_tp = unvaluation_tp;
+        updateFlag = true;
+    }
     if (updateFlag) {
         console.log(updateTpList);
         postData(window.location.pathname + '/save', { type:'update_tp', updateData: updateTpList }, function () {
             changePp = positive_tp;
             changeNp = negative_tp;
+            changeVp = valuation_tp;
+            changeUp = unvaluation_tp;
             changeTp = new_tp;
         });
     }

+ 22 - 5
app/public/js/change_information_approval.js

@@ -456,6 +456,10 @@ $(document).ready(() => {
     $('.approval-btn').on('click', function () {
         // 判断审批状态
         let returnflag = true;
+        const pData = {
+            change_id: $('#changeId').val(),
+            w_code: $.trim($('#w_code').val()),
+        }
         if ($(this).hasClass('btn-success')) {
             const sdesc = $('#success-approval').find('textarea').val();
             if (sdesc === '') {
@@ -467,21 +471,24 @@ $(document).ready(() => {
                 returnflag = false;
             } else if ($('input[name="p_code"]').val() !== undefined) {
                 $('input[name="p_code"]').val($.trim($('input[name="p_code"]').val()));
-                const postData = {
+                const postData2 = {
                     p_code: $('input[name="p_code"]').val(),
                 };
-                postDataWithAsync('/tender/' + $('#tenderId').val() + '/change/' + $('#changeId').val() + '/check/codeRepeat',postData, function (result) {
+                postDataWithAsync('/tender/' + $('#tenderId').val() + '/change/' + $('#changeId').val() + '/check/codeRepeat',postData2, function (result) {
                 }, function (data) {
                     returnflag = false;
                 });
+                pData.p_code = $('input[name="p_code"]').val();
             }
 
             if(returnflag) {
                 $('input[name="w_code"]').val($.trim($('#w_code').val()));
 
                 $('#success-approval').find('textarea').val(sdesc.replace(/\r\n/g, '<br/>').replace(/\n/g, '<br/>').replace(/\s/g, ' '));
-                if ($('#warning-text').length) $('#warning-text').remove()
-                $('#success-approval').submit();
+                if ($('#warning-text').length) $('#warning-text').remove();
+                // $('#success-approval').submit();
+                pData.status = auditConst.status.checked;
+                pData.sdesc = sdesc.replace(/\r\n/g, '<br/>').replace(/\n/g, '<br/>').replace(/\s/g, ' ');
             }
         } else {
             const sdesc = $('#fail-approval').find('textarea').val();
@@ -500,9 +507,17 @@ $(document).ready(() => {
             if(returnflag) {
                 $('#fail-approval').find('textarea').val(sdesc.replace(/\r\n/g, '<br/>').replace(/\n/g, '<br/>').replace(/\s/g, ' '));
                 $('input[name="w_code"]').val($.trim($('#w_code').val()));
-                $('#fail-approval').submit();
+                // $('#fail-approval').submit();
+                pData.status = type;
+                pData.sdesc = sdesc.replace(/\r\n/g, '<br/>').replace(/\n/g, '<br/>').replace(/\s/g, ' ');
             }
         }
+        if (pData.status) {
+            console.log(pData);
+            postData('/tender/' + $('#tenderId').val() + '/change/approval', pData, function (result) {
+                window.location.reload();
+            });
+        }
     })
 });
 const postDataWithAsync = function (url, data, successCallback, errorCallBack, showWaiting = true) {
@@ -708,6 +723,7 @@ function makePushBwmx(clinfo, listinfo, removeList, updateList) {
             if (needUpdate) {
                 updateList.push(oneUpdate);
             }
+            info = leafInfo;
         } else {
             toastr.warning('台账清单列表已不存在'+ clinfo.code +',已更新变更清单列表');
             if (changeStatus !== auditConst.status.revise) {
@@ -718,6 +734,7 @@ function makePushBwmx(clinfo, listinfo, removeList, updateList) {
             return false;
         }
     }
+    return info;
 }
 function isObjEqual(o1,o2){
     var props1 = Object.getOwnPropertyNames(o1);

+ 3 - 0
app/public/js/change_information_set.js

@@ -2043,6 +2043,9 @@ function checkChangeFrom() {
     if (returnFlag) {
         return false;
     }
+    postData(preUrl + '/audit/start', {}, function (result) {
+        window.location.reload();
+    })
 }
 // 检查上报情况
 function checkAuditorFrom () {

+ 1 - 0
app/router.js

@@ -549,6 +549,7 @@ module.exports = app => {
 
     app.post('/tender/:id/change/:cid/check/codeRepeat', sessionAuth, tenderCheck, uncheckTenderCheck, 'changeController.checkCodeRepeat');
     app.post('/tender/:id/change/:cid/info/copy', sessionAuth, tenderCheck, uncheckTenderCheck, 'changeController.copyChange');
+    app.post('/tender/:id/change/batch/fun', sessionAuth, tenderCheck, uncheckTenderCheck, 'changeController.batchChangeFun');
     // 变更单位管理
     app.post('/change/update/company', sessionAuth, 'changeController.updateCompany');
 

+ 78 - 0
app/service/change.js

@@ -21,6 +21,7 @@ const wxConst = require('../const/wechat_template');
 const pushType = require('../const/audit').pushType;
 const projectLogConst = require('../const/project_log');
 const pushOperate = require('../const/spec_3f').pushOperate;
+const shenpiConst = require('../const/shenpi');
 
 module.exports = app => {
     class Change extends app.BaseService {
@@ -2095,6 +2096,83 @@ module.exports = app => {
             }
             return list;
         }
+
+        async getUncheckList(tid, shenpi_status) {
+            const sql = 'select c.* from ?? as c where c.tid = ? ' +
+                'and (c.status = ? OR c.status = ?) AND c.code != "" AND c.name != "" AND c.content is not NULL AND c.content != ""';
+            const sqlParams = [this.tableName, tid, audit.change.status.uncheck, audit.change.status.checkNo];
+            const list = await this.db.query(sql, sqlParams);
+            const returnList = [];
+            for (const l of list) {
+                const changeList = await this.ctx.service.changeAuditList.getAllDataByCondition({ where: { cid: l.cid } });
+                // 清单名称不能为空
+                const nullList = this._.filter(changeList, function(item) {
+                    return !item.name;
+                });
+                if (nullList.length > 0) continue;
+                // 判断是否需要更新auditList
+                const auditList = await this.ctx.service.changeAudit.getAllDataByCondition({ where: { cid: l.cid, times: l.times }, orders: [['usite', 'asc']] });
+                auditList.shift();
+                if (shenpi_status === shenpiConst.sp_status.gdspl) {
+                    // 判断并获取审批组
+                    const group = await this.ctx.service.shenpiGroup.getSelectGroupByChangeType(tid, shenpiConst.sp_type.change, 'change', l.sp_group);
+                    if ((group && l.sp_group !== group.id) || (!group && l.sp_group !== 0)) {
+                        l.sp_group = group ? group.id : 0;
+                        await this.ctx.service.change.defaultUpdate({ sp_group: l.sp_group }, { where: { cid: l.cid } });
+                    }
+                    const condition = { tid, sp_type: shenpiConst.sp_type.change, sp_status: shenpi_status, sp_group: l.sp_group };
+                    const shenpiList = await this.ctx.service.shenpiAudit.getAllDataByCondition({ where: condition, orders: [['audit_order', 'asc']] });
+                    if (l.sp_group !== 0 || shenpiList.length !== 0) {
+                        // const shenpiIdList = _.map(shenpiList, 'audit_id');
+                        // 判断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.uid; });
+                                if (!shenpi || shenpi.audit_order !== audit.audit_order || shenpi.audit_type !== audit.audit_type) {
+                                    sameAudit = false;
+                                    break;
+                                }
+                            }
+                        }
+                        if (!sameAudit) {
+                            await this.ctx.service.changeAudit.updateNewAuditList(l, shenpiList);
+                        }
+                    }
+                } else if (shenpi_status === shenpiConst.sp_status.gdzs) {
+                    const shenpiInfo = await 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.usite === auditList.length - 1; });
+                    if (shenpiInfo && (lastAuditors.length === 0 || (lastAuditors.length > 1 || shenpiInfo.audit_id !== lastAuditors[0].uid))) {
+                        await this.service.changeAudit.updateLastAudit(l, auditList, shenpiInfo.audit_id);
+                    }
+                }
+                l.auditList = await this.ctx.service.changeAudit.getUniqUserGroup(l.cid, l.times);
+                l.changeList = changeList;
+                returnList.push(l);
+            }
+            return returnList;
+        }
+
+        async getCheckingList(tid, uid) {
+            const sql = 'select c.* from ?? as c LEFT JOIN ?? as ca ON c.cid = ca.cid where c.tid = ? and ca.status = ? and ca.uid = ?';
+            const sqlParams = [this.tableName, this.ctx.service.changeAudit.tableName, tid, audit.change.status.checking, uid];
+            const list = await this.db.query(sql, sqlParams);
+            const returnList = [];
+            // 判断是否是终审
+            for (const l of list) {
+                const changeList = await this.ctx.service.changeAuditList.getAllDataByCondition({ where: { cid: l.cid } });
+                const auditors = await this.ctx.service.changeAudit.getAuditorsNew(l.cid, l.times); // 全部参与的审批人
+                const auditorGroups = this.ctx.helper.groupAuditors(auditors, 'usort');
+                const userGroups = this.ctx.helper.groupAuditorsUniq(auditorGroups);
+                const finalAuditorIds = userGroups[userGroups.length - 1].map(x => { return x.uid; });
+                l.is_finalAudit = finalAuditorIds.includes(uid);
+                l.changeList = changeList;
+                l.p_code = l.p_code ? l.p_code : l.code;
+                returnList.push(l);
+            }
+            return returnList;
+        }
     }
 
     return Change;

+ 9 - 0
app/service/change_audit_list.js

@@ -408,6 +408,8 @@ module.exports = app => {
             let total_price = 0;
             let positive_tp = 0;
             let negative_tp = 0;
+            let valuation_tp = 0;
+            let unvaluation_tp = 0;
             const tp_decimal = this.ctx.change.tp_decimal ? this.ctx.change.tp_decimal : this.ctx.tender.info.decimal.tp;
             for (const cl of changeList) {
                 const price = this.ctx.helper.mul(cl.unit_price, cl.spamount, tp_decimal);
@@ -417,11 +419,18 @@ module.exports = app => {
                 } else {
                     negative_tp = this.ctx.helper.accAdd(negative_tp, price);
                 }
+                if (cl.is_valuation) {
+                    valuation_tp = this.ctx.helper.accAdd(valuation_tp, price);
+                } else {
+                    unvaluation_tp = this.ctx.helper.accAdd(unvaluation_tp, price);
+                }
             }
             const updateData = {
                 total_price,
                 positive_tp,
                 negative_tp,
+                valuation_tp,
+                unvaluation_tp,
             };
             if (updateTpDecimal) {
                 updateData.tp_decimal = tp_decimal;

+ 10 - 8
app/view/change/index.ejs

@@ -68,12 +68,14 @@
                     <span class="ml-3">本页小计:<%- page_total %>元</span><span class="ml-3">合计:<%- tp %>元</span>
                 </div>
             </div>
-            <% if (tender.user_id === ctx.session.sessionUser.accountId) { %>
             <div class="ml-auto">
-                <a href="#add-bj" data-toggle="modal" data-target="#add-bj" class="btn btn-sm btn-primary pull-right ml-1">新建变更令</a>
-                <a href="#setting" data-toggle="modal" data-target="#setting" class="btn btn-sm btn-outline-primary pull-right ml-1"><i class="fa fa-cog"></i></a>
+                <% if (tender.user_id === ctx.session.sessionUser.accountId) { %>
+                    <a href="#add-bj" data-toggle="modal" data-target="#add-bj" class="btn btn-sm btn-primary pull-right ml-1">新建变更令</a>
+                    <a href="#setting" data-toggle="modal" data-target="#setting" class="btn btn-sm btn-outline-primary pull-right ml-2"><i class="fa fa-cog"></i></a>
+                    <a href="#batch-sb" data-toggle="modal" data-target="#batch-sb" class="btn btn-sm btn-primary pull-right ml-2">批量上报</a>
+                <% } %>
+                    <a href="#batch-sp" data-toggle="modal" data-target="#batch-sp" class="btn btn-sm btn-success pull-right">批量审批</a>
             </div>
-            <% } %>
         </div>
     </div>
     <div class="content-wrap">
@@ -87,8 +89,8 @@
                     <tr><th width="3%">序号</th>
                         <th width="18%" id="sort_change">申请编号/变更令号</th><th width="24%">变更工程名称</th>
                         <th width="7%">变更性质</th><% if (ctx.session.sessionProject.page_show.openChangeState) { %><th width="7%">变更令状态</th><% } %>
-                        <th width="7%">变更金额</th><th width="7%">正变更金额</th>
-                        <th width="7%">负变更金额</th><th width="7%">审批状态</th>
+                        <th width="7%">变更金额</th><th width="7%">计价金额</th>
+                        <th width="7%">不计价金额</th><th width="7%">审批状态</th>
                         <th width="14%">审批进度</th><th width="4%">操作</th>
                     </tr>
                     </thead>
@@ -109,8 +111,8 @@
                         <td><%- ctx.helper._.find(changeState, { order: c.state }).name %></td>
                         <% } %>
                         <td style="text-align: right"><%= ctx.helper.roundNum(c.total_price, tpUnit) %></td>
-                        <td style="text-align: right"><%= ctx.helper.roundNum(c.positive_tp, tpUnit) %></td>
-                        <td style="text-align: right"><%= ctx.helper.roundNum(c.negative_tp, tpUnit) %></td>
+                        <td style="text-align: right"><%= ctx.helper.roundNum(c.valuation_tp, tpUnit) %></td>
+                        <td style="text-align: right"><%= ctx.helper.roundNum(c.unvaluation_tp, tpUnit) %></td>
                         <td class="text-center">
                             <% if (c.status === auditConst.status.uncheck && c.uid === ctx.session.sessionUser.accountId) { %>
                                 <a href="<%- '/tender/' + ctx.tender.id + '/change/' + c.cid %>/information" class="btn <%- auditConst.statusButtonClass[c.status] %> btn-sm"><%- auditConst.statusButton[c.status] %></a>

+ 2 - 0
app/view/change/information.ejs

@@ -485,6 +485,8 @@
     let changeTp = parseFloat('<%- change.total_price ? change.total_price : 0 %>');
     let changePp = parseFloat('<%- change.positive_tp ? change.positive_tp : 0 %>');
     let changeNp = parseFloat('<%- change.negative_tp ? change.negative_tp : 0 %>');
+    let changeVp = parseFloat('<%- change.valuation_tp ? change.valuation_tp : 0 %>');
+    let changeUp = parseFloat('<%- change.unvaluation_tp ? change.unvaluation_tp : 0 %>');
     const changeStatus = parseFloat('<%- change.status %>');
     const touristPermission = parseInt('<%- ctx.tender.touristPermission.file %>');
     const precision = JSON.parse('<%- JSON.stringify(precision) %>');

+ 7 - 9
app/view/change/information_modal.ejs

@@ -209,11 +209,10 @@
                         </div>
                     </div>
                 </div>
-                <form class="modal-footer" method="post" action="<%- preUrl %>/audit/start" onsubmit="return checkChangeFrom()">
-                    <button type="button" class="btn btn-secondary btn-sm" data-dismiss="modal">关闭</button>
-                    <input type="hidden" name="_csrf_j" value="<%= ctx.csrf %>">
-                    <button type="submit" class="btn btn-primary btn-sm">确认上报</button>
-                </form>
+                <div class="modal-footer">
+                    <button class="btn btn-secondary btn-sm" data-dismiss="modal">关闭</button>
+                    <button class="btn btn-primary btn-sm" onclick="checkChangeFrom();">确认上报</button>
+                </div>
             </div>
         </div>
     </div>
@@ -389,13 +388,12 @@
                         </div>
                     </div>
                 </div>
-                <form class="modal-footer" method="post" action="<%- preUrl %>/audit/start" onsubmit="return checkChangeFrom()">
+                <div class="modal-footer">
                     <button type="button" class="btn btn-secondary btn-sm" data-dismiss="modal">关闭</button>
-                    <input type="hidden" name="_csrf_j" value="<%= ctx.csrf %>">
                     <% if((ctx.change.status === auditConst.status.checkNo || ctx.change.status === auditConst.status.revise) && ctx.session.sessionUser.accountId === ctx.change.uid) { %>
-                        <button class="btn btn-primary btn-sm sp-list-item" type="submit">确认上报</button>
+                        <button class="btn btn-primary btn-sm sp-list-item" onclick="checkChangeFrom();">确认上报</button>
                     <% } %>
-                </form>
+                </div>
             </div>
         </div>
     </div>

+ 955 - 0
app/view/change/modal.ejs

@@ -167,7 +167,136 @@
         </div>
     </div>
 </div>
+<!--批量上报变更令-->
+<div class="modal fade" id="batch-sb" data-backdrop="static" style="">
+    <div class="modal-dialog modal-lg" style="max-width:1100px;" role="document">
+        <div class="modal-content">
+            <div class="modal-header">
+                <h5 class="modal-title">批量上报变更令</h5>
+                <button type="button" class="close" data-dismiss="modal" aria-label="Close">
+                    <span aria-hidden="true">&times;</span>
+                </button>
+            </div>
+            <div class="modal-body">
+                <div class="row">
+                    <!-- 左侧变更令 -->
+                    <div class="col-6" >
+                        <div class="modal-height-500" style="overflow-y:auto;">
+                            <table class="table table-hover table-bordered">
+                                <thead>
+                                <tr>
+                                    <th width="15%" class="text-center"><div class="align-middle"><input id="select-all-uncheck" type="checkbox"></div></th>
+                                    <th width="40%">变更令编号</th>
+                                    <th width="">原审批流程</th>
+                                </tr>
+                                </thead>
+                                <tbody id="uncheck_list">
+                                </tbody>
+                            </table>
+                        </div>
+                    </div>
+                    <!-- 右侧新审批流程 -->
+                    <div class="col-6">
+                        <div class=" ml-auto">
+                            <div class="dropdown text-right" id="show-audit-select">
+                            </div>
+                            <div class="card mt-1">
+                                <div class="card-header">
+                                    审批流程
+                                </div>
+                                <div class="modal-height-500" style="overflow-y:auto;">
+                                    <ul class="list-group list-group-flush" id="auditList">
+                                    </ul>
+                                </div>
+                            </div>
+                        </div>
+                    </div>
+                </div>
+                <div class="alert alert-warning mt-2 mb-0">批量操作会覆盖原审批流程,请合理选择。</div>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-sm btn-secondary" data-dismiss="modal">取消</button>
+                <button class="btn btn-sm btn-primary" id="batch_uncheck_btn">确定</button>
+            </div>
+        </div>
+    </div>
+</div>
+<!--批量上报变更令进度条modal-->
+<div class="modal fade" id="batch-sb-progress" data-backdrop="static" style="">
+    <div class="modal-dialog" role="document">
+        <div class="modal-content">
+            <div class="modal-header">
+                <h5 class="modal-title">批量上报变更令</h5>
+            </div>
+            <div class="modal-body">
+                <div class="progress">
+                    <div class="progress-tz-bar progress-bar progress-bar-striped progress-bar-animated" role="progressbar" style="width: 100%" aria-valuenow="100" aria-valuemin="0" aria-valuemax="100"></div>
+                </div>
+                <div class="mt-1 progress-tz-text">台账获取并处理中...</div>
+                <div class="change-progress">
+                </div>
+            </div>
+            <div class="modal-footer" style="display: none">
+                <button class="btn btn-sm btn-primary" onclick="window.location.reload();">刷新页面</button>
+            </div>
+        </div>
+    </div>
+</div>
 <% } %>
+<!--批量审批变更令-->
+<div class="modal fade" id="batch-sp" data-backdrop="static" style="">
+    <div class="modal-dialog modal-lg" role="document">
+        <div class="modal-content">
+            <div class="modal-header">
+                <h5 class="modal-title">批量审批变更令</h5>
+                <button type="button" class="close" data-dismiss="modal" aria-label="Close">
+                    <span aria-hidden="true">&times;</span>
+                </button>
+            </div>
+            <div class="modal-body">
+                <div class="modal-height-500" style="overflow-y:auto;">
+                    <table class="table table-hover table-bordered">
+                        <thead>
+                        <tr>
+                            <th width="5%"><div class="align-middle text-center"><input type="checkbox" id="select-all-checking"></div></th>
+                            <th width="25%">变更令编号</th>
+                            <th width="">变更令名称</th>
+                            <th width="25%">批量编号</th>
+                        </tr>
+                        </thead>
+                        <tbody id="checking_list">
+                        </tbody>
+                    </table>
+                </div>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-sm btn-secondary" data-dismiss="modal">取消</button>
+                <button type="button" class="btn btn-sm btn-primary" id="batch_checking_btn">确定</button>
+            </div>
+        </div>
+    </div>
+</div>
+<!--批量审批变更令进度条modal-->
+<div class="modal fade" id="batch-sp-progress" data-backdrop="static" style="">
+    <div class="modal-dialog" role="document">
+        <div class="modal-content">
+            <div class="modal-header">
+                <h5 class="modal-title">批量审批变更令</h5>
+            </div>
+            <div class="modal-body">
+                <div class="progress">
+                    <div class="progress-tz-bar progress-bar progress-bar-striped progress-bar-animated" role="progressbar" style="width: 100%" aria-valuenow="100" aria-valuemin="0" aria-valuemax="100"></div>
+                </div>
+                <div class="mt-1 progress-tz-text">台账获取并处理中...</div>
+                <div class="change-progress">
+                </div>
+            </div>
+            <div class="modal-footer" style="display: none">
+                <button class="btn btn-sm btn-primary" onclick="window.location.reload();">刷新页面</button>
+            </div>
+        </div>
+    </div>
+</div>
 <script>
     const ruleType = <%- ruleType %>;
     const ruleConst = JSON.parse('<%- JSON.stringify(ruleConst) %>');
@@ -197,4 +326,830 @@
     });
 </script>
 <script src="/public/js/moment/moment.min.js"></script>
+<script src="/public/js/decimal.min.js"></script>
+<script src="/public/js/zh_calc.js"></script>
+<script src="/public/js/path_tree.js"></script>
+<script src="/public/js/gcl_gather.js"></script>
+<script>
+    const shenpiConst = JSON.parse(unescape('<%- escape(JSON.stringify(shenpiConst)) %>'));
+    $(function () {
+        const intervalIds = {};
+        <% if (tender.user_id === ctx.session.sessionUser.accountId) { %>
+        let this_cid = null;
+        let shenpi_status = shenpiConst.sp_status.sqspr;
+        let accountList, accountGroup;
+        let uncheckList = [];
+        // 批量上报变更令部分
+        $('#batch-sb').on('show.bs.modal', function () {
+            postData(`/tender/${tenderId}/change/batch/fun`, { type: 'get_report_list' }, function (res) {
+                let html = '';
+                $('#select-all-uncheck').prop('checked', false);
+                shenpi_status = res.shenpi_status;
+                uncheckList = res.uncheckList;
+                for (const l of res.uncheckList) {
+                    html += '<tr>' +
+                        `<td class="text-center"><input type="checkbox" value="${l.cid}"></td>` +
+                        `<td class=""><a href="/tender/${tenderId}/change/${l.cid}/information" target="_blank">${l.code}</a></td>` +
+                        '<td>' + setAuditList(l.auditList) +'</td>' +
+                        '</tr>';
+                }
+                $('#uncheck_list').html(html);
+                if (res.uncheckList.length > 0) {
+                    $('#uncheck_list tr').eq(0).addClass('table-warning');
+                    this_cid = res.uncheckList[0].cid;
+                    makeSpList(res.uncheckList[0].auditList);
+                } else {
+                    $('#uncheck_list tr').removeClass('table-warning');
+                    this_cid = null;
+                    makeSpList([]);
+                }
+                let showAuditSelectHtml = '';
+                if (res.spGroupList && res.spGroupList.length > 0 && shenpi_status === shenpiConst.sp_status.gdspl) {
+                    let optionHtml = '';
+                    for (const g of res.spGroupList) { %>
+                        optionHtml += `<option value="${g.id}" ${res.uncheckList.length > 0 && g.id === res.uncheckList[0].sp_group ? 'selected' : ''}>${g.name}</option>`;
+                    }
+                    showAuditSelectHtml += '<div class="row">' +
+                        '<div class="col-7"></div>' +
+                    '<div class="col-5">' +
+                        '<select class="form-control form-control-sm change-sp-group">' + optionHtml + '</select>' +
+                    '</div></div>';
+                } else {
+                    accountList = res.accountList;
+                    accountGroup = res.accountGroup;
+                    showAuditSelectHtml += getSelectAuditHtml('report');
+                }
+                $('#show-audit-select').html(showAuditSelectHtml);
+            })
+        });
+
+        function setAuditList(auditList) {
+            let html = [];
+            // 去除原报
+            if (auditList.length > 1) {
+                for (const [i,a] of auditList.entries()) {
+                    if (i === 0) continue;
+                    const oneAuditNames = _.map(a, 'name');
+                    let names = oneAuditNames.join('、');
+                    if (oneAuditNames.length > 1) {
+                        names = (a[0].audit_type === auditType.key.and ? '(会)' : (a[0].audit_type === auditType.key.or ? '(或)' : '')) + names;
+                    }
+                    html.push(names);
+                }
+            }
+            return html.join('-');
+        }
+
+        function getSelectAuditHtml(code) {
+            let divhtml = '';
+            accountGroup.forEach((group, idx) => {
+                let didivhtml = '';
+                if(group) {
+                    group.groupList.forEach(item => {
+                        didivhtml += '<dd class="border-bottom p-2 mb-0 " data-id="' + item.id + '" >\n' +
+                            '<p class="mb-0 d-flex"><span class="text-primary">' + item.name + '</span><span\n' +
+                            '                                                                                class="ml-auto">' + item.mobile + '</span></p>\n' +
+                            '                                                                    <span class="text-muted">' + item.role + '</span>\n' +
+                            '                                                                    </dd>\n';
+                    });
+                    divhtml += '<dt><a href="javascript: void(0);" class="acc-btn" data-groupid="' + idx + '" data-type="hide"><i class="fa fa-plus-square"></i></a> ' + group.groupName + '</dt>\n' +
+                        '                                                                <div class="dd-content" data-toggleid="' + idx + '">\n' + didivhtml +
+                        '                                                                </div>\n';
+                }
+            });
+            const html =
+                '                                                    <button class="btn btn-outline-primary btn-sm dropdown-toggle" type="button" id="' + code + '_dropdownMenuButton" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">\n' +
+                '                                                        添加审批流程\n' +
+                '                                                    </button>\n' +
+                '                                                    <div class="dropdown-menu dropdown-menu-right" id="' + code + '_dropdownMenu" aria-labelledby="' + code + '_dropdownMenuButton" style="width:220px">\n' +
+                '                                                        <div class="mb-2 p-2"><input class="form-control form-control-sm gr-search"\n' +
+                '                                                                                     placeholder="姓名/手机 检索" autocomplete="off" data-code="' + code + '"></div>\n' +
+                '                                                        <dl class="list-unstyled book-list">\n' + divhtml +
+                '                                                        </dl>\n' +
+                '                                                    </div>\n';
+            return html;
+        }
+
+        // uncheckList tr  切换
+        $('#uncheck_list').on('click', 'tr', function () {
+            $('#uncheck_list tr').removeClass('table-warning');
+            $(this).addClass('table-warning');
+            this_cid = $(this).find('a').attr('href').split('/')[4];
+            makeSpList(uncheckList.find(l => l.cid === this_cid).auditList);
+        });
+
+        $('#uncheck_list').on('click', 'input[type="checkbox"]', function (e) {
+            e.stopPropagation();
+        });
+
+        // uncheckList tr全选
+        $('#select-all-uncheck').click(function () {
+            $('#uncheck_list tr').find('input[type="checkbox"]').prop('checked', $(this).prop('checked'));
+        });
+
+        let timer = null;
+        let oldSearchVal = null;
+        $('body').on('input propertychange', 'div[id$="_dropdownMenu"] .gr-search', function(e) {
+            oldSearchVal = e.target.value;
+            timer && clearTimeout(timer);
+            timer = setTimeout(() => {
+                const newVal = $(this).val();
+                const code = $(this).attr('data-code');
+                let html = '';
+                if (newVal && newVal === oldSearchVal) {
+                    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
+                                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 !== changesUid) {
+                                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);
+        });
+        // 添加审批流程按钮逻辑
+        $('body').on('click', 'div[id$="_dropdownMenu"] .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') {
+                $(this).parent().find(`div[data-toggleid="${idx}"]`).show(() => {
+                    $(this).children().find('i').removeClass('fa-plus-square').addClass('fa-minus-square-o');
+                    $(this).find('.acc-btn').attr('data-type', 'show');
+
+                })
+            } else {
+                $(this).parent().find(`div[data-toggleid="${idx}"]`).hide(() => {
+                    $(this).children().find('i').removeClass('fa-minus-square-o').addClass('fa-plus-square');
+                    $(this).find('.acc-btn').attr('data-type', 'hide');
+                })
+            }
+            return false;
+        });
+
+        // 添加到审批流程中
+        $('body').on('click', 'div[id$="_dropdownMenu"] dl dd', function () {
+            const id = parseInt($(this).data('id'));
+            if (!this_cid) {
+                toastr.error('请选择变更令');
+                return false;
+            }
+            if (id !== 0) {
+                postData('/tender/' + tenderId + '/change/' + this_cid + '/information/audit/add', { auditorId: id }, (datas) => {
+                    makeSpList(datas);
+                    const index = uncheckList.findIndex(item => item.cid === this_cid);
+                    if (index !== -1) {
+                        uncheckList[index].auditList = datas;
+                        $('#uncheck_list tr').eq(index).children('td').eq(2).html(setAuditList(datas));
+                    }
+                });
+            }
+        });
+
+        // 移除审批流程的审批人
+        $('body').on('click', '#auditList li a', function () {
+            const uid = $(this).parents('li').attr('data-auditid');
+            const li = $(this).parents('li');
+            const data = {
+                auditorId: uid,
+            };
+            if (!this_cid) {
+                toastr.error('请选择变更令');
+                return false;
+            }
+            postData('/tender/' + tenderId + '/change/' + this_cid + '/information/audit/delete', data, (datas) => {
+                li.remove();
+                let liIndex = 1;
+                $('#auditList li').each(function () {
+                    $(this).children('.col-auto').eq(0).text(liIndex);
+                    liIndex++;
+                });
+                const index = uncheckList.findIndex(item => item.cid === this_cid);
+                if (index !== -1) {
+                    uncheckList[index].auditList = datas;
+                    $('#uncheck_list tr').eq(index).children('td').eq(2).html(setAuditList(datas));
+                }
+            });
+        });
+        // 切换审批组
+        $('body').on('change', '.change-sp-group', function () {
+            const data = {
+                type: 'change_sp_group',
+                sp_group: parseInt($(this).val()),
+            }
+            if (!this_cid) {
+                toastr.error('请选择变更令');
+                return false;
+            }
+            if (!data.sp_group) {
+                toastr.error('请选择固定审批组');
+                return false;
+            }
+            console.log(data);
+            postData('/tender/' + tenderId + '/change/' + this_cid + '/information/audit/spgroup', data, (datas) => {
+                makeSpList(datas);
+                const index = uncheckList.findIndex(item => item.cid === this_cid);
+                if (index !== -1) {
+                    uncheckList[index].auditList = datas;
+                    $('#uncheck_list tr').eq(index).children('td').eq(2).html(setAuditList(datas));
+                }
+            });
+        });
+
+        function makeSpList(datas) {
+            const html = [];
+            // 如果是重新上报,添加到重新上报列表中
+            const auditorshtml = [];
+            if (datas.length > 0) {
+                for (const [index,data] of datas.entries()) {
+                    if (index !== 0) {
+                        html.push('<li class="list-group-item d-flex" data-auditid="'+ data[0].uid +'">');
+                        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>`);
+                        }
+                        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>');
+                    }
+                }
+            }
+            $('#auditList').html(html.join(''));
+        }
+
+        $('#batch_uncheck_btn').click(function () {
+            // 至少勾选一个
+            if ($('#uncheck_list input:checked').length === 0) {
+                toastr.error('请至少勾选一个变更令');
+                return false;
+            }
+            const cids = [];
+            $('#uncheck_list input:checked').each(function () {
+                cids.push($(this).val());
+            });
+            $('#batch-sb').modal('hide');
+            $('#batch-sb-progress').modal('show');
+            $('#batch-sb-progress .progress-tz-bar').css('width', '0%');
+            $('#batch-sb-progress .modal-footer').hide();
+            setProgress($('#batch-sb-progress .progress-tz-bar'), 30, 'tz1');
+            $('#batch-sb-progress .progress-tz-text').text('台账数据获取中...').removeClass('text-success');
+
+            let changeListData;
+            let gclGatherData;
+            postData('/tender/' + tenderId + '/change/defaultBills', { form: 'batch' }, async function (result) {
+                gclGatherModel.loadLedgerData(result.bills);
+                gclGatherModel.loadPosData(result.pos);
+
+                gclGatherData = gclGatherModel.gatherGclData();
+                gclGatherData = _.filter(gclGatherData, function (item) {
+                    return item.leafXmjs && item.leafXmjs.length !== 0;
+                });
+                for (const ggd in gclGatherData) {
+                    if (gclGatherData[ggd].leafXmjs && gclGatherData[ggd].leafXmjs.length === 0) {
+                        gclGatherData.splice(ggd, 1);
+                    }
+                    gclGatherData[ggd].code = gclGatherData[ggd].b_code;
+                    let hadcid = 0;
+                    for (const xmj of gclGatherData[ggd].leafXmjs) {
+                        const changeLedger = _.find(result.changeLedgerList, { id: xmj.gcl_id });
+                        const changePos = _.find(result.changePosList, { id: xmj.mx_id, lid: xmj.gcl_id });
+                        if (changeLedger || changePos) {
+                            xmj.cid = 1;
+                            xmj.ccid = changeLedger ? changeLedger.ccid : changePos.ccid;
+                            hadcid = 1;
+                        }
+                    }
+
+                    if (hadcid !== 0) gclGatherData[ggd].cid = 1;
+                }
+                // console.log(gclGatherData);
+                // 数组去重
+                const dealBillList = result.dealBills;
+                for (const db of gclGatherData) {
+                    const exist_index = dealBillList.findIndex(function (item) {
+                        return item.code === db.code && item.name === db.name && item.unit === db.unit && item.unit_price === db.unit_price;
+                    });
+                    if (exist_index !== -1) {
+                        dealBillList.splice(exist_index, 1);
+                    }
+                }
+                changeListData = gclGatherData.concat(dealBillList).sort(sortByCode);
+                console.log(changeListData);
+                stopProgress($('#batch-sb-progress .progress-tz-bar'), 'tz1');
+                $('#batch-sb-progress .progress-tz-text').text('台账数据加载完成').addClass('text-success');
+                $('#batch-sb-progress .change-progress').html('');
+                for (const c of cids) {
+                    const cInfo = uncheckList.find(item => item.cid === c);
+                    if (cInfo) {
+                        $('#batch-sb-progress .change-progress').append(`<div id="change-${cInfo.cid}-progress"><div class="mt-3 progress">` +
+                            '<div class="progress-bar progress-bar-striped progress-bar-animated" role="progressbar" style="width: 0%" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100"></div>' +
+                    '</div>' +
+                    `<div class="mt-1">变更令 <b>${cInfo.code}</b> <span class="progress-change-text">上报处理中...</span></div></div>`);
+                        await checkAndChange(cInfo, changeListData, result.changeLedgerList);
+                    }
+                }
+                $('#batch-sb-progress .modal-footer').show();
+            });
+        });
+        <% } %>
+        let checkingList = [];
+        $('#batch-sp').on('show.bs.modal', function () {
+            postData(`/tender/${tenderId}/change/batch/fun`, { type: 'get_shenpi_list' }, function (res) {
+                let html = '';
+                $('#select-all-checking').prop('checked', false);
+                checkingList = res.checkingList;
+                for (const l of res.checkingList) {
+                    const finalHtml = l.is_finalAudit ? `<input type="text" class="form-control form-control-sm final_pcode_input" data-cid="${l.cid}" value="${l.p_code}" />` : '';
+                    html += '<tr>' +
+                        `<td class="text-center"><input type="checkbox" value="${l.cid}"></td>` +
+                        `<td class=""><a href="/tender/${tenderId}/change/${l.cid}/information" target="_blank">${l.code}</a></td>` +
+                        `<td>${l.name}</td>` +
+                        `<td>${finalHtml}</td>` +
+                        '</tr>';
+                }
+                $('#checking_list').html(html);
+            });
+        });
+
+        $('body').on('change', '.final_pcode_input', function () {
+            const cid = $(this).data('cid');
+            const cInfo = checkingList.find(item => item.cid === cid);
+            cInfo.p_code = $(this).val();
+        });
+
+        $('#checking_list').on('click', 'input[type="checkbox"]', function (e) {
+            e.stopPropagation();
+        });
+
+        // uncheckList tr全选
+        $('#select-all-checking').click(function () {
+            $('#checking_list tr').find('input[type="checkbox"]').prop('checked', $(this).prop('checked'));
+        });
+
+        $('#batch_checking_btn').click(function () {
+            // 至少勾选一个
+            if ($('#checking_list input:checked').length === 0) {
+                toastr.error('请至少勾选一个变更令');
+                return false;
+            }
+            const cids = [];
+            $('#checking_list input:checked').each(function () {
+                cids.push($(this).val());
+            });
+            $('#batch-sp').modal('hide');
+            $('#batch-sp-progress').modal('show');
+            $('#batch-sp-progress .progress-tz-bar').css('width', '0%');
+            $('#batch-sp-progress .modal-footer').hide();
+            setProgress($('#batch-sp-progress .progress-tz-bar'), 30, 'tz2');
+            $('#batch-sp-progress .progress-tz-text').text('台账数据获取中...').removeClass('text-success');
+
+            let changeListData;
+            let gclGatherData;
+            postData('/tender/' + tenderId + '/change/defaultBills', { from: 'batch' }, async function (result) {
+                gclGatherModel.loadLedgerData(result.bills);
+                gclGatherModel.loadPosData(result.pos);
+
+                gclGatherData = gclGatherModel.gatherGclData();
+                gclGatherData = _.filter(gclGatherData, function (item) {
+                    return item.leafXmjs && item.leafXmjs.length !== 0;
+                });
+                for (const ggd in gclGatherData) {
+                    if (gclGatherData[ggd].leafXmjs && gclGatherData[ggd].leafXmjs.length === 0) {
+                        gclGatherData.splice(ggd, 1);
+                    }
+                    gclGatherData[ggd].code = gclGatherData[ggd].b_code;
+                    let hadcid = 0;
+                    for (const xmj of gclGatherData[ggd].leafXmjs) {
+                        const changeLedger = _.find(result.changeLedgerList, { id: xmj.gcl_id });
+                        const changePos = _.find(result.changePosList, { id: xmj.mx_id, lid: xmj.gcl_id });
+                        if (changeLedger || changePos) {
+                            xmj.cid = 1;
+                            xmj.ccid = changeLedger ? changeLedger.ccid : changePos.ccid;
+                            hadcid = 1;
+                        }
+                    }
+
+                    if (hadcid !== 0) gclGatherData[ggd].cid = 1;
+                }
+                // console.log(gclGatherData);
+                // 数组去重
+                const dealBillList = result.dealBills;
+                for (const db of gclGatherData) {
+                    const exist_index = dealBillList.findIndex(function (item) {
+                        return item.code === db.code && item.name === db.name && item.unit === db.unit && item.unit_price === db.unit_price;
+                    });
+                    if (exist_index !== -1) {
+                        dealBillList.splice(exist_index, 1);
+                    }
+                }
+                changeListData = gclGatherData.concat(dealBillList).sort(sortByCode);
+                console.log(changeListData);
+                stopProgress($('#batch-sp-progress .progress-tz-bar'), 'tz2');
+                $('#batch-sp-progress .progress-tz-text').text('台账数据加载完成').addClass('text-success');
+                $('#batch-sp-progress .change-progress').html('');
+                for (const c of cids) {
+                    const cInfo = checkingList.find(item => item.cid === c);
+                    if (cInfo) {
+                        $('#batch-sp-progress .change-progress').append(`<div id="change-${cInfo.cid}-progress"><div class="mt-3 progress">` +
+                            '<div class="progress-bar progress-bar-striped progress-bar-animated" role="progressbar" style="width: 0%" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100"></div>' +
+                            '</div>' +
+                            `<div class="mt-1">变更令 <b>${cInfo.code}</b> <span class="progress-change-text">审批处理中...</span></div></div>`);
+                        await checkAndChange(cInfo, changeListData, result.changeLedgerList, 'checking');
+                    }
+                }
+                $('#batch-sp-progress .modal-footer').show();
+            }, function (res) {
+            }, false);
+        });
+
+        let value = 0;
+        // let interval;
+        let stop = false;
+        function setProgress(_this, time = 50, name) {
+            let intervalId = setInterval(function () {
+                if (value < 100) {
+                    value = parseInt(value) + 1;
+                    _this.css("width", value + "%").text(value + "%");
+                } else if (value === 100) {
+                    value = parseInt(value) + 1;
+                    value = 30;
+                }
+            }, time);
+            intervalIds[name] = intervalId;
+        }
+        function resetProgress(_this) {
+            _this.removeClass('bg-success');
+            _this.css("width", "0%").text("0%").attr('aria-valuenow', '0');
+        }
+
+        function failProgress(_this, name) {
+            if (intervalIds[name]) {
+                _this.addClass('bg-danger');
+                _this.css("width", "100%").text("失败");
+                value = 0;
+                stop = true;
+                clearInterval(intervalIds[name]);
+                intervalIds[name] = 0;
+            }
+        }
+
+        function stopProgress(_this, name) {
+            if (intervalIds[name]) {
+                _this.addClass('bg-success');
+                _this.css("width", "100%").text("100%");
+                value = 0;
+                stop = true;
+                clearInterval(intervalIds[name]);
+                intervalIds[name] = 0;
+            }
+        }
+        async function checkAndChange(cInfo, changeListData, changeLedgerList, status = 'uncheck') {
+            setProgress($(`#change-${cInfo.cid}-progress .progress-bar`), 30, `change-${cInfo.cid}`);
+            // 根据已添加的清单显示
+            if (cInfo.changeList.length > 0 && cInfo.changeList[0]) {
+                const removeList = [];
+                const updateList = [];
+                const updateGclIdList = [];
+                for (const [index,clinfo] of cInfo.changeList.entries()) {
+                    if (clinfo.lid != 0) {
+                        let listinfo = changeListData.find(function (item) {
+                            return (item.id !== undefined && item.id == clinfo.lid) || (item.id === undefined && item.leafXmjs !== undefined && item.leafXmjs.length !== 0 && item.leafXmjs[0].gcl_id == clinfo.lid);
+                        });
+                        if (listinfo === undefined || (clinfo.lid && clinfo.gcl_id && clinfo.lid !== clinfo.gcl_id)) {
+                            // 有可能这部分台账发生变化,此时要更新清单lid信息,防止数据丢失
+                            const newlistinfo = changeListData.find(function (item) {
+                                return (item.id !== undefined && item.id == clinfo.gcl_id) || (item.id === undefined && item.leafXmjs !== undefined && item.leafXmjs.length !== 0 && _.find(item.leafXmjs, {gcl_id: clinfo.gcl_id }));
+                            });
+                            if ((listinfo === undefined && newlistinfo) || (listinfo && newlistinfo && !isObjEqual(listinfo, newlistinfo))) {
+                                listinfo = newlistinfo;
+                                updateList.push({id: clinfo.id, lid: newlistinfo.leafXmjs[0].gcl_id});
+                                // 更新lid
+                                cInfo.changeList[index].lid = newlistinfo.leafXmjs[0].gcl_id;
+                            }
+                        }
+                        if (listinfo === undefined) {
+                            // 针对旧数据获取清单信息
+                            listinfo = changeListData[clinfo.lid - 1];
+                            if (listinfo === undefined) {
+                                removeList.push(clinfo);
+                                break;
+                            }
+                        }
+                        const info = makePushBwmx(clinfo, listinfo, removeList, updateList);
+                        if (info) break;
+                        if (_.findIndex(changeLedgerList, { id: clinfo.gcl_id }) !== -1) {
+                            // 可能因为升降级关系:细目,分部分项等会发生变化,更新清单
+                            const updateInfo = {};
+                            if (info.code !== clinfo.xmj_code) {
+                                updateInfo.xmj_code = info.code;
+                                // changeList[index].xmj_code = info.code;
+                            }
+                            if (info.jldy !== clinfo.xmj_jldy) {
+                                updateInfo.xmj_jldy = info.jldy;
+                                // changeList[index].xmj_jldy = info.jldy;
+                            }
+                            if (info.dwgc !== clinfo.xmj_dwgc) {
+                                updateInfo.xmj_dwgc = info.dwgc;
+                                // changeList[index].xmj_dwgc = info.dwgc;
+                            }
+                            if (info.fbgc !== clinfo.xmj_fbgc) {
+                                updateInfo.xmj_fbgc = info.fbgc;
+                                // changeList[index].xmj_fbgc = info.fbgc;
+                            }
+                            if (info.fxgc !== clinfo.xmj_fxgc) {
+                                updateInfo.xmj_fxgc = info.fxgc;
+                                // changeList[index].xmj_fxgc = info.fxgc;
+                            }
+                            if (!_.isEmpty(updateInfo) && _.indexOf(updateGclIdList, clinfo.gcl_id) === -1) {
+                                updateGclIdList.push(clinfo.gcl_id);
+                                // updateInfo.gcl_id = info.id;
+                                updateList.push({ row: updateInfo, where: { tid: tenderId, gcl_id: clinfo.gcl_id } });
+                                break;
+                            }
+                        }
+                    }
+                }
+                await sleep(2000);
+                if (updateList.length > 0 || removeList.length > 0) {
+                    failProgress($('#change-' + cInfo.cid + '-progress .progress-bar'), `change-${cInfo.cid}`);
+                    $('#change-' + cInfo.cid + '-progress .mt-1').addClass('text-danger');
+                    $('#change-' + cInfo.cid + '-progress .progress-change-text').text('清单数据发生变化,需要进入到详情页处理');
+                    return;
+                }
+                // 上报或审批变更令
+                let ss = true;
+                if (status === 'uncheck') {
+                    postDataWithAsync('/tender/' + tenderId + '/change/' + cInfo.cid + '/information/audit/start', {}, function () {
+                    }, function (res) {
+                        ss = false;
+                        failProgress($('#change-' + cInfo.cid + '-progress .progress-bar'), `change-${cInfo.cid}`);
+                        $('#change-' + cInfo.cid + '-progress .mt-1').addClass('text-danger');
+                        $('#change-' + cInfo.cid + '-progress .progress-change-text').text(res);
+                        return;
+                    }, false);
+                } else {
+                    const pData = {
+                        change_id: cInfo.cid,
+                        w_code: cInfo.code,
+                        status: auditConst.status.checked,
+                        sdesc: '同意',
+                    }
+                    if (cInfo.is_finalAudit) {
+                        if (cInfo.p_code) {
+                            pData.p_code = cInfo.p_code;
+                            const postData2 = {
+                                p_code: cInfo.p_code,
+                            };
+                            let returnflag = true;
+                            postDataWithAsync('/tender/' + tenderId + '/change/' + cInfo.cid + '/check/codeRepeat',postData2, function (result) {
+                            }, function (data) {
+                                returnflag = false;
+                            });
+                            if (!returnflag) {
+                                failProgress($('#change-' + cInfo.cid + '-progress .progress-bar'), `change-${cInfo.cid}`);
+                                $('#change-' + cInfo.cid + '-progress .mt-1').addClass('text-danger');
+                                $('#change-' + cInfo.cid + '-progress .progress-change-text').text('批复编号不能与其它变更令重复!');
+                                return;
+                            }
+                        } else {
+                            failProgress($('#change-' + cInfo.cid + '-progress .progress-bar'), `change-${cInfo.cid}`);
+                            $('#change-' + cInfo.cid + '-progress .mt-1').addClass('text-danger');
+                            $('#change-' + cInfo.cid + '-progress .progress-change-text').text('变更令号(批复编号)不能为空!');
+                            return;
+                        }
+                    }
+                    postDataWithAsync('/tender/' + tenderId + '/change/approval', pData, function (result) {
+                    }, function (res) {
+                        ss = false;
+                        failProgress($('#change-' + cInfo.cid + '-progress .progress-bar'), `change-${cInfo.cid}`);
+                        $('#change-' + cInfo.cid + '-progress .mt-1').addClass('text-danger');
+                        $('#change-' + cInfo.cid + '-progress .progress-change-text').text(res);
+                        return;
+                    }, false);
+                }
+                if (ss) {
+                    stopProgress($('#change-' + cInfo.cid + '-progress .progress-bar'), `change-${cInfo.cid}`);
+                    $('#change-' + cInfo.cid + '-progress .mt-1').addClass('text-success');
+                    $('#change-' + cInfo.cid + '-progress .progress-change-text').text((status === 'uncheck' ? '上报' : '审批') + '成功');
+                }
+            }
+        }
+
+        const postDataWithAsync = function (url, data, successCallback, errorCallBack, showWaiting = true) {
+            if (showWaiting) showWaitingView();
+            $.ajax({
+                type:"POST",
+                url: url,
+                data: {'data': JSON.stringify(data)},
+                dataType: 'json',
+                cache: false,
+                async: false,
+                timeout: 60000,
+                beforeSend: function(xhr) {
+                    let csrfToken = Cookies.get('csrfToken_j');
+                    xhr.setRequestHeader('x-csrf-token', csrfToken);
+                },
+                success: function(result){
+                    if (result.err === 0) {
+                        if (successCallback) {
+                            successCallback(result.data);
+                        }
+                    } else {
+                        toastr.error(result.msg);
+                        if (errorCallBack) {
+                            errorCallBack(result.msg);
+                        }
+                    }
+                    if (showWaiting) closeWaitingView();
+                },
+                error: function(jqXHR, textStatus, errorThrown){
+                    toastr.error('error: ' + textStatus + " " + errorThrown);
+                    if (errorCallBack) {
+                        errorCallBack();
+                    }
+                    if (showWaiting) closeWaitingView();
+                }
+            });
+        };
+
+        function sleep(millisecond) {
+            return new Promise(resolve => {
+                setTimeout(() => {
+                    resolve()
+                }, millisecond)
+            })
+        }
+
+        function isObjEqual(o1,o2){
+            var props1 = Object.getOwnPropertyNames(o1);
+            var props2 = Object.getOwnPropertyNames(o2);
+            if (props1.length != props2.length) {
+                return false;
+            }
+            for (var i = 0,max = props1.length; i < max; i++) {
+                var propName = props1[i];
+                if (o1[propName] !== o2[propName]) {
+                    return false;
+                }
+            }
+            return true;
+        }
+
+        function makePushBwmx(clinfo, listinfo, removeList, updateList) {
+            let needUpdate = false;
+            const checkKey = ['name', 'code', 'unit', 'unit_price'];
+            const checkLeafKey = ['oamount', 'bwmx', 'code', 'dwgc', 'fbgc', 'fxgc', 'jldy'];
+            if (listinfo.leafXmjs !== undefined) {
+                const leafInfo = listinfo.leafXmjs.find(function (item) {
+                    // const flag = (item.bwmx === undefined || item.bwmx === clinfo.bwmx || item.jldy === clinfo.bwmx) && item.gcl_id === clinfo.gcl_id && (!clinfo.mx_id || (item.mx_id && clinfo.mx_id && item.mx_id === clinfo.mx_id)) && (item.quantity !== null ? item.quantity === parseFloat(clinfo.oamount) : 0 === parseFloat(clinfo.oamount));
+                    const flag = item.gcl_id === clinfo.gcl_id && (!clinfo.mx_id || (item.mx_id && clinfo.mx_id && item.mx_id === clinfo.mx_id));
+                    if (flag && item.code === clinfo.xmj_code) {
+                        return flag && item.code === clinfo.xmj_code;
+                    }
+                    return flag;
+                });
+                if (leafInfo) {
+                    const oneUpdate = { id: clinfo.id };
+                    // 判断要不要更新名称,单位,原数量,单价
+                    checkKey.forEach(function (key) {
+                        if (listinfo[key] !== clinfo[key]) {
+                            oneUpdate[key] = listinfo[key];
+                            // clinfo[key] = listinfo[key];
+                            if (key === 'unit') {
+                                const changeKey = ['oamount', 'oamount2', 'camount'];
+                                changeKey.forEach(function (key) {
+                                    const value = ZhCalc.round(clinfo[key], findDecimal(listinfo[key]));
+                                    if (value !== clinfo[key]) {
+                                        oneUpdate[key] = value;
+                                        // clinfo[key] = value;
+                                    }
+                                });
+                            }
+                            needUpdate = true;
+                        }
+                    });
+                    checkLeafKey.forEach(function (key) {
+                        // 只有数量是对比leafInfo,其它对比listinfo,且有些值需要重新计算
+                        if (key === 'oamount') {
+                            if (leafInfo.quantity !== clinfo[key]) {
+                                oneUpdate[key] = leafInfo.quantity;
+                                // clinfo[key] = leafInfo.quantity;
+                                needUpdate = true;
+                            }
+                        } else if (key === 'bwmx') {
+                            if (leafInfo[key] !== undefined && leafInfo[key] !== clinfo[key]) {
+                                oneUpdate[key] = leafInfo[key];
+                                // clinfo[key] = leafInfo[key];
+                                needUpdate = true;
+                            } else if (leafInfo[key] === undefined && leafInfo.jldy !== clinfo[key]) {
+                                oneUpdate[key] = leafInfo.jldy;
+                                // clinfo[key] = leafInfo.jldy;
+                                needUpdate = true;
+                            }
+                        } else {
+                            if (leafInfo[key] !== clinfo['xmj_' + key]) {
+                                oneUpdate['xmj_' + key] = leafInfo[key];
+                                // clinfo['xmj_' + key] = leafInfo[key];
+                                needUpdate = true;
+                            }
+                        }
+                    });
+                    if (needUpdate) {
+                        updateList.push(oneUpdate);
+                    }
+                } else {
+                    removeList.push(clinfo);
+                    needUpdate = true;
+                }
+            }
+            return needUpdate;
+        }
+
+        // 编号排序,多重判断
+        function sortByCode(a, b) {
+            let code1 = a.code.split('-');
+            let code2 = b.code.split('-');
+            let code1length = code1.length;
+            let code2length = code2.length;
+            for (let i = 0; i < code1length; i ++) {
+                if (i+1 <= code2length) {
+                    if (code1[i] != code2[i]) {
+                        if (/^\d+$/.test(code1[i]) && /^\d+$/.test(code2[i])) {
+                            return parseInt(code1[i]) - parseInt(code2[i]);
+                        } else if (!/^\d+$/.test(code1[i]) && /^\d+$/.test(code2[i])) {
+                            return 1;
+                        } else if (/^\d+$/.test(code1[i]) && !/^\d+$/.test(code2[i])) {
+                            return -1;
+                        } else {
+                            const str1length = code1[i].length;
+                            const str2length = code2[i].length;
+                            for (let j = 0; j < str1length; j++) {
+                                if (j+1 <= str2length) {
+                                    if (code1[i].charAt(j) != code2[i].charAt(j)) {
+                                        return code1[i].charAt(j).charCodeAt() - code2[i].charAt(j).charCodeAt();
+                                    }  else if (j+1 == str1length && code1[i].charAt(j) == code2[i].charAt(j)) {
+                                        if (str1length == str2length) {
+                                            return 0;
+                                        } else {
+                                            return str1length - str2length;
+                                        }
+                                    }
+                                } else {
+                                    if (j+1 >= str1length) {
+                                        return 1;
+                                    } else {
+                                        return -1;
+                                    }
+                                }
+                            }
+                        }
+                    } else if (i+1 == code1length && code1[i] == code2[i]) {
+                        if (code1length == code2length) {
+                            return 0;
+                        } else {
+                            return code1length - code2length;
+                        }
+                    }
+                } else {
+                    if (i+1 >= code1length) {
+                        return 1;
+                    } else {
+                        return -1;
+                    }
+                }
+            }
+        }
+    });
+</script>
 

+ 1 - 1
app/view/datacollect/index4GY18Y.ejs

@@ -1204,7 +1204,7 @@
                         dpgl_option.xAxis[0].data = dpgl_chart_option_name;
                         dpgl_option.series[0].data = dpgl_chart_option_data.end_gather_tp;
                         dpgl_option.series[1].data = dpgl_chart_option_data.end_sf_tp;
-                        if (cbgl_tenders.length >= 8) {
+                        if (cbgl_tenders.length >= 4) {
                             dpgl_option.dataZoom[0].show = true;
                         } else {
                             dpgl_option.dataZoom[0].show = false;

+ 17 - 7
db_script/change_valuation.js

@@ -6,13 +6,17 @@ const status = require('../app/const/audit').change.status;
 const querySql = BaseUtil.querySql;
 const ZhCalc = BaseUtil.ZhCalc;
 
-const checkChange = async function(change) {
+const checkChange = async function(change, decimal) {
     const changeBills = await querySql('Select * From zh_change_audit_list where cid = ?', [change.cid]);
     let valuation_tp = 0;
     let unvaluation_tp = 0;
     for (const cb of changeBills) {
-        valuation_tp = cb.is_valuation ? ZhCalc.add(valuation_tp, cb.checked_price) : valuation_tp;
-        unvaluation_tp = !cb.is_valuation ? ZhCalc.add(unvaluation_tp, cb.checked_price) : unvaluation_tp;
+        cb.tp = ZhCalc.mul(cb.spamount, cb.unit_price, change.tp_decimal || decimal.tp);
+        if (cb.is_valuation) {
+            valuation_tp = ZhCalc.add(valuation_tp, cb.tp);
+        } else {
+            unvaluation_tp = ZhCalc.add(unvaluation_tp, cb.tp);
+        }
     }
     await querySql('Update zh_change Set valuation_tp = ?, unvaluation_tp = ? Where cid = ?', [valuation_tp, unvaluation_tp, change.cid]);
     console.log(`Update Change ${change.cid}`);
@@ -23,9 +27,12 @@ const doComplete = async function() {
         const tender = await querySql('Select * From zh_tender');
         for (const t of tender) {
             console.log(`Update Tender ${t.id}:`);
-            const changes = await querySql('Select * From zh_change where tid = ? AND status = ?', [t.id, status.checked]);
+            const info = await querySql('Select * From zh_tender_info where tid = ?', [t.id]);
+            const decimal = info.length > 0 && info[0].decimal ? JSON.parse(info[0].decimal) : defaultInfo.defaultInfo.decimal;
+
+            const changes = await querySql('Select * From zh_change where tid = ?', [t.id]);
             for (const c of changes) {
-                await checkChange(c);
+                await checkChange(c, decimal);
             }
         }
     } catch (err) {
@@ -38,9 +45,12 @@ const doCompleteTest = async function(tid) {
         const tender = await querySql('Select * From zh_tender where id = ?', [tid]);
         for (const t of tender) {
             console.log(`Update Tender ${t.id}:`);
-            const changes = await querySql('Select * From zh_change where tid = ? AND status = ?', [t.id, status.checked]);
+            const info = await querySql('Select * From zh_tender_info where tid = ?', [t.id]);
+            const decimal = info.length > 0 && info[0].decimal ? JSON.parse(info[0].decimal) : defaultInfo.defaultInfo.decimal;
+
+            const changes = await querySql('Select * From zh_change where tid = ?', [t.id]);
             for (const c of changes) {
-                await checkChange(c);
+                await checkChange(c, decimal);
             }
         }
     } catch (err) {