Просмотр исходного кода

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

Tony Kang 4 месяцев назад
Родитель
Сommit
10891885dd
35 измененных файлов с 653 добавлено и 203 удалено
  1. 6 6
      app/const/contract.js
  2. 6 9
      app/controller/contract_controller.js
  3. 2 2
      app/controller/quality_controller.js
  4. 61 1
      app/controller/safe_controller.js
  5. 7 4
      app/public/js/advance.js
  6. 2 4
      app/public/js/change_apply.js
  7. 3 4
      app/public/js/change_plan.js
  8. 4 4
      app/public/js/change_project.js
  9. 24 11
      app/public/js/contract_detail.js
  10. 2 4
      app/public/js/contract_tender.js
  11. 23 5
      app/public/js/financial_pay_stage.js
  12. 2 2
      app/public/js/measure_material.js
  13. 92 0
      app/public/js/phase_pay_detail.js
  14. 9 9
      app/public/js/safe_bills_compare.js
  15. 35 9
      app/public/js/safe_stage.js
  16. 31 8
      app/public/js/setting_manage.js
  17. 2 1
      app/public/js/sp_setting_permission.js
  18. 8 1
      app/router.js
  19. 18 1
      app/service/contract_tree.js
  20. 3 1
      app/service/phase_pay_detail.js
  21. 96 3
      app/service/safe_stage.js
  22. 24 6
      app/service/safe_stage_audit.js
  23. 2 0
      app/service/safe_stage_bills.js
  24. 9 10
      app/service/sub_proj_permission.js
  25. 81 40
      app/view/advance/modal_audit.ejs
  26. 3 18
      app/view/change/apply_information_modal.ejs
  27. 3 18
      app/view/change/information_modal.ejs
  28. 16 5
      app/view/contract/col_set.ejs
  29. 2 2
      app/view/contract/index.ejs
  30. 3 1
      app/view/financial/pay_stage_modal.ejs
  31. 36 5
      app/view/payment/modal.ejs
  32. 1 2
      app/view/safe_calc/stage.ejs
  33. 17 0
      app/view/safe_calc/stage_modal.ejs
  34. 3 3
      app/view/safe_calc/sub_menu_list.ejs
  35. 17 4
      app/view/sp_setting/user_modal.ejs

+ 6 - 6
app/const/contract.js

@@ -35,8 +35,8 @@ const colSet = {
         { name: '计算2', field: 'calc2', fixed: [] },
     ],
     [type.income]: [
-        { name: '累计回', field: 'yf_price', fixed: ['alias'] },
-        { name: '回进度', field: 'stackedBar', fixed: ['alias'] },
+        { name: '累计回', field: 'yf_price', fixed: ['alias'] },
+        { name: '回进度', field: 'stackedBar', fixed: ['alias'] },
         { name: '累计实回', field: 'sf_price', fixed: ['alias'] },
         { name: '实回进度', field: 'stackedBarSf', fixed: ['alias'] },
         { name: '备注1', field: 'remark', fixed: [] },
@@ -52,8 +52,8 @@ const defaultColSet = {
         { field: 'stackedBar', show: 1 },
         { field: 'sf_price', show: 0 },
         { field: 'stackedBarSf', show: 0 },
-        { field: 'remark', show: 0, alias: '文本框1' },
-        { field: 'remark2', show: 0, alias: '文本框2' },
+        { field: 'remark', show: 0, alias: '备注1' },
+        { field: 'remark2', show: 0, alias: '备注2' },
         { field: 'calc', show: 0, alias: '计算1' },
         { field: 'calc2', show: 0, alias: '计算2' },
     ],
@@ -62,8 +62,8 @@ const defaultColSet = {
         { field: 'stackedBar', show: 1 },
         { field: 'sf_price', show: 0 },
         { field: 'stackedBarSf', show: 0 },
-        { field: 'remark', show: 0, alias: '文本框1' },
-        { field: 'remark2', show: 0, alias: '文本框2' },
+        { field: 'remark', show: 0, alias: '备注1' },
+        { field: 'remark2', show: 0, alias: '备注2' },
         { field: 'calc', show: 0, alias: '计算1' },
         { field: 'calc2', show: 0, alias: '计算2' },
     ],

+ 6 - 9
app/controller/contract_controller.js

@@ -56,14 +56,14 @@ module.exports = app => {
                 }
                 const accountList = await ctx.service.projectAccount.getAllSubProjectAccount(ctx.subProject);
                 renderData.accountList = accountList;
-                const unitList = await ctx.service.constructionUnit.getAllDataByCondition({where: {pid: ctx.session.sessionProject.id}});
+                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 };
                 }).filter(x => { return x.groupList.length > 0; });
                 // renderData.permissionConst = ctx.service.subProjPermission.PermissionConst;
                 renderData.categoryData = await this.ctx.service.category.getAllCategory(ctx.subProject);
-                renderData.companys = await this.ctx.service.constructionUnit.getAllDataByCondition({where: {pid: ctx.session.sessionProject.id}});
+                renderData.companys = await this.ctx.service.constructionUnit.getAllDataByCondition({ where: { pid: ctx.session.sessionProject.id } });
                 // renderData.templates = await this.ctx.service.filingTemplateList.getAllTemplate(ctx.session.sessionProject.id);
                 await this.layout('contract/index.ejs', renderData, 'contract/modal.ejs');
             } catch (err) {
@@ -280,7 +280,7 @@ module.exports = app => {
                     stdChapters,
                 };
 
-                const contractColSet = await ctx.service.contractColSet.getContractColSet(ctx.session.sessionProject.id, ctx.contractOptions.tid, ctx.contract_type);
+                const contractColSet = await ctx.service.contractColSet.getContractColSet(ctx.subProject.id, ctx.contractOptions.tid, ctx.contract_type);
                 renderData.colSet = ctx.service.contractColSet.analysisColSetWithDefine(contractConst.colSet[ctx.contract_type], contractColSet.info, contractConst.defaultColSet[ctx.contract_type]);
                 if (ctx.session.sessionUser.is_admin) {
                     const accountList = await ctx.service.projectAccount.getAllSubProjectAccount(ctx.subProject);
@@ -302,14 +302,14 @@ module.exports = app => {
         /**
          * 保存列设置
          * @param ctx
-         * @returns {Promise<void>}
+         * @return {Promise<void>}
          */
         async colSet(ctx) {
             try {
                 const colType = ctx.request.body.col_type;
                 const colSet = JSON.parse(ctx.request.body.col_set);
                 const contractType = ctx.request.body.type;
-                await ctx.service.contractColSet.setContractColSet(ctx.session.sessionProject.id, ctx.contractOptions.tid, contractType, colType, colSet);
+                await ctx.service.contractColSet.setContractColSet(ctx.subProject.id, ctx.contractOptions.tid, contractType, colType, colSet);
                 ctx.redirect(ctx.request.header.referer);
             } catch (err) {
                 ctx.log(err);
@@ -343,7 +343,7 @@ module.exports = app => {
                         return { groupName: item.name, groupList };
                     }).filter(x => { return x.groupList.length > 0; });
                     responseData.data.contractAudits = contractAudits;
-                    responseData.data.accountGroup = ctx.helper._.filter(accountGroup, function (item) {
+                    responseData.data.accountGroup = ctx.helper._.filter(accountGroup, function(item) {
                         return item.groupList.length > 0;
                     });
                 }
@@ -375,9 +375,6 @@ module.exports = app => {
                     case 'update':
                         responseData.data = await ctx.service.contractTree.updateCalc(options, data.postData);
                         break;
-                    case 'update-contract':
-                        responseData.data = await ctx.service.contract.updateCalc(options, data.postData);
-                        break;
                     case 'paste-block':
                         responseData.data = await this._pasteBlock(ctx, data.postData, options);
                         break;

+ 2 - 2
app/controller/quality_controller.js

@@ -155,7 +155,7 @@ module.exports = app => {
                 };
                 switch (data.type) {
                     case 'add-audit':
-                        if (!data.key || !tenderPermissionKeys.includes(data.key) || !Object.keys(tPsKeys).includes(data.key)) throw '参数有误';
+                        if (!data.key || (!tenderPermissionKeys.includes(data.key) && !Object.keys(tPsKeys).includes(data.key))) throw '参数有误';
                         // // 判断用户是单个还是数组
                         uids = data.id instanceof Array ? data.id : [data.id];
                         // // 判断该用户的组是否已加入到表中,已加入则提示无需添加
@@ -178,7 +178,7 @@ module.exports = app => {
                         responseData.data = await ctx.service.tenderPermission.getPartsPermission(tenderInfo.id, insertKeys);
                         break;
                     case 'del-audit':
-                        if (!data.key || !tenderPermissionKeys.includes(data.key) || !Object.keys(tPsKeys).includes(data.key)) throw '参数有误';
+                        if (!data.key || (!tenderPermissionKeys.includes(data.key) && !Object.keys(tPsKeys).includes(data.key))) throw '参数有误';
                         uids = data.id instanceof Array ? data.id : [data.id];
                         if (uids.length === 0) throw '没有选择要移除的用户';
                         const deleteKeys = data.together ? tPsKeys[data.key] : [data.key];

+ 61 - 1
app/controller/safe_controller.js

@@ -190,9 +190,24 @@ module.exports = app => {
         }
         async safeCompare(ctx) {
             try {
+                await this._getStageAuditViewData(ctx);
+                // 流程审批人相关数据
+                const accountList = await ctx.service.projectAccount.getAllSubProjectAccount(ctx.subProject);
+                const unitList = await ctx.service.constructionUnit.getAllDataByCondition({ where: { pid: ctx.session.sessionProject.id } });
+                const accountGroup = unitList.map(item => {
+                    const groupList = accountList.filter(item1 => item1.company === item.name);
+                    return { groupName: item.name, groupList };
+                }).filter(x => { return x.groupList.length > 0; });
+                // 是否已验证手机短信
+                const pa = await ctx.service.projectAccount.getDataById(ctx.session.sessionUser.accountId);
                 const renderData = {
                     jsFiles: this.app.jsFiles.common.concat(this.app.jsFiles.safe.compare),
-                    auditConst,
+                    auditConst: auditConst.common,
+                    accountList,
+                    accountGroup,
+                    shenpiConst,
+                    auditType: auditConst.auditType,
+                    authMobile: pa.auth_mobile,
                 };
                 await this.layout('safe_calc/compare.ejs', renderData);
             } catch (err) {
@@ -485,6 +500,51 @@ module.exports = app => {
             }
             ctx.redirect(ctx.request.header.referer);
         }
+        /**
+         * 期审批流程(POST)
+         * @param ctx
+         * @return {Promise<void>}
+         */
+        async loadAuditors(ctx) {
+            try {
+                const order = JSON.parse(ctx.request.body.data).order;
+                const tenderId = ctx.params.tid;
+                const stage = await ctx.service.safeStage.getStageByOrder(tenderId, order);
+                await ctx.service.safeStage.loadUser(stage);
+                await ctx.service.safeStage.loadAuditViewData(stage);
+                ctx.body = { err: 0, msg: '', data: stage };
+            } catch (error) {
+                ctx.log(error);
+                ctx.body = { err: 1, msg: error.toString(), data: null };
+            }
+        }
+
+        async loadPaySafeData(ctx) {
+            try {
+                // 先获取你创建的标段及参与的标段
+                const tenderList = await ctx.service.paymentTender.getAllDataByCondition({ where: { spid: ctx.subProject.id } });
+                // 获取你创建的目录及对应目录下的所有目录
+                const folderList = await ctx.service.paymentFolder.getAllDataByCondition({ where: { spid: ctx.subProject.id } });
+                for (const tender of tenderList) {
+                    tender.details = await ctx.service.paymentDetail.getAllDataByCondition({ where: { tender_id: tender.id, type: 1 } });
+                }
+                ctx.body = { err: 0, msg: '', data: { tenderList, folderList } };
+            } catch(err) {
+                ctx.log(error);
+                ctx.ajaxErrorBody(err, '获取安全生产费旧数据失败');
+            }
+        }
+        async copyPaySafeData(ctx) {
+            try {
+                const data = JSON.parse(ctx.request.body.data);
+                if (!data.tid) throw '参数错误';
+                await ctx.service.safeStage.copyPaySafeData(data.tid);
+                ctx.body = {err: 0, msg: '迁移旧数据成功', data: null };
+            } catch(err) {
+                ctx.log(err);
+                ctx.ajaxErrorBody(err, '迁移旧数据失败');
+            }
+        }
 
         async inspectionTender(ctx) {
             try {

+ 7 - 4
app/public/js/advance.js

@@ -221,15 +221,18 @@ $(document).ready(function () {
                         <div class="card">
                             <div class="card-body p-3">
                                 <div class="card-text">
-                                    <p class="mb-1"><span class="h5">${auditor.name}</span>
-                                        <span
+                                    <div class="col-9">
+                                        <span class="h6">${auditor.name}</span>
+                                        <span class="text-muted">${auditor.company} - ${auditor.role}</span>
+                                    </div>
+                                    <div class="col">
+                                                <span
                                             class="pull-right
                                                             ${auditConst.statusClass[auditor.status]}">${auditor.status !== auditConst.status.uncheck ? auditConst.statusString[auditor.status] : ''}
                                             ${auditor.status === auditConst.status.checkNo ? user.name : ''}
                                             ${auditor.status === auditConst.status.checkNoPre ? auditors[index-1].name : ''}
                                         </span>
-                                    </p>
-                                    <p class="text-muted mb-0">${auditor.role}</p>
+                                    </div>
                                 </div>
                             </div>`
 

+ 2 - 4
app/public/js/change_apply.js

@@ -281,10 +281,8 @@ $(document).ready(() => {
                     for (const [i, auditor] of group.auditors.entries()) {
                         historyHTML.push(`<div class="card-text p-2 py-3 row ${(i > 0 ? 'border-top' : '')}">`);
                         let companyRolePart = '';
-                        if (group.audit_order !== 0) {
-                            const rolePart = auditor.role ? ' - ' + auditor.role : '';
-                            companyRolePart = `<span class="text-muted ml-1">${auditor.company}${rolePart}</span>`;
-                        }
+                        const rolePart = auditor.role ? ' - ' + auditor.role : '';
+                        companyRolePart = `<span class="text-muted ml-1">${auditor.company}${rolePart}</span>`;
                         historyHTML.push(`<div class="col-10"><span class="h6">${auditor.name}</span>${companyRolePart}</div>`);
                         historyHTML.push('<div class="col">');
                         if (auditor.status === auditConst.status.checked || group.status === auditConst.status.cancelRevise) {

+ 3 - 4
app/public/js/change_plan.js

@@ -280,10 +280,9 @@ $(document).ready(() => {
                     for (const [i, auditor] of group.auditors.entries()) {
                         historyHTML.push(`<div class="card-text p-2 py-3 row ${(i > 0 ? 'border-top' : '')}">`);
                         let companyRolePart = '';
-                        if (group.audit_order !== 0) {
-                            const rolePart = auditor.role ? ' - ' + auditor.role : '';
-                            companyRolePart = `<span class="text-muted ml-1">${auditor.company}${rolePart}</span>`;
-                        }
+                        const rolePart = auditor.role ? ' - ' + auditor.role : '';
+                        companyRolePart = `<span class="text-muted ml-1">${auditor.company}${rolePart}</span>`;
+                        
                         historyHTML.push(`<div class="col-10"><span class="h6">${auditor.name}</span>${companyRolePart}</div>`);
                         historyHTML.push('<div class="col">');
                         if (auditor.status === auditConst.status.checked || group.status === auditConst.status.cancelRevise) {

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

@@ -288,10 +288,10 @@ $(document).ready(() => {
                     for (const [i, auditor] of group.auditors.entries()) {
                         historyHTML.push(`<div class="card-text p-2 py-3 row ${ ( i > 0 ? 'border-top' : '') }">`);
                         let companyRolePart = '';
-                        if (group.audit_order !== 0) {
-                            const rolePart = auditor.role ? ' - ' + auditor.role : '';
-                            companyRolePart = `<span class="text-muted ml-1">${auditor.company}${rolePart}</span>`;
-                        }
+                        
+                        const rolePart = auditor.role ? ' - ' + auditor.role : '';
+                        companyRolePart = `<span class="text-muted ml-1">${auditor.company}${rolePart}</span>`;
+                        
                         historyHTML.push(`<div class="col-10"><span class="h6">${auditor.name}</span>${companyRolePart}</div>`);
                         historyHTML.push('<div class="col">');
                         if (auditor.status === auditConst.status.checked || group.status === auditConst.status.cancelRevise) {

+ 24 - 11
app/public/js/contract_detail.js

@@ -57,7 +57,7 @@ $(document).ready(function() {
                 return !permission_edit || data.c_code;
             },
             calc: function (data) {
-                return !permission_edit || data?.children?.length > 0;
+                return !permission_edit || (data && data.children && data.children.length > 0);
             },
         },
     }
@@ -79,10 +79,10 @@ $(document).ready(function() {
     }
     // 根据 colSet 组装最终显示的列配置
     colSet.forEach(col => {
-      const colInfo = colMap?.[col.field];
-      if (colInfo && col.show) {
-        contractSpreadSetting.cols.push({...colInfo, title: col?.alias || col.name});
-      }
+        const colInfo = colMap && colMap[col.field];
+        if (colInfo && col.show) {
+            contractSpreadSetting.cols.push(Object.assign({}, colInfo, { title: (col && col.alias) || col.name }));
+        }
     });
 
 
@@ -120,7 +120,7 @@ $(document).ready(function() {
             node.stackedBarSf = [];
             node.stackedBarSfTips = [];
             node.stackedBarSf.push({ color: '#bbb', percent: ZhCalc.div(node.total_price, base), field: 'total_price', hintNum: false });
-            node.stackedBarSf.push({ color: '#007bff', percent: ZhCalc.div(node.sf_price, base), field: 'sf_price', hintNum: true });
+            node.stackedBarSf.push({ color: '#28a745', percent: ZhCalc.div(node.sf_price, base), field: 'sf_price', hintNum: true });
             node.stackedBarSfTips.push(`合同金额: ${node.total_price || 0}`);
             node.stackedBarSfTips.push(`累计${contract_type === contractConst.type.expenses ? '实付' : '实回'}: ${node.sf_price || 0}`);
         }
@@ -415,7 +415,7 @@ $(document).ready(function() {
         },
         setContract: function (sheet) {
             const node = SpreadJsObj.getSelectObject(sheet);
-            
+
             if (node && node.c_code) {
                 $('#htdetail-table').show();
                 $('#htpay-table').show();
@@ -597,7 +597,7 @@ $(document).ready(function() {
                 const col = info.sheet.zh_setting.cols[info.col];
                 const sortData = info.sheet.zh_dataType === 'tree' ? info.sheet.zh_tree.nodes : info.sheet.zh_data;
                 const node = sortData[info.row];
-                
+
                 const data = {
                     id: node.id,
                 };
@@ -607,16 +607,26 @@ $(document).ready(function() {
                 if (orgValue == info.editingText || ((!orgValue || orgValue === '') && (newValue === ''))) {
                     return;
                 }
+                if (['calc', 'calc2'].includes(col.field) && node.c_code && isNaN(newValue)) {
+                    toastr.error('不能输入其它非数字类型字符');
+                    SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                    return;
+                }
                 // 获取更新数据
                 if (info.editingText) {
                     const text = newValue;
-                    data[col.field] = text;
+                    // 如果列类型为数字,尝试转换为数值;非数字则保留为 null,防止 NaN 传播
+                    if (col.type === 'Number') {
+                        const num = parseFloat(text);
+                        data[col.field] = Number.isFinite(num) ? num : null;
+                    } else {
+                        data[col.field] = text;
+                    }
                 } else {
                     data[col.field] = null;
                 }
                 // 更新至服务器
-                
-                postData(window.location.pathname + '/update', {postType: node?.c_code ? 'update-contract' : 'update', postData: data}, function (result) {
+                postData(window.location.pathname + '/update', {postType: 'update', postData: data}, function (result) {
                     const refreshNode = contractTree.loadPostData(result);
                     contractTreeSpreadObj.refreshTree(info.sheet, refreshNode);
                 });
@@ -714,10 +724,13 @@ $(document).ready(function() {
         deletePress: function (sheet) {
             if (!sheet.zh_setting) return;
             const sel = sheet.getSelections()[0], datas = [];
+
             for (let iRow = sel.row; iRow < sel.row + sel.rowCount; iRow++) {
                 let bDel = false;
                 const node = sheet.zh_tree.nodes[iRow];
                 const data = sheet.zh_tree.getNodeKeyData(node);
+                if (node.c_code) data['c_code'] = node.c_code;
+
                 for (let iCol = sel.col; iCol < sel.col + sel.colCount; iCol++) {
                     const col = sheet.zh_setting.cols[iCol];
                     const style = sheet.getStyle(iRow, iCol);

+ 2 - 4
app/public/js/contract_tender.js

@@ -82,10 +82,10 @@ const tenderListSpec = (function(){
         html.push(`<tr>
                         <th style="width: 5%">合同个数</th>
                         <th style="width: 8%">合同金额</th>
-                        <th style="width: 15%">支出进度</th>
+                        <th style="width: 15%">应付进度</th>
                         <th style="width: 5%">合同个数</th>
                         <th style="width: 8%">合同金额</th>
-                        <th style="width: 15%">回进度</th>
+                        <th style="width: 15%">回进度</th>
                     </tr>`, '</thead>');
         return html.join('');
     }
@@ -110,5 +110,3 @@ const tenderListSpec = (function(){
     }
     return { getTenderNodeHtml, getTenderTreeHeaderHtml, calculateParent }
 })();
-
-

+ 23 - 5
app/public/js/financial_pay_stage.js

@@ -40,6 +40,7 @@ const tenderListSpec = (function(){
 
 
 let auditUtils;
+let globalSelectedUserIds = new Set();
 $(function () {
     autoFlashHeight();
 
@@ -97,7 +98,7 @@ $(function () {
 
     let timer = null
     let oldSearchVal = null
-    $('#liucheng').on('input propertychange', '.gr-search', function(e) {
+    $('#liucheng').on('input propertychange', '.gr-search', function (e) {
         oldSearchVal = e.target.value;
         timer && clearTimeout(timer);
         timer = setTimeout(() => {
@@ -106,9 +107,15 @@ $(function () {
             let html = '';
             if (newVal && newVal === oldSearchVal) {
                 accountList.filter(item => item && (item.name.indexOf(newVal) !== -1 || (item.mobile && item.mobile.indexOf(newVal) !== -1))).forEach(item => {
+                    let isSelected = globalSelectedUserIds.has(item.id);
+                    let displayStyle = isSelected ? 'display: inline;' : 'display: none;';
+                    
                     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>
+                                class="ml-auto">${item.mobile || ''}</span>
+                                <span class="selected-mark text-success ml-2" style="${displayStyle}"><i class="fa fa-check"></i></span>
+
+                        </p>
                         <span class="text-muted">${item.role || ''}</span>
                     </dd>`
                 });
@@ -122,9 +129,14 @@ $(function () {
                         </a> ${group.groupName}</dt>
                         <div class="dd-content" data-toggleid="${idx}">`;
                         group.groupList.forEach(item => {
+                            let isSelected = globalSelectedUserIds.has(item.id);
+                            let displayStyle = isSelected ? 'display: inline;' : 'display: none;';
                             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>
+                                        class="ml-auto">${item.mobile || ''}</span>
+                                    <span class="selected-mark text-success ml-2" style="${displayStyle}"><i class="fa fa-check"></i></span>
+                                
+                                </p>
                                 <span class="text-muted">${item.role || ''}</span>
                             </dd>`;
                         });
@@ -169,9 +181,12 @@ $(function () {
     });
     auditUtils = {
         makeReportListHtml: function (flow) {
+            globalSelectedUserIds = new Set((flow.permissionList || []).map(pl => pl.uid));
             let addHtml = '';
+            $('#report_audit_dropdownMenu dd .selected-mark').hide();
             $('#select-all-ptAudits').prop('checked', false);
             for (const pl of flow.permissionList) {
+                $(`#report_audit_dropdownMenu dd[data-id="${pl.uid}"] .selected-mark`).show();
                 addHtml += `<tr>
                                 <td class="text-center"><input type="checkbox" class="select-ptAudit" data-id="${pl.id}"></td><td>${pl.name}</td><td>${pl.company}</td>
                                 <td class="text-center"><input type="checkbox" class="save-report" data-id="${pl.id}" ${pl.is_report ? 'checked' : ''}></td><td class="text-center"><a href="javascript:void(0);" class="text-danger remove-audit" data-id="${pl.id}">移除</a></td>
@@ -328,7 +343,9 @@ $(function () {
     };
 
     // 选中填报人
-    $('body').on('click', 'div[id="report_audit_dropdownMenu"] dl dd', function () {
+    $('body').on('click', 'div[id="report_audit_dropdownMenu"] dl dd', function (e) {
+        console.log('选中填报人');
+        e.stopPropagation();
         const tid = parseInt($('#shenpi-tender-list tr.bg-warning').data('tid'));
         const tender = tenders.find(t => t.id === tid);
         if (!tender) {
@@ -497,7 +514,7 @@ $(function () {
     });
 
     // 选中审批人
-    $('body').on('click', '#shenpi-list div[id$="_dropdownMenu"] dl dd', function () {
+    $('body').on('click', '#shenpi-list div[id$="_dropdownMenu"] dl dd', function (e) {
         const tid = parseInt($('#shenpi-tender-list tr.bg-warning').data('tid'));
         const tender = tenders.find(t => t.id === tid);
         if (!tender) {
@@ -528,6 +545,7 @@ $(function () {
             audit_type: parseInt($(this).parents('li').find('select[class*="audit-type-key"]')[0].value),
             audit_order: $(this).parents('li').index() + 1,
         };
+        
         const _self = $(this);
         postData(`/sp/${spid}/financial/pay/stage/save`, { type: 'add-shenpi-audit', shenpi: prop, tid }, function (result) {
             const data = result.shenpi;

+ 2 - 2
app/public/js/measure_material.js

@@ -85,9 +85,9 @@ $(function () {
                                                 <div class="card">
                                                     <div class="card-body px-3 py-0">
                                                         <div class="card-text p-2 py-3 row">
-                                                            <div class="col">
+                                                            <div class="col-10">
                                                                 <span class="h6">${user.name}</span>
-                                                                <span class="text-muted ml-1">${user.role}</span>
+                                                                <span class="text-muted ml-1">${user.company} - ${user.role}</span>
                                                             </div>
                                                             <div class="col">
                                                                 <span class="pull-right text-success"><i class="fa fa-check-circle"></i></span>

+ 92 - 0
app/public/js/phase_pay_detail.js

@@ -846,12 +846,104 @@ $(document).ready(() => {
                 SpreadJsObj.loadSheetData(sheet, SpreadJsObj.DataType.Tree, payTree);
                 payEvent.refreshActn();
             },
+            clipboardPasting: function(e, info) {
+                if (!info.sheet.zh_setting) return;
+
+                const pasteData = info.pasteData.html
+                    ? SpreadJsObj.analysisPasteHtml(info.pasteData.html)
+                    : (info.pasteData.text === ''
+                        ? SpreadJsObj.Clipboard.getAnalysisPasteText()
+                        : SpreadJsObj.analysisPasteText(info.pasteData.text));
+
+                const range = info.cellRange;
+                if (range.colCount > 1) {
+                    toastr.error('请勿复制粘贴多列数据');
+                    return;
+                }
+                const col = info.sheet.zh_setting.cols[range.col];
+                const data = { postType: 'update', postData:[] };
+                for (let iRow = range.row; iRow < range.row + range.rowCount; iRow++) {
+                    const node = info.sheet.zh_tree.nodes[iRow];
+                    let valid;
+                    switch (col.field) {
+                        case 'name':
+                            valid = !payUtils.check.isFixed(node);
+                            break;
+                        case 'tp':
+                            valid = !payUtils.check.tpReadOnly(node);
+                            break;
+                        case 'start_tp':
+                            valid = !payUtils.check.startTpReadOnly(node);
+                            break;
+                        case 'range_tp':
+                            valid = !payUtils.check.rangeTpReadOnly(node);
+                            break;
+                        default:
+                            valid = false;
+                    }
+                    if (!valid) {
+                        toastr.error('不可复制粘贴数据');
+                        return;
+                    }
+                    const updateData = { id: node.id };
+                    const validText = col.wordWrap ? pasteData[iRow-range.row][range.col-range.col] : trimInvalidChar(pasteData[iRow-range.row][range.col-range.col]);
+                    switch(col.field) {
+                        case 'tp':
+                            const [tpValid, tpMsg] = payUtils.check.isSf(node)
+                                ? payCalc.checkSfExpr(validText, updateData, node, payTree)
+                                : payCalc.checkExpr(validText, updateData, node, payTree);
+                            if (!tpValid) {
+                                toastr.warning(tpMsg);
+                                SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                                return;
+                            }
+                            break;
+                        case 'start_tp':
+                            const [sValid, sMsg] = payCalc.checkStartExpr(node, validText, updateData);
+                            if (!sValid) {
+                                toastr.warning(sMsg);
+                                SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                                return;
+                            }
+                            break;
+                        case 'range_tp':
+                            const [rValid, rMsg] = payCalc.checkRangeExpr(node, validText, updateData);
+                            if (!rValid) {
+                                toastr.warning(rMsg);
+                                SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                                return;
+                            }
+                            break;
+                        default:
+                            if (col.type === 'Number') {
+                                updateData[col.field] = _.toNumber(validText) || 0;
+                            } else {
+                                updateData[col.field] = validText || '';
+                            }
+                            break;
+                    }
+                    data.postData.push(updateData);
+                }
+                if (data.postData.length === 0) return;
+
+                postData('update', data, function (result) {
+                    if (result.reload) {
+                        payEvent.reloadPays(result.reload);
+                    } else {
+                        const refreshData = payTree.loadPostData(result);
+                        payEvent.refreshTree(refreshData);
+                    }
+                }, function () {
+                    SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                });
+            },
         };
         spread.bind(spreadNS.Events.SelectionChanged, payEvent.selectionChanged);
         if (!readOnly) {
             spread.bind(spreadNS.Events.EditStarting, payEvent.editStarting);
             spread.bind(spreadNS.Events.EditEnded, payEvent.editEnded);
             spread.bind(spreadNS.Events.ButtonClicked, payEvent.buttonClicked);
+            spread.bind(spreadNS.Events.ClipboardPasting, payEvent.clipboardPasting);
             SpreadJsObj.addDeleteBind(spread, payEvent.deletePress);
             $('a[name="base-opr"]').click(function () {
                 payEvent.baseOpr(this.getAttribute('type'));

+ 9 - 9
app/public/js/safe_bills_compare.js

@@ -57,11 +57,11 @@ $(document).ready(function() {
                 this.spreadSetting.cols.push(col);
             }
             for (const role of roles) {
-                this.spreadSetting.fieldSufs.push(role.order);
+                this.spreadSetting.fieldSufs.push(role.active_order);
                 for (const ec of this.spreadSetting.extraCols) {
                     const col = JSON.parse(JSON.stringify(ec));
                     col.title = _.replace(col.title, '%s', role.name);
-                    col.field = _.replace(col.field, '{%d}', role.order);
+                    col.field = _.replace(col.field, '{%d}', role.active_order);
                     this.spreadSetting.cols.push(col);
                 }
             }
@@ -74,7 +74,7 @@ $(document).ready(function() {
             const findHis = function (role, history) {
                 let his = null;
                 for (const h of history) {
-                    if (h.times < role.times || (h.times === role.times && h.order <= role.order)) {
+                    if (h.audit_times < role.audit_times || (h.audit_times === role.audit_times && h.active_order <= role.active_order)) {
                         his = h;
                     } else {
                         break;
@@ -84,16 +84,16 @@ $(document).ready(function() {
             };
             for (const d of datas) {
                 if (!d.tree_is_leaf) continue;
-                d.cur_his.sort((x, y) => { return x.times === y.times ? x.order - y.order : x.times - y.times; });
+                d.cur_his.sort((x, y) => { return x.audit_times === y.audit_times ? x.active_order - y.active_order : x.audit_times - y.audit_times; });
                 for (const r of roles) {
                     if (r.latest) {
-                        d[`qty_${r.order}`] = d.cur_qty;
-                        d[`tp_${r.order}`] = d.cur_tp;
+                        d[`qty_${r.active_order}`] = d.cur_qty;
+                        d[`tp_${r.active_order}`] = d.cur_tp;
                     } else {
                         const rHis = findHis(r, d.cur_his);
                         if (rHis) {
-                            d[`qty_${r.order}`] = rHis.qty;
-                            d[`tp_${r.order}`] = rHis.tp;
+                            d[`qty_${r.active_order}`] = rHis.qty;
+                            d[`tp_${r.active_order}`] = rHis.tp;
                         }
                     }
                 }
@@ -103,7 +103,7 @@ $(document).ready(function() {
             this.initSpread(roles);
             this.analysisCompareData(datas, roles);
             this.tree.loadDatas(datas);
-            this.tree.setting.calcFields = roles.map(x => { return `tp_${x.order}`});
+            this.tree.setting.calcFields = roles.map(x => { return `tp_${x.audit_order}`});
             treeCalc.calculateAll(this.tree);
             SpreadJsObj.loadSheetData(this.sheet, SpreadJsObj.DataType.Tree, this.tree);
             SpreadJsObj.loadTopAndSelect(this.sheet, this.ckBillsSpread);

+ 35 - 9
app/public/js/safe_stage.js

@@ -60,7 +60,7 @@ $(document).ready(() => {
             }
         });
         return auditorsHTML;
-    }
+    };
     const getAuditHistroyHtml = function (auditHistory) {
         const historyHTML = [];
         auditHistory.forEach((his, idx) => {
@@ -134,7 +134,7 @@ $(document).ready(() => {
             historyHTML.push('</ul>');
         });
         return historyHTML.join('');
-    }
+    };
     // 获取审批流程
     $('a[data-target="#sp-list" ]').on('click', function () {
         postData('stage/auditors', { order: $(this).attr('stage-order') }, function (result) {
@@ -143,6 +143,39 @@ $(document).ready(() => {
         });
     });
 
+    // 迁移旧数据
+    $('#move-his').click(function() {
+        postData('payhis', {}, function(result) {
+            const validTenders = result.tenderList.filter(x => { return x.details.length > 0});
+            if (validTenders.length === 0) {
+                toastr.warning('该项目下,无可迁移的旧数据');
+                return;
+            }
+
+            const html = [];
+            for (const vt of validTenders) {
+                html.push('<tr>', `<td class="text-center"><input type="checkbox" name="move-his-check" value="${vt.id}"></td>`, `<td>${vt.name}</td>`, `<td class="text-center">共${vt.details.length}期</td>`, '</tr>');
+            }
+            $('#mhsb-list').html(html.join(''));
+            $('#move-his-safe-bills').modal('show');
+        });
+    });
+    $('body').on('click', '[name=move-his-check]', function(){
+        const tenders = $('[name=move-his-check]');
+        for (const t of tenders) {
+            if (t !== this) t.checked = false;
+        }
+    });
+    $('#move-his-ok').click(function() {
+        if ($('[name=move-his-check]:checked').length === 0) {
+            toastr.warning('请选择需要迁移的旧标段');
+            return;
+        }
+        postData('copyPayHis', { tid: $('[name=move-his-check]:checked').val()}, function() {
+            window.location.reload();
+        });
+    });
+
     $.subMenu({
         menu: '#sub-menu', miniMenu: '#sub-mini-menu', miniMenuList: '#mini-menu-list',
         toMenu: '#to-menu', toMiniMenu: '#to-mini-menu',
@@ -159,11 +192,4 @@ $(document).ready(() => {
             autoFlashHeight();
         }
     });
-
-    $('select[name=stage]').select2({
-        language: 'zh-CN',
-        width: '100%',
-        multiple: true,
-        maximumSelectionlength: 6,
-    });
 });

+ 31 - 8
app/public/js/setting_manage.js

@@ -263,10 +263,10 @@ $(document).ready(() => {
             else if (href === '#tzpro') currentNavType = 'schedule';
             else if (href === '#htgl') currentNavType = 'contract';
             else if (href === '#sgrz') currentNavType = 'construction';
-            else if (href === '#zlgl') currentNavType = 'quality';
-            else if (href === '#zlxj') currentNavType = 'inspection';
+            else if (href === '#quality') currentNavType = 'quality';
+            else if (href === '#safe') currentNavType = 'safe';
             if(currentNavType) {
-                setTimeout(() => updateSelectedMarks(currentNavType), 0);
+                setTimeout(() => updateSelectedMarks(), 0);
             }
         }
         
@@ -436,6 +436,10 @@ $(document).ready(() => {
             postData('/sp/' + spid + '/' + type + '/' + cur_tenderid + '/audit/save', { type: 'del-audit', id }, function (data) {
                 $('#'+ type + '-users').find('tr[data-id="'+ id +'"]').remove();
                 $('#remove-user').modal('hide');
+                selectedUserIdsByType[type] = selectedUserIdsByType[type].filter(function(userId) {
+                    return userId !== showId;
+                });
+                updateSelectedMarks();
             });
         } else if (_.includes(tenderPermissionKeys, type)) {
             postData('/sp/' + spid + '/quality/' + cur_tenderid + '/audit/save', { type: 'del-audit', id, key: type }, function (data) {
@@ -459,6 +463,10 @@ $(document).ready(() => {
             postData('/sp/' + spid + '/quality/' + cur_tenderid + '/audit/save', { type: 'del-audit', id, key: type, together: 1 }, function (data) {
                 $('#'+ type + '-users').find('tr[data-uid="'+ id +'"]').remove();
                 $('#remove-user').modal('hide');
+                selectedUserIdsByType[type] = selectedUserIdsByType[type].filter(function(userId) {
+                    return userId !== showId;
+                });
+                updateSelectedMarks();
             });
         } else {
             
@@ -626,6 +634,7 @@ $(document).ready(() => {
                 }
                 if (_.includes(saIdList, id)) {
                     toastr.error('该用户已存在列表中,无需重复添加');
+                    e.stopPropagation();
                     return;
                 }
 
@@ -667,6 +676,7 @@ $(document).ready(() => {
                 }
                 if (_.includes(saIdList, id)) {
                     toastr.error('该用户已存在列表中,无需重复添加');
+                    e.stopPropagation();
                     return;
                 }
 
@@ -676,6 +686,7 @@ $(document).ready(() => {
                 };
                 postData('/sp/' + spid + '/contract/tender/' + cur_tenderid + '/audit/save', prop, function (datas) {
                     setContractHtml(datas);
+                    updateSelectedMarks();
                 });
             } else if (type === 'construction') {
                 const user = _.find(accountList, function (item) {
@@ -687,6 +698,7 @@ $(document).ready(() => {
                 }
                 if (_.includes(saIdList, id)) {
                     toastr.error('该用户已存在列表中,无需重复添加');
+                    e.stopPropagation();
                     return;
                 }
 
@@ -707,6 +719,7 @@ $(document).ready(() => {
                 }
                 if (_.includes(saIdList, id)) {
                     toastr.error('该用户已存在列表中,无需重复添加');
+                    e.stopPropagation();
                     return;
                 }
 
@@ -725,9 +738,9 @@ $(document).ready(() => {
                 }
                 if (_.includes(saIdList, id)) {
                     toastr.error('该用户已存在列表中,无需重复添加');
+                    e.stopPropagation();
                     return;
                 }
-
                 const prop = {
                     id: id,
                     type: 'add-audit',
@@ -1084,8 +1097,8 @@ function updateSelectedMarks(forcedType = null) {
             case 'tzpro': currentType = 'schedule'; break;
             case 'htgl': currentType = 'contract'; break;
             case 'sgrz': currentType = 'construction'; break;
-            case 'zlgl': currentType = 'quality'; break;
-            case 'zlxj': currentType = 'inspection'; break;
+            case 'quality': currentType = 'quality'; break;
+            case 'safe': currentType = 'safe'; break;
             default: currentType = null;
         }
     }
@@ -1098,6 +1111,7 @@ function updateSelectedMarks(forcedType = null) {
             case 'construction': currentUserListSelector = '#construction-users'; break;
             case 'quality': currentUserListSelector = '#quality-users'; break;
             case 'inspection': currentUserListSelector = '#inspection-users'; break;
+            case 'safe': currentUserListSelector = '#safe-users'; break;
         }
         selectedIdsArray = selectedUserIdsByType[currentType];
     } else {
@@ -1185,7 +1199,8 @@ let selectedUserIdsByType = {
     contract: [],
     construction: [],
     quality: [],
-    inspection: []
+    inspection: [],
+    safe: []
 };
 function setTouristHtml(tourists) {
     let html = '';
@@ -1336,7 +1351,15 @@ function setTenderPermissionsHtml(auditList, key, keys) {
     for (const m of auditList) {
         html.push(getUserPermissionsHtml(m, key, keys));
     }
+    if (Array.isArray(auditList)) {
+        auditList.forEach(function(sa) {
+            if (sa.uid !== undefined && sa.uid !== null) {
+                selectedUserIdsByType[key].push(parseInt(sa.uid, 10)); 
+            }
+        });
+    }
     $('#' + key + '-users').html(html.join(''));
+    updateSelectedMarks();
 }
 
 const getUserPermissionsHtml = function(user, key, keys) {
@@ -1351,7 +1374,7 @@ const getUserPermissionsHtml = function(user, key, keys) {
             html.push(`<td class="text-center"><input type="checkbox" data-key="${block.key}" data-block="${p.key}" data-value="${p.value}" ${checked}></td>`);
         }
     }
-    html.push(`<td class="text-center"><a href="#remove-user1" data-id="${user.uid}" data-toggle="modal" data-target="#remove-user" class="btn btn-sm btn-outline-danger remove-${key}-user">移除</a></td>`);
+    html.push(`<td class="text-center"><a href="#remove-user1" data-id="${user.uid}" data-show-id="${user.uid}" data-toggle="modal" data-target="#remove-user" class="btn btn-sm btn-outline-danger remove-${key}-user">移除</a></td>`);
     html.push('</tr>');
     return html.join('');
 };

+ 2 - 1
app/public/js/sp_setting_permission.js

@@ -288,7 +288,8 @@ $(document).ready(() => {
         for (const u of userCheck) {
             copyData.uid.push(parseInt(u.getAttribute('uid')));
         }
-        postData(`/sp/${spid}/setting/user/permission/update`, { copy: copyData }, function(result) {
+        const existType = $('[name=exist-type]:checked').val();
+        postData(`/sp/${spid}/setting/user/permission/update`, { copy: copyData, existType }, function(result) {
             toastr.success('设置成功');
             $('#copy-user-batch').modal('hide');
         });

+ 8 - 1
app/router.js

@@ -328,6 +328,7 @@ module.exports = app => {
     app.post('/sp/:id/contract/tender/:tid/detail/:type/:cid/pay/:cpid/file/upload', sessionAuth, subProjectCheck, contractCheck, 'contractController.uploadFile');
     app.post('/sp/:id/contract/tender/:tid/detail/:type/:cid/pay/:cpid/file/delete', sessionAuth, subProjectCheck, contractCheck, 'contractController.deleteFile');
     app.get('/sp/:id/contract/tender/:tid/detail/:type/:cid/pay/:cpid/file/:fid/download', sessionAuth, subProjectCheck, contractCheck, 'contractController.downloadFile');
+    app.post('/sp/:id/contract/tender/:tid/col-set', sessionAuth, subProjectCheck, contractCheck, 'contractController.colSet');
     // 项目合同管理
     app.get('/sp/:id/contract', sessionAuth, subProjectCheck, contractCheck, 'contractController.detail');
     app.post('/sp/:id/contract/audit/save', sessionAuth, subProjectCheck, contractCheck, 'contractController.auditSave');
@@ -345,6 +346,7 @@ module.exports = app => {
     app.post('/sp/:id/contract/detail/:type/:cid/pay/:cpid/file/upload', sessionAuth, subProjectCheck, contractCheck, 'contractController.uploadFile');
     app.post('/sp/:id/contract/detail/:type/:cid/pay/:cpid/file/delete', sessionAuth, subProjectCheck, contractCheck, 'contractController.deleteFile');
     app.get('/sp/:id/contract/detail/:type/:cid/pay/:cpid/file/:fid/download', sessionAuth, subProjectCheck, contractCheck, 'contractController.downloadFile');
+    app.post('/sp/:id/contract/col-set', sessionAuth, subProjectCheck, contractCheck, 'contractController.colSet');
 
 
     // 资料归集-列表
@@ -511,7 +513,7 @@ module.exports = app => {
     app.post('/sp/:id/safe/tender/:tid/stage/add', sessionAuth, subProjectCheck, tenderCheck, tenderPermissionCheck, 'safeController.addStage');
     app.post('/sp/:id/safe/tender/:tid/stage/delete', sessionAuth, subProjectCheck, tenderCheck, tenderPermissionCheck, 'safeController.delStage');
     app.post('/sp/:id/safe/tender/:tid/stage/save', sessionAuth, subProjectCheck, tenderCheck, tenderPermissionCheck, 'safeController.saveStage');
-    app.post('/sp/:id/safe/tender/:tid/stage/auditors', sessionAuth, subProjectCheck, tenderCheck, tenderPermissionCheck, 'payController.loadAuditors');
+    app.post('/sp/:id/safe/tender/:tid/stage/auditors', sessionAuth, subProjectCheck, tenderCheck, tenderPermissionCheck, 'safeController.loadAuditors');
     app.get('/sp/:id/safe/tender/:tid/stage/:sorder/bills', sessionAuth, subProjectCheck, tenderCheck, tenderPermissionCheck, safeStageCheck, 'safeController.safeBills');
     app.get('/sp/:id/safe/tender/:tid/stage/:sorder/compare', sessionAuth, subProjectCheck, tenderCheck, tenderPermissionCheck, safeStageCheck, 'safeController.safeCompare');
     app.post('/sp/:id/safe/tender/:tid/stage/:sorder/load', sessionAuth, subProjectCheck, tenderCheck, tenderPermissionCheck, safeStageCheck, 'safeController.safeLoad');
@@ -525,6 +527,8 @@ module.exports = app => {
     app.post('/sp/:id/safe/tender/:tid/stage/:sorder/audit/check', sessionAuth, subProjectCheck, tenderCheck, tenderPermissionCheck, safeStageCheck, 'safeController.stageAuditCheck');
     app.post('/sp/:id/safe/tender/:tid/stage/:sorder/audit/checkAgain', sessionAuth, subProjectCheck, tenderCheck, tenderPermissionCheck, safeStageCheck, 'safeController.stageAuditCheckAgain');
     app.post('/sp/:id/safe/tender/:tid/stage/:sorder/audit/checkCancel', sessionAuth, subProjectCheck, tenderCheck, tenderPermissionCheck, safeStageCheck, 'safeController.stageAuditCheckCancel');
+    app.post('/sp/:id/safe/tender/:tid/payhis', sessionAuth, subProjectCheck, projectManagerCheck, 'safeController.loadPaySafeData');
+    app.post('/sp/:id/safe/tender/:tid/copyPayHis', sessionAuth, subProjectCheck, tenderCheck, projectManagerCheck, 'safeController.copyPaySafeData');
     // 安全巡检
     app.get('/sp/:id/safe/inspection', sessionAuth, subProjectCheck, 'safeController.inspectionTender');
     app.get('/sp/:id/safe/tender/:tid/inspection', sessionAuth, subProjectCheck, tenderCheck, tenderPermissionCheck, 'safeController.inspection');
@@ -1194,4 +1198,7 @@ module.exports = app => {
     app.get('/wx/work/:corpid/project', wxWorkAuth, 'wechatController.workProject');
     app.get('/wx/work/:corpid/test', wxWorkAuth, 'wechatController.workTest');
     app.get('/wx/tips', 'wechatController.tips');
+
+    // bimface
+    // app.get('/bimface', sessionAuth, 'bimfaceController.index');
 };

+ 18 - 1
app/service/contract_tree.js

@@ -184,23 +184,40 @@ module.exports = app => {
             const transaction = await this.db.beginTransaction();
             try {
                 const updateDatas = [];
+                const updateContractDatas = [];
                 for (const row of datas) {
                     const updateNode = await this.getDataById(row.id);
                     if (!updateNode) {
+                        const contractNode = await this.ctx.service.contract.getDataById(row.id);
+                        if (contractNode) {
+                          const updateContractData = this._filterUpdateInvalidField(contractNode.id, row)
+                          updateContractDatas.push(updateContractData);
+                          continue;
+                        }
                         throw '提交数据错误';
                     }
                     const updateData = this._filterUpdateInvalidField(updateNode.id, row);
                     // 如非子节点,需要更新底下所有已选清单的分部分项等数据
                     updateDatas.push(updateData);
                 }
+
                 if (updateDatas.length > 0) await transaction.updateRows(this.tableName, updateDatas);
+                if (updateContractDatas.length > 0) await transaction.updateRows(this.ctx.service.contract.tableName, updateContractDatas);
                 await transaction.commit();
             } catch (err) {
                 await transaction.rollback();
                 throw err;
             }
 
-            return { update: await this.getDataById(ids) };
+            return { update: await this.getDataByIds(ids) };
+        }
+
+        async getDataByIds(ids) {
+          const resultData = []
+          for (const id of ids) {
+            resultData.push(await this.getDataByCondition({ id }) || await this.ctx.service.contract.getDataByCondition({ id }));
+          }
+          return resultData;
         }
 
         async getDataByKid(options, kid) {

+ 3 - 1
app/service/phase_pay_detail.js

@@ -101,7 +101,8 @@ class PayCalculator {
         }
         for (const b of this.bases) {
             const value = b.checkStart && (!pay.pre_used && pay.start_tp)
-                ? this.ctx.helper.sub(this.addBase[pay.checkStart], pay.start_tp)
+                // ? Math.max(this.ctx.helper.sub(this.addBase[b.checkStart], pay.start_tp), 0)
+                ? this.ctx.helper.sub(this.addBase[b.checkStart], pay.start_tp)
                 : b.value;
             formula = formula.replace(b.reg, value);
         }
@@ -370,6 +371,7 @@ class PhasePayDetail extends TreeService {
         const payCalc = new PayCalculator(this.ctx, prePhase);
         for (const pd of preData) {
             delete pd.id;
+            delete pd.postil;
             pd.tp = 0;
             pd.pre_tp = pd.end_tp;
             pd.phase_id = phasePay.id;

+ 96 - 3
app/service/safe_stage.js

@@ -9,6 +9,7 @@
  */
 
 const audit = require('../const/audit').common;
+const auditType = require('../const/audit').auditType;
 const shenpiConst = require('../const/shenpi');
 
 module.exports = app => {
@@ -112,7 +113,7 @@ module.exports = app => {
                 await conn.delete(this.ctx.service.safeStageFile.tableName, { stage_id: id });
                 await conn.delete(this.ctx.service.safeStageAudit.tableName, { stage_id: id });
                 // 记录删除日志
-                // await this.ctx.service.projectLog.addProjectLog(conn, projectLogConst.type.safeStage, projectLogConst.status.delete, `第${info.phase_order}期`);
+                // await this.ctx.service.projectLog.addProjectLog(conn, projectLogConst.type.safeStage, projectLogConst.status.delete, `第${info.stage_order}期`);
                 await conn.commit();
             } catch (err) {
                 await conn.rollback();
@@ -230,7 +231,7 @@ module.exports = app => {
                     safeStage.curSort = 0;
                 } else {
                     const checkNoAudit = await this.service.safeStageAudit.getDataByCondition({
-                        phase_id: safeStage.id, audit_times: safeStage.audit_times - 1, audit_status: audit.status.checkNo,
+                        stage_id: safeStage.id, audit_times: safeStage.audit_times - 1, audit_status: audit.status.checkNo,
                     });
                     safeStage.curSort = checkNoAudit.active_order;
                 }
@@ -251,7 +252,7 @@ module.exports = app => {
             const shenpi_status = info.shenpi.safe_stage;
             if ((safeStage.audit_status === status.uncheck || safeStage.audit_status === status.checkNo) && shenpi_status !== shenpiConst.sp_status.sqspr) {
                 // 进一步比较审批流是否与审批流程设置的相同,不同则替换为固定审批流或固定的终审
-                const auditList = await this.ctx.service.safeStageAudit.getAllDataByCondition({ where: { phase_id: safeStage.id, audit_times: safeStage.audit_times }, orders: [['audit_order', 'asc']] });
+                const auditList = await this.ctx.service.safeStageAudit.getAllDataByCondition({ where: { stage_id: safeStage.id, audit_times: safeStage.audit_times }, orders: [['audit_order', 'asc']] });
                 auditList.shift();
                 if (shenpi_status === shenpiConst.sp_status.gdspl) {
                     const shenpiList = await this.ctx.service.shenpiAudit.getAllDataByCondition({ where: { tid: safeStage.tid, sp_type: shenpiConst.sp_type.safe_payment, sp_status: shenpi_status } });
@@ -284,6 +285,98 @@ module.exports = app => {
                 }
             }
         }
+
+        async _getUserInfo(id) {
+            if (!this.cacheUserInfo) this.cacheUserInfo = [];
+            const cache = this.cacheUserInfo.find(x => { return x.id === id; });
+            if (cache) return cache;
+            const user = await this.ctx.service.projectAccount.getDataById(id);
+            this.cacheUserInfo.push(user);
+            return user;
+        }
+        async copyPaySafeData(payTenderId) {
+            const details = await this.ctx.service.paymentDetail.getAllDataByCondition({ where: { tender_id: payTenderId, type: 1 } });
+            const tid = this.ctx.tender.id;
+            const conn = await this.db.beginTransaction();
+            try {
+                const insertStage = [], insertBills = [], insertAudit = [], insertFile = [];
+                for (const detail of details) {
+                    const stage = {
+                        id: this.uuid.v4(), tid, create_user_id: detail.uid, update_user_id: this.ctx.session.sessionUser.accountId,
+                        stage_order: detail.order, stage_code: detail.code, stage_date: detail.s_time,
+                        audit_times: detail.times, audit_status: detail.status,
+                        bills_decimal: detail.bills_decimal || JSON.stringify({ up: 2, tp: 2, qty: 3 }),
+                        create_time: detail.in_time, final_auditor_str: '',
+                    };
+                    insertStage.push(stage);
+                    const safeBills = await this.ctx.service.paymentSafeBills.getAllDataByCondition({ where: { detail_id: detail.id } });
+                    for (const sb of safeBills) {
+                        sb.tender_id = tid;
+                        delete sb.detail_id;
+                        sb.stage_id = stage.id;
+                        const his = sb.cur_his ? JSON.parse(sb.cur_his) : [];
+                        for (const h of his) {
+                            h.audit_times = h.times;
+                            h.active_order = h.order;
+                            delete h.times;
+                            delete h.order;
+                        }
+                        sb.cur_his = JSON.stringify(his);
+                        insertBills.push(sb);
+                    }
+                    const user = await this._getUserInfo(detail.uid);
+                    const audits = await this.ctx.service.paymentDetailAudit.getAllDataByCondition({ where: { td_id: detail.id }, orders: [['times', 'asc'], ['order', 'asc']]});
+                    if (detail.status === audit.status.checked) {
+                        const fa = await this._getUserInfo(audits[audits.length - 1].aid);
+                        stage.final_auditor_str =`${fa.name}${(fa.role ? '-' + fa.role : '')}`;
+                    }
+                    if (audits.length === 0) {
+                        insertAudit.push({
+                            tid, stage_id: stage.id, audit_id: user.id,
+                            name: user.name, company: user.company, role: user.role, mobile: user.mobile,
+                            audit_times: a.times, audit_order: 0, active_order: 0, audit_type: auditType.key.common,
+                        });
+                    }
+                    for (const a of audits) {
+                        const auditor = await this._getUserInfo(a.aid);
+                        if (a.order === 1) {
+                            insertAudit.push({
+                                tid, stage_id: stage.id, audit_id: user.id,
+                                name: user.name, company: user.company, role: user.role, mobile: user.mobile,
+                                audit_times: a.times, audit_order: 0, active_order: 0, audit_type: auditType.key.common,
+                                audit_time: a.begin_time, audit_status: audit.status.checked,
+                            });
+                        }
+                        const same = audits.filter(x => { return a.aid === x.aid && a.times === x.times; });
+                        const audit_order = this.ctx.helper._.min(same.map(x => { return x.order}));
+                        insertAudit.push({
+                           tid, stage_id: stage.id, audit_id: auditor.id,
+                           name: auditor.name, company: auditor.company, role: auditor.role, mobile: auditor.mobile,
+                           audit_times: a.times, audit_order, active_order: a.order, audit_type: auditType.key.common,
+                           audit_status: a.status, audit_time: a.end_time, opinion: a.opinion || '',
+                        });
+                    }
+                    const files = await this.ctx.service.paymentDetailAtt.getAllDataByCondition({ where: { td_id: detail.id } });
+                    for (const f of files) {
+                        const fu = await this._getUserInfo(f.uid);
+                        insertFile.push({
+                            id: this.uuid.v4(), tid, stage_id: stage.id, type: 'bills', rela_id: f.safe_id,
+                            filename: f.filename, fileext: f.fileext, filesize: f.filesize, filepath: f.filepath,
+                            user_id: fu.id, user_name: fu.name, user_company: fu.company, user_role: fu.role,
+                            create_time: fu.upload_time, update_time: fu.upload_time,
+                        });
+                    }
+                }
+                await conn.insert(this.tableName, insertStage);
+                await conn.insert(this.ctx.service.safeStageBills.tableName, insertBills);
+                await conn.insert(this.ctx.service.safeStageAudit.tableName, insertAudit);
+                if (insertFile.length > 0) await conn.insert(this.ctx.service.safeStageFile.tableName, insertFile);
+                await conn.commit();
+            } catch (err) {
+                await conn.rollback();
+                throw err;
+            }
+        }
     }
 
     return SafeStage;

+ 24 - 6
app/service/safe_stage_audit.js

@@ -73,7 +73,7 @@ module.exports = app => {
                 for (const g of group) {
                     const his = {
                         auditYear: '', auditDate: '', auditTime: '', audit_time: null,
-                        audit_type: g[0].audit_type, audit_order: g[0].audit_order,
+                        audit_type: g[0].audit_type, audit_order: g[0].audit_order, audit_times: g[0].audit_times, active_order: g[0].active_order,
                         auditors: g
                     };
                     his.is_final = his.audit_order === max_order;
@@ -114,6 +114,22 @@ module.exports = app => {
             const sqlParam = [tenderId];
             return this.db.query(sql, sqlParam);
         }
+        // 获取审核比较 可查看的列表,它用不可修改
+        async getViewFlow(safeStage) {
+            const result = [];
+            if (safeStage.audit_status === auditConst.common.status.uncheck) return result;
+
+            const group = await this.getAuditorHistory(safeStage.id, safeStage.curTimes);
+            const latestGroup = group[group.length - 1];
+            latestGroup.forEach(x => {
+                if (x.audit_status === auditConst.common.status.checked) result.push(x);
+                if (x.audit_status === auditConst.common.status.checking && !safeStage.readOnly) {
+                    x.latest = true;
+                    result.push(x);
+                }
+            });
+            return result;
+        }
         // ***** 查询审批人相关
 
         // ***** 修改审批人相关
@@ -202,13 +218,13 @@ module.exports = app => {
             return true;
         }
         // 拷贝上一期审批流程
-        async copyPreAuditors(transaction, prePhasePay, newPhasePay) {
-            const auditors = prePhasePay ? await this.getUniqAuditors(prePhasePay) : [];
+        async copyPreAuditors(transaction, preSafeStage, newSafeStage) {
+            const auditors = preSafeStage ? await this.getUniqAuditors(preSafeStage) : [];
             const newAuditors = [];
             // 添加原报
             const user = await this.ctx.service.projectAccount.getDataById(this.ctx.session.sessionUser.accountId);
             newAuditors.push({
-                tid: newPhasePay.tid, stage_id: newPhasePay.id,
+                tid: newSafeStage.tid, stage_id: newSafeStage.id,
                 audit_id: this.ctx.session.sessionUser.accountId,
                 audit_times: 1, audit_order: 0, audit_type: auditConst.auditType.key.common,
                 active_order: 0, audit_status: auditConst.safeStage.status.uncheck,
@@ -218,7 +234,7 @@ module.exports = app => {
             for (const a of auditors) {
                 if (a.audit_order === 0) continue;
                 newAuditors.push({
-                    tid: newPhasePay.tid, stage_id: newPhasePay.id,
+                    tid: newSafeStage.tid, stage_id: newSafeStage.id,
                     audit_id: a.audit_id,
                     audit_times: 1, audit_order: a.audit_order, audit_type: a.audit_type,
                     active_order: a.audit_order, audit_status: auditConst.safeStage.status.uncheck,
@@ -497,7 +513,7 @@ module.exports = app => {
                     // await this.ctx.service.specMsg.addSafeStageMsg(transaction, this.ctx.session.sessionProject.id, safeStage, pushOperate.safeStage.flow);
                 } else {
                     // 同步 期信息
-                    await transaction.update(this.ctx.service.safeStage.tableName, { id: safeStage.id, audit_status: auditConst.safeStage.status.checking, ...paySum });
+                    await transaction.update(this.ctx.service.safeStage.tableName, { id: safeStage.id, audit_status: auditConst.safeStage.status.checking, ...billsSum });
                 }
                 await transaction.commit();
             } catch (err) {
@@ -994,6 +1010,8 @@ module.exports = app => {
                     id: safeStage.id, audit_times: safeStage.audit_times - 1, audit_status: auditConst.safeStage.status.checking,
                 });
                 // todo 修改安全生产费数据
+                await this._cacheAuditData(transaction, safeStage,
+                    { audit_id: selfAuditor.audit_id, audit_times: selfAuditor.audit_times, audit_order: selfAuditor.audit_order, active_order: selfAuditor.active_order + 1});
                 // await this.ctx.service.safeStageBills.initPhaseDataByAuditCancel(transaction, safeStage, selfAuditor.audit_times, selfAuditor.active_order, selfAuditor.audit_times, selfAuditor.active_order + 1);
                 // await transaction.update(this.ctx.service.safeStageBills.tableName,
                 //     { audit_times: selfAuditor.audit_times, audit_sort: selfAuditor.active_order + 2, master_id: `${safeStage.id}-${selfAuditor.audit_times}-${selfAuditor.active_order + 2}` },

+ 2 - 0
app/service/safe_stage_bills.js

@@ -514,6 +514,8 @@ module.exports = app => {
                 if (l.cur_read_qty !== l.cur_qty || l.cur_read_tp !== l.cur_tp) {
                     const data = { id: l.id, cur_read_qty: l.cur_qty, cur_read_tp: l.cur_tp };
                     const his = l.cur_his ? JSON.parse(l.cur_his) : [];
+                    const fi = his.find(x => { return x.audit_times === auditInfo.audit_times && x.active_order === auditInfo.active_order; });
+                    if (fi >= 0) his.splice(fi, 1);
                     his.push({ ...auditInfo, qty: l.cur_qty, tp: l.cur_tp, up: l.unit_price });
                     data.cur_his = JSON.stringify(his);
                     data.cur_read_qty = l.cur_qty;

+ 9 - 10
app/service/sub_proj_permission.js

@@ -285,7 +285,6 @@ module.exports = app => {
                         //         const contractAudit = await this.ctx.service.contractAudit.getDataByCondition({ spid: spAudit.spid, uid: spAudit.uid });
                         //         const contractPermission = x.contract_permission ? this._.map(x.contract_permission.split(','), this._.toInteger) : [];
                         //         const newContractPermission = contractPermission.length > 0 ? await this.getContractPermission(contractPermission) : [];
-                        //         console.log(newContractPermission, contractAudit);
                         //         if (!contractAudit && contractPermission.length === 0) continue;
                         //         if (contractAudit) {
                         //             if (contractPermission.length === 0 || this._.intersection([3,4,5], contractPermission).length === 0) {
@@ -347,21 +346,21 @@ module.exports = app => {
 
             const insertData = [], updateData = [];
             for (const spid of copyData.spid) {
-                const exist = await this.getAllDataByCondition({ columns: ['uid'], where: { spid } });
+                const exist = await this.getAllDataByCondition({ columns: ['id', 'uid'], where: { spid } });
                 copyPermission.forEach(cp => {
                     const ecp = exist.find(x => { return x.uid === cp.uid; });
-                    if (ecp && force) {
-                        const udata = { id: ecp.id };
-                        for (const prop in cp) {
-                            if (prop.indexOf('_permission') > 1) udata[prop] = cp[prop];
-                        }
-                        updateData.push(udata)
-                    } else {
+                    if (!ecp) {
                         const idata = { id: this.uuid.v4(), spid, pid: cp.pid, uid: cp.uid };
                         for (const prop in cp) {
                             if (prop.indexOf('_permission') > 1) idata[prop] = cp[prop];
                         }
                         insertData.push(idata);
+                    } else if (force) {
+                        const udata = { id: ecp.id };
+                        for (const prop in cp) {
+                            if (prop.indexOf('_permission') > 1) udata[prop] = cp[prop];
+                        }
+                        updateData.push(udata);
                     }
                 });
             }
@@ -374,7 +373,7 @@ module.exports = app => {
             if (data.add) result.add = await this._addUser(subProject, data.add);
             if (data.del) result.del = await this._delUser(subProject, data.del);
             if (data.update) result.update = await this._updateUserPermission(data.update);
-            if (data.copy) await this._copyUserPermission(data.copy);
+            if (data.copy) await this._copyUserPermission(data.copy, data.existType === 'cover');
             return result;
         }
 

+ 81 - 40
app/view/advance/modal_audit.ejs

@@ -97,12 +97,14 @@
                                     <div class="timeline-item-content">
                                         <div class="card">
                                             <div class="card-body p-3">
-                                                <div class="card-text">
-                                                    <p class="mb-1"><span
-                                                            class="h5"><%- advance.user.name %></span><span
-                                                            class="pull-right text-success"><%- idx !== 0 ? '重新' : '' %>上报审批</span>
-                                                    </p>
-                                                    <p class="text-muted mb-0"><%- advance.user.role %></p>
+                                                <div class="card-text p-2 py-3 row">
+                                                    <div class="col-9">
+                                                        <span class="h5"><%- advance.user.name %></span>
+                                                        <span class="text-muted"><%- advance.user.company %> - <%- advance.user.role %></span>
+                                                    </div>
+                                                    <div class="col">
+                                                        <span class="pull-right text-success"><%- idx !== 0 ? '重新' : '' %>上报审批</span>
+                                                    </div>
                                                 </div>
                                             </div>
                                         </div>
@@ -134,11 +136,15 @@
                                     <div class="timeline-item-content">
                                         <div class="card">
                                             <div class="card-body p-3">
-                                                <div class="card-text">
-                                                    <p class="mb-1"><span class="h5"><%- auditor.name %></span><span
+                                                <div class="card-text p-2 py-3 row">
+                                                    <div class="col-9">
+                                                        <span class="h5"><%- auditor.name %></span>
+                                                        <span class="text-muted"><%- auditor.company %> - <%- auditor.role %></span>
+                                                    </div>
+                                                    <div class="col">
+                                                        <span
                                                             class="pull-right <%- auditConst.statusClass[auditor.status] %>"><%- auditConst.statusString[auditor.status] %></span>
-                                                    </p>
-                                                    <p class="text-muted mb-0"><%- auditor.role %></p>
+                                                    </div>
                                                 </div>
                                             </div>
 
@@ -178,16 +184,20 @@
                                     <div class="timeline-item-content">
                                         <div class="card">
                                             <div class="card-body p-3">
-                                                <div class="card-text">
-                                                    <p class="mb-1"><span class="h5"><%- auditor.name %></span>
+                                                <div class="card-text p-2 py-3 row">
+                                                    <div class="col-9">
+                                                        <span class="h5"><%- auditor.name %></span>
+                                                        <span class="text-muted"><%- auditor.company %> - <%- auditor.role %></span>
+                                                    </div>
+                                                    <div class="col">
                                                         <span
                                                             class="pull-right
                                                                             <%- auditConst.statusClass[auditor.status] %>"><%- auditor.status !== auditConst.status.uncheck ? auditConst.statusString[auditor.status] : ''%>
                                                             <%- auditor.status === auditConst.status.checkNo ? advance.user.name : '' %>
                                                             <%- auditor.status === auditConst.status.checkNoPre ? history.find(item => item.sort === auditor.sort-1).name : '' %>
                                                         </span>
-                                                    </p>
-                                                    <p class="text-muted mb-0"><%- auditor.role %></p>
+                                                    </div>
+                                                    
                                                 </div>
                                             </div>
                                             <!--审批意见-->
@@ -276,12 +286,18 @@
                                             <div class="timeline-item-content">
                                                 <div class="card">
                                                     <div class="card-body p-3">
-                                                        <div class="card-text">
-                                                            <p class="mb-1"><span
-                                                                    class="h5"><%- advance.user.name %></span><span
+                                                        <div class="card-text p-2 py-3 row">
+                                                            <div class="col-9">
+                                                                <span class="h5"><%- advance.user.name %></span>
+                                                                <span class="text-muted"><%- advance.user.company %> - <%- advance.user.role %></span>
+                                                            </div>
+                                                            <div class="col">
+                                                                <span
                                                                     class="pull-right text-success"><%- idx !== 0 ? '重新' : '' %>上报审批</span>
-                                                            </p>
-                                                            <p class="text-muted mb-0"><%- advance.user.role %></p>
+                                                            
+                                                            </div>
+
+                                                            
                                                         </div>
                                                     </div>
                                                 </div>
@@ -313,11 +329,16 @@
                                             <div class="timeline-item-content">
                                                 <div class="card">
                                                     <div class="card-body p-3">
-                                                        <div class="card-text">
-                                                            <p class="mb-1"><span class="h5"><%- auditor.name %></span><span
+                                                        <div class="card-text p-2 py-3 row">
+                                                            <div class="col-9">
+                                                                <span class="h5"><%- auditor.name %></span>
+                                                                <span class="text-muted"><%- auditor.company %> - <%- auditor.role %></span>
+                                                            </div>
+                                                            <div class="col">
+                                                                <span
                                                                     class="pull-right <%- auditConst.statusClass[auditor.status] %>"><%- auditConst.statusString[auditor.status] %></span>
-                                                            </p>
-                                                            <p class="text-muted mb-0"><%- auditor.role %></p>
+                                                            </div>
+
                                                         </div>
                                                     </div>
                                                     <!--审批意见-->
@@ -362,16 +383,20 @@
                                             <div class="timeline-item-content">
                                                 <div class="card">
                                                     <div class="card-body p-3">
-                                                        <div class="card-text">
-                                                            <p class="mb-1"><span class="h5"><%- auditor.name %></span>
+                                                        <div class="card-text p-2 py-3 row">
+                                                            <div class="col-9">
+                                                                <span class="h5"><%- auditor.name %></span>
+                                                                <span class="text-muted"><%- auditor.company %> - <%- auditor.role %></span>
+                                                            </div>
+                                                            <div class="col">
                                                                 <span
                                                                     class="pull-right
                                                                                     <%- auditConst.statusClass[auditor.status] %>"><%- auditor.status !== auditConst.status.uncheck ? auditConst.statusString[auditor.status] : ''%>
                                                                     <%- auditor.status === auditConst.status.checkNo ? advance.user.name : '' %>
                                                                     <%- auditor.status === auditConst.status.checkNoPre ? history.find(item => item.sort === auditor.sort-1).name : '' %>
                                                                 </span>
-                                                            </p>
-                                                            <p class="text-muted mb-0"><%- auditor.role %></p>
+                                                            </div>
+
                                                         </div>
                                                     </div>
                                                     <!--审批意见-->
@@ -468,12 +493,17 @@
                                             <div class="timeline-item-content">
                                                 <div class="card">
                                                     <div class="card-body p-3">
-                                                        <div class="card-text">
-                                                            <p class="mb-1"><span
-                                                                    class="h5"><%- advance.user.name %></span><span
+                                                        <div class="card-text p-2 py-3 row">
+                                                            <div class="col-9">
+                                                                <span class="h5"><%- advance.user.name %></span>
+                                                                <span class="text-muted"><%- advance.user.company %> - <%- advance.user.role %></span>
+                                                            </div>
+                                                            <div class="col">
+                                                                <span
                                                                     class="pull-right text-success"><%- idx !== 0 ? '重新' : '' %>上报审批</span>
-                                                            </p>
-                                                            <p class="text-muted mb-0"><%- advance.user.role %></p>
+                                                            
+                                                            </div>
+                                                            
                                                         </div>
                                                     </div>
                                                 </div>
@@ -505,11 +535,17 @@
                                             <div class="timeline-item-content">
                                                 <div class="card">
                                                     <div class="card-body p-3">
-                                                        <div class="card-text">
-                                                            <p class="mb-1"><span class="h5"><%- auditor.name %></span><span
+                                                        <div class="card-text p-2 py-3 row">
+                                                            <div class="col-9">
+                                                                <span class="h5"><%- auditor.name %></span>
+                                                                <span class="text-muted"><%- auditor.company %> - <%- auditor.role %></span>
+                                                            </div>
+                                                            <div class="col">
+                                                                <span
                                                                     class="pull-right <%- auditConst.statusClass[auditor.status] %>"><%- auditConst.statusString[auditor.status] %></span>
-                                                            </p>
-                                                            <p class="text-muted mb-0"><%- auditor.role %></p>
+                                                            
+                                                            </div>
+
                                                         </div>
                                                     </div>
 
@@ -575,16 +611,21 @@
                                             <div class="timeline-item-content">
                                                 <div class="card">
                                                     <div class="card-body p-3">
-                                                        <div class="card-text">
-                                                            <p class="mb-1"><span class="h5"><%- auditor.name %></span>
+                                                        <div class="card-text p-2 py-3 row">
+                                                            <div class="col-9">
+                                                                <span class="h5"><%- auditor.name %></span>
+                                                                <span class="text-muted"><%- auditor.company %> - <%- auditor.role %></span>
+                                                            </div>
+                                                            <div class="col">
                                                                 <span
                                                                     class="pull-right
                                                                                     <%- auditConst.statusClass[auditor.status] %>"><%- auditor.status !== auditConst.status.uncheck ? auditConst.statusString[auditor.status] : ''%>
                                                                     <%- auditor.status === auditConst.status.checkNo ? advance.user.name : '' %>
                                                                     <%- auditor.status === auditConst.status.checkNoPre ? history.find(item => item.sort === auditor.sort-1).name : '' %>
                                                                 </span>
-                                                            </p>
-                                                            <p class="text-muted mb-0"><%- auditor.role %></p>
+                                                            </div>
+
+
                                                         </div>
                                                     </div>
                                                     <!--审批意见-->

+ 3 - 18
app/view/change/apply_information_modal.ejs

@@ -346,18 +346,13 @@
                                                                 <div class="card-text p-2 py-3 row <%- ( i > 0 ? 'border-top' : '') %>">
                                                                     <div class="col-10">
                                                                         <span class="h6"><%- auditor.name %></span>
-                                                                        <% if (group.audit_order === 0) { %>
-                                                                                <% if (auditor.role && auditor.role.trim()) { %>
-                                                                                    <span class="text-muted ml-1"><%- auditor.role %></span>
-                                                                                <% } %>
-                                                                            <% } else { %>
+                                                                        
                                                                                 <span class="text-muted ml-1">
                                                                                     <%- auditor.company %>
                                                                                     <% if (auditor.role && auditor.role.trim()) { %>
                                                                                         - <%- auditor.role %>
                                                                                     <% } %>
                                                                                 </span>
-                                                                            <% } %>
                                                                     </div>
                                                                     <div class="col">
                                                                         <% if (auditor.status === auditConst.status.checked || auditor.status === auditConst.status.cancelRevise) { %>
@@ -546,18 +541,13 @@
                                                                     <div class="card-text p-2 py-3 row <%- ( i > 0 ? 'border-top' : '') %>">
                                                                         <div class="col-10">
                                                                             <span class="h6"><%- auditor.name %></span>
-                                                                            <% if (group.audit_order === 0) { %>
-                                                                                <% if (auditor.role && auditor.role.trim()) { %>
-                                                                                    <span class="text-muted ml-1"><%- auditor.role %></span>
-                                                                                <% } %>
-                                                                            <% } else { %>
+                                                                            
                                                                                 <span class="text-muted ml-1">
                                                                                     <%- auditor.company %>
                                                                                     <% if (auditor.role && auditor.role.trim()) { %>
                                                                                         - <%- auditor.role %>
                                                                                     <% } %>
                                                                                 </span>
-                                                                            <% } %>
                                                                         </div>
                                                                         <div class="col">
                                                                             <% if (auditor.status === auditConst.status.checked || auditor.status === auditConst.status.cancelRevise) { %>
@@ -750,18 +740,13 @@
                                                                     <div class="card-text p-2 py-3 row <%- ( i > 0 ? 'border-top' : '') %>">
                                                                         <div class="col">
                                                                             <span class="h6"><%- auditor.name %></span>
-                                                                            <% if (group.audit_order === 0) { %>
-                                                                                <% if (auditor.role && auditor.role.trim()) { %>
-                                                                                    <span class="text-muted ml-1"><%- auditor.role %></span>
-                                                                                <% } %>
-                                                                            <% } else { %>
+                                                                            
                                                                                 <span class="text-muted ml-1">
                                                                                     <%- auditor.company %>
                                                                                     <% if (auditor.role && auditor.role.trim()) { %>
                                                                                         - <%- auditor.role %>
                                                                                     <% } %>
                                                                                 </span>
-                                                                            <% } %>
                                                                         </div>
                                                                         <div class="col">
                                                                             <% if (auditor.status === auditConst.status.checked || auditor.status === auditConst.status.cancelRevise) { %>

+ 3 - 18
app/view/change/information_modal.ejs

@@ -357,18 +357,13 @@
                                                                 <div class="card-text p-2 py-3 row <%- ( i > 0 ? 'border-top' : '') %>">
                                                                     <div class="col-10">
                                                                         <span class="h6"><%- auditor.name %></span>
-                                                                        <% if (group.audit_order === 0) { %>
-                                                                                <% if (auditor.role && auditor.role.trim()) { %>
-                                                                                    <span class="text-muted ml-1"><%- auditor.role %></span>
-                                                                                <% } %>
-                                                                            <% } else { %>
+                                                                        
                                                                                 <span class="text-muted ml-1">
                                                                                     <%- auditor.company %>
                                                                                     <% if (auditor.role && auditor.role.trim()) { %>
                                                                                         - <%- auditor.role %>
                                                                                     <% } %>
                                                                                 </span>
-                                                                            <% } %>
                                                                     </div>
                                                                     <div class="col">
                                                                         <% if (auditor.status === auditConst.status.checked || group.status === auditConst.status.cancelRevise) { %>
@@ -525,18 +520,13 @@
                                                                 <div class="card-text p-2 py-3 row <%- ( i > 0 ? 'border-top' : '') %>">
                                                                     <div class="col-10">
                                                                         <span class="h6"><%- auditor.name %></span>
-                                                                        <% if (group.audit_order === 0) { %>
-                                                                                <% if (auditor.role && auditor.role.trim()) { %>
-                                                                                    <span class="text-muted ml-1"><%- auditor.role %></span>
-                                                                                <% } %>
-                                                                            <% } else { %>
+                                                                        
                                                                                 <span class="text-muted ml-1">
                                                                                     <%- auditor.company %>
                                                                                     <% if (auditor.role && auditor.role.trim()) { %>
                                                                                         - <%- auditor.role %>
                                                                                     <% } %>
                                                                                 </span>
-                                                                            <% } %>
                                                                     </div>
                                                                     <div class="col">
                                                                         <% if (auditor.status === auditConst.status.checked || group.status === auditConst.status.cancelRevise) { %>
@@ -705,18 +695,13 @@
                                                                 <div class="card-text p-2 py-3 row <%- ( i > 0 ? 'border-top' : '') %>">
                                                                     <div class="col-10">
                                                                         <span class="h6"><%- auditor.name %></span>
-                                                                        <% if (group.audit_order === 0) { %>
-                                                                                <% if (auditor.role && auditor.role.trim()) { %>
-                                                                                    <span class="text-muted ml-1"><%- auditor.role %></span>
-                                                                                <% } %>
-                                                                            <% } else { %>
+                                                                        
                                                                                 <span class="text-muted ml-1">
                                                                                     <%- auditor.company %>
                                                                                     <% if (auditor.role && auditor.role.trim()) { %>
                                                                                         - <%- auditor.role %>
                                                                                     <% } %>
                                                                                 </span>
-                                                                            <% } %>
                                                                     </div>
                                                                     <div class="col">
                                                                         <% if (auditor.status === auditConst.status.checked || group.status === auditConst.status.cancelRevise) { %>

+ 16 - 5
app/view/contract/col_set.ejs

@@ -7,10 +7,10 @@
             </div>
             <div class="modal-body">
                 <table class="table table-hover table-bordered">
-                    <thead><tr><th width="150px">列名</th><th width="50px">显示</th><th width="150px">别名</th><th width="80px">操作</th></tr></thead>
+                    <thead><tr><th width="150px" class="text-center">列名</th><th width="50px" class="text-center">显示</th><th width="150px" class="text-center">别名</th><th width="80px" class="text-center">操作</th></tr></thead>
                     <tbody class="text-center">
                     <% for (const cs of colSet) { %>
-                      <tr code="<%- cs.field %>"> 
+                      <tr code="<%- cs.field %>">
                           <td>
                               <%- cs.name %>
                               <% if (cs.hint) { %>
@@ -61,6 +61,9 @@
 // 立即获取表格 tbody 并绑定事件(确保脚本加载时即可初始化)
 const $tbody = $('#col-set').find('.modal-body table tbody');
 
+// 保存初始 HTML,在关闭时恢复(保证再次打开时为初始状态)
+const _initialColSetTbodyHtml = $tbody.html();
+
 // 更新上下移链接显示状态
 function updateMoveButtons() {
     const $currentRows = $tbody.find('tr');
@@ -95,12 +98,20 @@ $('#col-set').on('shown.bs.modal', function() {
     updateMoveButtons();
 });
 
+// 模态框隐藏时恢复到初始内容并重新绑定必要状态
+$('#col-set').on('hidden.bs.modal', function() {
+    // 恢复初始 tbody 内容
+    $tbody.html(_initialColSetTbodyHtml);
+    // 重新计算按钮显示状态
+    updateMoveButtons();
+});
+
 // 事件委托:从一开始就绑定上/下移事件
 $tbody.on('click', '.move-up', function(e) {
     e.preventDefault();
     const $currentRow = $(this).closest('tr');
     const $prevRow = $currentRow.prev('tr');
-    
+
     if ($prevRow.length) {
         $currentRow.insertBefore($prevRow);
         updateMoveButtons();
@@ -111,7 +122,7 @@ $tbody.on('click', '.move-down', function(e) {
     e.preventDefault();
     const $currentRow = $(this).closest('tr');
     const $nextRow = $currentRow.next('tr');
-    
+
     if ($nextRow.length) {
         $currentRow.insertAfter($nextRow);
         updateMoveButtons();
@@ -144,4 +155,4 @@ function onSetCol() {
     $('input[name=col_set]').val(JSON.stringify(colSet));
     return true;
 }
-</script>
+</script>

+ 2 - 2
app/view/contract/index.ejs

@@ -29,10 +29,10 @@
                     <tr class="text-center">
                         <th width="6%">合同个数</th>
                         <th width="10%">合同金额</th>
-                        <th width="15%">支出进度</th>
+                        <th width="15%">应付进度</th>
                         <th width="6%">合同个数</th>
                         <th width="10%">合同金额</th>
-                        <th width="15%">回进度</th>
+                        <th width="15%">回进度</th>
                     </tr>
                     <tbody id="projectList">
                     </tbody>

+ 3 - 1
app/view/financial/pay_stage_modal.ejs

@@ -121,7 +121,9 @@
                                                 <% group.groupList.forEach(item => { %>
                                                     <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>
+                                                                    class="ml-auto"><%- item.mobile %></span>
+                                                            <span class="selected-mark text-success ml-2" style="display:none;"><i class="fa fa-check"></i></span>
+                                                        </p>
                                                         <span class="text-muted"><%- item.role %></span>
                                                     </dd>
                                                 <% });%>

+ 36 - 5
app/view/payment/modal.ejs

@@ -140,7 +140,9 @@
                                         <% group.groupList.forEach(item => { %>
                                             <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>
+                                                            class="ml-auto"><%- item.mobile %></span>
+                                                    <span class="selected-mark text-success ml-2" style="display: <% if (permissionAudits.some(pa => pa.uid == item.id)) { %>inline-block<% } else { %>none<% } %>;"><i class="fa fa-check"></i></span>
+                                                </p>
                                                 <span class="text-muted"><%- item.role %></span>
                                             </dd>
                                         <% });%>
@@ -262,12 +264,15 @@
                     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>
+                                class="ml-auto">${item.mobile || ''}</span>
+                            <span class="selected-mark text-success ml-2" style="display:none;"><i class="fa fa-check"></i></span>
+                        </p>
                         <span class="text-muted">${item.role || ''}</span>
                     </dd>`
                     })
                     $('.book-list').empty()
                     $('.book-list').append(html)
+                    syncUserSelectionMarks(permissionAudits); 
                 } else {
                     if (!$('.acc-btn').length) {
                         accountGroup.forEach((group, idx) => {
@@ -279,7 +284,9 @@
                             group.groupList.forEach(item => {
                                     html += `<dd class="border-bottom p-2 mb-0 " data-id="${item.id}" >
                                     <p class="mb-0 d-flex"><span class="text-primary">${item.name}</span><span
-                                            class="ml-auto">${item.mobile || ''}</span></p>
+                                            class="ml-auto">${item.mobile || ''}</span>
+                                        <span class="selected-mark text-success ml-2" style="display:none;"><i class="fa fa-check"></i></span>
+                                    </p>
                                     <span class="text-muted">${item.role || ''}</span>
                                 </dd>`
                             });
@@ -287,6 +294,7 @@
                         })
                         $('.book-list').empty()
                         $('.book-list').append(html)
+                        syncUserSelectionMarks(permissionAudits); 
                     }
                 }
             }, 400);
@@ -310,7 +318,8 @@
             return false
         });
         // 添加到成员中
-        $('dl').on('click', 'dd', function () {
+        $('dl').on('click', 'dd', function (e) {
+            e.stopPropagation();
             const id = parseInt($(this).data('id'));
             const groupName = $(this).data('groupname');
             console.log(groupName);
@@ -318,6 +327,8 @@
                 postData('/sp/' + spid + '/payment/permission/save', { type: 'add-audit', id: id }, function (result) {
                     permissionAudits = result;
                     setList(permissionAudits);
+                    console.log(permissionAudits);
+                    syncUserSelectionMarks(permissionAudits);
                 })
             } else if (groupName && groupName !== '') {
                 const groupAuditList = _.filter(accountList, { company: groupName });
@@ -330,6 +341,7 @@
                         // toastr.success(`成功添加 位用户`);
                         permissionAudits = result;
                         setList(permissionAudits);
+                        syncUserSelectionMarks(permissionAudits);
                     })
                 } else {
                     toastr.warning('暂无用户添加');
@@ -337,6 +349,25 @@
             }
         });
 
+        function syncUserSelectionMarks(currentPermissionAudits) {
+            let currentAuditorIds = new Set();
+            if (Array.isArray(currentPermissionAudits)) {
+                currentAuditorIds = new Set(currentPermissionAudits.map(pa => pa.uid));
+            }
+            $('.book-list dd[data-id]').each(function() {
+                const userId = parseInt($(this).data('id'));
+                if (isNaN(userId)) return;
+
+                const selectedMark = $(this).find('.selected-mark');
+                if (currentAuditorIds.has(userId)) {
+                    selectedMark.show();
+                } else {
+                    selectedMark.hide();
+                }
+            });
+        }
+
+
         function setList(datas) {
             let list = '';
             for (const pa of datas) {
@@ -398,7 +429,7 @@
                 $('#del-permission-audit').modal('hide');
                 permissionAudits = result;
                 setList(permissionAudits);
-
+                syncUserSelectionMarks(permissionAudits);
             })
         });
 

+ 1 - 2
app/view/safe_calc/stage.ejs

@@ -36,7 +36,7 @@
             </div>
             <div class="ml-auto">
                 <% if (stages.length === 0 && ctx.session.sessionUser.is_admin) { %>
-                <a href="#add-qi" data-toggle="modal" data-target="#add-qi" class="btn btn-primary btn-sm">迁移旧数据</a>
+                <button class="btn btn-primary btn-sm" id="move-his">迁移旧数据</button>
                 <% } %>
                 <% if ((stages.length === 0 || stages[0].audit_status === auditConst.status.checked) && ctx.permission.safe_payment.add) { %>
                 <a href="#add-qi" data-toggle="modal" data-target="#add-qi" class="btn btn-primary btn-sm">开始新一期</a>
@@ -108,7 +108,6 @@
     </div>
 </div>
 <script>
-    const stages = JSON.parse('<%- JSON.stringify(stages) %>');
     const auditType = JSON.parse('<%- JSON.stringify(auditType) %>');
     const auditConst = JSON.parse('<%- JSON.stringify(auditConst) %>');
 </script>

+ 17 - 0
app/view/safe_calc/stage_modal.ejs

@@ -81,6 +81,23 @@
         </div>
     </div>
 </div>
+<div class="modal" id="move-his-safe-bills" data-backdrop="stativ">
+    <div class="modal-dialog" role="document">
+        <div class="modal-content">
+            <div class="modal-header"><h5 class="modal-title">迁移旧数据</h5></div>
+            <div class="modal-body modal-height-300">
+                <table class="table table-bordered table-hover">
+                    <thead><tr class="text-center"><th>选择</th><th>名称</th><th>安全生产费</th></tr></thead>
+                    <tbody id="mhsb-list"></tbody>
+                </table>
+            </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="move-his-ok">确定</button>
+            </div>
+        </div>
+    </div>
+</div>
 <script>
     $('.datepicker-here').datepicker({
         autoClose: true,

+ 3 - 3
app/view/safe_calc/sub_menu_list.ejs

@@ -1,5 +1,5 @@
 <nav-menu title="返回" url="/sp/<%- ctx.subProject.id %>/safe/tender/<%- ctx.tender.id  %>/stage" tclass="text-primary" ml="1" icon="fa-chevron-left"></nav-menu>
-<nav-menu title="安全生产费" url="/sp/<%- ctx.subProject.id %>/safe/tender/<%- ctx.tender.id %>/safe/<%= ctx.safeStage.stage_order %>/bills" ml="3" active="<%= ctx.url.indexOf('bills') %>"></nav-menu>
-<nav-menu title="审核比较" url="/sp/<%- ctx.subProject.id %>/safe/tender/<%- ctx.tender.id %>/safe/<%= ctx.safeStage.stage_order %>/compare" ml="3" active="<%= ctx.url.indexOf('compare') %>"></nav-menu>
-<nav-menu title="输出报表" url="/sp/<%- ctx.subProject.id %>/safe/tender/<%- ctx.tender.id %>/safe/<%= ctx.safeStage.stage_order %>/report" ml="3" active="<%= ctx.url.indexOf('report') %>"></nav-menu>
+<nav-menu title="安全生产费" url="/sp/<%- ctx.subProject.id %>/safe/tender/<%- ctx.tender.id %>/stage/<%= ctx.safeStage.stage_order %>/bills" ml="3" active="<%= ctx.url.indexOf('bills') %>"></nav-menu>
+<nav-menu title="审核比较" url="/sp/<%- ctx.subProject.id %>/safe/tender/<%- ctx.tender.id %>/stage/<%= ctx.safeStage.stage_order %>/compare" ml="3" active="<%= ctx.url.indexOf('compare') %>"></nav-menu>
+<nav-menu title="输出报表" url="/sp/<%- ctx.subProject.id %>/safe/tender/<%- ctx.tender.id %>/stage/<%= ctx.safeStage.stage_order %>/report" ml="3" active="<%= ctx.url.indexOf('report') %>"></nav-menu>
 <% include ./audit_btn.ejs %>

+ 17 - 4
app/view/sp_setting/user_modal.ejs

@@ -181,10 +181,23 @@
                     </div>
                 </div>
             </div>
-            <div class="modal-footer">
-                <input type="hidden" id="source-permission">
-                <button type="button" class="btn btn-sm btn-secondary" data-dismiss="modal">关闭</button>
-                <button type="button" class="btn btn-sm btn-sm btn-primary" id="copy-user-batch-ok">确定</button>
+            <div class="modal-footer" style="justify-content: space-between">
+                <div class="d-flex">
+                    <div class="form-check form-check-inline">已有用户:</div>
+                    <div class="form-check form-check-inline">
+                        <input class="form-check-input" type="radio" name="exist-type" id="inlineRadio2" value="cover" checked>
+                        <label class="form-check-label" for="inlineRadio2">覆盖</label>
+                    </div>
+                    <div class="form-check form-check-inline">
+                        <input class="form-check-input" type="radio" name="exist-type" id="inlineRadio3" value="skip">
+                        <label class="form-check-label" for="inlineRadio3">跳过</label>
+                    </div>
+                </div>
+                <div class="d-flex">
+                    <input type="hidden" id="source-permission">
+                    <button type="button" class="btn btn-sm btn-secondary" data-dismiss="modal">关闭</button>
+                    <button type="button" class="btn btn-sm btn-sm btn-primary ml-1" id="copy-user-batch-ok">确定</button>
+                </div>
             </div>
         </div>
     </div>