Parcourir la source

Merge branch 'uat' of maixinrong/Calculation into master

maixinrong il y a 3 ans
Parent
commit
3cac457658
100 fichiers modifiés avec 3413 ajouts et 719 suppressions
  1. 2 2
      app/base/base_service.js
  2. 11 6
      app/controller/change_controller.js
  3. 2 1
      app/controller/datacollect_controller.js
  4. 95 5
      app/controller/material_controller.js
  5. 38 13
      app/controller/profile_controller.js
  6. 63 32
      app/controller/report_archive_controller.js
  7. 18 15
      app/controller/report_controller.js
  8. 2 0
      app/controller/revise_controller.js
  9. 186 11
      app/controller/setting_controller.js
  10. 51 46
      app/controller/stage_controller.js
  11. 5 2
      app/controller/tender_controller.js
  12. 0 1
      app/extend/helper.js
  13. 6 0
      app/lib/stage_im.js
  14. 40 27
      app/lib/sum_load.js
  15. 42 0
      app/middleware/auto_finish_logger.js
  16. 6 0
      app/middleware/auto_logger.js
  17. 29 1
      app/public/js/advance_audit.js
  18. 1 1
      app/public/js/change_information.js
  19. 9 8
      app/public/js/change_information_set.js
  20. 109 25
      app/public/js/change_plan_information.js
  21. 1 1
      app/public/js/gcl_gather.js
  22. 135 68
      app/public/js/ledger.js
  23. 6 6
      app/public/js/material.js
  24. 68 25
      app/public/js/material_checklist.js
  25. 103 35
      app/public/js/material_list.js
  26. 130 2
      app/public/js/measure_material.js
  27. 31 2
      app/public/js/profile.js
  28. 135 63
      app/public/js/revise.js
  29. 240 8
      app/public/js/setting.js
  30. 4 4
      app/public/js/shares/cs_tools.js
  31. 2 1
      app/public/js/shares/gcl_gather_compare.js
  32. 4 1
      app/public/js/shares/tender_select.js
  33. 2 2
      app/public/js/spreadjs_rela/spreadjs_zh.js
  34. 66 38
      app/public/js/stage.js
  35. 57 16
      app/public/js/stage_change.js
  36. 26 18
      app/public/js/stage_im.js
  37. 96 0
      app/public/report/js/rpt_archive.js
  38. 3 3
      app/public/report/js/rpt_custom.js
  39. 19 0
      app/reports/rpt_component/jpc_cross_tab.js
  40. 3 3
      app/reports/rpt_component/jpc_flow_tab.js
  41. 8 2
      app/router.js
  42. 8 4
      app/service/advance.js
  43. 4 1
      app/service/change.js
  44. 117 0
      app/service/construction_unit.js
  45. 5 7
      app/service/deal_bills.js
  46. 12 0
      app/service/ledger.js
  47. 6 1
      app/service/material.js
  48. 116 20
      app/service/material_list.js
  49. 68 0
      app/service/material_list_gcl.js
  50. 61 0
      app/service/pos.js
  51. 9 2
      app/service/project_account.js
  52. 6 0
      app/service/report.js
  53. 63 0
      app/service/report_memory.js
  54. 47 0
      app/service/revise_pos.js
  55. 14 2
      app/service/stage.js
  56. 7 2
      app/service/stage_bills.js
  57. 18 6
      app/service/stage_change.js
  58. 2 3
      app/service/stage_change_final.js
  59. 220 0
      app/service/stage_import_change.js
  60. 2 2
      app/service/stage_rela.js
  61. 13 5
      app/view/advance/detail.ejs
  62. 1 1
      app/view/advance/modal_audit.ejs
  63. 1 1
      app/view/change/apply_information_modal.ejs
  64. 1 1
      app/view/change/info_modal.ejs
  65. 2 0
      app/view/change/information.ejs
  66. 1 1
      app/view/change/information_modal.ejs
  67. 4 0
      app/view/change/plan_information.ejs
  68. 1 1
      app/view/change/plan_information_modal.ejs
  69. 1 1
      app/view/change/project_information_modal.ejs
  70. 46 23
      app/view/datacollect/index.ejs
  71. 1 1
      app/view/layout/layout.ejs
  72. 27 1
      app/view/ledger/explode.ejs
  73. 16 2
      app/view/ledger/explode_modal.ejs
  74. 1 1
      app/view/material/checklist.ejs
  75. 1 1
      app/view/material/file_modal.ejs
  76. 6 0
      app/view/material/index.ejs
  77. 2 1
      app/view/material/list.ejs
  78. 20 2
      app/view/material/modal.ejs
  79. 14 2
      app/view/profile/sign.ejs
  80. 16 0
      app/view/report/archive_popup.ejs
  81. 12 0
      app/view/report/index_archive.ejs
  82. 5 5
      app/view/report/rpt_all_popup.ejs
  83. 20 0
      app/view/report/stage_archive_modal.ejs
  84. 1 1
      app/view/revise/index.ejs
  85. 28 3
      app/view/revise/info.ejs
  86. 15 1
      app/view/revise/info_modal.ejs
  87. 55 40
      app/view/setting/user.ejs
  88. 34 20
      app/view/setting/user_modal.ejs
  89. 62 40
      app/view/setting/user_permission.ejs
  90. 16 10
      app/view/setting/user_permission_modal.ejs
  91. 139 0
      app/view/setting/user_unit.ejs
  92. 137 0
      app/view/setting/user_unit_modal.ejs
  93. 2 0
      app/view/stage/audit_modal.ejs
  94. 2 2
      app/view/stage/deal_modal.ejs
  95. 2 2
      app/view/stage/modal.ejs
  96. 1 1
      app/view/stage/pay_modal.ejs
  97. 1 1
      app/view/stage_extra/bonus_modal.ejs
  98. 2 2
      app/view/stage_rela/detail_modal.ejs
  99. 65 2
      builder_report_index_define.js
  100. 0 0
      config/config.default.js

+ 2 - 2
app/base/base_service.js

@@ -139,7 +139,7 @@ class BaseService extends Service {
      * @param {Object} condition - 更新的条件筛选
      * @return {Boolean} - 返回更新结果
      */
-    async update(data, condition) {
+    async update(data, condition, transaction = false) {
         if (Object.keys(data).length <= 0 || Object.keys(condition).length <= 0) {
             return false;
         }
@@ -159,7 +159,7 @@ class BaseService extends Service {
         const whereString = where.join(' AND ');
 
         const sql = 'UPDATE ' + this.tableName + ' SET ' + paramString + ' WHERE ' + whereString;
-        const result = await this.db.query(sql, sqlParam);
+        const result = transaction ? await transaction.query(sql, sqlParam) : await this.db.query(sql, sqlParam);
         return result.affectedRows > 0;
     }
 

+ 11 - 6
app/controller/change_controller.js

@@ -79,7 +79,7 @@ module.exports = app => {
                             changeAudit = await ctx.service.changeAudit.getLastUser(c.cid, c.times - 1, status);
                             auditStatus = c.uid === ctx.session.sessionUser.accountId ? 1 : 0;
                             const back_changeUsedData = await ctx.service.stageChange.getFinalUsedData(ctx.tender.id, c.cid);
-                            c.stageChangeNum = this.ctx.helper.sum(back_changeUsedData.map(x => { return Math.abs(x.used_qty); }));
+                            c.stageChangeNum = this.ctx.helper.sum(back_changeUsedData.map(x => { return Math.abs(x.qty); }));
                             break;
                         case 6:
                             changeAudit = await ctx.service.changeAudit.getLastBackUser(c.cid, c.times);
@@ -89,7 +89,7 @@ module.exports = app => {
                         case 9:
                             auditStatus = 9;
                             const changeUsedData = await ctx.service.stageChange.getFinalUsedData(ctx.tender.id, c.cid);
-                            c.stageChangeNum = this.ctx.helper.sum(changeUsedData.map(x => { return Math.abs(x.used_qty); }));
+                            c.stageChangeNum = this.ctx.helper.sum(changeUsedData.map(x => { return Math.abs(x.qty); }));
                             break;
                         default:
                             break;
@@ -563,7 +563,7 @@ module.exports = app => {
 
                 // 获取是否已存在调用变更令
                 const changeUsedData = await ctx.service.stageChange.getFinalUsedData(ctx.tender.id, change.cid);
-                renderData.stageChangeNum = this.ctx.helper.sum(changeUsedData.map(x => { return Math.abs(x.used_qty); }));
+                renderData.stageChangeNum = this.ctx.helper.sum(changeUsedData.map(x => { return Math.abs(x.qty); }));
                 await this.layout('change/info.ejs', renderData, 'change/info_modal.ejs');
             } catch (err) {
                 this.log(err);
@@ -760,7 +760,7 @@ module.exports = app => {
                 // 获取是否已存在调用变更令
                 let changeUsedData = await ctx.service.stageChange.getFinalUsedData(ctx.tender.id, change.cid);
                 changeUsedData = ctx.helper._.filter(changeUsedData, function(item) {
-                    return item.used_qty !== null;
+                    return item.qty !== null;
                 })
                 renderData.changeUsedData = changeUsedData;
                 renderData.stageChangeNum = this.ctx.helper.sum(changeUsedData.map(x => { return Math.abs(x.used_qty); }));
@@ -1151,6 +1151,11 @@ module.exports = app => {
                     fileData.id = result.insertId;
                     fileData.orginpath = ctx.app.config.fujianOssPath + filepath;
                     delete fileData.filepath;
+                    if (!ctx.helper.canPreview(fileData.fileext)) {
+                        fileData.filepath = `/change/download/file/${fileData.id}`;
+                    } else {
+                        fileData.filepath = ctx.app.config.fujianOssPath + filepath;
+                    }
                     files.push(fileData);
                     ++index;
                 }
@@ -1395,7 +1400,7 @@ module.exports = app => {
 
                 // 获取是否已存在调用变更令
                 const changeUsedData = await ctx.service.stageChange.getFinalUsedData(ctx.tender.id, changeData.cid);
-                const stageChangeNum = this.ctx.helper.sum(changeUsedData.map(x => { return Math.abs(x.used_qty); }));
+                const stageChangeNum = this.ctx.helper.sum(changeUsedData.map(x => { return Math.abs(x.qty); }));
                 if (stageChangeNum !== 0) {
                     throw '该变更令已被调用,无法重新审批';
                 }
@@ -1451,7 +1456,7 @@ module.exports = app => {
 
                 // 获取是否已存在调用变更令
                 // const changeUsedData = await ctx.service.stageChange.getFinalUsedData(ctx.tender.id, changeData.cid);
-                // const stageChangeNum = this.ctx.helper.sum(changeUsedData.map(x => { return Math.abs(x.used_qty); }));
+                // const stageChangeNum = this.ctx.helper.sum(changeUsedData.map(x => { return Math.abs(x.qty); }));
                 // if (stageChangeNum !== 0) {
                 //     throw '该变更令已被调用,无法重新审批';
                 // }

+ 2 - 1
app/controller/datacollect_controller.js

@@ -126,7 +126,8 @@ module.exports = app => {
                         t.total_price = sum.total_price;
                         t.deal_tp = sum.deal_tp;
                     }
-                    t.material_tp = await ctx.service.material.getSumMaterial(t.id);
+                    t.change_tp = await ctx.service.change.getChangeTp(t.id);
+                    // t.material_tp = await ctx.service.material.getSumMaterial(t.id);
                     // 获取本标段 本月计量期审批通过数目,变更令审批通过数目,台账修订通过数目,材料调差通过数目
                     t.month_stage_num = await ctx.service.stageAudit.getNumByMonth(t.id, startMonth, endMonth);
                     t.month_change_num = await ctx.service.changeAudit.getNumByMonth(t.id, startMonth, endMonth);

+ 95 - 5
app/controller/material_controller.js

@@ -81,6 +81,11 @@ module.exports = app => {
                     }
                     s.decimal = s.decimal ? JSON.parse(s.decimal) : materialConst.decimal;
                 }
+                if (lastMaterial && lastMaterial.status === auditConst.status.checked && ctx.session.sessionUser.accountId === ctx.tender.data.user_id && stages.length > 0) {
+                    renderData.lastMaterialList = lastMaterial.is_new ? await ctx.service.materialListGcl.getAllDataByCondition({ where: { tid: ctx.tender.id } }) : await ctx.service.materialList.getMaterialData(ctx.tender.id, lastMaterial.id);
+                } else {
+                    renderData.lastMaterialList = null;
+                }
                 renderData.stages = stages;
                 renderData.openMaterialTax = openMaterialTax;
                 renderData.allMaterialTax = allMaterialTax;
@@ -166,7 +171,7 @@ module.exports = app => {
                 if (ctx.session.sessionUser.accountId !== ctx.tender.data.user_id) {
                     throw '您无权创建材料调差期';
                 }
-                const data = ctx.request.body;
+                const data = JSON.parse(ctx.request.body.data);
                 if (data.s_order === '') {
                     throw '没有选中计量期';
                 }
@@ -179,10 +184,12 @@ module.exports = app => {
                 if (!newMaterial) {
                     throw '新增材料调差期失败,请重试';
                 }
-                ctx.redirect('/tender/' + ctx.tender.id + '/measure/material/' + newMaterial.order);
+                // ctx.redirect('/tender/' + ctx.tender.id + '/measure/material/' + newMaterial.order);
+                ctx.body = { err: 0, msg: '', data: newMaterial };
             } catch (err) {
                 this.log(err);
-                ctx.redirect(ctx.request.header.referer);
+                // ctx.redirect(ctx.request.header.referer);
+                ctx.body = { err: 1, msg: err.toString(), data: null };
             }
         }
 
@@ -479,6 +486,89 @@ module.exports = app => {
                 responseData.data.materialChecklistData = materialChecklistData.sort(function(a, b) {
                     return ctx.helper.compareCode(a.b_code, b.b_code);
                 });
+                const lastMaterial = await ctx.service.material.getLastestMaterial(ctx.tender.id, true);
+                if (!lastMaterial.is_new) {
+                    const materialListData = await ctx.service.materialList.getMaterialData(ctx.tender.id, lastMaterial.id);
+                    const hadBillsList = [];
+                    for (const ml of materialListData) {
+                        const hb = _.find(hadBillsList, { gcl_id: ml.gcl_id, mb_id: ml.mb_id });
+                        if (!hb) {
+                            hadBillsList.push({
+                                gcl_id: ml.gcl_id,
+                                mb_id: ml.mb_id,
+                                order: ml.order,
+                                quantity: ml.quantity,
+                                expr: ml.expr,
+                            });
+                        }
+                    }
+                    // 批量插入并修改is_new值
+                    await ctx.service.materialListGcl.setData(lastMaterial.id, hadBillsList);
+                    responseData.data.gclList = hadBillsList;
+                } else {
+                    const hadBillsList = await ctx.service.materialListGcl.getAllDataByCondition({ where: { tid: ctx.tender.id } });
+                    responseData.data.gclList = hadBillsList;
+                }
+                ctx.body = responseData;
+            } catch (err) {
+                this.log(err);
+                ctx.body = { err: 1, msg: err.toString(), data: null };
+            }
+        }
+
+        /**
+         * 调差清单新建台账数据获取 (Post)
+         * @param {Object} ctx - egg全局变量
+         * @return {Promise<void>}
+         */
+        async loadGclData(ctx) {
+            try {
+                // const data = JSON.parse(ctx.request.body.data);
+                // const filter = data.filter.split(';');
+                const responseData = { err: 0, msg: '', data: {} };
+                const data = JSON.parse(ctx.request.body.data);
+                if (data.stage_id === '') {
+                    throw '没有选中计量期';
+                }
+                // 取所有已被调用的工料清单表
+                // responseData.data.materialListData = await ctx.service.materialList.getMaterialData(ctx.tender.id, ctx.material.id);
+                // responseData.data.materialNotJoinListData = await ctx.service.materialListNotjoin.getAllDataByCondition({ where: { tid: ctx.tender.id, mid: ctx.material.id } });
+                // 获取清单数据
+                responseData.data.ledger = await ctx.service.ledger.getData(ctx.tender.id);
+                responseData.data.pos = await ctx.service.pos.getPosData({ tid: ctx.tender.id });
+                // 获取所选期数据并合并相加同类清单项
+                responseData.data.curLedgerData = await ctx.service.stageBills.getStagesData(ctx.tender.id, data.stage_id.join(','));
+                responseData.data.curPosData = await ctx.service.stagePos.getStagesData(ctx.tender.id, data.stage_id.join(','), 'list');
+
+                // 获取gclidlist值
+                const lastMaterial = await ctx.service.material.getLastestCompleteMaterial(ctx.tender.id);
+                // responseData.data.materialListData = await ctx.service.materialList.getMaterialData(ctx.tender.id, lastMaterial.id);
+                if (!lastMaterial.is_new) {
+                    const materialListData = await ctx.service.materialList.getMaterialData(ctx.tender.id, lastMaterial.id);
+                    const hadBillsList = [];
+                    for (const ml of materialListData) {
+                        const hb = _.find(hadBillsList, { gcl_id: ml.gcl_id, mb_id: ml.mb_id });
+                        if (!hb) {
+                            hadBillsList.push({
+                                gcl_id: ml.gcl_id,
+                                mb_id: ml.mb_id,
+                                order: ml.order,
+                                quantity: ml.quantity,
+                                expr: ml.expr,
+                            });
+                        }
+                    }
+                    // 批量插入并修改is_new值
+                    await ctx.service.materialListGcl.setData(lastMaterial.id, hadBillsList);
+                    // responseData.data.gclList = hadBillsList;
+                }
+                const hadBillsList = await ctx.service.materialListGcl.getAllDataByCondition({ where: { tid: ctx.tender.id } });
+                responseData.data.gclList = hadBillsList;
+                // 获取清单设置已选清单
+                // const materialChecklistData = await ctx.service.materialChecklist.getAllDataByCondition({ where: { tid: ctx.tender.id } });
+                // responseData.data.materialChecklistData = materialChecklistData.sort(function(a, b) {
+                //     return ctx.helper.compareCode(a.b_code, b.b_code);
+                // });
                 ctx.body = responseData;
             } catch (err) {
                 this.log(err);
@@ -1366,10 +1456,10 @@ module.exports = app => {
                         if (isNaN(data.updateData.quantity)) {
                             throw '不能输入其它非数字类型字符';
                         }
-                        responseData.data = await ctx.service.materialList.saves(data.updateData);
+                        responseData.data = await ctx.service.materialList.saves(data.updateData, true);
                         break;
                     case 'pastes':
-                        responseData.data = await ctx.service.materialList.savePastes(data.updateData);
+                        responseData.data = await ctx.service.materialList.savePastes(data.updateData, true);
                         // 取所有工料表
                         break;
                     case 'resetChecklist':

+ 38 - 13
app/controller/profile_controller.js

@@ -247,6 +247,7 @@ module.exports = app => {
 
             const renderData = {
                 accountData,
+                fujianOssPath: ctx.app.config.fujianOssPath,
             };
             await this.layout('profile/sign.ejs', renderData);
         }
@@ -351,12 +352,22 @@ module.exports = app => {
 
                 // 获取账号数据
                 const accountData = await ctx.service.projectAccount.getDataByCondition({ id: sessionUser.accountId });
-
-                if (accountData.sign_path === '') {
-                    throw '不存在签名';
+                const data = JSON.parse(ctx.request.body.data);
+                let result = false;
+                if (data.type && data.type === 'stamp') {
+                    if (!accountData.stamp_path) {
+                        throw '不存在签章';
+                    }
+                    // 删除oss文件
+                    await ctx.app.fujianOss.delete(ctx.app.config.fujianOssFolder + accountData.stamp_path);
+                    // 删除库
+                    result = await ctx.service.projectAccount.update({ stamp_path: null }, { id: sessionUser.accountId });
+                } else {
+                    if (accountData.sign_path === '') {
+                        throw '不存在签名';
+                    }
+                    result = await ctx.service.projectAccount.update({ sign_path: '' }, { id: sessionUser.accountId });
                 }
-
-                const result = await ctx.service.projectAccount.update({ sign_path: '' }, { id: sessionUser.accountId });
                 if (!result) {
                     throw '移除签名失败';
                 }
@@ -412,15 +423,29 @@ module.exports = app => {
                 const stream = await ctx.getFileStream();
                 const create_time = Date.parse(new Date()) / 1000;
                 const fileInfo = path.parse(stream.filename);
-                const dirName = 'public/upload/sign';
-                const fileName = moment().format('YYYYMMDD') + '_sign_' + create_time + fileInfo.ext;
-                await ctx.helper.saveStreamFile(stream, path.join(this.app.baseDir, 'app', dirName, fileName));
-                await sendToWormhole(stream);
-                const result = await ctx.service.projectAccount.update({ sign_path: fileName }, { id: ctx.session.sessionUser.accountId });
-                if (result) {
-                    responseData.data = { sign_path: fileName };
+                if (stream.fields && stream.fields.type && stream.fields.type === 'stamp') {
+                    // const dirName = 'app/public/upload/sign/profile';
+                    // const fileName = moment().format('YYYYMMDD') + '_sign_' + create_time + fileInfo.ext;
+                    const filepath = `app/public/upload/sign/profile/qianzhang_${create_time + fileInfo.ext}`;
+                    await ctx.app.fujianOss.put(ctx.app.config.fujianOssFolder + filepath, stream);
+                    await sendToWormhole(stream);
+                    const result = await ctx.service.projectAccount.update({ stamp_path: filepath }, { id: ctx.session.sessionUser.accountId });
+                    if (result) {
+                        responseData.data = { stamp_path: filepath };
+                    } else {
+                        throw '添加数据库失败';
+                    }
                 } else {
-                    throw '添加数据库失败';
+                    const dirName = 'public/upload/sign';
+                    const fileName = moment().format('YYYYMMDD') + '_sign_' + create_time + fileInfo.ext;
+                    await ctx.helper.saveStreamFile(stream, path.join(this.app.baseDir, 'app', dirName, fileName));
+                    await sendToWormhole(stream);
+                    const result = await ctx.service.projectAccount.update({ sign_path: fileName }, { id: ctx.session.sessionUser.accountId });
+                    if (result) {
+                        responseData.data = { sign_path: fileName };
+                    } else {
+                        throw '添加数据库失败';
+                    }
                 }
             } catch (err) {
                 this.log(err);

+ 63 - 32
app/controller/report_archive_controller.js

@@ -55,6 +55,7 @@ module.exports = app => {
             const treeNodes = await ctx.service.rptTreeNode.getNodesByProjectId([-1, tender.data.project_id]);
             const custTreeNodes = await ctx.service.rptTreeNodeCust.getCustFoldersByUserId(this.ctx.session.sessionUser.accountId);
             const stageList = await ctx.service.stage.getValidStagesShort(tender.id);
+            const isAdmin = ctx.session.sessionUser.is_admin;
             //
             // 。。。
             let archiveList = [];
@@ -142,11 +143,12 @@ module.exports = app => {
                 authMobile: accountInfo.auth_mobile,
                 accountGroup: newAccountGroup,
                 accountList,
+                isAdmin,
             };
             if (stage_id === -1) {
-                await this.layout('report/index_archive.ejs', renderData);
+                await this.layout('report/index_archive.ejs', renderData, 'report/archive_popup.ejs');
             } else {
-                await this.layout('report/index_archive.ejs', renderData, 'stage/audit_modal.ejs');
+                await this.layout('report/index_archive.ejs', renderData, 'report/stage_archive_modal.ejs');
             }
         }
 
@@ -408,23 +410,36 @@ module.exports = app => {
             }
         }
 
-        async removeReportArchiveEncryption(ctx) {
+        async _removeReportArchiveEncryption(ctx) {
+            let rst = null;
             try {
                 const prjId = ctx.params.prjId;
                 const stgId = ctx.params.stgId;
-                const rptId = ctx.params.rptId;
-                // const uuid = ctx.params.uuid;
+                const rptId = parseInt(ctx.params.rptId);
+                const uuid = ctx.params.orgName;
                 const orgArchiveList = await ctx.service.rptArchiveEncryption.getPrjStgArchiveEncryption(prjId, stgId);
                 if (orgArchiveList.length > 0) {
                     const contentArr = JSON.parse(orgArchiveList[0].content);
-                    for (let idx = 0; idx < contentArr.length; idx++) {
-                        if (contentArr[idx].rpt_id === rptId) {
+                    for (let idx = contentArr.length - 1; idx >= 0; idx--) {
+                        if (contentArr[idx].rpt_id === rptId && contentArr[idx].uuid === uuid) {
                             contentArr.splice(idx, 1);
                             break;
                         }
                     }
-                    const updatedRst = await ctx.service.rptArchive.updateArchive(prjId, stgId, contentArr);
-                    ctx.body = { err: 0, msg: '', data: { updatedRst } };
+                    // const updatedRst = await ctx.service.rptArchive.updateArchive(prjId, stgId, contentArr);
+                    rst = await ctx.service.rptArchiveEncryption.updateArchiveEncryption(orgArchiveList[0].id, prjId, stgId, contentArr);
+                }
+            } catch (err) {
+                this.log(err);
+            }
+            return rst;
+        }
+
+        async removeReportArchiveEncryption(ctx) {
+            try {
+                const rst = await this._removeReportArchiveEncryption(ctx);
+                if (rst) {
+                    ctx.body = { err: 0, msg: '', data: { updatedRst: rst } };
                 } else {
                     ctx.body = { err: 0, msg: '', data: { updatedRst: null } };
                 }
@@ -434,35 +449,19 @@ module.exports = app => {
             }
         }
 
-        async removeReportArchive(ctx) {
+        async _removeReportArchive(ctx) {
+            let rst = null;
             try {
                 const prjId = ctx.params.prjId;
                 const stgId = ctx.params.stgId;
                 const rptId = ctx.params.rptId;
                 const orgUuidName = ctx.params.orgName;
-                const fileName = orgUuidName + '.PDF';
-                console.log('removing fileName: ' + fileName);
-                const fullName = path.join(this.app.baseDir, 'app', 'public/archive', fileName);
-                // await ctx.helper.saveStreamFile(stream, path.join(this.app.baseDir, 'app', 'public/archive', fileName));
-                // fs.stat(fullName, function(err, data) {
-                //     if (err) {
-                //         console.log(err);
-                //     } else {
-                //         fs.unlink(fullName, function(err) {
-                //             if (err) {
-                //                 console.log(err);
-                //             }
-                //         });
-                //     }
-                // });
-                const oss_result = await ctx.app.signPdfOss.delete('archive/' + fileName);
-                if (!(oss_result && oss_result.res.status === 204)) {
-                    throw '删除归档文件失败';
-                }
+                // const fileName = orgUuidName + '.PDF';
                 const orgArchiveList = await ctx.service.rptArchive.getPrjStgArchive(prjId, stgId);
                 if (orgArchiveList.length > 0) {
                     const contentArr = JSON.parse(orgArchiveList[0].content);
-                    for (const item of contentArr) {
+                    for (let idx = contentArr.length - 1; idx >= 0; idx--) {
+                        const item = contentArr[idx];
                         if (item.rpt_id === rptId) {
                             if (item.items && item.items.length > 0) {
                                 for (const subIdx in item.items) {
@@ -471,12 +470,44 @@ module.exports = app => {
                                         break;
                                     }
                                 }
+                                if (item.items.length === 0) {
+                                    contentArr.splice(idx, 1);
+                                }
                             }
                             break;
                         }
                     }
-                    const updatedRst = await ctx.service.rptArchive.updateArchive(prjId, stgId, contentArr);
-                    ctx.body = { err: 0, msg: orgUuidName, data: { fileName, updatedRst } };
+                    rst = await ctx.service.rptArchive.updateArchive(orgArchiveList[0].id, prjId, stgId, contentArr);
+                }
+            } catch (err) {
+                this.log(err);
+            }
+            return rst;
+        }
+
+        async removeReportArchive(ctx) {
+            try {
+                const orgUuidName = ctx.params.orgName;
+                const fileName = orgUuidName + '.PDF';
+                // console.log(ctx.params);
+                console.log('removing fileName: ' + fileName);
+                // const fullName = path.join(this.app.baseDir, 'app', 'public/archive', fileName);
+                const oss_sign_result = await ctx.app.signPdfOss.delete(`archive/sign/${fileName}`);
+                if (oss_sign_result && oss_sign_result.res && oss_sign_result.res.status === 204) {
+                    // console.log('删除归档的签名信息成功!');
+                    const oss_result = await ctx.app.signPdfOss.delete(`archive/${fileName}`);
+                    if (!(oss_result && oss_result.res.status === 204)) {
+                        throw '删除归档文件失败';
+                    }
+                } else {
+                    throw '删除归档签名文件失败';
+                }
+                // 还有加密签名信息
+                const archiveSignRemovedRst = await this._removeReportArchiveEncryption(ctx);
+                // console.log(archiveSignRemovedRst);
+                const archiveRemovedRst = await this._removeReportArchive(ctx);
+                if (archiveRemovedRst) {
+                    ctx.body = { err: 0, msg: orgUuidName, data: { fileName, updatedRst: archiveRemovedRst } };
                 } else {
                     ctx.body = { err: 0, msg: orgUuidName, data: { fileName, updatedRst: null } };
                 }

+ 18 - 15
app/controller/report_controller.js

@@ -85,6 +85,7 @@ module.exports = app => {
                 const custTreeNodes = await ctx.service.rptTreeNodeCust.getCustFoldersByUserId(this.ctx.session.sessionUser.accountId);
                 const custCfg = await ctx.service.rptCustomizeCfg.getCustomizeCfgByUserId('Administrator');
                 const stageList = await ctx.service.stage.getValidStagesShort(tender.id);
+                const isAdmin = ctx.session.sessionUser.is_admin;
                 // console.log(stage);
                 // console.log(stageList);
                 // console.log('ctx.stage.id: ' + ctx.stage.id);
@@ -317,6 +318,7 @@ module.exports = app => {
                     archiveList,
                     lastAuditor,
                     rpt_id: ctx.query.rpt_id,
+                    isAdmin,
                 };
                 await this.layout('report/index.ejs', renderData, 'report/rpt_all_popup.ejs');
                 // await this.layout('report/index.ejs', renderData);
@@ -434,7 +436,7 @@ module.exports = app => {
                 signatureRelInfo: roleRel,
                 stageAudit: stgAudit,
                 stageAuditOrg: stgAuditForOrg,
-                debugInfo: ctx.app.config.is_debug ? ctx.debugInfo : null,
+                debugInfo: ctx.session.sessionUser.loginStatus ? ctx.debugInfo : null,
                 customDefine: rptTpl[JV.NODE_CUSTOM_DEFINE],
                 stageFlow,
                 customSelect,
@@ -1463,7 +1465,7 @@ function mergeTextSignature(isTxtSignature, status, pageData, singleRoleRel, rpt
         let roleRelContent = [];
         if (_roleRelList && _roleRelList.rel_content !== null && _roleRelList.rel_content !== undefined && _roleRelList.rel_content !== '') {
             roleRelContent = JSON.parse(_roleRelList.rel_content);
-        };
+        }
         for (const page of _page.items) {
             if (page.signature_cells) {
                 const deleteSCellsIdx = [];
@@ -1475,23 +1477,24 @@ function mergeTextSignature(isTxtSignature, status, pageData, singleRoleRel, rpt
                         sCell.path = '';
                         sCell.pic = '';
                     }
-                    for (const role_rel of roleRelContent) {
-                        if (sCell.signature_name === role_rel.signature_name) {
-                            if (status === 3) {
+                    if (status === 3) {
+                        for (const role_rel of roleRelContent) {
+                            if (sCell.signature_name === role_rel.signature_name) {
                                 sCell.Value = role_rel.user_name; // 只有审核通过了才需要文本签名内容,但不管如何,都不影响迁移
+                                break;
                             }
-                            const newCell = {
-                                font: 'Footer',
-                                control: sCell.control,
-                                style: sCell.style,
-                                Value: sCell.Value,
-                                area: { Left: sCell.area.Left, Right: sCell.area.Right, Top: sCell.area.Top, Bottom: sCell.area.Bottom },
-                            };
-                            page.cells.push(newCell); // 迁移
-                            deleteSCellsIdx.push(scIdx);
-                            break;
                         }
                     }
+                    // 无论怎么处理,都得迁移
+                    const newCell = {
+                        font: 'Footer',
+                        control: sCell.control,
+                        style: sCell.style,
+                        Value: sCell.Value,
+                        area: { Left: sCell.area.Left, Right: sCell.area.Right, Top: sCell.area.Top, Bottom: sCell.area.Bottom },
+                    };
+                    page.cells.push(newCell); // 迁移
+                    deleteSCellsIdx.push(scIdx);
                 }
                 // 删除 page.signature_cells 签名(草图不能删);
                 if (isDeleteSignCell) {

+ 2 - 0
app/controller/revise_controller.js

@@ -647,6 +647,8 @@ module.exports = app => {
                     return await this.ctx.service.revisePos.deletePos(revise.tid, data.postData);
                 case 'paste':
                     return await this.ctx.service.revisePos.pastePosData(revise.tid, revise.id, data.postData);
+                case 'insert':
+                    return await this.ctx.service.revisePos.insertPos(revise.tid, revise.id, data.postData);
                 default:
                     throw '未知操作';
             }

+ 186 - 11
app/controller/setting_controller.js

@@ -17,6 +17,8 @@ const projectLog = require('../const/project_log');
 const imType = require('../const/tender').imType;
 const S2b = require('../lib/s2b');
 const measureType = require('../const/tender').measureType;
+const sendToWormhole = require('stream-wormhole');
+const path = require('path');
 
 module.exports = app => {
 
@@ -107,6 +109,10 @@ module.exports = app => {
                 if (ctx.query.keyword) {
                     keyword = ctx.query.keyword;
                 }
+                let company = '';
+                if (ctx.query.company) {
+                    company = ctx.query.company;
+                }
 
                 const page = ctx.page;
                 const pageSize = ctx.pageSize;
@@ -146,6 +152,8 @@ module.exports = app => {
                     queryData: JSON.stringify(ctx.urlInfo.query),
                 };
 
+                const unitList = await ctx.service.constructionUnit.getAllDataByCondition({ where: { pid: projectId } });
+
                 const renderData = {
                     projectData,
                     accountData,
@@ -154,6 +162,8 @@ module.exports = app => {
                     pageInfo,
                     keyword,
                     user_total,
+                    unitList,
+                    company,
                     // rule: JSON.stringify(frontRule),
                 };
                 await this.layout('setting/user.ejs', renderData, 'setting/user_modal.ejs');
@@ -209,22 +219,29 @@ module.exports = app => {
                     throw '没有访问权限';
                 }
 
+                let keyword = '';
+                if (ctx.query.keyword) {
+                    keyword = ctx.query.keyword;
+                }
+
+                let company = '';
+                if (ctx.query.company) {
+                    company = ctx.query.company;
+                }
+
                 // 获取数据规则
-                // const rule = ctx.service.projectAccount.rule('updateUser');
-                // const frontRule = ctx.helper.validateConvert(rule);
                 const page = ctx.page;
                 const pageSize = ctx.pageSize;
+                const columns = ['id', 'account', 'name', 'company', 'role', 'mobile', 'auth_mobile', 'telephone', 'enable', 'is_admin', 'bind', 'account_group', 'permission', 'cooperation'];
+                // 过滤数据
+                ctx.service.projectAccount.searchFilter(ctx.request.query, projectId, columns);
                 ctx.sort = ['id', 'desc'];
-                const total = await ctx.service.projectAccount.count({ project_id: projectId });
-                // 获取项目用户列表
-                // const accountData = await ctx.service.projectAccount.getAllDataByCondition({
-                //     where: { project_id: projectId },
-                //     columns: ['id', 'account', 'name', 'company', 'role', 'is_admin', 'account_group', 'permission', 'cooperation'],
-                // });
+                const total = await ctx.service.projectAccount.getCountWithBuilder();
 
-                const columns = ['id', 'account', 'name', 'company', 'role', 'is_admin', 'account_group', 'permission', 'cooperation'];
+                const accountData = await ctx.service.projectAccount.getListWithBuilder();
 
-                const accountData = await ctx.service.projectAccount.getListByProjectId(columns, projectId);
+                // 获取账号个数
+                const user_total = await ctx.service.projectAccount.count({ project_id: projectId });
 
                 // 分页相关
                 const pageInfo = {
@@ -236,14 +253,19 @@ module.exports = app => {
                     queryData: JSON.stringify(ctx.urlInfo.query),
                 };
 
+                const unitList = await ctx.service.constructionUnit.getAllDataByCondition({ where: { pid: projectId } });
+
                 const renderData = {
                     projectData,
                     accountData,
                     accountGroup,
                     permission,
+                    keyword,
                     permissionStr: JSON.stringify(permission),
                     pageInfo,
-                    user_total: total,
+                    user_total,
+                    unitList,
+                    company,
                     // rule: JSON.stringify(frontRule),
                 };
                 await this.layout('setting/user_permission.ejs', renderData, 'setting/user_permission_modal.ejs');
@@ -254,6 +276,159 @@ module.exports = app => {
         }
 
         /**
+         * 项目权限 -- 参建单位(Get)
+         * @param ctx
+         * @return {Promise<void>}
+         */
+        async userUnit(ctx) {
+            try {
+                // 获取项目数据
+                const projectId = ctx.session.sessionProject.id;
+                const projectData = await ctx.service.project.getDataById(projectId);
+                if (projectData === null) {
+                    throw '没有对应的项目数据';
+                }
+                if (ctx.session.sessionUser.is_admin === 0) {
+                    throw '没有访问权限';
+                }
+
+                const unitList = await ctx.service.constructionUnit.getAllDataByCondition({ where: { pid: projectId } });
+                for (const u of unitList) {
+                    const accountList = await ctx.service.projectAccount.getAllDataByCondition({ where: { project_id: projectId, company: u.name } });
+                    u.account_num = accountList.length;
+                }
+
+                // 获取账号个数
+                const user_total = await ctx.service.projectAccount.count({ project_id: projectId });
+
+                const renderData = {
+                    projectData,
+                    accountGroup,
+                    unitList,
+                    user_total,
+                    fujianOssPath: ctx.app.config.fujianOssPath,
+                };
+                await this.layout('setting/user_unit.ejs', renderData, 'setting/user_unit_modal.ejs');
+            } catch (error) {
+                console.log(error);
+                ctx.redirect('/dashboard');
+            }
+        }
+
+        /**
+         * 项目设置 -- 单位新增设置(Post)
+         * @param ctx
+         * @return {Promise<void>}
+         */
+        async userUnitAdd(ctx) {
+            const projectData = ctx.session.sessionProject;
+            try {
+                // 验证数据
+                if (projectData.id === undefined) {
+                    throw '不存在对应的项目数据';
+                }
+
+                // 获取验证规则
+                const rule = ctx.service.constructionUnit.rule('add');
+                ctx.validate(rule);
+                ctx.request.body.pid = projectData.id;
+                const result = await ctx.service.constructionUnit.save(ctx.request.body);
+                if (!result) {
+                    throw '保存单位失败';
+                }
+
+                this.setMessage('保存单位成功', this.messageType.SUCCESS);
+                ctx.redirect('/' + ctx.controllerName + '/user/unit');
+            } catch (error) {
+                console.log(error);
+                this.setMessage(error.toString(), this.messageType.ERROR);
+                ctx.redirect(ctx.request.header.referer);
+            }
+        }
+
+        /**
+         * 项目设置 -- 单位编辑(Post)
+         * @param ctx
+         * @return {Promise<void>}
+         */
+        async userUnitSave(ctx) {
+            const projectData = ctx.session.sessionProject;
+            const responseData = {
+                err: 0, msg: '', data: null,
+            };
+            try {
+                // 验证数据
+                if (projectData.id === undefined) {
+                    throw '不存在对应的项目数据';
+                }
+                const data = JSON.parse(ctx.request.body.data);
+                switch (data.type) {
+                    case 'update':
+                        const post_data = {
+                            pid: projectData.id,
+                            id: data.id,
+                        };
+                        post_data[data.val_name] = data.val;
+                        if (data.name) post_data.name = data.name;
+                        const result = await ctx.service.constructionUnit.save(post_data);
+                        if (!result) {
+                            throw '保存单位失败';
+                        }
+                        if (post_data.name) {
+                            const accountList = await ctx.service.projectAccount.getAllDataByCondition({ where: { project_id: projectData.id, company: post_data.name } });
+                            responseData.data = { account_num: accountList.length };
+                        }
+                        break;
+                    case 'delete':
+                        const accountList = await ctx.service.projectAccount.getAllDataByCondition({ where: { project_id: projectData.id, company: data.name } });
+                        if (accountList.length > 0) {
+                            throw '该单位下还存在账号,无法删除';
+                        }
+                        await ctx.service.constructionUnit.del(data.id);
+                        break;
+                    case 'del-sign':
+                        const info = await ctx.service.constructionUnit.getDataById(data.id);
+                        if (info.sign_path) {
+                            await ctx.app.fujianOss.delete(ctx.app.config.fujianOssFolder + info.sign_path);
+                        }
+                        await ctx.service.constructionUnit.update({ sign_path: null }, { id: info.id });
+                        break;
+                    default:
+                        break;
+                }
+                ctx.body = responseData;
+            } catch (err) {
+                this.log(err);
+                ctx.body = { err: 1, msg: err.toString(), data: null };
+            }
+        }
+
+        async userUnitUpload(ctx) {
+            const responseData = {
+                err: 0, msg: '', data: null,
+            };
+            try {
+                const stream = await ctx.getFileStream();
+                const create_time = Date.parse(new Date()) / 1000;
+                const fileInfo = path.parse(stream.filename);
+                const filepath = `app/public/upload/sign/unit/qianzhang_${create_time + fileInfo.ext}`;
+                await ctx.app.fujianOss.put(ctx.app.config.fujianOssFolder + filepath, stream);
+                await sendToWormhole(stream);
+                const result = await ctx.service.constructionUnit.update({ sign_path: filepath }, { id: stream.fields.id });
+                if (result) {
+                    responseData.data = { sign_path: filepath };
+                } else {
+                    throw '添加数据库失败';
+                }
+            } catch (err) {
+                this.log(err);
+                responseData.err = 1;
+                responseData.msg = err;
+            }
+            ctx.body = responseData;
+        }
+
+        /**
          * 项目设置 -- 账号启用和停用设置(Post)
          * @param ctx
          * @return {Promise<void>}

+ 51 - 46
app/controller/stage_controller.js

@@ -249,15 +249,11 @@ module.exports = app => {
             //     ? await ctx.helper.loadLedgerDataFromOss(ctx.stage.ledgerHis.bills_file)
             //     : await ctx.service.ledger.getAllDataByCondition({ where: { tender_id: ctx.tender.id } });
             const ledgerData = ctx.stage.ledgerHis
-                ? await ctx.helper.loadLedgerDataFromOss(ctx.stage.ledgerHis.bills_file)
+                ? await ctx.service.ledger.loadDataFromOss(ctx.tender.id, ctx.stage.ledgerHis.bills_file)
                 : await ctx.service.ledger.getAllDataByCondition({ columns: ledgerColumn, where: { tender_id: ctx.tender.id } });
             const dgnData = await ctx.service.stageBillsDgn.getDgnData(ctx.tender.id);
-            for (const d of dgnData) {
-                const l = ctx.app._.find(ledgerData, { id: d.id });
-                ctx.app._.assignIn(l, d);
-            }
-            let curStageData,
-                preStageData;
+            const importData = await ctx.service.stageImportChange.getImportLid(ctx.stage.id);
+            let curStageData;
             // 当前操作人查看最新数据,其他人查看历史数据
             if (ctx.stage.readOnly) {
                 curStageData = await ctx.service.stageBills.getAuditorStageData2(ctx.tender.id, ctx.stage.id, ctx.stage.curTimes, ctx.stage.curOrder);
@@ -269,24 +265,21 @@ module.exports = app => {
                 }
             }
             // 查询截止上期数据
-            if (ctx.stage.order > 1) {
-                preStageData = await ctx.service.stageBillsFinal.getFinalData(ctx.tender.data, ctx.stage.order - 1);
-            } else {
-                preStageData = [];
-            }
+            const preStageData = ctx.stage.order > 1 ? await ctx.service.stageBillsFinal.getFinalData(ctx.tender.data, ctx.stage.order - 1) : [];
             this.ctx.helper.assignRelaData(ledgerData, [
+                { data: dgnData, fields: ['deal_dgn_qty1', 'deal_dgn_qty2', 'c_dgn_qty1', 'c_dgn_qty2'], prefix: '', relaId: 'id'},
+                { data: importData, fields: ['is_import'], prefix: '', relaId: 'lid'},
                 { data: curStageData, fields: ['contract_qty', 'contract_expr', 'contract_tp', 'qc_qty', 'qc_tp', 'postil'], prefix: '', relaId: 'lid' },
                 { data: preStageData, fields: ['contract_qty', 'contract_tp', 'qc_qty', 'qc_tp', 'used'], prefix: 'pre_', relaId: 'lid' },
             ]);
             return ledgerData;
         }
         async _getStagePosData(ctx, posColumn) {
-            let curStageData,
-                preStageData;
-            // const posData = await ctx.service.pos.getAllDataByCondition({ where: { tid: ctx.tender.id } });
-            const posData = await ctx.service.pos.getAllDataByCondition({ columns: posColumn, where: { tid: ctx.tender.id } });
+            let curStageData;
+            const posData =  ctx.stage.ledgerHis
+                ? await ctx.service.ledger.loadDataFromOss(ctx.tender.id, ctx.stage.ledgerHis.pos_file)
+                : await ctx.service.pos.getAllDataByCondition({ columns: posColumn, where: { tid: ctx.tender.id } });
             // 根据当前人,或指定对象查询数据
-            // console.time('cur');
             if (ctx.stage.readOnly) {
                 curStageData = await ctx.service.stagePos.getAuditorStageData2(ctx.tender.id,
                     ctx.stage.id, ctx.stage.curTimes, ctx.stage.curOrder);
@@ -298,18 +291,8 @@ module.exports = app => {
                     await ctx.service.stagePos.deleteById(surplus);
                 }
             }
-            // console.timeEnd('cur');
             // 查询截止上期数据
-            // console.time('pre');
-            if (ctx.stage.order > 1) {
-                preStageData = await ctx.service.stagePosFinal.getFinalData(ctx.tender.data, ctx.stage.order - 1);
-            } else {
-                preStageData = [];
-            }
-            // console.timeEnd('pre');
-            // console.time('assign');
-            // console.log('cur: ' + curStageData.length);
-            // console.log('pre: ' + preStageData.length);
+            const preStageData = ctx.stage.order > 1 ? await ctx.service.stagePosFinal.getFinalData(ctx.tender.data, ctx.stage.order - 1) : [];
             this.ctx.helper.assignRelaData(posData, [
                 { data: curStageData, fields: ['contract_qty', 'contract_expr', 'qc_qty', 'postil'], prefix: '', relaId: 'pid' },
                 { data: preStageData, fields: ['contract_qty', 'qc_qty'], prefix: 'pre_', relaId: 'pid' },
@@ -363,6 +346,9 @@ module.exports = app => {
                         case 'change':
                             responseData.data.changeData = await this._getStageChangeData(ctx);
                             break;
+                        case 'import_change':
+                            responseData.data.import_change = await this.ctx.service.stageImportChange.getStageImportData(this.ctx.stage);
+                            break;
                         case 'dealBills':
                             responseData.data.dealBills = await ctx.service.dealBills.getAllDataByCondition({
                                 where: { tender_id: this.ctx.tender.id },
@@ -436,7 +422,6 @@ module.exports = app => {
                     preStageData;
                 responseData.data = await ctx.service.pos.getPosDataWithAddStageOrder(condition);
                 // 根据当前人,或指定对象查询数据
-                // console.time('cur');
                 const curWhere = JSON.parse(ctx.request.body.data);
                 if (ctx.stage.readOnly) {
                     curStageData = await ctx.service.stagePos.getAuditorStageData2(ctx.tender.id,
@@ -444,23 +429,16 @@ module.exports = app => {
                 } else {
                     curStageData = await ctx.service.stagePos.getLastestStageData2(ctx.tender.id, ctx.stage.id, curWhere);
                 }
-                // console.timeEnd('cur');
                 // 查询截止上期数据
-                // console.time('pre');
                 if (ctx.stage.order > 1) {
                     preStageData = await ctx.service.stagePosFinal.getFinalData(ctx.tender.data, ctx.stage.order - 1);
                 } else {
                     preStageData = [];
                 }
-                // console.timeEnd('pre');
-                // console.time('assign');
-                // console.log('cur: ' + curStageData.length);
-                // console.log('pre: ' + preStageData.length);
                 this.ctx.helper.assignRelaData(responseData.data, [
                     { data: curStageData, fields: ['contract_qty', 'qc_qty', 'postil'], prefix: '', relaId: 'pid' },
                     { data: preStageData, fields: ['contract_qty', 'qc_qty'], prefix: 'pre_', relaId: 'pid' },
                 ]);
-                // console.timeEnd('assign');
                 ctx.body = responseData;
             } catch (err) {
                 this.log(err);
@@ -575,7 +553,7 @@ module.exports = app => {
                 if (!data.cid) {
                     throw '查询数据错误';
                 }
-                const detailData = await this._getChangeDetailData(ctx.tender.id, ctx.stage.id, data.cid);
+                const detailData = await this._getChangeDetailData(ctx.tender.id, ctx.stage.id, data.cid, data.is_import);
                 ctx.body = { err: 0, msg: '', data: detailData };
             } catch (err) {
                 this.log(err);
@@ -975,7 +953,6 @@ module.exports = app => {
 
                 ctx.body = responseData;
             } catch (err) {
-                console.log(err);
                 this.log(err);
                 ctx.body = { err: 1, msg: err.toString(), data: null };
             }
@@ -1054,12 +1031,17 @@ module.exports = app => {
          * @return {Promise<{}>}
          * @private
          */
-        async _getChangeDetailData(tid, sid, cid) {
+        async _getChangeDetailData(tid, sid, cid, isImport) {
             const data = {};
             data.attachments = await this.ctx.service.changeAtt.getChangeAttachment(cid);
             data.bills = await this.ctx.service.changeAuditList.getAllDataByCondition({ where: { cid } });
-            data.addUsedBills = await this.ctx.service.stageChange.getUsedData(tid, cid);
-            data.curUsedBills = await this.ctx.service.stageChange.getStageUsedData(sid, cid);
+            if (isImport) {
+                data.endUsedBills = await this.ctx.service.stageImportChange.getChangeEndUsedData(tid, cid);
+                data.curUsedBills = await this.ctx.service.stageImportChange.getChangeUsedData(sid, cid);
+            } else {
+                data.endUsedBills = await this.ctx.service.stageChange.getUsedData(tid, cid);
+                data.curUsedBills = await this.ctx.service.stageChange.getStageUsedData(sid, cid);
+            }
             return data;
         }
         /**
@@ -1076,14 +1058,37 @@ module.exports = app => {
                 data.changes = await ctx.service.stageChange.getChangeWithUsedInfo(ctx.stage);
                 if (data.changes.length > 0) {
                     const change = data.changes[0];
-                    change.detail = await this._getChangeDetailData(ctx.tender.id, ctx.stage.id, change.cid);
+                    change.detail = await this._getChangeDetailData(ctx.tender.id, ctx.stage.id, change.cid, false);
                 }
+                data.import_changes = await ctx.service.stageImportChange.getChangeWithUsedInfo(ctx.stage);
+                if (data.import_changes.length > 0) {
+                    const change = data.import_changes[0];
+                    change.detail = await this._getChangeDetailData(ctx.tender.id, ctx.stage.id, change.cid, true);
+                }
+                data.used_import_cid = await ctx.service.stageImportChange.getStageUsedChangeId(ctx.stage.id);
                 ctx.body = { err: 0, msg: '', data };
             } catch (err) {
                 this.log(err);
                 ctx.body = { err: 1, msg: err.toString(), data: null };
             }
         }
+
+        async getImportChangeData(ctx) {
+            try {
+                const data = JSON.parse(ctx.request.body.data);
+                if (!data.import_lid) throw '查询数据错误';
+                const result = {};
+                result.import_changes = await ctx.service.stageImportChange.getChangeWithUsedInfo(ctx.stage, data.import_lid);
+                if (result.import_changes.length > 0) {
+                    const change = result.import_changes[0];
+                    change.detail = await this._getChangeDetailData(ctx.tender.id, ctx.stage.id, change.cid, true);
+                }
+                ctx.body = { err: 0, msg: '', data: result };
+            } catch (err) {
+                this.log(err);
+                ctx.body = { err: 1, msg: err.toString(), data: null };
+            }
+        }
         /**
          * 变更令 (Get)
          * @param ctx
@@ -1254,7 +1259,6 @@ module.exports = app => {
                     }
                     const cacheKey = 'smsCode:' + ctx.session.sessionUser.accountId;
                     const cacheCode = await app.redis.get(cacheKey);
-                    // console.log(cacheCode);
                     if (cacheCode === null || code === undefined || cacheCode !== (code + pa.auth_mobile)) {
                         throw '验证码不正确!';
                     }
@@ -1359,16 +1363,17 @@ module.exports = app => {
                 if (data.main) {
                     result.main = {};
                     result.main.ledger = ctx.stage.ledgerHis
-                        ? await ctx.helper.loadLedgerDataFromOss(ctx.stage.ledgerHis.bills_file)
+                        ? await ctx.service.ledger.loadDataFromOss(ctx.tender.id, ctx.stage.ledgerHis.bills_file)
                         : await ctx.service.ledger.getData(ctx.tender.id);
                     result.main.pos = ctx.stage.ledgerHis
-                        ? await ctx.helper.loadLedgerDataFromOss(ctx.stage.ledgerHis.pos_file)
+                        ? await ctx.service.pos.loadDataFromOss(ctx.tender.id, ctx.stage.ledgerHis.pos_file)
                         : await ctx.service.pos.getPosData({ tid: ctx.tender.id });
                 }
                 for (const order of data.roles) {
                     const data = { order, bills: [], pos: [] };
                     const compareTimes = ctx.stage.status === auditConst.status.checkNo && !ctx.stage.readOnly
-                        ? ctx.stage.curTimes - 1 : ctx.stage.curTimes;
+                        ? ( order === 0 ? ctx.stage.curTimes : ctx.stage.curTimes - 1)
+                        : ctx.stage.curTimes;
                     data.bills = await ctx.service.stageBills.getAuditorStageData2(ctx.tender.id, ctx.stage.id, compareTimes, order);
                     data.pos = await ctx.service.stagePos.getAuditorStageData2(ctx.tender.id, ctx.stage.id, compareTimes, order);
                     result.roles.push(data);

+ 5 - 2
app/controller/tender_controller.js

@@ -1306,13 +1306,16 @@ module.exports = app => {
                         break;
                     case 'stage':
                         const stageData = await this.ctx.service.stageBills.sumLoad(data.lid, data.tenders, data.cover);
+                        stageData.import_change = { target: { import_lid: data.lid } };
+                        stageData.import_change.data = await this.ctx.service.stageImportChange.getLeafXmjStageImportData(ctx.stage, data.lid);
+                        await ctx.service.stage.updateCheckCalcFlag(ctx.stage, true);
+                        await ctx.service.stage.updateCacheTime(ctx.stage.id);
                         ctx.body = {err: 0, msg: '', data: stageData};
                         break;
                     default:
                         throw '数据错误';
                 }
             } catch (err) {
-                console.log(err);
                 this.log(err);
                 ctx.ajaxErrorBody(err, '导入数据失败');
             }
@@ -1377,7 +1380,7 @@ module.exports = app => {
                             responseData.data[f] = await ctx.service.change.getAllDataByCondition({
                                 columns: [ 'cid', 'code', 'name', 'selected' ],
                                 where,
-                                orders: [['sin_time', 'desc']],
+                                orders: [['in_time', 'desc']],
                             });
                             break;
                         case 'change_project':

+ 0 - 1
app/extend/helper.js

@@ -282,7 +282,6 @@ module.exports = {
                 data,
                 dataType,
             }) : await this.ctx.curl(url);
-            // console.log(response);
             if (response.status !== 200) {
                 if (count > 0) {
                     count = count - 1;

+ 6 - 0
app/lib/stage_im.js

@@ -120,6 +120,7 @@ class StageIm {
         } else {
             this.changes = await this.ctx.service.stageChange.getLastestAllStageData(this.ctx.tender.id, this.ctx.stage.id);
         }
+        this.import_changes = await this.ctx.service.stageImportChange.getStageImportData(this.ctx.stage);
     }
 
     // 检查汇总节点
@@ -497,6 +498,7 @@ class StageIm {
                         im.changes.push(c);
                     }
                 }
+                this.import_changes.forEach(x => { if (x.lid === p.id) im.changes.push(x) });
             } else {
                 for (const pp of posRange) {
                     if ((!pp.qc_qty || pp.qc_qty === 0)) {
@@ -728,6 +730,7 @@ class StageIm {
                         imDefault.changes.push(c);
                     }
                 }
+                this.import_changes.forEach(x => { if (x.lid === p.id) imDefault.changes.push(x) });
 
                 imDefault.contract_jl = this.ctx.helper.add(imDefault.contract_jl, p.contract_qty);
                 imDefault.qc_jl = this.ctx.helper.add(imDefault.qc_jl, p.qc_qty);
@@ -810,6 +813,7 @@ class StageIm {
                     im.changes.push(c);
                 }
             }
+            this.import_changes.forEach(x => { if (x.lid === node.id) im.changes.push(x) });
         } else {
             for (const p of posRange) {
                 if ((!p.qc_qty || p.qc_qty === 0)) {
@@ -984,6 +988,7 @@ class StageIm {
                             im.changes.push(c);
                         }
                     }
+                    this.import_changes.forEach(x => { if (x.lid === p.id) im.changes.push(x) });
                 }
             }
         }
@@ -1061,6 +1066,7 @@ class StageIm {
         this.stage = stage;
         this.details = details;
         this.changes = changes;
+        this.import_changes = [];
 
         const self = this;
         // 初始化

+ 40 - 27
app/lib/sum_load.js

@@ -8,6 +8,7 @@
  * @version
  */
 const Ledger = require('../lib/ledger');
+const auditConst = require('../const/audit');
 
 class loadGclBaseTree {
     /**
@@ -329,6 +330,7 @@ class gatherStageGclTree extends loadGclBaseTree {
             const relaPos = pos.filter(x => {return x.lid === d.id});
             const baseNode = {
                 id: d.id,
+                is_import: d.is_import,
                 ledger_id: d.ledger_id,
                 ledger_pid: d.ledger_pid,
                 level: d.level,
@@ -338,11 +340,15 @@ class gatherStageGclTree extends loadGclBaseTree {
                 name: d.name,
                 unit: d.unit,
                 unit_price: d.unit_price,
-                org_contract_qty: this.cover ? d.contract_qty || 0 : 0,
-                org_contract_tp: this.cover ? d.contract_tp || 0 : 0,
+                org_contract_qty: d.contract_qty || 0,
+                org_contract_tp: d.contract_tp || 0,
+                org_qc_qty: d.qc_qty || 0,
+                org_qc_tp: d.qc_tp || 0,
                 org_order: d.order,
                 contract_qty: 0,
                 contract_tp: 0,
+                qc_qty: 0,
+                qc_tp: 0,
                 is_tp: d.is_tp,
                 hasPos: relaPos.length > 0,
             };
@@ -357,12 +363,7 @@ class gatherStageGclTree extends loadGclBaseTree {
         if (!node.change_detail) node.change_detail = [];
         for (const cd of source.change_detail) {
             if (!cd.qty) continue;
-            let ncd = node.change_detail.find(x => { return x.cid === cd.cid; });
-            if (!ncd) {
-                ncd = { cid: cd.cid, c_code: cd.c_code };
-                node.change_detail.push(ncd);
-            }
-            ncd.qty = this.ctx.helper.add(ncd.qty, cd.qty);
+            node.change_detail.push(cd);
         }
     }
     gather(source, parent) {
@@ -376,36 +377,43 @@ class gatherStageGclTree extends loadGclBaseTree {
         } else {
             node.contract_qty = this.ctx.helper.add(node.contract_qty, source.contract_qty);
             node.contract_tp = this.ctx.helper.mul(node.unit_price, node.contract_qty, this.ctx.tender.info.decimal.tp);
+
+            node.qc_qty = this.ctx.helper.add(node.qc_qty, source.qc_qty);
+            node.qc_tp = this.ctx.helper.mul(node.unit_price, node.qc_qty, this.ctx.tender.info.decimal.tp);
         }
         this._gatherChange(node, source);
         return node;
     }
     getUpdateData() {
-        const result = {update: [], errors: []};
+        const result = { update: [], errors: [], qc_detail: [] };
         for (const bn of this.baseNodes) {
-            if (bn.contract_qty !== bn.org_contract_qty || bn.contract_tp !== bn.org_contract_tp) {
-                result.update.push({lid: bn.id, contract_qty: bn.contract_qty, contract_tp: bn.contract_tp });
+            if (!this.cover && !bn.is_import && !bn.contract_qty && !bn.qc_qty && !bn.contract_tp) continue;
+
+            if (!bn.is_import && bn.org_qc_qty !== 0 && bn.qc_qty !== 0) {
+                result.errors.push({ b_code: bn.b_code, name: bn.name, unit: bn.unit, qc_qty: bn.qc_qty, ledger_id: bn.ledger_id, type: 'qc-conflict'});
+                continue;
+            }
+            if (bn.is_import || this.cover || bn.contract_qty !== bn.org_contract_qty || bn.contract_tp !== bn.org_contract_tp || bn.qc_qty !== bn.org_qc_qty) {
+                let data = { lid: bn.id, contract_qty: bn.contract_qty, contract_tp: bn.contract_tp };
+                if (!bn.is_import && bn.org_qc_qty) {
+                    data.qc_qty = bn.org_qc_qty;
+                    data.qc_tp = bn.org_qc_tp;
+                } else {
+                    data.qc_qty = bn.qc_qty;
+                    data.qc_tp = bn.qc_tp;
+                }
+                result.update.push(data);
             }
             if (bn.change_detail && bn.change_detail.length > 0) {
                 for (const cd of bn.change_detail) {
-                    result.errors.push({
-                        ledger_id: bn.ledger_id,
-                        b_code: bn.b_code, name: bn.name, unit: bn.unit,
-                        c_code: cd.c_code, qty: cd.qty, type: 'qc',
+                    result.qc_detail.push({
+                        lid: bn.id, rela_tid: cd.tid, rela_sid: cd.sid, rela_lid: cd.lid, rela_cid: cd.cid, rela_cbid: cd.cbid, rela_qty: cd.qty
                     });
                 }
             }
         }
         for (const i of this.items) {
-            result.errors.push({ b_code: i.b_code, name: i.name, unit: i.unit, qty: i.contract_qty, type: 'miss' });
-            if (i.change_detail && i.change_detail.length > 0) {
-                for (const cd of i.change_detail) {
-                    result.errors.push({
-                        b_code: i.b_code, name: i.name, unit: i.unit,
-                        c_code: cd.c_code, qty: cd.qty, type: 'miss-qc',
-                    });
-                }
-            }
+            result.errors.push({ b_code: i.b_code, name: i.name, unit: i.unit, qty: i.contract_qty, qc_qty: i.qc_qty, type: 'miss' });
         }
         return result;
     }
@@ -493,6 +501,7 @@ class sumLoad {
             if (!b) continue;
             b.contract_qty = csb.contract_qty;
             b.contract_tp = csb.contract_tp;
+            b.qc_qty = csb.qc_qty;
         }
         for (const csc of curStageChange) {
             if (!csc.qty) continue;
@@ -500,9 +509,9 @@ class sumLoad {
             const b = billsIndex[csc.lid];
             if (!b) continue;
             if (!b.change_detail) b.change_detail = [];
-            let c = b.change_detail.find(x => { return x.cid === csc.cid });
+            let c = b.change_detail.find(x => { return x.cbid === csc.cbid });
             if (!c) {
-                c = { cid: csc.cid };
+                c = { tid: csc.tid, sid: csc.sid, cid: csc.cid, cbid: csc.cbid, lid: csc.lid, c_code: csc.c_code };
                 b.change_detail.push(c);
             }
             c.qty = this.ctx.helper.add(c.qty, csc.qty);
@@ -516,7 +525,9 @@ class sumLoad {
         });
         const posterity = await this.ctx.service.ledger.getPosterityByParentId(this.ctx.tender.id, select.ledger_id);
         const stageBills = await this.ctx.service.stageBills.getLastestStageData2(this.ctx.tender.id, this.ctx.stage.id);
+        const importLid = await this.ctx.service.stageImportChange.getLeafXmjImportLid(this.ctx.stage.id, select.id);
         this.ctx.helper.assignRelaData(posterity, [
+            { data: importLid, fields: [ 'is_import' ], prefix: '', relaId: 'lid' },
             { data: stageBills, fields: ['contract_qty', 'contract_tp', 'qc_qty', 'qc_tp' ], prefix: '', relaId: 'lid' },
         ]);
         const pos = await this.ctx.service.revisePos.getData(this.ctx.tender.id);
@@ -528,7 +539,9 @@ class sumLoad {
             const stage = await this.ctx.service.stage.getDataByCondition({tid: tender.tid, order: tender.stage});
             if (!stage) throw '选择的期不存在';
             const curStageData = await this.ctx.service.stageBills.getLastestStageData2(tender.tid, stage.id);
-            const curStageChange = await this.ctx.service.stageChangeFinal.getSumLoadFinalData(stage.id);
+            const curStageChange = stage.status === auditConst.stage.status.checked
+                ? await this.ctx.service.stageChangeFinal.getSumLoadFinalData(stage.id)
+                : await this.ctx.service.stageChange.getSumLoadFinalData(stage.id);
             this._loadCurStageAndChange(billsData, curStageData, curStageChange);
             const billsTree = new Ledger.billsTree(this.ctx, {
                 id: 'ledger_id',

+ 42 - 0
app/middleware/auto_finish_logger.js

@@ -0,0 +1,42 @@
+'use strict';
+
+/**
+ * 自动记录日志
+ *
+ * @author CaiAoLin
+ * @date 2017/10/30
+ * @version
+ */
+const os = require('os');
+
+module.exports = options => {
+    return async function autoFinishLogger(ctx, next) {
+        await next();
+
+        if (ctx.url.indexOf('/public/') === 0) return;
+
+        const responseTime = new Date();
+        const runTime = responseTime.getTime() - ctx.logInfo.time.getTime();
+        let logData;
+        if (ctx.session && ctx.session.sessionUser) {
+            const cpus = os.cpus();
+            const endCpus = cpus.map(x => {
+                return `${((1-x.times.idle/(x.times.idle+x.times.user+x.times.nice+x.times.sys+x.times.irq))*100).toFixed(2)}%`
+            });
+            logData = {
+                requestTime: ctx.logInfo.time, responseTime, runTime,
+                beginCpus: ctx.logInfo.cpus, endCpus,
+                user: ctx.session.sessionUser.accountId, userName: ctx.session.sessionUser.name, loginType: ctx.session.sessionUser.loginType,
+                pCode: ctx.session.sessionProject.code,
+                data: ctx.request.body,
+            };
+        } else {
+            logData = {
+                requestTime: ctx.logTime, responseTime, runTime,
+                data: ctx.body,
+            };
+        }
+        const bLogger = runTime > 500 ? ctx.getLogger('warning') : ctx.getLogger('finish');
+        bLogger.info(JSON.stringify(logData));
+    };
+};

+ 6 - 0
app/middleware/auto_logger.js

@@ -7,6 +7,7 @@
  * @date 2017/10/30
  * @version
  */
+const os = require('os');
 
 module.exports = options => {
     return function* autoLogger(next) {
@@ -36,6 +37,11 @@ module.exports = options => {
             };
             bLogger.info(JSON.stringify(logData));
         }
+        this.logInfo = { time: new Date() };
+        const cpus = os.cpus();
+        this.logInfo.cpus = cpus.map(x => {
+            return `${((1-x.times.idle/(x.times.idle+x.times.user+x.times.nice+x.times.sys+x.times.irq))*100).toFixed(2)}%`
+        });
 
         // 自动记录log的action
         // const autoLogAction = ['save', 'delete'];

+ 29 - 1
app/public/js/advance_audit.js

@@ -267,7 +267,7 @@ $(document).ready(function () {
             // 支付比例转化
             val = fixedToSub(val)
             if (val.toFixed(2) === max.toFixed(2)) {
-              // 比例达到最大,特殊处理金额的显示小数点
+                // 比例达到最大,特殊处理金额的显示小数点
                 val = fixedToSub(max, 2);
                 isLimitMax = true
             }
@@ -298,6 +298,34 @@ $(document).ready(function () {
         }, 500);
     })
 
+    const payDate = $('.pay-date-input').datepicker().data('datepicker');
+    payDate.selectDate(new Date(advance.pay_time));
+
+    $('.pay-date-input').datepicker({
+        onShow: function () {
+            $('.pay-date-input').attr('readOnly', true);
+        },
+        onHide: function (dp, animationCompleted) {
+            if(animationCompleted) {
+                $('.pay-date-input').attr('readOnly', false);
+                const val = $('.pay-date-input').val();
+                // 日期格式判断
+                if (!(isNaN(val)&&!isNaN(Date.parse(val)))) {
+                    toastr.error('日期格式有误!');
+                    $('.pay-date-input').val(moment(advance.pay_time).format('YYYY-MM-DD'));
+                    payDate.selectDate(new Date(advance.pay_time));
+                } else if(val !== moment(advance.pay_time).format('YYYY-MM-DD')) {
+                    const data = {
+                        pay_time: val
+                    };
+                    // console.log(data);
+                    update(data);
+                    advance.pay_time = new Date(val);
+                }
+            }
+        }
+    });
+
     function checkInput() {
         const newVal = {
             cur_amount: parseFloat($(`.pay-input[data-type=${1}]`).val()),

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

@@ -326,7 +326,7 @@ function getAllList(currPageNum = 1) {
         html += `<tr>
         <td width="25"><input type="checkbox" class="check-file" file-id=${att.id}></td>
         <td>${((currPageNum-1)*pageCount)+index+1}</td>
-        <td><a href="javascript:void(0)" class="pl-0 col-11 att-file-name" file-id=${att.id}>${att.filename}${att.fileext}</a></td>
+        <td><a href="${att.filepath}" target="_blank" class="pl-0 col-11 att-file-name" file-id=${att.id}>${att.filename}${att.fileext}</a></td>
         <td>${moment(att.in_time * 1000).format('YYYY-MM-DD')}<br>${bytesToSize(att.filesize)}</td>
         <td>
             <a href="/change/download/file/${att.id}" class="mr-2" title="下载"><span class="fa fa-download text-primary"></span></a>`

+ 9 - 8
app/public/js/change_information_set.js

@@ -154,7 +154,7 @@ $(document).ready(() => {
                 return ZhCalc.round(data.camount, findDecimal(data.unit));
             },
             del_list: function (data) {
-                return !_.find(changeUsedData, { id: data.id }) ? '移除' : '';
+                return !_.find(changeUsedData, { cbid: data.id }) ? '移除' : '';
             }
         },
         readOnly: {
@@ -162,7 +162,7 @@ $(document).ready(() => {
                 return !readOnly && data.lid != 0;
             },
             isEdit2: function (data) {
-                return !readOnly && (data.lid != 0 || (data.lid == 0 && _.findIndex(changeUsedData, { id: data.id }) !== -1));
+                return !readOnly && (data.lid != 0 || (data.lid == 0 && _.findIndex(changeUsedData, { cbid: data.id }) !== -1));
             },
         },
     };
@@ -211,7 +211,7 @@ $(document).ready(() => {
         del: function () {
             const select = SpreadJsObj.getSelectObject(changeSpreadSheet);
             const index = changeList.indexOf(select);
-            if (index > -1 && !_.find(changeUsedData, { id: select.id })) {
+            if (index > -1 && !_.find(changeUsedData, { cbid: select.id })) {
                 postData(window.location.pathname + '/save', {type: 'del', id: select.id}, function (result) {
                     changeList.splice(index, 1);
                     changeSpreadSheet.deleteRows(index, 1);
@@ -240,7 +240,7 @@ $(document).ready(() => {
             const sel = info.sheet.getSelections()[0];
             const col = info.sheet.zh_setting.cols[sel.col];
             const data = SpreadJsObj.getSelectObject(info.sheet);
-            if (col && col.field === 'del_list' && !_.find(changeUsedData, { id: data.id })) {
+            if (col && col.field === 'del_list' && !_.find(changeUsedData, { cbid: data.id })) {
                 changeSpreadObj.del();
             }
             changeSpreadObj.resetXmjSpread(data);
@@ -281,7 +281,7 @@ $(document).ready(() => {
                 }
                 if(col.field === 'camount') {
                     // 判断是否 正数必须大于等于限制值,负数必须小于等于限制值,否则无法更改
-                    const usedInfo = _.find(changeUsedData, { id: select.id });
+                    const usedInfo = _.find(changeUsedData, { cbid: select.id });
                     if (usedInfo && usedInfo.used_qty >= 0 && validText < usedInfo.used_qty) {
                         toastr.error('清单变更数值必须大于等于已调用值 ' + usedInfo.used_qty);
                         SpreadJsObj.reLoadRowData(info.sheet, info.row);
@@ -371,7 +371,7 @@ $(document).ready(() => {
                         }
                         if(colSetting.field === 'camount') {
                             // 判断是否 正数必须大于等于限制值,负数必须小于等于限制值,否则无法更改
-                            const usedInfo = _.find(changeUsedData, { id: sortData[curRow].id });
+                            const usedInfo = _.find(changeUsedData, { cbid: sortData[curRow].id });
                             if (usedInfo && usedInfo.used_qty >= 0 && validText < usedInfo.used_qty) {
                                 toastr.error(hintRow ? '清单' + (hintRow+1) + '行变更数值必须大于等于已调用值 ' + usedInfo.used_qty : '清单变更数值必须大于等于已调用值 ' + usedInfo.used_qty);
                                 bPaste = false;
@@ -619,7 +619,7 @@ $(document).ready(() => {
                         const sel = changeSpreadSheet.getSelections()[0];
                         changeSpreadObj.resetXmjSpread(select);
                         // console.log(select, sel);
-                        if (!readOnly && select && sel.row !== changeSpreadSheet.getRowCount() - 1 && !_.find(changeUsedData, { id: select.id })) {
+                        if (!readOnly && select && sel.row !== changeSpreadSheet.getRowCount() - 1 && !_.find(changeUsedData, { cbid: select.id })) {
                             return false;
                         } else {
                             return true;
@@ -659,7 +659,8 @@ $(document).ready(() => {
                     + '!_!' + (leaf.gcl_id ? leaf.gcl_id : '0') + '!_!' + (leaf.mx_id ? leaf.mx_id : '') + '!_!' +
                     (bwmx !== undefined ? bwmx : leaf.jldy ? leaf.jldy : '') + '*;*' + quantity) !== -1 && isCheck ?
                     'checked' : '';
-                const isUsed = _.find(changeUsedData, { gcl_id: leaf.gcl_id, bwmx: (bwmx ? bwmx : leaf.jldy ? leaf.jldy : ''), oamount: leaf.quantity });
+                const existGcl = _.find(changeList, {gcl_id: leaf.gcl_id, bwmx: (bwmx ? bwmx : leaf.jldy ? leaf.jldy : ''), oamount: leaf.quantity});
+                const isUsed = existGcl ? _.find(changeUsedData, { cbid: existGcl.id }) : null;
                 const isDisabled = isUsed ? 'disabled ' : '';
                 codeHtml += '<tr quantity="' + quantity + '" gcl_id="' + gcl_id + '" mx_id="' + mx_id + '">' +
                     '<td class="text-center">' + (index+1) + (leaf.cid ? '<i class="text-danger" style="font-weight: 900">*</i>' : '') + '</td>' +

+ 109 - 25
app/public/js/change_plan_information.js

@@ -180,6 +180,7 @@ $(document).ready(() => {
 
     const changeSpreadSetting = {
         cols: [
+            {title: '新增单价', colSpan: '1', rowSpan: '2', field: 'new_up', hAlign: 1, width: 35, cellType: 'checkbox', readOnly: 'readOnly.isEdit'},
             {title: '清单编号', colSpan: '1', rowSpan: '2', field: 'code', hAlign: 0, width: 110, formatter: '@', readOnly: 'readOnly.isEdit'},
             {title: '名称', colSpan: '1', rowSpan: '2', field: 'name', hAlign: 0, width: 130, formatter: '@', readOnly: 'readOnly.isEdit'},
             {title: '单位', colSpan: '1', rowSpan: '2', field: 'unit', hAlign: 1, width: 60, formatter: '@', readOnly: 'readOnly.isEdit', cellType: 'unit', comboItems: changeUnits, comboEdit: true},
@@ -226,6 +227,8 @@ $(document).ready(() => {
             changeSpreadSetting.cols.push(newColTp);
         }
     }
+    changeSpreadSetting.cols.push({title: '备注1', colSpan: '1', rowSpan: '2', field: 'ex_memo1', hAlign: 0, width: 110, formatter: '@', readOnly: 'readOnly.isEdit2'});
+    changeSpreadSetting.cols.push({title: '备注2', colSpan: '1', rowSpan: '2', field: 'ex_memo2', hAlign: 0, width: 110, formatter: '@', readOnly: 'readOnly.isEdit2'});
 
     const changeCol = {
         getValue: {
@@ -255,13 +258,18 @@ $(document).ready(() => {
             isEdit: function (data) {
                 return readOnly;
             },
+            isEdit2: function (data) {
+                return !(!readOnly || change.shenpiPower);
+            },
         },
     };
     const changeSpreadObj = {
         makeSjsFooter: function() {
             // 增加汇总行并设为锁定禁止编辑状态
             changeSpreadSheet.addRows(changeSpreadSheet.getRowCount(), 1);
-            changeSpreadSheet.setValue(changeSpreadSheet.getRowCount() - 1, 0, '合计');
+            changeSpreadSheet.setValue(changeSpreadSheet.getRowCount() - 1, 1, '合计');
+            const cellType1 = new GC.Spread.Sheets.CellTypes.Text();
+            changeSpreadSheet.getCell(changeSpreadSheet.getRowCount() - 1, 0).cellType(cellType1);
             changeSpreadSheet.setStyle(changeSpreadSheet.getRowCount() - 1, -1, style1);
             changeSpreadObj.countSum();
         },
@@ -275,11 +283,15 @@ $(document).ready(() => {
         },
         setRowValueAndSum: function (data, row, col) {
             for (const j in change.listAudits) {
-                const sum = ZhCalc.round(ZhCalc.mul(ZhCalc.round(data.unit_price, unitPriceUnit), parseFloat(changeSpreadSheet.getValue(row, 10 + parseInt(j)*2))), totalPriceUnit);
-                changeSpreadSheet.setValue(row, 11 + j*2, sum !== 0 ? sum : null);
+                const sum = ZhCalc.round(ZhCalc.mul(ZhCalc.round(data.unit_price, unitPriceUnit), parseFloat(changeSpreadSheet.getValue(row, 11 + parseInt(j)*2))), totalPriceUnit);
+                changeSpreadSheet.setValue(row, 12 + j*2, sum !== 0 ? sum : null);
             }
             // const sum = ZhCalc.round(ZhCalc.mul(data.unit_price, data.spamount), totalPriceUnit);
             // changeSpreadSheet.setValue(row, col+1, sum !== 0 ? sum : null);
+            const count = changeSpreadSetting.cols.length;
+            if (col+1 >= count - 2) {
+                return;
+            }
             const rowCount = changeSpreadSheet.getRowCount();
             // 用户的数据合计
             let audit_sum = 0;
@@ -294,20 +306,20 @@ $(document).ready(() => {
                 cSum = 0,
                 sSum = 0;
             for (let i = 0; i < rowCount - 1; i++) {
-                oSum = ZhCalc.add(oSum, changeSpreadSheet.getValue(i, 5));
-                cSum = ZhCalc.add(cSum, changeSpreadSheet.getValue(i, 7));
-                sSum = ZhCalc.add(sSum, changeSpreadSheet.getValue(i, 9));
+                oSum = ZhCalc.add(oSum, changeSpreadSheet.getValue(i, 6));
+                cSum = ZhCalc.add(cSum, changeSpreadSheet.getValue(i, 8));
+                sSum = ZhCalc.add(sSum, changeSpreadSheet.getValue(i, 10));
             }
-            changeSpreadSheet.setValue(changeSpreadSheet.getRowCount() - 1, 5, oSum !== 0 ? oSum : null);
-            changeSpreadSheet.setValue(changeSpreadSheet.getRowCount() - 1, 7, cSum !== 0 ? cSum : null);
-            changeSpreadSheet.setValue(changeSpreadSheet.getRowCount() - 1, 9, sSum !== 0 ? sSum : null);
+            changeSpreadSheet.setValue(changeSpreadSheet.getRowCount() - 1, 6, oSum !== 0 ? oSum : null);
+            changeSpreadSheet.setValue(changeSpreadSheet.getRowCount() - 1, 8, cSum !== 0 ? cSum : null);
+            changeSpreadSheet.setValue(changeSpreadSheet.getRowCount() - 1, 10, sSum !== 0 ? sSum : null);
             // 用户的数据合计
             for (const j in change.listAudits) {
                 let audit_sum = 0;
                 for(let i = 0; i < rowCount - 1; i++){
-                    audit_sum = ZhCalc.add(audit_sum, changeSpreadSheet.getValue(i, 11 + j*2));
+                    audit_sum = ZhCalc.add(audit_sum, changeSpreadSheet.getValue(i, 12 + j*2));
                 }
-                changeSpreadSheet.setValue(changeSpreadSheet.getRowCount() - 1, 11 + j*2, audit_sum !== 0 ? audit_sum : null);
+                changeSpreadSheet.setValue(changeSpreadSheet.getRowCount() - 1, 12 + j*2, audit_sum !== 0 ? audit_sum : null);
             }
         },
         deletePress: function (sheet) {
@@ -315,11 +327,13 @@ $(document).ready(() => {
         },
         showHideAudit: function (show = false) {
             const count = changeSpreadSetting.cols.length;
-            for (let i = 10; i < count; i++) {
+            for (let i = 11; i < count; i++) {
                 changeSpreadSheet.setColumnVisible(i, show, GC.Spread.Sheets.SheetArea.viewport);
             }
-            changeSpreadSheet.setColumnVisible(8, !show, GC.Spread.Sheets.SheetArea.viewport);
+            changeSpreadSheet.setColumnVisible(count - 1, true, GC.Spread.Sheets.SheetArea.viewport);
+            changeSpreadSheet.setColumnVisible(count - 2, true, GC.Spread.Sheets.SheetArea.viewport);
             changeSpreadSheet.setColumnVisible(9, !show, GC.Spread.Sheets.SheetArea.viewport);
+            changeSpreadSheet.setColumnVisible(10, !show, GC.Spread.Sheets.SheetArea.viewport);
         },
         valueChanged: function (e, info) {
             // 防止ctrl+z撤销数据
@@ -365,6 +379,9 @@ $(document).ready(() => {
                 const type = SpreadJsObj.getSelectObject(info.sheet) ? 'update' : 'add';
                 const select = type === 'update' ? SpreadJsObj.getSelectObject(info.sheet) : {unit: ''};
                 const col = info.sheet.zh_setting.cols[info.col];
+                if (col.field === 'new_up') {
+                    return;
+                }
                 // 未改变值则不提交
                 let validText = is_numeric(info.editingText) ? parseFloat(info.editingText) : (info.editingText ? trimInvalidChar(info.editingText) : '');
                 const orgValue = type === 'update' ? select[col.field] : '';
@@ -435,11 +452,13 @@ $(document).ready(() => {
                 const sortData = info.sheet.zh_data || [];
                 const range = info.cellRange;
                 const data = [];
+                let haveNew = false;
                 for (let iRow = 0; iRow < range.rowCount; iRow++) {
                     let bPaste = true;
                     const curRow = range.row + iRow;
                     // const materialData = JSON.parse(JSON.stringify(sortData[curRow]));
                     const cLData = curRow >= sortData.length ? {unit: ''} : {id: sortData[curRow].id};
+                    haveNew = curRow >= sortData.length ? curRow : false;
                     const hintRow = range.rowCount > 1 ? curRow : '';
                     let sameCol = 0;
                     for (let iCol = 0; iCol < range.colCount; iCol++) {
@@ -507,8 +526,16 @@ $(document).ready(() => {
                 // 更新至服务器
                 postData(preUrl + '/list/save', { type:'paste', updateData: data }, function (result) {
                     changeList = result;
+                    if (haveNew) {
+                        SpreadJsObj.initSheet(changeSpreadSheet, changeSpreadSetting);
+                    }
                     SpreadJsObj.loadSheetData(changeSpreadSheet, SpreadJsObj.DataType.Data, changeList);
                     changeSpreadObj.makeSjsFooter();
+                    if (haveNew) {
+                        changeSpreadSheet.setSelection(haveNew, 1, 1, 1);
+                        changeSpreadSheet.getParent().focus();
+                        changeSpreadSheet.showRow(haveNew, spreadNS.VerticalPosition.center);
+                    }
                 }, function () {
                     SpreadJsObj.reLoadRowData(info.sheet, info.cellRange.row, info.cellRange.rowCount);
                     return;
@@ -546,12 +573,61 @@ $(document).ready(() => {
                 });
             }
         };
-        // changeSpread.bind(spreadNS.Events.CellChanged, changeSpreadObj.cellChanged);
+        changeSpreadObj.buttonClicked = function (e, info) {
+            if (info.sheet.zh_setting) {
+                const select = SpreadJsObj.getSelectObject(info.sheet);
+                const col = info.sheet.zh_setting.cols[info.col];
+                if(!select) {
+                    toastr.error('请添加清单编号再勾选');
+                    if (info.sheet.isEditing()) {
+                        info.sheet.endEdit(true);
+                    }
+                    return;
+                } else if (col.field === 'new_up') {
+                    if (info.sheet.isEditing()) {
+                        info.sheet.endEdit(true);
+                    }
+                    select.new_up = info.sheet.getValue(info.row, info.col) ? 0 : 1;
+                    postData(preUrl + '/list/save', { type: 'update', updateData: { id: select.id, new_up: select.new_up } }, function (result) {
+                        changeList.splice(info.row, 1, select);
+                        SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                        changeSpreadObj.countSum();
+                    }, function () {
+                        select.new_up = info.sheet.getValue(info.row, info.col) ? 1 : 0;
+                        SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                    });
+                }
+                // if (materialCol.readOnly.isEdit(select)) {
+                //     return;
+                // }
+                // if (col.field === 'new_up') {
+                //     if (info.sheet.isEditing()) {
+                //         info.sheet.endEdit(true);
+                //     }
+                //     select.is_summary = info.sheet.getValue(info.row, info.col) ? 1 : 0;
+                //     delete select.waitingLoading;
+                //     // 更新至服务器
+                //     postData(window.location.pathname + '/save', { type:'update', updateData: select }, function (result) {
+                //         m_tp = result.m_tp;
+                //         if (materialTax) {
+                //             m_tax_tp = result.m_tax_tp;
+                //         }
+                //         resetTpTable();
+                //         SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                //     }, function () {
+                //         select.is_summary = info.sheet.getValue(info.row, info.col) ? 0 : 1;
+                //         SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                //     });
+                // }
+            }
+        };
         changeSpread.bind(spreadNS.Events.EditEnded, changeSpreadObj.editEnded);
+        changeSpread.bind(spreadNS.Events.ButtonClicked, changeSpreadObj.buttonClicked);
         changeSpread.bind(spreadNS.Events.ClipboardPasted, changeSpreadObj.clipboardPasted);
         changeSpread.bind(spreadNS.Events.ValueChanged, changeSpreadObj.valueChanged);
         SpreadJsObj.addDeleteBind(changeSpread, changeSpreadObj.deletePress);
 
+
         let batchInsertObj;
         $.contextMenu.types.batchInsert = function (item, opt, root) {
             const self = this;
@@ -706,14 +782,19 @@ $(document).ready(() => {
                     }
                     validText = ZhCalc.round(validText, findDecimal(select.unit)) || 0;
                     // 判断是否 正数必须大于等于限制值,负数必须小于等于限制值,否则无法更改
+                    select.spamount = ZhCalc.round(validText, findDecimal(select.unit)) || 0;
                 }
                 select[col.field] = validText;
-                select.spamount = ZhCalc.round(validText, findDecimal(select.unit)) || 0;
 
                 const data = {
                     id: select.id,
-                    spamount: select.spamount,
+                    // spamount: select.spamount,
                 };
+                if (col.field === 'ex_memo1' || col.field === 'ex_memo2') {
+                    data[col.field] = select[col.field];
+                } else {
+                    data.spamount = select.spamount;
+                }
                 console.log(data);
 
                 // 更新至服务器
@@ -723,7 +804,9 @@ $(document).ready(() => {
                     changeSpreadObj.setRowValueAndSum(select, info.row, info.col);
                 }, function () {
                     select[col.field] = orgValue;
-                    select.spamount = orgValue;
+                    if (col.field !== 'ex_memo1' && col.field !== 'ex_memo2') {
+                        select.spamount = orgValue;
+                    }
                     SpreadJsObj.reLoadRowData(info.sheet, info.row);
                     changeSpreadObj.setRowValueAndSum(select, info.row, info.col);
                 });
@@ -769,7 +852,11 @@ $(document).ready(() => {
                     }
                     // cLData[colSetting.field] = validText;
                     sortData[curRow][colSetting.field] = validText;
-                    cLData.spamount = validText;
+                    if (colSetting.field === 'ex_memo1' || colSetting.field === 'ex_memo2') {
+                        cLData[colSetting.field] = validText;
+                    } else {
+                        cLData.spamount = validText;
+                    }
                 }
                 if (bPaste) {
                     data.push(cLData);
@@ -861,14 +948,11 @@ $(document).ready(() => {
             //     postData(preUrl + '/list/save', {type: 'update_tp', updateData: new_tp}, function (result) {
             //     });
             // }
-            if (change.status === auditConst.status.checked) {
-                changeSpreadObj.showHideAudit(false);
-            } else {
-                changeSpreadObj.showHideAudit(true);
-            }
+            changeSpreadObj.showHideAudit(true);
+        }
+        if (change.status === auditConst.status.checked) {
+            changeSpreadObj.showHideAudit(false);
         }
-
-
     });
 
     // 审批流程展示与隐藏

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

@@ -398,7 +398,7 @@ const gclGatherModel = (function () {
         calculateGatherData();
         gatherDealBillsData();
         gclList.sort(function (a, b) {
-            return compareCode(a.b_code, b.b_code);
+            return compareCode(a.b_code, b.b_code) || ZhCalc.sub(a.unit_price, b.unit_price);
         });
 
         return gclList;

+ 135 - 68
app/public/js/ledger.js

@@ -1849,57 +1849,38 @@ $(document).ready(function() {
                     return;
                 }
 
-                const data = {};
-                if (col.field === 'name') {
-                    if (newText === '' && posData) {
-                        toastr.error('计量单元名称不可为空');
-                        SpreadJsObj.reLoadRowData(info.sheet, info.row);
-                        return;
-                    } else if (!posData) {
-                        if (newText && newText !== '') {
-                            data.updateType = 'add';
-                            const sortData = info.sheet.zh_data;
-                            const order = (!sortData || sortData.length === 0) ? 1 : Math.max(sortData[sortData.length - 1].porder + 1, sortData.length + 1);
-                            data.updateData = { name: newText, lid: node.id, tid: tender.id, porder: order};
-                        } else {
-                            return;
+                const data = { updateType: posData ? 'update' : 'add' };
+                if (!posData) {
+                    const sortData = info.sheet.zh_data;
+                    const order = (!sortData || sortData.length === 0) ? 1 : Math.max(sortData[sortData.length - 1].porder + 1, sortData.length + 1);
+                    data.updateData = { lid: node.id, tid: tender.id, porder: order };
+                } else {
+                    data.updateData = { id: posData.id };
+                }
+                if (col.type === 'Number') {
+                    const exprInfo = getExprInfo(col.field);
+                    const num = _.toNumber(newText);
+                    if (_.isFinite(num)) {
+                        data.updateData[col.field] = num;
+                        if (exprInfo) {
+                            data.updateData[exprInfo.expr] = '';
                         }
                     } else {
-                        data.updateType = 'update';
-                        data.updateData = {id: posData.id, name: newText};
-                    }
-                } else if (!posData) {
-                    toastr.warning('新增计量单元请先输入名称');
-                    SpreadJsObj.reLoadRowData(info.sheet, info.row);
-                    return;
-                } else {
-                    data.updateType = 'update';
-                    data.updateData = {id: posData.id};
-
-                    if (col.type === 'Number') {
-                        const exprInfo = getExprInfo(col.field);
-                        const num = _.toNumber(newText);
-                        if (_.isFinite(num)) {
-                            data.updateData[col.field] = num;
+                        try {
+                            data.updateData[col.field] = math.evaluate(transExpr(newText));
                             if (exprInfo) {
-                                data.updateData[exprInfo.expr] = '';
-                            }
-                        } else {
-                            try {
-                                data.updateData[col.field] = math.evaluate(transExpr(newText));
-                                if (exprInfo) {
-                                    data.updateData[exprInfo.expr] = newText;
-                                }
-                            } catch(err) {
-                                toastr.error('输入的表达式非法');
-                                SpreadJsObj.reLoadRowData(info.sheet, info.row);
-                                return;
+                                data.updateData[exprInfo.expr] = newText;
                             }
+                        } catch(err) {
+                            toastr.error('输入的表达式非法');
+                            SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                            return;
                         }
-                    } else {
-                        data.updateData[col.field] = newText;
                     }
+                } else {
+                    data.updateData[col.field] = newText;
                 }
+
                 postData('/tender/' + getTenderId() + '/pos/update', data, function (result) {
                     const updateRst = pos.updateDatas(result.pos);
                     // 刷新当前行, 不适用于新增(在非下一空白行新增)
@@ -1938,18 +1919,12 @@ $(document).ready(function() {
                         const data = {id: node.id};
                         for (let iCol = sel.col; iCol < sel.col + sel.colCount; iCol++) {
                             const colSetting = sheet.zh_setting.cols[iCol];
-                            if (colSetting.field === 'name') {
-                                toastr.error('部位名称不能为空');
-                                return;
-                            }
                             const style = sheet.getStyle(iRow, iCol);
                             if (!style.locked) {
                                 const colSetting = sheet.zh_setting.cols[iCol];
                                 data[colSetting.field] = null;
                                 const exprInfo = getExprInfo(colSetting.field);
-                                if (exprInfo) {
-                                    data[exprInfo.expr] = '';
-                                }
+                                if (exprInfo) data[exprInfo.expr] = '';
                                 bDel = true;
                             }
                         }
@@ -1999,6 +1974,24 @@ $(document).ready(function() {
                 });
             }
         },
+        insertPos: function (sheet) {
+            const selection = sheet.getSelections();
+            const sortData = sheet.zh_data;
+            const row = selection[0].row;
+            const node = SpreadJsObj.getSelectObject(ledgerSpread.getActiveSheet());
+            const select = SpreadJsObj.getSelectObject(sheet);
+            const porder = select ? select.porder : sortData.length + 1;
+            const data = {
+                updateType: 'insert',
+                updateData: [ { lid: node.id, tid: tender.id, porder } ],
+            };
+            postData('/tender/' + getTenderId() + '/pos/update', data, function (result) {
+                const updateRst = pos.updateDatas(result.pos);
+                posOperationObj.loadCurPosData();
+                treeOperationObj.refreshOperationValid(ledgerSpread.getActiveSheet());
+            });
+
+        },
         /**
          * 粘贴单元格响应事件
          * @param e
@@ -2021,7 +2014,6 @@ $(document).ready(function() {
             }
 
             const hint = {
-                name: {type: 'warning', msg: '计量单元名称不可为空,已过滤'},
                 expr: {type: 'warning', msg: '粘贴了表达式非法,已过滤'},
             };
             if (info.sheet.zh_setting) {
@@ -2065,15 +2057,6 @@ $(document).ready(function() {
                                     toastMessageUniq(hint.expr);
                                 }
                             }
-                        } else if (colSetting.field === 'name') {
-                            if (!posData[colSetting.field]) {
-                                delete posData[colSetting.field];
-                                toastMessageUniq(hint.name);
-                                bPaste = false;
-                                break;
-                            } else {
-                                bPaste = true;
-                            }
                         } else {
                             bPaste = true;
                         }
@@ -2184,6 +2167,17 @@ $(document).ready(function() {
                 return target.hitTestType === GC.Spread.Sheets.SheetArea.viewport || target.hitTestType === GC.Spread.Sheets.SheetArea.rowHeader;
             },
             items: {
+                'insert': {
+                    name: '插入',
+                    icon: 'fa-plus',
+                    disabled: function (key, opt) {
+                        const node = SpreadJsObj.getSelectObject(ledgerSpread.getActiveSheet());
+                        return node && node.children && node.children.length > 0;
+                    },
+                    callback: function (key, opt) {
+                        posOperationObj.insertPos(posSpread.getActiveSheet());
+                    }
+                },
                 'delete': {
                     name: '删除',
                     icon: 'fa-remove',
@@ -2212,8 +2206,6 @@ $(document).ready(function() {
                 }
             }
         });
-    } else {
-        SpreadJsObj.forbiddenSpreadContextMenu('#pos-spread', posSpread);
     }
 
     postData(window.location.pathname + '/load', {}, function (data) {
@@ -2792,6 +2784,50 @@ $(document).ready(function() {
                     }
                 });
             }
+            this.initDealBillsSearch();
+        }
+        search(keyword) {
+            this.searchResult = [];
+            this.searchCur = 0;
+            if (keyword) {
+                for (const [i, d] of this.data.entries()) {
+                    if (d.code.indexOf(keyword) >= 0 || d.name.indexOf(keyword) >= 0) this.searchResult.push(d);
+                }
+            }
+            $('#dbs-count').html(`结果:${this.searchResult.length}`);
+            if (this.searchResult.length > 0) SpreadJsObj.locateData(this.sheet, this.searchResult[0]);
+        }
+        searchPre() {
+            this.searchCur = this.searchCur - 1;
+            if (this.searchCur < 0) this.searchCur = this.searchResult.length - 1;
+            SpreadJsObj.locateData(this.sheet, this.searchResult[this.searchCur]);
+        }
+        searchNext() {
+            this.searchCur = this.searchCur + 1;
+            if (this.searchCur >= this.searchResult.length) this.searchCur = 0;
+            SpreadJsObj.locateData(this.sheet, this.searchResult[this.searchCur]);
+        }
+        searchClear() {
+            $('#dbs-keyword').val('');
+            this.searchResult = [];
+            this.searchCur = 0;
+            $('#dbs-count').html(`结果:${this.searchResult.length}`);
+        }
+        initDealBillsSearch() {
+            const self = this;
+            $('#dbs-keyword').change(function () { self.search(this.value); });
+            $('#dbs-pre').click(function (e) {
+                self.searchPre();
+                e.stopPropagation();
+            });
+            $('#dbs-next').click(function (e) {
+                self.searchNext();
+                e.stopPropagation();
+            });
+            $('#dbs-clear').click(function (e) {
+                self.searchClear();
+                e.stopPropagation();
+            });
         }
         loadData () {
             const self = this;
@@ -2893,6 +2929,7 @@ $(document).ready(function() {
                 font: '12px 微软雅黑',
             };
             this.dealSpread = SpreadJsObj.createNewSpread($('.batch-r', this.obj)[0]);
+            this.dealSheet = this.dealSpread.getActiveSheet();
             // 初始化 清单编号、部位数量复核表 表格
             this.initView();
             SpreadJsObj.initSheet(this.dealSpread.getActiveSheet(), this.dealSpreadSetting);
@@ -3058,6 +3095,40 @@ $(document).ready(function() {
             $('input[name=batch-filter]').change(function () {
                 setLocalCache('zh-calc-batch-filter', this.checked ? 1 : 0);
             });
+            this.initDealBillsSearch();
+        }
+        search(keyword) {
+            this.searchResult = [];
+            this.searchCur = 0;
+            if (keyword) {
+                for (const [i, d] of this.dealSheet.zh_data.entries()) {
+                    if (d.code.indexOf(keyword) >= 0 || d.name.indexOf(keyword) >= 0) this.searchResult.push(d);
+                }
+            }
+            $('#batch-dbs-count').html(`结果:${this.searchResult.length}`);
+            if (this.searchResult.length > 0) SpreadJsObj.locateData(this.dealSheet, this.searchResult[0]);
+        }
+        searchPre() {
+            this.searchCur = this.searchCur - 1;
+            if (this.searchCur < 0) this.searchCur = this.searchResult.length - 1;
+            SpreadJsObj.locateData(this.dealSheet, this.searchResult[this.searchCur]);
+        }
+        searchNext() {
+            this.searchCur = this.searchCur + 1;
+            if (this.searchCur >= this.searchResult.length) this.searchCur = 0;
+            SpreadJsObj.locateData(this.dealSheet, this.searchResult[this.searchCur]);
+        }
+        initDealBillsSearch() {
+            const self = this;
+            $('#batch-dbs-keyword').change(function () { self.search(this.value); });
+            $('#batch-dbs-pre').click(function (e) {
+                self.searchPre();
+                e.stopPropagation();
+            });
+            $('#batch-dbs-next').click(function (e) {
+                self.searchNext();
+                e.stopPropagation();
+            });
         }
         // 初始化左侧表格
         initView () {
@@ -3084,10 +3155,6 @@ $(document).ready(function() {
             posSheet.clear(0, 0, posSheet.getRowCount(), posSheet.getColumnCount(), GC.Spread.Sheets.SheetArea.viewport, GC.Spread.Sheets.StorageType.data);
             posSheet.setSelection(0, 0, 1 ,1);
             SpreadJsObj.endMassOperation(posSheet);
-            // 检查签约清单数据,以工具栏数据为准
-            if (dealBills) {
-                SpreadJsObj.loadSheetData(this.dealSpread.getActiveSheet(), 'data', dealBills.data);
-            }
             this.dealSpread.getActiveSheet().setSelection(0, 0, 1, 1);
         }
         // 获取界面数据
@@ -3386,7 +3453,7 @@ $(document).ready(function() {
                 for (const [i, p] of posRange.entries()) {
                     data.push({
                         pos_code: (i + 1) + '', name: p.name,
-                        quantity: p.quantity, drawing_code: p.drawing_code, memo: p.memo
+                        quantity: p[qtyF], drawing_code: p.drawing_code, memo: p.memo
                     });
                 }
             }

+ 6 - 6
app/public/js/material.js

@@ -106,7 +106,7 @@ $(document).ready(() => {
     const materialSpreadSetting = {
         emptyRows: 0,
         headRows: 2,
-        headRowHeight: [25, 25],
+        headRowHeight: [25, 32],
         defaultRowHeight: 21,
         headerFont: '12px 微软雅黑',
         font: '12px 微软雅黑',
@@ -133,9 +133,9 @@ $(document).ready(() => {
     );
     if (materialTax) {
         materialSpreadSettingCols = _.concat(materialSpreadSettingCols, [
-            {title: '本期材料调差|上涨幅度(%)', colSpan: '5|1', rowSpan: '1|1', field: 'm_up_risk', hAlign: 2, width: 90, type: 'Number', readOnly: 'readOnly.isEdit'},
-            {title: '|下跌幅度(%)', colSpan: '|1', rowSpan: '|1', field: 'm_down_risk', hAlign: 2, width: 90, type: 'Number', readOnly: 'readOnly.isEdit'},
-            {title: '|有效价差', colSpan: '|1', rowSpan: '|1', field: 'm_spread', hAlign: 2, width: 80, type: 'Number', readOnly: true, getValue: 'getValue.m_spread'},
+            {title: '本期材料调差|上涨 幅度(%)', colSpan: '5|1', rowSpan: '1|1', field: 'm_up_risk', hAlign: 2, width: 50, type: 'Number', readOnly: 'readOnly.isEdit'},
+            {title: '|下跌 幅度(%)', colSpan: '|1', rowSpan: '|1', field: 'm_down_risk', hAlign: 2, width: 50, type: 'Number', readOnly: 'readOnly.isEdit'},
+            {title: '|有效价差', colSpan: '|1', rowSpan: '|1', field: 'm_spread', hAlign: 2, width: 60, type: 'Number', readOnly: true, getValue: 'getValue.m_spread'},
             {title: '|调差金额', colSpan: '|1', rowSpan: '|1', field: 'm_tp', hAlign: 2, width: 100, type: 'Number', readOnly: true, getValue: 'getValue.m_tp'},
             {title: '|调差金额(材料税)', colSpan: '|1', rowSpan: '|1', field: 'm_tax_tp', hAlign: 2, width: 100, type: 'Number', readOnly: true, getValue: 'getValue.m_tax_tp'},
             {title: '截止上期|调差金额', colSpan: '2|1', rowSpan: '1|1', field: 'pre_tp', hAlign: 2, width: 100, type: 'Number', readOnly: true},
@@ -145,8 +145,8 @@ $(document).ready(() => {
         ])
     } else {
         materialSpreadSettingCols = _.concat(materialSpreadSettingCols, [
-            {title: '本期材料调差|上涨幅度(%)', colSpan: '4|1', rowSpan: '1|1', field: 'm_up_risk', hAlign: 2, width: 90, type: 'Number', readOnly: 'readOnly.isEdit'},
-            {title: '|下跌幅度(%)', colSpan: '|1', rowSpan: '|1', field: 'm_down_risk', hAlign: 2, width: 90, type: 'Number', readOnly: 'readOnly.isEdit'},
+            {title: '本期材料调差|上涨 幅度(%)', colSpan: '4|1', rowSpan: '1|1', field: 'm_up_risk', hAlign: 2, width: 50, type: 'Number', readOnly: 'readOnly.isEdit'},
+            {title: '|下跌 幅度(%)', colSpan: '|1', rowSpan: '|1', field: 'm_down_risk', hAlign: 2, width: 50, type: 'Number', readOnly: 'readOnly.isEdit'},
             {title: '|有效价差', colSpan: '|1', rowSpan: '|1', field: 'm_spread', hAlign: 2, width: 80, type: 'Number', readOnly: true, getValue: 'getValue.m_spread'},
             {title: '|调差金额', colSpan: '|1', rowSpan: '|1', field: 'm_tp', hAlign: 2, width: 100, type: 'Number', readOnly: true, getValue: 'getValue.m_tp'},
             {title: '截止上期|调差金额', colSpan: '1|1', rowSpan: '1|1', field: 'pre_tp', hAlign: 2, width: 100, type: 'Number', readOnly: true},

+ 68 - 25
app/public/js/material_checklist.js

@@ -130,16 +130,17 @@ $(document).ready(() => {
         curLedgerData = result.curLedgerData;
         pos = result.pos;
         curPosData = result.curPosData;
-        materialListData = result.materialListData;
+        // materialListData = result.materialListData;
         notJoinList = result.materialNotJoinListData;
         materialChecklistData = result.materialChecklistData;
+        gclList = result.gclList;
         // 解析清单汇总数据
         gclGatherModel.loadLedgerData(ledger, curLedgerData);
         gclGatherModel.loadPosData(pos, curPosData);
         gclGatherData = gclGatherModel.gatherGclData();
         console.log(gclGatherData);
-        const hadBillsidList = _.uniq(_.map(materialListData, 'gcl_id'));
-        console.log(hadBillsidList);
+        const hadBillsidList = _.uniq(_.map(gclList, 'gcl_id'));
+        console.log(hadBillsidList, materialChecklistData);
         // 对比清单设置和调差清单,还要和台账对比,显示已选清单列表 不同则更新到清单设置页中
         const pushData = [];
         const updateData = [];
@@ -159,17 +160,36 @@ $(document).ready(() => {
         const removeData = [];
         for (const mc of materialChecklistData) {
             const gcl = _.find(gclGatherData, { b_code: mc.b_code, name: mc.name, unit: mc.unit, unit_price: mc.unit_price });
+            console.log(gcl);
             // 判断是否已不存在工料清单,台账修改过后删除之
             if (!gcl) {
                 removeData.push(mc.id);
-            }
-            // 更新had_bills值
-            if (mc.had_bills === 1) {
-                if (_.indexOf(hadBillsidList, gcl.leafXmjs ? gcl.leafXmjs[0].gcl_id : null) === -1) {
-                    updateData.push({ id: mc.id, mid: materialID, had_bills: 0 });
+            } else {
+                const gcl_ids = gcl.leafXmjs ? _.uniq(_.map(gcl.leafXmjs, 'gcl_id')) : [];
+                const jiaoji = _.intersection(gcl_ids, hadBillsidList);
+                // const leafXmjs = gcl.leafXmjs ? gcl.leafXmjs.filter(item => item.gather_qty) : [];
+                // 更新had_bills值
+                const updateObj = { id: mc.id };
+                if (mc.had_bills === 1) {
+                    if (jiaoji.length === 0) {
+                        updateObj.mid = materialID;
+                        updateObj.had_bills = 0;
+                        // updateData.push({ id: mc.id, mid: materialID, had_bills: 0 });
+                    }
+                } else if (mc.had_bills === 0) {
+                    if (jiaoji.length !== 0) {
+                        updateObj.had_bills = 1;
+                    }
                 }
+                // 更新工程量及台账金额
+                if (mc.quantity !== (gcl.quantity ? gcl.quantity : null)) {
+                    updateObj.quantity = gcl.quantity ? gcl.quantity : null;
+                    updateObj.total_price = gcl.total_price ? gcl.total_price : null;
+                }
+                if(!_.isEqual(updateObj,{ id: mc.id })) updateData.push(updateObj);
             }
         }
+        console.log(pushData, removeData, updateData);
         setChecklistData(pushData, removeData, updateData, true);
     });
     function setChecklistData(pushData, removeData, updateData = [], sendmsg = false) {
@@ -239,17 +259,33 @@ $(document).ready(() => {
     let materialList = [];
     function loadMaterialData(iGclRow, iLXmjRow) {
         const gcl = gclGatherData[iGclRow];
-        if (gcl && gcl.leafXmjs[iLXmjRow]) {
-            const xmj = gcl.leafXmjs[iLXmjRow];
+        // const leafXmjs = gcl && gcl.leafXmjs ? gcl.leafXmjs.filter(item => {
+        //     return item.qc_qty || item.contract_qty
+        // }) : null;
+        if (gcl && gcl.leafXmjs) {
+            const gcl_ids = gcl.leafXmjs ? _.uniq(_.map(gcl.leafXmjs, 'gcl_id')) : [];
+            // const xmj2 = gcl.leafXmjs[iLXmjRow];
             materialList = [];
-            materialList = _.filter(materialListData, function (m) {
-                return m.gcl_id === xmj.gcl_id && m.xmj_id === xmj.id && ((xmj.mx_id !==undefined && m.mx_id === xmj.mx_id) || xmj.mx_id === undefined);
-            });
-            // for (const m of materialListData) {
-            //     if (m.gcl_id === xmj.gcl_id && m.xmj_id === xmj.id && ((xmj.mx_id !==undefined && m.mx_id === xmj.mx_id) || xmj.mx_id === undefined)) {
-            //         materialList.push(m);
-            //     }
-            // }
+            const newMaterialList = _.uniqBy(_.filter(gclList, function (m) {
+                return _.indexOf(gcl_ids, m.gcl_id) !== -1;
+            }), 'mb_id');
+            for(const m of newMaterialList) {
+                const bills = _.find(materialBillsData, { id: m.mb_id });
+                if(bills) {
+                    m.code = bills.code;
+                    m.name = bills.name;
+                    m.unit = bills.unit;
+                }
+            }
+            console.log(newMaterialList);
+            materialList = newMaterialList;
+            // const leafXmjs = gcl.leafXmjs.filter(item => {
+            //     return item.qc_qty || item.contract_qty
+            // });
+            // const xmj = leafXmjs[iLXmjRow];
+            // materialList = _.filter(materialListData, function (m) {
+            //     return xmj && m.gcl_id === xmj.gcl_id && m.xmj_id === xmj.id && ((xmj.mx_id !==undefined && m.mx_id === xmj.mx_id) || xmj.mx_id === undefined);
+            // });
             // 对清单调差工料table的单位数量进行改变
             materialSpreadSetting.cols[materialSpreadSetting.cols.length - 2].title = '|' + gcl.unit + '数量 �';
             SpreadJsObj.initSheet(materialSpread.getActiveSheet(), materialSpreadSetting);
@@ -332,7 +368,7 @@ $(document).ready(() => {
         if (isCheck) {
             newMaterialChecklistData = [];
             $('#notBills_checkList').prop('checked', false);
-            const materialList0 = _.uniq(_.map(_.filter(materialListData, { quantity: 0 }), 'gcl_id'));
+            const materialList0 = _.uniq(_.map(_.filter(gclList, { quantity: 0 }), 'gcl_id'));
             if (materialList0.length > 0) {
                 const hadMaterialChecklistData = _.filter(materialChecklistData, { had_bills: 1 });
                 for (const h of hadMaterialChecklistData) {
@@ -387,7 +423,8 @@ $(document).ready(() => {
         // 上传到数据库
         console.log(datas, gcl);
         postData(window.location.pathname + '/save', {type: 'adds', checklist: { id: select.id, had_bills: 1 }, postData: {xmjs: datas, mbIds: mb_id}}, function (result) {
-            materialListData = result;
+            // materialListData = result;
+            gclList = result;
             materialChecklistData[index].had_bills = 1;
             loadMaterialData(gclIndex, 0);
             // SpreadJsObj.reLoadRowData(ledgerSpread.getActiveSheet(), index);
@@ -416,9 +453,12 @@ $(document).ready(() => {
                     datas.push(data);
                 }
                 const xmj = gcl[0];
-                const materialCount = _.size(_.filter(materialListData, function (m) {
-                    return m.gcl_id === xmj.gcl_id && m.xmj_id === xmj.id && ((xmj.mx_id !==undefined && m.mx_id === xmj.mx_id) || xmj.mx_id === undefined);
+                const materialCount = _.size(_.filter(gclList, function (m) {
+                    return m.gcl_id === xmj.gcl_id;
                 }));
+                // const materialCount = _.size(_.filter(materialListData, function (m) {
+                //     return m.gcl_id === xmj.gcl_id && m.xmj_id === xmj.id && ((xmj.mx_id !==undefined && m.mx_id === xmj.mx_id) || xmj.mx_id === undefined);
+                // }));
                 let checklist = false;
                 if (materialCount === 1) {
                     checklist = {
@@ -428,7 +468,8 @@ $(document).ready(() => {
                 }
                 console.log(datas, materialSelect.mb_id, checklist);
                 postData(window.location.pathname + '/save', {type: 'dels', checklist, postData: { xmjs: datas, mb_id: materialSelect.mb_id }}, function (result) {
-                    materialListData = result;
+                    // materialListData = result;
+                    gclList = result;
                     if (checklist) materialChecklistData[index].had_bills = checklist.had_bills;
                     loadMaterialData(gclIndex, 0);
                     // SpreadJsObj.reLoadRowData(ledgerSpread.getActiveSheet(), index);
@@ -499,7 +540,8 @@ $(document).ready(() => {
                     }
                     console.log(exprQuantity, datas, select.mb_id);
                     postData(window.location.pathname + '/save', { type:'updates', updateData: { xmjs: datas, expr: exprQuantity.expr, quantity: exprQuantity.quantity, mb_id: select.mb_id } }, function (result) {
-                        materialListData = result;
+                        // materialListData = result;
+                        gclList = result;
                         loadMaterialData(gclIndex, 0);
                         materialSpread.getActiveSheet().setSelection(info.row + 1, info.col, 1, 1);
                     }, function () {
@@ -597,7 +639,8 @@ $(document).ready(() => {
                 console.log(data, datas);
                 // 更新至服务器
                 postData(window.location.pathname + '/save', { type:'pastes', updateData: { xmjs: datas, pasteData: data } }, function (result) {
-                    materialListData = result;
+                    // materialListData = result;
+                    gclList = result;
                     loadMaterialData(gclIndex, 0);
                     materialSpread.getActiveSheet().setSelection(info.cellRange.row, info.cellRange.col, info.cellRange.rowCount, info.cellRange.colCount);
                 }, function () {

+ 103 - 35
app/public/js/material_list.js

@@ -203,26 +203,84 @@ $(document).ready(() => {
         readOnly: true,
     };
     // 加载清单数据 - 暂时统一加载,如有需要,切换成动态加载并缓存
-    postData(window.location.pathname + '/load', {}, function (result) {
+    postData(window.location.pathname + '/load', {}, async function (result) {
         ledger = result.ledger;
         curLedgerData = result.curLedgerData;
         pos = result.pos;
         curPosData = result.curPosData;
         materialListData = result.materialListData;
+        const gclList = result.gclList;
         notJoinList = result.materialNotJoinListData;
         materialChecklistData = result.materialChecklistData;
         // 解析清单汇总数据
         gclGatherModel.loadLedgerData(ledger, curLedgerData);
         gclGatherModel.loadPosData(pos, curPosData);
-        gclGatherData = gclGatherModel.gatherGclData().filter(item => {
-            return item.qc_qty || item.contract_qty
-        });
+        gclGatherData = gclGatherModel.gatherGclData();
         console.log(gclGatherData);
         if (openMaterialChecklist) {
+            const hadBillsidList = _.uniq(_.map(gclList, 'gcl_id'));
+            console.log(hadBillsidList);
+            // 判断是否有修订影响到本次数据,并有几率修改清单设置页的值
+            const pushData = [];
+            const updateData = [];
+            for (const hb of hadBillsidList) {
+                const gcl = _.find(gclGatherData, function (item) {
+                    return item.leafXmjs && item.leafXmjs.length > 0 && _.findIndex(item.leafXmjs, { gcl_id : hb }) !== -1;
+                });
+                if (gcl) {
+                    const mc = _.find(materialChecklistData, { b_code: gcl.b_code, name: gcl.name, unit: gcl.unit, unit_price: gcl.unit_price });
+                    // const newOrder = _.indexOf(gclGatherData, gcl);
+                    // console.log(newOrder);
+                    if (!mc && _.findIndex(pushData, { b_code: gcl.b_code, name: gcl.name, unit: gcl.unit, unit_price: gcl.unit_price }) === -1) {
+                        pushData.push({ b_code: gcl.b_code, name: gcl.name, unit: gcl.unit, unit_price: gcl.unit_price, quantity: (gcl.quantity ? gcl.quantity : null), total_price: (gcl.total_price ? gcl.total_price : null), had_bills: 1 });
+                    }
+                }
+            }
+            const removeData = [];
+            for (const mc of materialChecklistData) {
+                const gcl = _.find(gclGatherData, { b_code: mc.b_code, name: mc.name, unit: mc.unit, unit_price: mc.unit_price });
+                // 判断是否已不存在工料清单,台账修改过后删除之
+                if (!gcl) {
+                    removeData.push(mc.id);
+                } else {
+                    // 更新had_bills值
+                    const updateObj = { id: mc.id };
+                    const gcl_ids = gcl.leafXmjs ? _.uniq(_.map(gcl.leafXmjs, 'gcl_id')) : [];
+                    const jiaoji = _.intersection(gcl_ids, hadBillsidList);
+                    if (mc.had_bills === 1) {
+                        if (jiaoji.length === 0) {
+                            updateObj.mid = materialID;
+                            updateObj.had_bills = 0;
+                            // updateData.push({ id: mc.id, mid: materialID, had_bills: 0 });
+                        }
+                    } else if (mc.had_bills === 0) {
+                        if (jiaoji.length !== 0) {
+                            updateObj.had_bills = 1;
+                        }
+                    }
+                    // 更新工程量及台账金额
+                    if (mc.quantity !== (gcl.quantity ? gcl.quantity : null)) {
+                        updateObj.quantity = gcl.quantity ? gcl.quantity : null;
+                        updateObj.total_price = gcl.total_price ? gcl.total_price : null;
+                    }
+                    if(!_.isEqual(updateObj,{ id: mc.id })) updateData.push(updateObj);
+                }
+            }
+            console.log(pushData, removeData, updateData);
+            if (pushData.length > 0 || removeData.length > 0 || updateData.length > 0) {
+                // materialChecklistData = await postDataAsync('/tender/'+ tenderID +'/measure/material/'+ stage_order +'/checklist/save', { type: 'resetChecklist', pushData, removeData, updateData })
+            }
+            gclGatherData = gclGatherData.filter(item => {
+                return item.qc_qty || item.contract_qty
+            });
             // 取交集
             gclGatherData = _.filter(gclGatherData, function (item) {
                 return _.find(materialChecklistData, { b_code: item.b_code, name: item.name, unit: item.unit, unit_price: item.unit_price });
             });
+        } else {
+            gclGatherData = gclGatherData.filter(item => {
+                return item.qc_qty || item.contract_qty
+            });
         }
         calculateJiaCha(gclGatherData);
         SpreadJsObj.initSheet(leafXmjSpread.getActiveSheet(), leafXmjSpreadSetting);
@@ -295,19 +353,20 @@ $(document).ready(() => {
     let materialList = [];
     function loadMaterialData(iGclRow, iLXmjRow) {
         const gcl = gclGatherData[iGclRow];
-        // const leafXmjs = gcl.leafXmjs.filter(item => {
-        //     return item.qc_qty || item.contract_qty
-        // });
-        // console.log(iLXmjRow, leafXmjs, materialListData);
-        if (gcl && gcl.leafXmjs[iLXmjRow]) {
-            const xmj = gcl.leafXmjs[iLXmjRow];
+        const leafXmjs = gcl && gcl.leafXmjs ? gcl.leafXmjs.filter(item => {
+            return item.qc_qty || item.contract_qty
+        }) : null;
+        console.log(gcl, leafXmjs);
+        if (leafXmjs) {
+            const xmj = leafXmjs[iLXmjRow];
+            console.log(xmj, iLXmjRow);
             materialList = [];
             for (const m of materialListData) {
                 if (m.gcl_id === xmj.gcl_id && m.xmj_id === xmj.id && ((xmj.mx_id !==undefined && m.mx_id === xmj.mx_id) || xmj.mx_id === undefined)) {
                     materialList.push(m);
                 }
             }
-            console.log(materialList);
+            console.log(xmj, materialList);
             SpreadJsObj.loadSheetData(materialSpread.getActiveSheet(), SpreadJsObj.DataType.Data, materialList);
         } else {
             SpreadJsObj.loadSheetData(materialSpread.getActiveSheet(), SpreadJsObj.DataType.Data, []);
@@ -376,8 +435,8 @@ $(document).ready(() => {
         const index = gclGatherData.indexOf(select);
         const leafXmjSheet = leafXmjSpread.getActiveSheet();
         const leafXmjSelect = SpreadJsObj.getSelectObject(leafXmjSheet);
-        const gcl = gclGatherData[index].leafXmjs;
-        // const leafXmjIndex = gcl.indexOf(leafXmjSelect);
+        const gcl = gclGatherData[index].leafXmjs.filter(item => item.gather_qty !== null && item.gather_qty !== undefined);
+        const leafXmjIndex = gcl.indexOf(leafXmjSelect);
         // const xmj = gcl[leafXmjIndex];
         // const data = {
         //     xmj_id: xmj.id,
@@ -401,25 +460,25 @@ $(document).ready(() => {
 
 
         // 上传到数据库
-        console.log(datas, gcl);
+        console.log(datas, gcl, mb_id);
         postData(window.location.pathname + '/save', {type: 'adds', postData: {xmjs: datas, mbIds: mb_id}}, function (result) {
             materialListData = result;
             // toastr.success('已成功应用');
             calculateJiaCha(gclGatherData);
             // const index = gclGatherData.indexOf(ledgerSelect);
             loadLeafXmjData(index);
-            const xmjSheet = leafXmjSpread.getActiveSheet();
-            const xmjSelect = SpreadJsObj.getSelectObject(xmjSheet);
-            const xmjIndex = gclGatherData[index].leafXmjs.indexOf(xmjSelect);
-            loadMaterialData(index, xmjIndex);
+            // const xmjSheet = leafXmjSpread.getActiveSheet();
+            // const xmjSelect = SpreadJsObj.getSelectObject(xmjSheet);
+            // const xmjIndex = gclGatherData[index].leafXmjs.indexOf(xmjSelect);
+            loadMaterialData(index, leafXmjIndex);
             SpreadJsObj.reLoadRowData(ledgerSpread.getActiveSheet(), index);
             $('#addgl').modal('hide');
         });
         // postData(window.location.pathname + '/save', {type: 'add', postData: data}, function (result) {
-            // // 添加到materialList里
-            // materialListData = result;
-            // loadMaterialData(index, leafXmjIndex);
-            // $('#addgl').modal('hide');
+        //     // 添加到materialList里
+        //     materialListData = result;
+        //     loadMaterialData(index, leafXmjIndex);
+        //     $('#addgl').modal('hide');
         // });
         $('#materialBills').find('input:disabled').prop('checked', true);
     });
@@ -525,7 +584,10 @@ $(document).ready(() => {
                 const sheet = ledgerSpread.getActiveSheet();
                 const select = SpreadJsObj.getSelectObject(sheet);
                 const index = gclGatherData.indexOf(select);
-                const gcl = gclGatherData[index].leafXmjs;
+                const leafXmjSheet = leafXmjSpread.getActiveSheet();
+                const leafXmjSelect = SpreadJsObj.getSelectObject(leafXmjSheet);
+                const gcl = gclGatherData[index].leafXmjs.filter(item => item.gather_qty !== null && item.gather_qty !== undefined);
+                const leafXmjIndex = gcl.indexOf(leafXmjSelect);
                 const datas = [];
                 for (const xmj of gcl) {
                     const data = {
@@ -541,10 +603,10 @@ $(document).ready(() => {
                     calculateJiaCha(gclGatherData);
                     // const index = gclGatherData.indexOf(ledgerSelect);
                     loadLeafXmjData(index);
-                    const xmjSheet = leafXmjSpread.getActiveSheet();
-                    const xmjSelect = SpreadJsObj.getSelectObject(xmjSheet);
-                    const xmjIndex = gclGatherData[index].leafXmjs.indexOf(xmjSelect);
-                    loadMaterialData(index, xmjIndex);
+                    // const xmjSheet = leafXmjSpread.getActiveSheet();
+                    // const xmjSelect = SpreadJsObj.getSelectObject(xmjSheet);
+                    // const xmjIndex = gclGatherData[index].leafXmjs.indexOf(xmjSelect);
+                    loadMaterialData(index, leafXmjIndex);
                     SpreadJsObj.reLoadRowData(ledgerSpread.getActiveSheet(), index);
                 });
                 // postData(window.location.pathname + '/save', {type: 'del', id: select.id, mb_id: select.mb_id}, function (result) {
@@ -623,7 +685,10 @@ $(document).ready(() => {
                     const ledgerSheet = ledgerSpread.getActiveSheet();
                     const ledgerSelect = SpreadJsObj.getSelectObject(ledgerSheet);
                     const index = gclGatherData.indexOf(ledgerSelect);
-                    const gcl = gclGatherData[index].leafXmjs;
+                    const gcl = gclGatherData[index].leafXmjs.filter(item => item.gather_qty !== null && item.gather_qty !== undefined);
+                    const xmjSheet = leafXmjSpread.getActiveSheet();
+                    const xmjSelect = SpreadJsObj.getSelectObject(xmjSheet);
+                    const xmjIndex = gcl.indexOf(xmjSelect);
                     const datas = [];
                     for (const xmj of gcl) {
                         const data = {
@@ -639,9 +704,9 @@ $(document).ready(() => {
                         calculateJiaCha(gclGatherData);
                         // const index = gclGatherData.indexOf(ledgerSelect);
                         loadLeafXmjData(index);
-                        const xmjSheet = leafXmjSpread.getActiveSheet();
-                        const xmjSelect = SpreadJsObj.getSelectObject(xmjSheet);
-                        const xmjIndex = gclGatherData[index].leafXmjs.indexOf(xmjSelect);
+                        // const xmjSheet = leafXmjSpread.getActiveSheet();
+                        // const xmjSelect = SpreadJsObj.getSelectObject(xmjSheet);
+                        // const xmjIndex = gclGatherData[index].leafXmjs.indexOf(xmjSelect);
                         loadMaterialData(index, xmjIndex);
                         SpreadJsObj.reLoadRowData(ledgerSpread.getActiveSheet(), index);
                         materialSpread.getActiveSheet().setSelection(info.row + 1, info.col, 1, 1);
@@ -748,7 +813,10 @@ $(document).ready(() => {
                 const ledgerSheet = ledgerSpread.getActiveSheet();
                 const ledgerSelect = SpreadJsObj.getSelectObject(ledgerSheet);
                 const index = gclGatherData.indexOf(ledgerSelect);
-                const gcl = gclGatherData[index].leafXmjs;
+                const gcl = gclGatherData[index].leafXmjs.filter(item => item.gather_qty !== null && item.gather_qty !== undefined);
+                const xmjSheet = leafXmjSpread.getActiveSheet();
+                const xmjSelect = SpreadJsObj.getSelectObject(xmjSheet);
+                const xmjIndex = gcl.indexOf(xmjSelect);
                 const datas = [];
                 for (const xmj of gcl) {
                     const data2 = {
@@ -765,9 +833,9 @@ $(document).ready(() => {
                     calculateJiaCha(gclGatherData);
                     // const index = gclGatherData.indexOf(ledgerSelect);
                     loadLeafXmjData(index);
-                    const xmjSheet = leafXmjSpread.getActiveSheet();
-                    const xmjSelect = SpreadJsObj.getSelectObject(xmjSheet);
-                    const xmjIndex = gclGatherData[index].leafXmjs.indexOf(xmjSelect);
+                    // const xmjSheet = leafXmjSpread.getActiveSheet();
+                    // const xmjSelect = SpreadJsObj.getSelectObject(xmjSheet);
+                    // const xmjIndex = gclGatherData[index].leafXmjs.indexOf(xmjSelect);
                     loadMaterialData(index, xmjIndex);
                     SpreadJsObj.reLoadRowData(ledgerSpread.getActiveSheet(), index);
                     materialSpread.getActiveSheet().setSelection(info.cellRange.row, info.cellRange.col, info.cellRange.rowCount, info.cellRange.colCount);

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

@@ -235,7 +235,8 @@ $(function () {
             $('#s_order').val(order_array.join(','));
         }
     });
-
+    let interval;
+    let stop = false;
     // 提交表单判断
     $('#addMaterial').click(function () {
         $(this).attr('disabled', true);
@@ -245,8 +246,135 @@ $(function () {
             return false;
         }
         $(this).text('正在生成新一期数据中,请勿刷新本页!...');
-        $(this).parents('form').submit();
+        const _self = $(this);
+        const stage_id = [];
+        $('#add-qi input[name="stage_id[]"]:checked').each(function () {
+            stage_id.push(parseInt($(this).val()));
+        });
+        const newMaterialData = {
+            s_order: $('#s_order').val(),
+            period: $('#add-qi input[name="period"]').val(),
+            stage_id,
+        };
+        if (lastMaterialListNum === 0) {
+            console.log(newMaterialData);
+            postData(preUrl + '/measure/material/add', newMaterialData, function (result) {
+                window.location.href = preUrl + '/measure/material/' + result.order;
+            }, function () {
+                _self.attr('disabled', false).text('确认添加');
+            });
+            return;
+        }
+        $('#add-qi').modal('hide');
+        $('#okedit').modal('show');
+        interval = setInterval(progress, 50);
+        postData(preUrl + '/measure/material/gcl/load', { stage_id }, function (result) {
+            // console.log(result);
+            const ledger = result.ledger;
+            const curLedgerData = result.curLedgerData;
+            const pos = result.pos;
+            const curPosData = result.curPosData;
+            const gclList = result.gclList;
+            gclGatherModel.loadLedgerData(ledger, curLedgerData);
+            gclGatherModel.loadPosData(pos, curPosData);
+            const gclGatherData = gclGatherModel.gatherGclData();
+            // const gclGatherData = gclGatherModel.gatherGclData().filter(item => {
+            //     return item.qc_qty || item.contract_qty
+            // });
+            // 获取需要新增的工料关联清单
+            const insertGcl = [];
+            const removeGclList = [];
+            for (const g of gclList) {
+                const gcl = _.find(gclGatherData, function (item) {
+                    return item.leafXmjs && item.leafXmjs.length > 0 && _.findIndex(item.leafXmjs, { gcl_id : g.gcl_id }) !== -1;
+                });
+                if (gcl) {
+                    const ig = _.find(insertGcl, { gcl });
+                    if (ig) {
+                        if(_.findIndex(ig.bills, { mb_id: g.mb_id }) === -1) {
+                            ig.bills.push(g);
+                        }
+                    } else {
+                        insertGcl.push({
+                            bills: [g],
+                            gcl,
+                            leafXmjs: _.filter(gcl.leafXmjs, function(item) { return item.gather_qty !== undefined && item.gather_qty !== null }),
+                        })
+                    }
+                } else {
+                    removeGclList.push(g);
+                }
+            }
+            const insertList = [];
+            const insertGclList = [];
+            for (const one of insertGcl) {
+                if (one.leafXmjs && one.leafXmjs.length > 0) {
+                    for (const xmj of one.leafXmjs) {
+                        const newgcl = _.find(gclList, { gcl_id: xmj.gcl_id });
+                        for (const bill of one.bills) {
+                            insertList.push({
+                                gcl_id: xmj.gcl_id,
+                                mx_id: xmj.mx_id ? xmj.mx_id : null,
+                                xmj_id: xmj.id ? xmj.id : null,
+                                gather_qty: xmj.gather_qty,
+                                quantity: bill.quantity,
+                                expr: bill.expr,
+                                mb_id: bill.mb_id,
+                                order: bill.order,
+                            });
+                            if (!newgcl) {
+                                insertGclList.push({
+                                    gcl_id: xmj.gcl_id,
+                                    quantity: bill.quantity,
+                                    expr: bill.expr,
+                                    mb_id: bill.mb_id,
+                                    order: bill.order,
+                                });
+                            }
+                        }
+                    }
+                }
+            }
+            // 可能需要新增list_gcl表
+            newMaterialData.material_list = insertList;
+            newMaterialData.insertGclList = insertGclList;
+            newMaterialData.removeGclList = removeGclList;
+            console.log(newMaterialData);
+            postData(preUrl + '/measure/material/add', newMaterialData, function (result) {
+                window.location.href = preUrl + '/measure/material/' + result.order;
+            }, function () {
+                stop = true;
+                // clearInterval(interval);
+                // console.log(_self.parents('div[id="add-qi"]'));
+                // $('#add-qi').modal('show');
+                // $('#okedit').modal('hide');
+                // _self.parents('div[id="add-qi"]').modal('show');
+                _self.attr('disabled', false).text('确认添加');
+            });
+            // return;
+        });
+        // $(this).parents('form').submit();
     });
+
+    let value = 0;
+    function progress(){
+        if (stop) {
+            setTimeout(function () {
+                $('#add-qi').modal('show');
+                $('#okedit').modal('hide');
+                value = 0;
+                clearInterval(interval);
+            }, 500);
+        }
+        if (value < 100) {
+            value = parseInt(value) + 1;
+            $("#material-progress").css("width", value + "%").text(value + "%");
+        } else if (value === 100) {
+            value = parseInt(value) + 1;
+            value = 30;
+        }
+    };
+
     let editPeriod = null;
     $('.edit-material').on('click', function () {
         const order = parseInt($(this).data('order'));

+ 31 - 2
app/public/js/profile.js

@@ -115,7 +115,15 @@ $(document).ready(function() {
         $('#delete-sign').click(function () {
             postData('/profile/sign/delete', {}, function (result) {
                 $('#sign-show').html('');
-                toast('移除成功', 'success');
+                toastr.success('移除成功');
+            })
+        });
+
+        // 移除签章
+        $('#delete-stamp').click(function () {
+            postData('/profile/sign/delete', { type: 'stamp' }, function (result) {
+                $('#stamp-show').html('');
+                toastr.success('移除成功');
             })
         });
 
@@ -136,7 +144,7 @@ $(document).ready(function() {
             const ext = file.name.toLowerCase().split('.').splice(-1)[0];
             const imgStr = /(jpg|jpeg|png|bmp|BMP|JPG|PNG|JPEG)$/;
             if (!imgStr.test(ext)) {
-                toast('请上传正确的图片格式文件','error');
+                toastr.error('请上传正确的图片格式文件');
                 return
             }
             if ($(this).val()) {
@@ -149,6 +157,27 @@ $(document).ready(function() {
                 });
             }
         })
+
+        // 上传签章
+        $('#stamp-upload').change(function () {
+            const file = this.files[0];
+            const ext = file.name.toLowerCase().split('.').splice(-1)[0];
+            const imgStr = /(jpg|jpeg|png|bmp|BMP|JPG|PNG|JPEG)$/;
+            if (!imgStr.test(ext)) {
+                toastr.error('请上传正确的图片格式文件');
+                return
+            }
+            if ($(this).val()) {
+                const formData = new FormData();
+                formData.append('type', 'stamp');
+                formData.append('file', this.files[0]);
+                postDataWithFile('/profile/sign/upload', formData, function (result) {
+                    const html = '<img src="'+ fujianOssPath + result.stamp_path +'" width="90">';
+                    $('#stamp-show').html(html);
+                    $('#stamp-upload').val('');
+                });
+            }
+        })
     } catch (error) {
         console.log(error);
     }

+ 135 - 63
app/public/js/revise.js

@@ -1590,54 +1590,37 @@ $(document).ready(() => {
             }
 
             const data = {postType: 'pos'};
-            if (col.field === 'name') {
-                if (newText === '' && posData) {
-                    toastr.error('部位名称不可为空', 'error');
-                    SpreadJsObj.reLoadRowData(info.sheet, info.row);
-                    return;
-                } else if (!posData) {
-                    if (newText && newText !== '') {
-                        data.posPostType = 'add';
-                        const sortData = info.sheet.zh_data;
-                        const order = (!sortData || sortData.length === 0) ? 1 : Math.max(sortData[sortData.length - 1].porder + 1, sortData.length + 1);
-                        data.postData = { name: newText, lid: node.id, porder: order};
-                    } else {
-                        return;
-                    }
-                } else {
-                    data.posPostType = 'update';
-                    data.postData = {id: posData.id, name: newText};
-                }
-            } else if (!posData) {
-                toastr.warning('新增计量单元请先输入名称');
-                SpreadJsObj.reLoadRowData(info.sheet, info.row);
-                return;
+            if (!posData) {
+                const sortData = info.sheet.zh_data;
+                const order = (!sortData || sortData.length === 0) ? 1 : Math.max(sortData[sortData.length - 1].porder + 1, sortData.length + 1);
+                data.posPostType = 'add';
+                data.postData = { lid: node.id, porder: order };
             } else {
                 data.posPostType = 'update';
-                data.postData = {id: posData.id};
-                if (col.type === 'Number') {
-                    const exprInfo = getExprInfo(col.field);
-                    const num = _.toNumber(newText);
-                    if (_.isFinite(num)) {
-                        data.postData[col.field] = num;
+                data.postData = { id: posData.id };
+            }
+            if (col.type === 'Number') {
+                const exprInfo = getExprInfo(col.field);
+                const num = _.toNumber(newText);
+                if (_.isFinite(num)) {
+                    data.postData[col.field] = num;
+                    if (exprInfo) {
+                        data.postData[exprInfo.expr] = '';
+                    }
+                } else {
+                    try {
+                        data.postData[col.field] = math.evaluate(transExpr(newText));
                         if (exprInfo) {
-                            data.postData[exprInfo.expr] = '';
-                        }
-                    } else {
-                        try {
-                            data.postData[col.field] = math.evaluate(transExpr(newText));
-                            if (exprInfo) {
-                                data.postData[exprInfo.expr] = newText;
-                            }
-                        } catch(err) {
-                            toastr.error('输入的表达式非法');
-                            SpreadJsObj.reLoadRowData(info.sheet, info.row);
-                            return;
+                            data.postData[exprInfo.expr] = newText;
                         }
+                    } catch(err) {
+                        toastr.error('输入的表达式非法');
+                        SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                        return;
                     }
-                } else {
-                    data.postData[col.field] = newText;
                 }
+            } else {
+                data.postData[col.field] = newText;
             }
             postData(window.location.pathname + '/update', data, function (result) {
                 const updateRst = pos.updateDatas(result.pos);
@@ -1671,10 +1654,6 @@ $(document).ready(() => {
                     const data = {id: node.id};
                     for (let iCol = sel.col; iCol < sel.col + sel.colCount; iCol++) {
                         const colSetting = sheet.zh_setting.cols[iCol];
-                        if (colSetting.field === 'name') {
-                            toastr.error('部位名称不能为空');
-                            return;
-                        }
                         const style = sheet.getStyle(iRow, iCol);
                         if (!style.locked) {
                             const colSetting = sheet.zh_setting.cols[iCol];
@@ -1704,6 +1683,25 @@ $(document).ready(() => {
                 });
             }
         },
+        insertPos: function (sheet) {
+            const selection = sheet.getSelections();
+            const sortData = sheet.zh_data;
+            const row = selection[0].row;
+            const node = SpreadJsObj.getSelectObject(billsSheet);
+            const select = SpreadJsObj.getSelectObject(sheet);
+            const porder = select ? select.porder : sortData.length + 1;
+            const data = {
+                postType: 'pos',
+                posPostType: 'insert',
+                postData: [ { lid: node.id, porder } ],
+            };
+            postData(window.location.pathname + '/update', data, function (result) {
+                const updateRst = pos.updateDatas(result.pos);
+                posSpreadObj.loadCurPosData();
+                billsTreeSpreadObj.refreshOperationValid(billsSheet);
+            });
+
+        },
         /**
          * 删除 计量单元
          * @param sheet
@@ -1765,15 +1763,7 @@ $(document).ready(() => {
 
             const data = [];
             const sortData = info.sheet.zh_data || [];
-            if (sortData.length === 0 || info.cellRange.row + info.cellRange.rowCount > sortData.length) {
-                if (info.cellRange.col !== 0) {
-                    toastr.warning('新增计量单元请先输入名称');
-                    posSpreadObj.loadCurPosData();
-                    return;
-                }
-            }
             const hint = {
-                name: {type: 'warning', msg: '计量单元名称不可为空,已过滤'},
                 expr: {type: 'warning', msg: '粘贴了表达式非法,已过滤'},
             };
             const lastOrder = sortData.length > 0 ? sortData[sortData.length - 1].porder + 1 : 1;
@@ -1803,11 +1793,6 @@ $(document).ready(() => {
                                 bPaste = false;
                             }
                         }
-                    } else if (colSetting.field === 'name') {
-                        if (!posData[colSetting.field]) {
-                            toastMessageUniq(hint.name);
-                            bPaste = false;
-                        }
                     }
                 }
                 if (bPaste) {
@@ -1911,6 +1896,17 @@ $(document).ready(() => {
                 return target.hitTestType === spreadNS.SheetArea.viewport || target.hitTestType === spreadNS.SheetArea.rowHeader;
             },
             items: {
+                'insert': {
+                    name: '插入',
+                    icon: 'fa-plus',
+                    disabled: function (key, opt) {
+                        const node = SpreadJsObj.getSelectObject(billsSheet);
+                        return node && node.children && node.children.length > 0;
+                    },
+                    callback: function (key, opt) {
+                        posSpreadObj.insertPos(posSheet);
+                    }
+                },
                 'delete': {
                     name: '删除',
                     icon: 'fa-remove',
@@ -1981,6 +1977,7 @@ $(document).ready(() => {
             this.url = '/tender/' + window.location.pathname.split('/')[2] + '/deal';
             this.spreadSetting = spreadSetting;
             this.spread = SpreadJsObj.createNewSpread(this.obj);
+            this.sheet = this.spread.getActiveSheet();
             SpreadJsObj.initSheet(this.spread.getActiveSheet(), this.spreadSetting);
             if (!readOnly) {
                 this.spread.bind(spreadNS.Events.CellDoubleClick, function (e, info) {
@@ -2023,6 +2020,50 @@ $(document).ready(() => {
                 });
             }
             SpreadJsObj.forbiddenSpreadContextMenu(selector, this.spread);
+            this.initDealBillsSearch();
+        }
+        search(keyword) {
+            this.searchResult = [];
+            this.searchCur = 0;
+            if (keyword) {
+                for (const [i, d] of this.data.entries()) {
+                    if (d.code.indexOf(keyword) >= 0 || d.name.indexOf(keyword) >= 0) this.searchResult.push(d);
+                }
+            }
+            $('#dbs-count').html(`结果:${this.searchResult.length}`);
+            if (this.searchResult.length > 0) SpreadJsObj.locateData(this.sheet, this.searchResult[0]);
+        }
+        searchPre() {
+            this.searchCur = this.searchCur - 1;
+            if (this.searchCur < 0) this.searchCur = this.searchResult.length - 1;
+            SpreadJsObj.locateData(this.sheet, this.searchResult[this.searchCur]);
+        }
+        searchNext() {
+            this.searchCur = this.searchCur + 1;
+            if (this.searchCur >= this.searchResult.length) this.searchCur = 0;
+            SpreadJsObj.locateData(this.sheet, this.searchResult[this.searchCur]);
+        }
+        searchClear() {
+            $('#dbs-keyword').val('');
+            this.searchResult = [];
+            this.searchCur = 0;
+            $('#dbs-count').html(`结果:${this.searchResult.length}`);
+        }
+        initDealBillsSearch() {
+            const self = this;
+            $('#dbs-keyword').change(function () { self.search(this.value); });
+            $('#dbs-pre').click(function (e) {
+                self.searchPre();
+                e.stopPropagation();
+            });
+            $('#dbs-next').click(function (e) {
+                self.searchNext();
+                e.stopPropagation();
+            });
+            $('#dbs-clear').click(function (e) {
+                self.searchClear();
+                e.stopPropagation();
+            });
         }
         loadData (callback) {
             if (this.loaded) {
@@ -2165,6 +2206,7 @@ $(document).ready(() => {
                 font: '12px 微软雅黑',
             };
             this.dealSpread = SpreadJsObj.createNewSpread($('.batch-r', this.obj)[0]);
+            this.dealSheet = this.dealSpread.getActiveSheet();
             // 初始化 清单编号、部位数量复核表 表格
             this.initView();
             SpreadJsObj.initSheet(this.dealSpread.getActiveSheet(), this.dealSpreadSetting);
@@ -2332,6 +2374,40 @@ $(document).ready(() => {
                     },
                 }
             });
+            this.initDealBillsSearch();
+        }
+        search(keyword) {
+            this.searchResult = [];
+            this.searchCur = 0;
+            if (keyword) {
+                for (const [i, d] of this.dealSheet.zh_data.entries()) {
+                    if (d.code.indexOf(keyword) >= 0 || d.name.indexOf(keyword) >= 0) this.searchResult.push(d);
+                }
+            }
+            $('#batch-dbs-count').html(`结果:${this.searchResult.length}`);
+            if (this.searchResult.length > 0) SpreadJsObj.locateData(this.dealSheet, this.searchResult[0]);
+        }
+        searchPre() {
+            this.searchCur = this.searchCur - 1;
+            if (this.searchCur < 0) this.searchCur = this.searchResult.length - 1;
+            SpreadJsObj.locateData(this.dealSheet, this.searchResult[this.searchCur]);
+        }
+        searchNext() {
+            this.searchCur = this.searchCur + 1;
+            if (this.searchCur >= this.searchResult.length) this.searchCur = 0;
+            SpreadJsObj.locateData(this.dealSheet, this.searchResult[this.searchCur]);
+        }
+        initDealBillsSearch() {
+            const self = this;
+            $('#batch-dbs-keyword').change(function () { self.search(this.value); });
+            $('#batch-dbs-pre').click(function (e) {
+                self.searchPre();
+                e.stopPropagation();
+            });
+            $('#batch-dbs-next').click(function (e) {
+                self.searchNext();
+                e.stopPropagation();
+            });
         }
         // 初始化左侧表格
         initView () {
@@ -2357,10 +2433,6 @@ $(document).ready(() => {
             posSheet.clear(0, 0, posSheet.getRowCount(), posSheet.getColumnCount(), GC.Spread.Sheets.SheetArea.viewport, GC.Spread.Sheets.StorageType.data);
             posSheet.setSelection(0, 0, 1 ,1);
             SpreadJsObj.endMassOperation(posSheet);
-            // 检查签约节点数据,以工具栏数据为准
-            if (dealBills) {
-                SpreadJsObj.loadSheetData(this.dealSpread.getActiveSheet(), 'data', dealBills.data);
-            }
             this.dealSpread.getActiveSheet().setSelection(0, 0, 1, 1);
         }
         // 获取界面数据

+ 240 - 8
app/public/js/setting.js

@@ -25,12 +25,12 @@ $(document).ready(() => {
         const account = $(this).data('account');
         $('#edit-user input[name="account"]').val(account.account);
         $('#edit-user input[name="name"]').val(account.name);
-        $('#edit-user input[name="company"]').val(account.company);
+        $('#edit-user select[name="company"]').val(account.company);
         $('#edit-user input[name="role"]').val(account.role);
         $('#edit-user input[name="mobile"]').val(account.mobile);
         $('#edit-user input[name="telephone"]').val(account.telephone);
         $('#edit-user input[name="id"]').val(account.id);
-        $('#edit-user select[name="account_group"]').val(account.account_group);
+        $('#edit-user input[name="account_group"]').val(account.account_group);
         $('#edit-user input[class="account-check"]').val(account.account);
         $('#edit-user input[data-mobile="auth-mobile"]').val(account.auth_mobile);
         $('#edit-user input[name="mobile"]').attr('readOnly', account.bind === 1);
@@ -45,6 +45,13 @@ $(document).ready(() => {
         $('#edit-password input[name="reset_password"]').val('');
     });
 
+    // 选择单位自动配置账号组
+    $('select[name="company"]').change(function () {
+        const company = $(this).val();
+        const oneUnit = _.find(unitList, { name: company });
+        $(this).siblings('input[name="account_group"]').val(oneUnit ? oneUnit.type : 7);
+    });
+
     // 分配随机密码
     $("#rand-password").click(function() {
         const password = randPassword();
@@ -196,10 +203,214 @@ $(document).ready(() => {
     // 设置页显示数目
     $('.nav-tabs .nav-link').each(function () {
         const pageSize = getLocalCache('account-pageSize') ? getLocalCache('account-pageSize') : '';
-        if (getLocalCache('account-pageSize') && $(this).attr('href').indexOf('pageSize') === -1) {
+        if (getLocalCache('account-pageSize') && $(this).attr('href').indexOf('pageSize') === -1 && $(this).attr('href').indexOf('unit') === -1) {
             $(this).attr('href', $(this).attr('href') + '?pageSize=' + getLocalCache('account-pageSize'));
         }
     });
+    // 设置页显示数目
+    $('#user-list .list-group-item').each(function (k,v) {
+        const pageSize = getLocalCache('account-pageSize') ? getLocalCache('account-pageSize') : '';
+        if (pageSize) {
+            $(this).attr('href', $(this).attr('href') + '&pageSize=' + pageSize);
+        }
+    })
+
+    // 参建单位页切换单位右侧显示
+    $('#unit_list tr').click(function () {
+        const id = parseInt($(this).data('id'));
+        const one = _.find(unitList, { id });
+        if (one) {
+            $(this).siblings('tr').removeClass('table-warning');
+            $(this).addClass('table-warning');
+            $('#unit_name').val(one.name);
+            $('#unit_corporation').val(one.corporation);
+            $('#unit_credit_code').val(one.credit_code);
+            $('#unit_tel').val(one.tel);
+            $('#unit_website').val(one.website);
+            $('#unit_region').val(one.region);
+            $('#unit_address').val(one.address);
+            $('#unit_basic').val(one.basic);
+            $('#unit_type').val(one.type);
+            if(one.sign_path) {
+                $('#sign-show').html('<img src="' + fujianOssPath + one.sign_path + '" width="120">');
+                $('#delete-sign').show();
+                $('#upload-sign').hide();
+            } else {
+                $('#sign-show').html('');
+                $('#delete-sign').hide();
+                $('#upload-sign').show();
+            }
+            oneUnit = one;
+        }
+    });
+
+    // 参建单位编辑
+    // 回车提交
+    $('#one_unit input').on('keypress', function () {
+        if(window.event.keyCode === 13) {
+            $(this).blur();
+        }
+    });
+
+    $('#one_unit input').blur(function () {
+        console.log('hello');
+        const val_name = $(this).data('name');
+        let val = _.trim($(this).val()) !== '' ? _.trim($(this).val()) : null;
+        if (!oneUnit) {
+            toastr.error('所选单位有误,请重新选择');
+            return false;
+        }
+        switch(val_name) {
+            case 'name':
+                if(val && val.length > 100) {
+                    toastr.error('单位名称超过100个字,请缩减名称');
+                    $(this).val(oneUnit[val_name]);
+                    return false;
+                }
+                const tongming = _.find(unitList, { name: val });
+                if (tongming && tongming.id !== oneUnit.id) {
+                    toastr.error('单位名称不能重复');
+                    $(this).val(oneUnit[val_name]);
+                    return false;
+                }
+                break;
+            default:
+                if(val && val.length > 255) {
+                    toastr.error('超出字段范围,请缩减');
+                    $(this).val(oneUnit[val_name]);
+                    return false;
+                }
+                break;
+        }
+        if(oneUnit[val_name] !== val) {
+            const _self = $(this);
+            postData('/setting/user/unit/save', { type: 'update', id: oneUnit.id, val_name, val}, function (result) {
+                oneUnit[val_name] = val;
+                _self.val(oneUnit[val_name]);
+                if (val_name === 'name') {
+                    oneUnit.account_num = result.account_num;
+                    $('#unit_list tr[class="table-warning"]').find('a').text(oneUnit[val_name]);
+                    $('#unit_list tr[class="table-warning"]').children('td').eq(2).text(result.account_num);
+                }
+            }, function () {
+                _self.val(oneUnit[val_name]);
+            })
+        } else {
+            $(this).val(oneUnit[val_name]);
+        }
+    });
+
+    $('#one_unit textarea').blur(function () {
+        const val_name = $(this).data('name');
+        let val = _.trim($(this).val()) !== '' ? _.trim($(this).val()) : null;
+        if (!oneUnit) {
+            toastr.error('所选单位有误,请重新选择');
+            return false;
+        }
+        if(oneUnit[val_name] !== val) {
+            const _self = $(this);
+            postData('/setting/user/unit/save', { type: 'update', id: oneUnit.id, val_name, val}, function (result) {
+                oneUnit[val_name] = val;
+                _self.val(oneUnit[val_name]);
+                $('#unit_list tr[class="table-warning"]').children('td').eq(4).text(oneUnit[val_name]);
+            }, function () {
+                _self.val(oneUnit[val_name]);
+            })
+        } else {
+            $(this).val(oneUnit[val_name]);
+        }
+    });
+
+    $('#one_unit select').change(function () {
+        const val_name = $(this).attr('data-name');
+        let val = _.trim($(this).val()) !== '' ? _.trim($(this).val()) : null;
+        if (!oneUnit) {
+            toastr.error('所选单位有误,请重新选择');
+            return false;
+        }
+        if(oneUnit[val_name] !== val) {
+            const _self = $(this);
+            postData('/setting/user/unit/save', { type: 'update', id: oneUnit.id, name: oneUnit.name, val_name, val}, function (result) {
+                oneUnit[val_name] = val;
+                _self.val(oneUnit[val_name]);
+                $('#unit_list tr[class="table-warning"]').children('td').eq(3).text(accountGroup[parseInt(oneUnit[val_name])]);
+            }, function () {
+                _self.val(oneUnit[val_name]);
+            })
+        } else {
+            $(this).val(oneUnit[val_name]);
+        }
+    });
+
+    // 删除单位弹窗
+    $('#del-modal-btn').click(function () {
+        if (!oneUnit) {
+            toastr.error('所选单位有误,请重新选择');
+            return false;
+        }
+        if (oneUnit.account_num === 0) {
+            $('.del-btn').show();
+            $('.not-del-btn').hide();
+        } else {
+            $('.del-btn').hide();
+            $('.not-del-btn').show();
+        }
+    });
+
+    // 删除单位
+    $('#delete-unit').click(function () {
+        if (!oneUnit) {
+            toastr.error('所选单位有误,请重新选择');
+            return false;
+        }
+        postData('/setting/user/unit/save', { type: 'delete', name: oneUnit.name, id: oneUnit.id }, function (result) {
+            window.location.href = '/setting/user/unit';
+        })
+    });
+
+    // 上传签章
+    $('#sign-upload').change(function () {
+        if (!oneUnit) {
+            toastr.error('所选单位有误,请重新选择');
+            return false;
+        }
+        const file = this.files[0];
+        const ext = file.name.toLowerCase().split('.').splice(-1)[0];
+        const imgStr = /(jpg|jpeg|png|bmp|BMP|JPG|PNG|JPEG)$/;
+        if (!imgStr.test(ext)) {
+            toastr.error('请上传正确的图片格式文件');
+            return
+        }
+        if ($(this).val()) {
+            const formData = new FormData();
+            formData.append('file', this.files[0]);
+            formData.append('id', oneUnit.id);
+            postDataWithFile('/setting/user/unit/upload', formData, function (result) {
+                const html = '<img src="'+ fujianOssPath + result.sign_path +'" width="120">';
+                $('#sign-show').html(html);
+                $('#sign-upload').val('');
+                oneUnit.sign_path = result.sign_path;
+                $('#upload-sign').hide();
+                $('#delete-sign').show();
+                toastr.success('上传成功');
+            });
+        }
+    });
+
+    // 移除签章
+    $('#delete-sign').click(function () {
+        if (!oneUnit) {
+            toastr.error('所选单位有误,请重新选择');
+            return false;
+        }
+        postData('/setting/user/unit/save', { type: 'del-sign', id: oneUnit.id }, function (result) {
+            $('#sign-show').html('');
+            toastr.warning('已移除');
+            oneUnit.sign_path = null;
+            $('#upload-sign').show();
+            $('#delete-sign').hide();
+        })
+    });
 });
 
 function checkPasswordForm() {
@@ -226,7 +437,7 @@ function checkPasswordForm() {
 function checkUserForm(status) {
     try {
         if (status === 'add') {
-            if ($('#add-user select[name="account_group"]').val() == 0) {
+            if ($('#add-user input[name="account_group"]').val() == 0) {
                 throw '请选择账号组';
             }
             if ($('#add-user input[name="account"]').val() == '' || $('#add-user input[name="account"]').hasClass('is-invalid')) {
@@ -241,8 +452,8 @@ function checkUserForm(status) {
             if ($('#add-user input[name="name"]').val() == '') {
                 throw '姓名不能为空';
             }
-            if ($('#add-user input[name="company"]').val() == '') {
-                throw '单位名称不能为空';
+            if (_.findIndex(unitList, { name: $('#add-user select[name="company"]').val() }) === -1) {
+                throw '请选择单位名称';
             }
             if ($('#add-user input[name="role"]').val() == '') {
                 throw '职位名称不能为空';
@@ -262,8 +473,8 @@ function checkUserForm(status) {
             if ($('#edit-user input[name="name"]').val() == '') {
                 throw '姓名不能为空';
             }
-            if ($('#edit-user input[name="company"]').val() == '') {
-                throw '单位名称不能为空';
+            if (_.findIndex(unitList, { name: $('#edit-user select[name="company"]').val() }) === -1) {
+                throw '请选择单位名称';
             }
             if ($('#edit-user input[name="role"]').val() == '') {
                 throw '职位名称不能为空';
@@ -281,6 +492,27 @@ function checkUserForm(status) {
 }
 
 /**
+ * 表单检测
+ */
+function checkUnitForm() {
+    try {
+        if ($('#add-company input[name="name"]').val() == '') {
+            throw '单位名称不能为空';
+        }
+        if ($('#add-company select[name="type"]').val() == 0) {
+            throw '请选择类型';
+        }
+        // 检测同名
+        if (_.findIndex(unitList, { name: $('#add-company input[name="name"]').val() }) !== -1) {
+            throw '已存在对应的单位名称';
+        }
+    } catch (err) {
+        toastr.error(err);
+        return false;
+    }
+}
+
+/**
  * 随机密码
  */
 function randPassword() {

+ 4 - 4
app/public/js/shares/cs_tools.js

@@ -959,14 +959,14 @@ const showSelectTab = function(select, spread, afterShow) {
                     { title: '清单编号', field: 'b_code', width: 80, formatter: '@' },
                     { title: '清单名称', field: 'name', width: 120, formatter: '@' },
                     { title: '单位', field: 'unit', width: 50, formatter: '@' },
-                    { title: '数量', field: 'qty', width: 60 },
+                    { title: setting.type === 'stage' ? '合同数量' : '数量', field: 'qty', width: 60 },
+                    { title: '变更数量', field: 'qc_qty', width: 60, visible: setting.type === 'stage' },
                     {
                         title: '类型', field: 'type', width: 100, getValue: function (x) {
                             switch (x.type) {
                                 case 'less': return '数量变少';
                                 case 'miss': return '找不到清单';
-                                case 'qc': return '变更';
-                                case 'miss-qc': return '变更(找不到清单)';
+                                case 'qc-conflict': return '变更冲突(已调用变更令)';
                                 default: return '';
                             }
                         }
@@ -1118,7 +1118,7 @@ const showSelectTab = function(select, spread, afterShow) {
             `            <a class="dropdown-item" href="javascript: void(0);" tag="last" name="${relaSelect.showLevel}">最底层</a>\n` +
             '        </div>\n' +
             '    </div>' +
-            `    <div class=input-group input-group-sm pr-1"><select class="form-control form-control-sm">${stdLibHtml.join('')}</select></div>\n` +
+            `    <div class="input-group input-group-sm pr-1"><select class="form-control form-control-sm col-auto">${stdLibHtml.join('')}</select></div>\n` +
             '    <div class="ml-1">\n' +
             '        <div class="dropdown">\n' +
             '            <button class="btn btn-sm btn-outline-secondary dropdown-toggle" type="button" id="dropdownMenuButton" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">\n' +

+ 2 - 1
app/public/js/shares/gcl_gather_compare.js

@@ -457,7 +457,8 @@ const gclCompareModel = (function () {
      */
     function checkDiffer() {
         gclList.sort((a, b) => {
-            return compareCode(a.b_code, b.b_code);
+            const iCode = compareCode(a.b_code, b.b_code);
+            return iCode ? iCode : (a.unit_price - b.unit_price);
         });
         for (const gcl of gclList) {
             gcl.differ = false;

+ 4 - 1
app/public/js/shares/tender_select.js

@@ -106,6 +106,9 @@ const TenderSelect = function (setting) {
         loadHistory: function () {
             postData('/list/load', {type: tsObj.setting.type, tid: tsObj.select.tender_id, lid: tsObj.select.id}, data => {
                 tsObj.orgHistory = data.history || {};
+                // 屏蔽自己
+                const curIndex = data.tenders.findIndex(x => { return x.id === tsObj.select.tender_id });
+                if (curIndex >= 0) data.tenders.splice(curIndex, 1);
                 tsObj.tenderSourceTree = Tender2Tree.convert(data.category, data.tenders, data.ledgerAuditConst, data.stageAuditConst);
                 tsObj.trHistory = JSON.parse(JSON.stringify(tsObj.orgHistory));
                 if (tsObj.orgHistory.tenders) {
@@ -181,7 +184,7 @@ const TenderSelect = function (setting) {
                 if (tsObj.setting.type === 'stage') updateData.cover = $('#ts-cover')[0].checked;
                 if (updateData.tenders.length > 0) {
                     postData(window.location.pathname + '/sumLoad', updateData, result => {
-                        tsObj.setting.afterLoad(result);
+                        tsObj.setting.afterLoad(result, tsObj.select);
                         $('#tender-select').modal('hide');
                     });
                 } else {

+ 2 - 2
app/public/js/spreadjs_rela/spreadjs_zh.js

@@ -661,6 +661,7 @@ const SpreadJsObj = {
                 sheet.extendCellType.checkbox = new spreadNS.CellTypes.CheckBox();
             }
             sheet.getRange(-1, col, -1, 1).cellType(sheet.extendCellType.checkbox);
+            sheet.getRange(-1, col, -1, 1).hAlign(col ? col.hAlign : spreadNS.HorizontalAlign.center);// 空白行不居中问题,checkbox要默认居中
         }
         if (colSetting.cellType === 'unit') {
             if (!sheet.extendCellType.unit) {
@@ -955,7 +956,6 @@ const SpreadJsObj = {
         return copyData.join('\r\n');
     },
     analysisPasteText: function (text, transpose = false) {
-        console.log(text);
         const result = [];
         if (text === '') return result;
 
@@ -973,7 +973,7 @@ const SpreadJsObj = {
     },
     transposePasteData: function (data) {
         const result = [];
-        const rowCount = data.length, colCount = data.reduce((r, x) => { return r ? Math.max(r, x.length) : x.length; });
+        const rowCount = data.length, colCount = data.reduce((r, x) => { return Math.max(r, x.length); }, 0);
         for (let c = 0; c < colCount; c++) {
             const newRow = [];
             for (let r = 0; r < rowCount; r++) {

+ 66 - 38
app/public/js/stage.js

@@ -594,7 +594,7 @@ $(document).ready(() => {
             if (this.changes) {
                 sheet.setSelection(0, 0, 1, 1);
                 this._filterChange(!$('#filterEmpty')[0].checked, $('#matchPos')[0].checked);
-                this._loadChangeDetail(this.changes[0]);
+                this._loadChangeDetail(this.displayChanges[0]);
             } else {
                 toastr.error('查询变更令有误,请刷新页面后重试');
             }
@@ -611,6 +611,7 @@ $(document).ready(() => {
                 }
             }
             SpreadJsObj.loadSheetData(this.spread.getActiveSheet(), SpreadJsObj.DataType.Data, this.displayChanges);
+            this._loadChangeDetail(this.displayChanges[0]);
         }
         loadChanges(data) {
             this.callData = data;
@@ -657,7 +658,7 @@ $(document).ready(() => {
             case 'dagl': data.dagl_url && window.open(data.dagl_url); break;
             case 'gxby': data.gxby_url && window.open(data.gxby_url); break;
             case 'qc_qty':
-                if (data.children && data.children.length > 0 || data.lock) return;
+                if (data.children && data.children.length > 0 || data.lock || data.is_import) return;
                 const nodePos = stagePos.getLedgerPos(data.id);
                 if (nodePos && nodePos.length > 0) return;
                 changesObj.loadChanges({bills: data});
@@ -830,6 +831,7 @@ $(document).ready(() => {
         relaSpread: slSpread,
         storeKey: 'stage-slm-' + stage.id,
         id: 'stage-slm',
+        type: 'stage',
         afterLocated:  function () {
             stagePosSpreadObj.loadCurPosData();
         },
@@ -1455,8 +1457,12 @@ $(document).ready(() => {
     const addTag = newTag({ledgerSheet: slSpread.getActiveSheet(), billsTag});
     const tenderSelect = TenderSelect({
         type: 'stage',
-        afterLoad: function (result) {
+        afterLoad: function (result, select) {
             const nodes = stageTree.loadPostStageData(result);
+            const posterity = stageTree.getPosterity(select);
+            for (const p of posterity) {
+                p.is_import = !!result.import_change.data.find(x => { return x.lid === p.id });
+            }
             stageTreeSpreadObj.refreshTreeNodes(slSpread.getActiveSheet(), nodes);
             if (detail) {
                 detail.loadStageLedgerUpdateData(result, nodes);
@@ -1469,6 +1475,8 @@ $(document).ready(() => {
             } else {
                 sumLoadMiss.clearMissData();
             }
+            // todo 优化,仅加载需要刷新的变更令
+            if (checkedChanges) checkedChanges.reloadChangeData();
         }
     });
     $.contextMenu({
@@ -2025,7 +2033,7 @@ $(document).ready(() => {
     });
 
     // 加载计量单元数据 - 暂时统一加载,如有需要,切换成动态加载并缓存
-    postData(window.location.pathname + '/load', { filter: 'ledger;pos;detail;change;tag;cooperation' }, function (result) {
+    postData(window.location.pathname + '/load', { filter: 'ledger;pos;detail;change;import_change;tag;cooperation' }, function (result) {
         // 加载树结构
         stageTree.loadDatas(result.ledgerData);
         checkShowLast(result.ledgerData.length);
@@ -2051,7 +2059,7 @@ $(document).ready(() => {
         SpreadJsObj.resetTopAndSelect(spSpread.getActiveSheet());
         // 加载中间计量
         stageIm.init(stage, imType, tenderInfo.decimal);
-        stageIm.loadData(result.ledgerData, result.posData, result.detailData, result.changeData, result.detailAtt);
+        stageIm.loadData(result.ledgerData, result.posData, result.detailData, result.changeData, result.import_change, result.detailAtt);
 
         errorList.loadHisErrorData();
         checkList.loadHisCheckData();
@@ -3500,17 +3508,6 @@ $(document).ready(() => {
                     $('#view-calc-remark').text(img_remark);
 
                     $('#edit-img').modal('hide');
-                    // updateImageData = updateData;
-                    // $('#edit-img').modal('hide');
-                    // postData(window.location.pathname + '/detail/merge-img', updateData, function (result) {
-                    //     stageIm.loadUpdateDetailData(result);
-                    //     data.calc_img = result.calc_img;
-                    //     data.calc_img_org = result.calc_img_org;
-                    //     const calcImgSrc = data && data.calc_img ? '/' + data.calc_img : '';
-                    //     $('#show-calc-img').attr('src', calcImgSrc);
-                    //     $('#calc-img').attr('src', calcImgSrc);
-                    //     $('#view-calc-img').attr('src', calcImgSrc);
-                    //     $('#edit-img').modal('hide');
                     // });
                 } else if (data.calc_img) {
                     self.updateImageData = {updateType: 'clear', lid: data.lid, pid: data.pid, uuid: data.uuid, calc_img_remark: img_remark};
@@ -3522,16 +3519,6 @@ $(document).ready(() => {
                     $('#view-calc-remark').val(img_remark);
                     $('#text-edit').val(img_remark);
                     $('#edit-img').modal('hide');
-                    // postData(window.location.pathname + '/detail/merge-img', {updateType: 'clear', lid: data.lid, pid: data.pid, uuid: data.uuid}, function (result) {
-                    //     stageIm.loadUpdateDetailData(result);
-                    //     data.calc_img = result.calc_img;
-                    //     data.calc_img_org = result.calc_img_org;
-                    //     const calcImgSrc = data && data.calc_img ? '/' + data.calc_img : '';
-                    //     $('#show-calc-img').attr('src', calcImgSrc);
-                    //     $('#calc-img').attr('src', calcImgSrc);
-                    //     $('#view-calc-img').attr('src', calcImgSrc);
-                    //     $('#edit-img').modal('hide');
-                    // });
                 } else {
                     self.updateImageData = updateData;
                     $('#show-calc-img').attr('src', '');
@@ -3660,7 +3647,7 @@ $(document).ready(() => {
             const self = this;
             this.changeSpreadSetting = {
                 cols: [
-                    {title: '变更令号', colSpan: '1', rowSpan: '1', field: 'p_code', hAlign: 0, width: 100, formatter: '@'},
+                    {title: '变更令号', colSpan: '1', rowSpan: '1', field: 'p_code', hAlign: 0, width: 100, formatter: '@', cellType: 'tip', getTip: function (x) { return x.t_name ? `所属标段:${x.t_name}` : ''; }},
                     {title: '变更名称', colSpan: '1', rowSpan: '1', field: 'name', hAlign: 0, width: 180, formatter: '@'},
                     {title: '金额', colSpan: '1', rowSpan: '1', field: 'total_price', hAlign: 2, width: 80, type: 'Number'},
                     {title: '批复文号', colSpan: '1', rowSpan: '1', field: 'w_code', hAlign: 0, width: 100, formatter: '@'},
@@ -3673,6 +3660,13 @@ $(document).ready(() => {
                 headerFont: '12px 微软雅黑',
                 font: '12px 微软雅黑',
                 readOnly: true,
+                getColor: function (sheet, data, row, col, defaultColor) {
+                    if (data && data.is_import) {
+                        return '#eee';
+                    } else {
+                        return defaultColor;
+                    }
+                }
             };
             this.changeSpread = SpreadJsObj.createNewSpread(setting.changeObj[0]);
             this.changeSheet = this.changeSpread.getActiveSheet();
@@ -3718,7 +3712,7 @@ $(document).ready(() => {
                 if (change.detail) {
                     self.loadChangeDetailData();
                 } else {
-                    postData(window.location.pathname + '/change/detail', {cid: change.cid}, function (result) {
+                    postData(window.location.pathname + '/change/detail', {cid: change.cid, is_import: change.is_import}, function (result) {
                         change.detail = result;
                         self.analyzeChange(change);
                         self.loadChangeDetailData();
@@ -3741,8 +3735,14 @@ $(document).ready(() => {
                         name: '定位至台账',
                         icon: 'fa-sign-in',
                         callback: function (key, opt) {
+                            const change = SpreadJsObj.getSelectObject(self.changeSheet);
                             const changeBills = SpreadJsObj.getSelectObject(self.changeBillsSheet);
-                            if (changeBills.gcl_id) {
+                            if (change.is_import) {
+                                if (changeBills.pos && changeBills.pos.length > 0) {
+                                    SpreadJsObj.locateTreeNode(slSpread.getActiveSheet(), changeBills.pos[0].ledger_id, true);
+                                    stagePosSpreadObj.loadCurPosData();
+                                }
+                            } else if (changeBills.gcl_id) {
                                 const node = stageTree.nodes.find(x => {return x.id === changeBills.gcl_id});
                                 SpreadJsObj.locateTreeNode(slSpread.getActiveSheet(), node.ledger_id, true);
                                 stagePosSpreadObj.loadCurPosData();
@@ -3793,23 +3793,23 @@ $(document).ready(() => {
         reloadChangeData() {
             const self = this;
             postData(window.location.pathname + '/change/data', null, function (result) {
-                self.changes = result.changes;
+                self.changes = [];
+                result.changes.forEach(x => { x.is_import = 0; self.changes.push(x); });
+                result.import_changes.forEach(x => { x.is_import = 1; delete x.total_price; self.changes.push(x); });
+                if (result.changes.length > 0) self.analyzeChange(result.changes[0]);
+                if (result.import_changes.length > 0) self.analyzeChange(result.import_changes[0]);
                 SpreadJsObj.loadSheetData(self.changeSheet, SpreadJsObj.DataType.Data, self.changes);
                 self.changeSheet.setSelection(0, 0, 1, 1);
-                if (self.changes.length > 0) self.analyzeChange(result.changes[0]);
                 self.curChangeId = self.changes.length > 0 ? self.changes[0].id : null;
                 self.loadChangeDetailData();
             });
         }
-        analyzeChange(change) {
-            change.bills = change.detail.bills;
+        _analyzeCommon(change) {
             for (const b of change.bills) {
-                const aub = change.detail.addUsedBills.find(function (x) {
-                    return x.id === b.id;
+                const eub = change.detail.endUsedBills.find(function (x) {
+                    return x.cbid === b.id;
                 });
-                if (aub) {
-                    b.used_qty = aub.used_qty;
-                }
+                if (eub) b.used_qty = eub.qty;
                 b.qty = _.toNumber(b.samount);
                 b.valid_qty = ZhCalc.sub(b.qty, b.used_qty);
                 b.tp = ZhCalc.round(ZhCalc.mul(b.qty, b.unit_price), tenderInfo.decimal.tp);
@@ -3821,6 +3821,34 @@ $(document).ready(() => {
                 }
             }
         }
+        _analyzeImport(change) {
+            for (const b of change.bills) {
+                const eub = change.detail.endUsedBills.find(function (x) {
+                    return x.rela_cbid === b.id;
+                });
+                if (eub) b.used_qty = eub.rela_qty;
+                b.qty = _.toNumber(b.samount);
+                delete b.unit_price;
+                // b.tp = ZhCalc.round(ZhCalc.mul(b.qty, b.unit_price), tenderInfo.decimal.tp);
+                b.pos = _.filter(change.detail.curUsedBills, { rela_cbid: b.id });
+                b.cur_qty = 0;
+                for (const p of b.pos) {
+                    p.qty = p.rela_qty;
+                    const node = stageTree.datas.find(x => { return x.id === p.lid; });
+                    p.ledger_id = node.ledger_id;
+                    p.f_qty = node.quantity;
+                    b.cur_qty = ZhCalc.add(b.cur_qty, p.rela_qty);
+                }
+            }
+        }
+        analyzeChange(change) {
+            change.bills = change.detail.bills;
+            if (change.is_import) {
+                this._analyzeImport(change);
+            } else {
+                this._analyzeCommon(change);
+            }
+        }
     }
     // 展开收起附件
     $('a', '.right-nav').bind('click', function () {

+ 57 - 16
app/public/js/stage_change.js

@@ -30,21 +30,16 @@ class ChangeAnalysis {
         }
         return null;
     }
-    analyze (change) {
-        change.filterBills = false;
-        change.attachments = change.detail.attachments;
-        change.bills = change.detail.bills;
+    _analyzeCommon(change) {
         for (const b of change.bills) {
-            const aub = change.detail.addUsedBills.filter(function (x) {
+            const eub = change.detail.endUsedBills.filter(function (x) {
                 return x.cbid === b.id;
             });
-            if (aub) {
-                b.used_qty = ZhCalc.sum(aub.map(x => { return x.qty}));
-            }
+            if (eub) b.used_qty = ZhCalc.sum(eub.map(x => { return x.qty }));
             b.qty = _.toNumber(b.samount);
             b.valid_qty = ZhCalc.sub(b.qty, b.used_qty);
             b.tp = ZhCalc.round(ZhCalc.mul(b.qty, b.unit_price), tenderInfo.decimal.tp);
-            b.pos = _.filter(change.detail.curUsedBills, {cbid: b.id});
+            b.pos = _.filter(change.detail.curUsedBills, { cbid: b.id });
             b.cur_qty = 0;
             for (const p of b.pos) {
                 // 查询最底层项目节
@@ -57,7 +52,45 @@ class ChangeAnalysis {
                 p.f_qty = p.p_qty ? p.p_qty : p.l_qty;
                 b.cur_qty = ZhCalc.add(b.cur_qty, p.qty);
             }
+        }
+    }
+    _analyzeImport(change) {
+        for (const b of change.bills) {
+            const eub = change.detail.endUsedBills.filter(function (x) {
+                return x.rela_cbid === b.id;
+            });
+            if (eub) b.used_qty = ZhCalc.sum(eub.map(x => { return x.rela_qty }));
+
+            // 移除单价、部位,不计算数量、金额、未变更
+            delete b.unit_price;
+            b.qty = _.toNumber(b.samount);
+            // b.valid_qty = ZhCalc.sub(b.qty, b.used_qty);
+            // b.tp = ZhCalc.round(ZhCalc.mul(b.qty, b.unit_price), tenderInfo.decimal.tp);
 
+            b.pos = _.filter(change.detail.curUsedBills, { rela_cbid: b.id });
+            b.cur_qty = 0;
+            for (const p of b.pos) {
+                p.qty = p.rela_qty;
+                // 查询最底层项目节
+                const node = this.ledgerTree.datas.find(x => { return x.id === p.lid; });
+                const leafXmj = this.getLeafXmj(node);
+                if (leafXmj) {
+                    p.leaf_xmj_code = leafXmj.code;
+                    p.leaf_xmj_name = leafXmj.name;
+                }
+                p.f_qty = node.quantity;
+                b.cur_qty = ZhCalc.add(b.cur_qty, p.qty);
+            }
+        }
+    }
+    analyze (change) {
+        change.filterBills = false;
+        change.attachments = change.detail.attachments;
+        change.bills = change.detail.bills;
+        if (change.is_import) {
+            this._analyzeImport(change);
+        } else {
+            this._analyzeCommon(change);
         }
     }
 }
@@ -70,7 +103,7 @@ $(document).ready(() => {
     // 初始化变更令spread
     const changeSpreadSetting = {
         cols: [
-            {title: '变更令号', colSpan: '1', rowSpan: '1', field: 'p_code', hAlign: 0, width: 150, formatter: '@'},
+            {title: '变更令号', colSpan: '1', rowSpan: '1', field: 'p_code', hAlign: 0, width: 150, formatter: '@', cellType: 'tip', getTip: function (x) { return x.t_name ? `所属标段:${x.t_name}` : ''; }},
             {title: '变更名称', colSpan: '1', rowSpan: '1', field: 'name', hAlign: 0, width: 350, formatter: '@'},
             {title: '金额', colSpan: '1', rowSpan: '1', field: 'total_price', hAlign: 2, width: 100, type: 'Number'},
             {title: '批复文号', colSpan: '1', rowSpan: '1', field: 'w_code', hAlign: 0, width: 150, formatter: '@'},
@@ -92,6 +125,13 @@ $(document).ready(() => {
             colWidth: true,
         },
         readOnly: true,
+        getColor: function (sheet, data, row, col, defaultColor) {
+            if (data && data.is_import) {
+                return '#eee';
+            } else {
+                return defaultColor;
+            }
+        }
     };
     const changeSpread = SpreadJsObj.createNewSpread($('#bgl-spread')[0]);
     SpreadJsObj.initSheet(changeSpread.getActiveSheet(), changeSpreadSetting);
@@ -297,7 +337,7 @@ $(document).ready(() => {
             if (change.detail) {
                 changeSpreadObj.loadChangeDetailData();
             } else {
-                postData(window.location.pathname + '/detail', {cid: change.cid}, function (result) {
+                postData(window.location.pathname + '/detail', {cid: change.cid, is_import: change.is_import}, function (result) {
                     change.detail = result;
                     analysis.analyze(change);
                     changeSpreadObj.loadChangeDetailData();
@@ -316,14 +356,15 @@ $(document).ready(() => {
 
     postData(window.location.pathname + '/data', null, function (result) {
         tenderInfo = result.tenderInfo;
-        changes = result.changes;
-        usedChangesId = result.usedChangesId;
+        changes = [];
+        result.changes.forEach(x => { x.is_import = 0; changes.push(x); });
+        result.import_changes.forEach(x => { x.is_import = 1; delete x.total_price; changes.push(x); });
+        usedChangesId = [...result.usedChangesId, ...result.used_import_cid];
         ledger = result.ledger;
         // ------------begin 预处理数据
         analysis = new ChangeAnalysis(ledger);
-        if (changes.length > 0) {
-            analysis.analyze(changes[0]);
-        }
+        if (result.changes.length > 0) analysis.analyze(result.changes[0]);
+        if (result.import_changes.length > 0) analysis.analyze(result.import_changes[0]);
         // ------------end 预处理数据
         // ------------begin 加载数据至界面
         SpreadJsObj.loadSheetData(changeSpread.getActiveSheet(), SpreadJsObj.DataType.Data, changes);

+ 26 - 18
app/public/js/stage_im.js

@@ -13,7 +13,7 @@ const stageIm = (function () {
     const resetFields = ['peg', 'bw', 'xm', 'drawing_code', 'calc_memo', 'position', 'jldy'];
     const splitChar = '-';
     const mergeChar = ';';
-    let stage, imType, decimal, details, changes, detailsAtt, ImData, pre, orgImData;
+    let stage, imType, decimal, details, changes, importChanges, detailsAtt, ImData, pre, orgImData;
     let up_field = 'unit_price';
     const gsTreeSetting = {
         id: 'ledger_id',
@@ -67,7 +67,7 @@ const stageIm = (function () {
         }
     }
 
-    function loadData (ledger, pos, stageDetail, stageChange, stageDetailAtt) {
+    function loadData (ledger, pos, stageDetail, stageChange, stageImportChange, stageDetailAtt) {
         up_field = 'unit_price';
         gsTree.loadDatas(ledger);
         treeCalc.calculateAll(gsTree);
@@ -79,6 +79,7 @@ const stageIm = (function () {
         details = stageDetail;
 
         changes = stageChange;
+        importChanges = stageImportChange;
 
         detailsAtt = stageDetailAtt;
     }
@@ -94,6 +95,7 @@ const stageIm = (function () {
         details = stageDetail;
 
         changes = stageChange;
+        importChanges = [];
 
         detailsAtt = stageDetailAtt;
     }
@@ -515,6 +517,7 @@ const stageIm = (function () {
                         im.changes.push(c);
                     }
                 }
+                importChanges.forEach(x => { if (x.lid === p.id) im.changes.push(x); });
             } else {
                 for (const pp of posRange) {
                     if ((!pp.qc_qty || pp.qc_qty === 0)) {
@@ -706,6 +709,7 @@ const stageIm = (function () {
                             imDefault.changes.push(c);
                         }
                     }
+                    importChanges.forEach(x => { if (x.lid === p.id) imDefault.changes.push(x); });
 
                     imDefault.contract_jl = ZhCalc.add(imDefault.contract_jl, p.contract_tp);
                     imDefault.qc_jl = ZhCalc.add(imDefault.qc_jl, p.qc_tp);
@@ -784,11 +788,10 @@ const stageIm = (function () {
                     im.changes.push(c);
                 }
             }
+            importChanges.forEach(x => { if (x.lid === node.id) im.changes.push(x); });
         } else {
             for (const p of posRange) {
-                if ((!p.qc_qty || p.qc_qty === 0)) {
-                    continue;
-                }
+                if ((!p.qc_qty || p.qc_qty === 0)) continue;
                 for (const c of changes) {
                     if (c.lid === node.id && c.pid === p.id && c.qty && c.qty !== 0) {
                         im.changes.push(c);
@@ -910,6 +913,7 @@ const stageIm = (function () {
                             im.changes.push(c);
                         }
                     }
+                    importChanges.forEach(x => { if (x.lid === p.id) im.changes.push(x); });
                 }
             }
         }
@@ -1101,20 +1105,10 @@ const stageIm = (function () {
                 changes.push(c);
             }
         }
-        return refreshImData(refreshNodes);
-    }
-    function loadUpdatePosData(data, refreshNodes) {
-        if (data.pos) {
-            if (data.pos.pos && data.pos.pos.length > 0 && typeof data.pos.pos[0] === 'string') {
-                gsPos.removeDatas(data.pos.pos);
-            } else {
-                gsPos.updateDatas(data.pos.pos);
-            }
-            if (data.pos.curStageData) {
-                gsPos.loadCurStageData(data.pos.curStageData);
-            }
+        if (data.import_change) {
+            _.remove(importChanges, data.import_change.target);
+            data.import_change.data.forEach(x => { importChanges.push(x) });
         }
-        gsTree.loadPostStageData(data.ledger);
         return refreshImData(refreshNodes);
     }
     function loadUpdateChangeData(data, refreshNodes) {
@@ -1130,6 +1124,20 @@ const stageIm = (function () {
         }
         return refreshImData(refreshNodes);
     }
+    function loadUpdatePosData(data, refreshNodes) {
+        if (data.pos) {
+            if (data.pos.pos && data.pos.pos.length > 0 && typeof data.pos.pos[0] === 'string') {
+                gsPos.removeDatas(data.pos.pos);
+            } else {
+                gsPos.updateDatas(data.pos.pos);
+            }
+            if (data.pos.curStageData) {
+                gsPos.loadCurStageData(data.pos.curStageData);
+            }
+        }
+        gsTree.loadPostStageData(data.ledger);
+        return refreshImData(refreshNodes);
+    }
 
     function getParentCheckNode(node) {
         let parent = node;

+ 96 - 0
app/public/report/js/rpt_archive.js

@@ -66,6 +66,8 @@ let rptArchiveObj = {
         me.refreshNodes();
         rptArchiveObj._countChkedRptTpl();
         rptArchiveObj._buildeArchiveDateSelect();
+        rptArchiveObj._iniArchiveItemForDeleteShow();
+        rptArchiveObj._iniArchiveItemForDeleteShow();
     },
     toggleBtn: function (enabled) {
         if (current_stage_status === 3 && enabled) {
@@ -131,6 +133,7 @@ let rptArchiveObj = {
             me._countChkedRptTpl();
             me._buildeArchiveDateSelect();
             me._requestArchiveReport();
+            rptArchiveObj._iniArchiveItemForDeleteShow();
         }
     },
 
@@ -228,10 +231,12 @@ let rptArchiveObj = {
             });
             // me._updateSignHtmlAndFrame();
             me._requestArchiveReport();
+            me._iniArchiveItemForDeleteShow();
         } else {
             me.currentArchivePdfPath = oss_path + '/'+ me.currentArchiveUuid +'.PDF?' + new Date(me.currentArchiveDateStr.slice(3).replace(/-/g, '/')).getTime();
             $('#iframe_made').html('<iframe src="/archive/pdf/show?file=' + me.currentArchivePdfPath + '" height="750px" width="100%" style="border: none;"></iframe>');
             me._buildeArchiveDateSelect();
+            me._iniArchiveItemForDeleteShow();
         }
     },
 
@@ -296,6 +301,7 @@ let rptArchiveObj = {
 
     _countChkedRptTpl: function () {
         let me = rptArchiveObj;
+        $('#delete_archive_btn').attr('disabled', '');
         if (me.treeObj) {
             me.checkedRptTplNodes = [];
             let chkNodes = me.treeObj.getCheckedNodes(true), cnt = 0, hasCurrentNode = false;
@@ -317,9 +323,23 @@ let rptArchiveObj = {
             $("#export_div").find("span").each(function(cIdx,elementSpan){
                 elementSpan.innerText = cnt;
             });
+            if (cnt) {
+                $('#delete_archive_btn').removeAttr('disabled');
+            }
         }
     },
 
+    _iniArchiveItemForDeleteShow: function() {
+        let me = rptArchiveObj;
+        const dispArchInfoBody = $('#disp_archive_info_body');
+        dispArchInfoBody.empty();
+        let targetDom = document.getElementById("currentDrpArchiveSelect");
+        let firstStr = `<h6>确认删除本张【${targetDom.innerText}】归档报表?</h6>`;
+        let secondStr = `<h6>删除后,数据无法恢复,请谨慎操作。</h6>`;
+        dispArchInfoBody.append(firstStr);
+        dispArchInfoBody.append(secondStr);
+    },
+
     showArchivedItem: function(currentNode) {
         //初始化当前报表已经归档的信息
         //ARCHIVE_LIST结构:[{rpt_id, items: [{uuid, update_time, is_common}...最多3个]}...] (当前项目、当前期的所有报表归档信息)
@@ -694,6 +714,73 @@ let rptArchiveObj = {
         } else {
             alert('请选择打开一个报表!');
         }
+    },
+    
+    _getOneValidLeafNode: function(node) {
+        let rst = null;
+        let me = rptArchiveObj;
+        if (node) {
+            const preNode = node.getPreNode();
+            if (preNode) {
+                rst = preNode;
+            } else {
+                const nodes = me.treeObj.getNodes();
+                for (let dtlNode of nodes) {
+                    if (dtlNode !== node && dtlNode.nodeType === 2) {
+                        rst = dtlNode;
+                        break;
+                    }
+                }
+            }
+        }
+        return rst;
+    },
+
+    removeArchive: function() {
+        let me = rptArchiveObj;
+        const bkRefId = parseInt(me.currentNode.refId);
+        const bkUuid = me.currentArchiveUuid;
+        if (me.currentNode && me.currentArchiveUuid) {
+            const url = `/tender/report_api/removeArchive/${PROJECT_ID}/${current_stage_id}/${me.currentNode.refId}/${me.currentArchiveUuid}`;
+            CommonAjax.postXsrfEx(url, '', 3000, true, getCookie('csrfToken_j'),
+                function(result){
+                    // console.log(result.data);
+                    let hasRemovedAll = false;
+                    for (let idx = ARCHIVE_LIST.length - 1; idx >= 0; idx--) {
+                        if (bkRefId === parseInt(ARCHIVE_LIST[idx].rpt_id)) {
+                            for (let dtIdx = ARCHIVE_LIST[idx].items.length - 1; dtIdx >= 0; dtIdx--) {
+                                if (ARCHIVE_LIST[idx].items[dtIdx].uuid === bkUuid) {
+                                    ARCHIVE_LIST[idx].items.splice(dtIdx, 1);
+                                    break;
+                                }
+                            }
+                            if (ARCHIVE_LIST[idx].items.length === 0) {
+                                ARCHIVE_LIST.splice(idx, 0);
+                                hasRemovedAll = true;
+                                break;
+                            }
+                        }
+                    }
+                    //要刷新当前归档
+                    if (hasRemovedAll) {
+                        const validNode = me._getOneValidLeafNode(me.currentNode);
+                        // me.iniPage();
+                        me.treeObj.removeNode(me.currentNode);
+                        if (validNode) {
+                            me.currentNode = validNode;
+                            me.onClick(null, null, me.currentNode);
+                            me.treeObj.selectNode(me.currentNode, false);
+                            me._iniArchiveItemForDeleteShow();
+                        }
+                    } else {
+                        me.currentArchiveUuid = null;
+                        me.currentArchiveDateStr = null;
+                        me.currentArchivePdfPath = null;
+                        me.onClick(null, null, me.currentNode);
+                    }
+                }
+            );
+        }
     }
 };
 
@@ -789,4 +876,13 @@ $(function () {
             alert('请选择打开一个报表!');
         }
     });
+
+    $('#delete_file').click(function () {
+        if (rptArchiveObj.currentArchiveUuid && rptArchiveObj.currentArchivePdfPath) {
+            console.log(rptArchiveObj.currentArchivePdfPath);
+            rptArchiveObj.removeArchive();
+        } else {
+            alert('请选择打开一个报表!');
+        }
+    });
 })

+ 3 - 3
app/public/report/js/rpt_custom.js

@@ -29,7 +29,7 @@ const rptCustomObj = (function () {
     const sStageSelect = 'stage_select';
     const grSpreadSetting = {
         baseCols: [
-            {title: '名称', colSpan: '1', rowSpan: '1', field: 'name', hAlign: 0, width: 180, formatter: '@', readOnly: true},
+            {title: '名称', colSpan: '1', rowSpan: '1', field: 'name', hAlign: 0, width: 240, formatter: '@', readOnly: true},
         ],
         extraCols: [
             {title: '%s', colSpan: '1', rowSpan: '1', field: '%s', hAlign: 1, vAlign: '1', width: 60, cellType: 'checkbox', readOnly: true},
@@ -273,7 +273,7 @@ const rptCustomObj = (function () {
         grSpreadSetting.cols = [];
         for (const bc of grSpreadSetting.baseCols) {
             grSpreadSetting.cols.push(bc);
-            if (bc.field === 'name') bc.width = gsSetting.nameColWidth ? gsSetting.nameColWidth : 180;
+            if (bc.field === 'name') bc.width = gsSetting.nameColWidth ? gsSetting.nameColWidth : 240;
         }
         if (gsSetting.special) {
             for (const s of gsSetting.special) {
@@ -838,7 +838,7 @@ const rptCustomObj = (function () {
         const spreadSetting = {
             cols: [
                 {title: '选择', field: 'selected', hAlign: 1, width: 40, formatter: '@', cellType: 'checkbox', readOnly: true},
-                {title: '名称', field: 'name', hAlign: 0, width: 180, formatter: '@', readOnly: true, cellType: 'tree'},
+                {title: '名称', field: 'name', hAlign: 0, width: 300, formatter: '@', readOnly: true, cellType: 'tree'},
                 {title: '期数', field: 'phase', hAlign: 1, width: 60, formatter: '@', readOnly: true},
                 {title: '审批状态', field: 'status', hAlign: 1, width: 60, formatter: '@', readOnly: true}
             ],

+ 19 - 0
app/reports/rpt_component/jpc_cross_tab.js

@@ -635,8 +635,27 @@ JpcCrossTabSrv.prototype.createNew = function() {
             rst = rst.concat(tabRstLst[i]);
             tabRstLst[i] = null;
         }
+        me.checkCombineEvent(JV.RUN_TYPE_AFTER_COMBINE, [], [], rst, $CURRENT_RPT, dataObj);
         return rst;
     };
+    JpcCrossTabResult.checkCombineEvent = function($RUN_TYPE, $VER_COMB_ARRAY, $HOR_COMB_ARRAY, $CURRENT_CELL_ITEMS, $CURRENT_RPT, $CURRENT_DATA) {
+        if ($CURRENT_RPT.formulas) {
+            for (let execFmlIdx = 0; execFmlIdx < $CURRENT_RPT.formulas.length; execFmlIdx++) {
+                if ($CURRENT_RPT.formulas[execFmlIdx][JV.PROP_RUN_TYPE] === $RUN_TYPE) {
+                    const expression = $CURRENT_RPT.formulas[execFmlIdx][JV.PROP_EXPRESSION];
+                    if (expression) {
+                        const $ME = $CURRENT_RPT.formulas[execFmlIdx];
+                        const $JE = JE;
+                        try {
+                            eval(expression);
+                        } catch (ex) {
+                            console.log(ex);
+                        }
+                    }
+                }
+            }
+        }
+    };
     JpcCrossTabResult.outputRowTabCommon = function(rptTpl, dataObj, page, bands, tabStr, rowFieldsIdxArr, unitFactor, controls, $CURRENT_RPT, customizeCfg) {
         const me = this;
         const rst = [];

+ 3 - 3
app/reports/rpt_component/jpc_flow_tab.js

@@ -1282,12 +1282,12 @@ JpcFlowTabSrv.prototype.createNew = function() {
         for (let idIdx = eliminateCells.length - 1; idIdx >= 0; idIdx--) {
             rst.splice(eliminateCells[idIdx], 1);
         }
-        me.checkCombineEvent(JV.RUN_TYPE_BEFORE_COMBINE, verticalCombinePos, horizonCombinePos, rst, $CURRENT_RPT);
+        me.checkCombineEvent(JV.RUN_TYPE_BEFORE_COMBINE, verticalCombinePos, horizonCombinePos, rst, $CURRENT_RPT, dataObj);
         me.combinePageCells(rst, verticalCombinePos, horizonCombinePos);
-        me.checkCombineEvent(JV.RUN_TYPE_AFTER_COMBINE, verticalCombinePos, horizonCombinePos, rst, $CURRENT_RPT);
+        me.checkCombineEvent(JV.RUN_TYPE_AFTER_COMBINE, verticalCombinePos, horizonCombinePos, rst, $CURRENT_RPT, dataObj);
         return rst;
     };
-    JpcFlowTabResult.checkCombineEvent = function($RUN_TYPE, $VER_COMB_ARRAY, $HOR_COMB_ARRAY, $CURRENT_CELL_ITEMS, $CURRENT_RPT) {
+    JpcFlowTabResult.checkCombineEvent = function($RUN_TYPE, $VER_COMB_ARRAY, $HOR_COMB_ARRAY, $CURRENT_CELL_ITEMS, $CURRENT_RPT, $CURRENT_DATA) {
         if ($CURRENT_RPT.formulas) {
             for (let execFmlIdx = 0; execFmlIdx < $CURRENT_RPT.formulas.length; execFmlIdx++) {
                 if ($CURRENT_RPT.formulas[execFmlIdx][JV.PROP_RUN_TYPE] === $RUN_TYPE) {

+ 8 - 2
app/router.js

@@ -80,6 +80,10 @@ module.exports = app => {
     // 账号设置
     app.get('/setting/user', sessionAuth, 'settingController.user');
     app.get('/setting/user/permission/set', sessionAuth, 'settingController.userPermissionSet');
+    app.get('/setting/user/unit', sessionAuth, 'settingController.userUnit');
+    app.post('/setting/user/unit/add', sessionAuth, 'settingController.userUnitAdd');
+    app.post('/setting/user/unit/save', sessionAuth, 'settingController.userUnitSave');
+    app.post('/setting/user/unit/upload', sessionAuth, 'settingController.userUnitUpload');
     // 账号停用和启用
     app.post('/setting/user/switch', sessionAuth, 'settingController.userSwitch');
     app.post('/setting/user/add', sessionAuth, datetimeFill, 'settingController.addUser');
@@ -313,6 +317,7 @@ module.exports = app => {
     // 变更概况
     app.get('/tender/:id/measure/stage/:order/change', sessionAuth, tenderCheck, uncheckTenderCheck, stageCheck, 'stageController.change');
     app.post('/tender/:id/measure/stage/:order/change/data', sessionAuth, tenderCheck, uncheckTenderCheck, stageCheck, 'stageController.getChangeData');
+    app.post('/tender/:id/measure/stage/:order/change/iData', sessionAuth, tenderCheck, uncheckTenderCheck, stageCheck, 'stageController.getImportChangeData');
     app.post('/tender/:id/measure/stage/:order/change/detail', sessionAuth, tenderCheck, uncheckTenderCheck, stageCheck, 'stageController.changeDetail');
 
     // 审批
@@ -538,7 +543,8 @@ module.exports = app => {
     app.post('/tender/:id/measure/material/:order/file/find', sessionAuth, tenderCheck, uncheckTenderCheck, materialCheck, 'materialController.getCurMatericalFiles');
     app.post('/tender/:id/measure/material/:order/file/delete', sessionAuth, tenderCheck, uncheckTenderCheck, materialCheck, 'materialController.deleteFile');
     app.post('/tender/:id/measure/material/:order/file/download/compresse-file', sessionAuth, tenderCheck, uncheckTenderCheck, 'materialController.downloadZip');
-
+    // 调差新建期数据获取
+    app.post('/tender/:id/measure/material/gcl/load', sessionAuth, tenderCheck, uncheckTenderCheck, 'materialController.loadGclData');
     // 个人账号相关
     app.get('/profile/info', sessionAuth, 'profileController.info');
     app.get('/profile/sms', sessionAuth, 'profileController.sms');
@@ -629,7 +635,7 @@ module.exports = app => {
     // 总分包
     app.post('/tender/:id/ledger/sumLoad', sessionAuth, tenderCheck, uncheckTenderCheck, 'tenderController.sumLoad');
     app.post('/tender/:id/measure/stage/:order/sumLoad', sessionAuth, tenderCheck, uncheckTenderCheck, stageCheck, 'tenderController.sumLoad');
-    app.post('/tender/:id/revise/info/sumLoad', sessionAuth, tenderCheck, uncheckTenderCheck, reviseCheck, 'tenderController.sumLoad');
+    app.post('/tender/:id/revise/:rid/info/sumLoad', sessionAuth, tenderCheck, uncheckTenderCheck, reviseCheck, 'tenderController.sumLoad');
 
     // 扫码登录
     app.get('/wxAuth', 'loginController.wxAuth');

+ 8 - 4
app/service/advance.js

@@ -123,7 +123,7 @@ module.exports = app => {
             } else {
                 latestOrder.order = latestOrder.order + 1;
             }
-            const record = await this.db.insert(this.tableName, { type, uid, tid, status: auditConst.status.uncheck, order: latestOrder.order, prev_amount: latestOrder.prev_total_amount, prev_total_amount: latestOrder.prev_total_amount });
+            const record = await this.db.insert(this.tableName, { type, uid, tid, status: auditConst.status.uncheck, order: latestOrder.order, prev_amount: latestOrder.prev_total_amount, prev_total_amount: latestOrder.prev_total_amount, pay_time: new Date() });
             const auditors = await ctx.service.advanceAudit.getAuditGroupByList(latestOrder.id, latestOrder.times);
             for (let idx = 0; idx < auditors.length; idx++) {
                 const { audit_id } = auditors[idx];
@@ -196,9 +196,13 @@ module.exports = app => {
          */
         async updateAdvance(payload, id) {
             const { ctx } = this;
-            const prevRecord = await this.getPreviousRecord(ctx.tender.id, ctx.advance.type) || { prev_total_amount: 0 };
-            const { cur_amount } = payload;
-            payload.prev_total_amount = ctx.helper.add(cur_amount, prevRecord.prev_total_amount);
+            if (!payload.pay_time) {
+                const prevRecord = await this.getPreviousRecord(ctx.tender.id, ctx.advance.type) || { prev_total_amount: 0 };
+                const { cur_amount } = payload;
+                payload.prev_total_amount = ctx.helper.add(cur_amount, prevRecord.prev_total_amount);
+            } else {
+                payload.pay_time = new Date(payload.pay_time);
+            }
             return await this.update(payload, {
                 id,
             });

+ 4 - 1
app/service/change.js

@@ -121,7 +121,10 @@ module.exports = app => {
                         change.class = classIndex ? classIndex : changeConst.class.A.value;
                         const qualityIndex = planInfo.quality && this._.indexOf(changeConst.qualityName, planInfo.quality) !== -1 ? this._.indexOf(changeConst.qualityName, planInfo.quality) : 0;
                         change.quality = qualityIndex ? qualityIndex : changeConst.quality.common.value;
-                        change.content = planInfo.reason.replace(/[\r\n]/g, '\r\n') + '\r\n' + planInfo.content.replace(/[\r\n]/g, '\r\n');
+                        let content = planInfo.reason ? planInfo.reason.replace(/[\r\n]/g, '\r\n') : '';
+                        content = content + (planInfo.content ? (planInfo.reason ? '\r\n' : '') + planInfo.content.replace(/[\r\n]/g, '\r\n') : '');
+                        change.content = content ? content : null;
+                        change.expr = planInfo.expr;
                     }
                 }
                 const operate = await this.transaction.insert(this.tableName, change);

+ 117 - 0
app/service/construction_unit.js

@@ -0,0 +1,117 @@
+'use strict';
+
+/**
+ * Created by EllisRan on 2020/3/3.
+ */
+
+const BaseService = require('../base/base_service');
+
+module.exports = app => {
+
+    class ConstructionUnit extends BaseService {
+
+        /**
+         * 构造函数
+         *
+         * @param {Object} ctx - egg全局变量
+         * @return {void}
+         */
+        constructor(ctx) {
+            super(ctx);
+            this.tableName = 'construction_unit';
+            this.dataId = 'id';
+        }
+
+        /**
+         * 数据验证规则
+         *
+         * @param {String} scene - 场景
+         * @return {Object} - 返回数据
+         */
+        rule(scene) {
+            let rule = {};
+            switch (scene) {
+                case 'add':
+                    rule = {
+                        name: { type: 'string', required: true },
+                        type: { type: 'string', required: true },
+                    };
+                    break;
+                default:
+                    break;
+            }
+            return rule;
+        }
+
+        /**
+         * 修改单位数据
+         *
+         * @param {Object} data - post过来的数据
+         * @return {Boolean} - 返回修改结果
+         */
+        async save(data) {
+            if (data._csrf_j !== undefined) {
+                delete data._csrf_j;
+            }
+            const id = data.id !== undefined ? parseInt(data.id) : 0;
+            const transaction = await this.db.beginTransaction();
+            try {
+                if (id > 0) {
+                    // 修改操作时
+                    delete data.create_time;
+                    data.id = id;
+                } else {
+                    // 重名检测
+                    const accountData = await this.db.select(this.tableName, {
+                        where: {
+                            name: data.name,
+                            pid: data.pid,
+                        },
+                    });
+                    if (accountData.length > 0) {
+                        throw '已存在对应的单位名';
+                    }
+                    data.create_time = new Date();
+                }
+                // 并修改用户的用户组及单位名称
+                const updateRow = {};
+                if (data.name) updateRow.company = data.name;
+                if (data.type) updateRow.account_group = data.type;
+                if (!this._.isEmpty(updateRow)) {
+                    const info = id !== 0 ? await this.getDataById(id) : null;
+                    await this.ctx.service.projectAccount.update(updateRow, {
+                        project_id: data.pid,
+                        company: info ? info.name : data.name,
+                    }, transaction);
+                    // egg 自带的update方法无法使用UPDATE zh_project_account SET company = ? WHERE project_id = ? AND company = ? ,未知原因
+                    // await transaction.update(this.ctx.service.projectAccount.tableName, updateRow,
+                    //     {
+                    //         where: {
+                    //             project_id: data.pid,
+                    //             company: info ? info.name : data.name,
+                    //         },
+                    //     }
+                    // );
+                }
+                const operate = id === 0 ? await transaction.insert(this.tableName, data) :
+                    await transaction.update(this.tableName, data);
+                transaction.commit();
+                const result = operate.affectedRows > 0;
+                return result;
+            } catch (e) {
+                transaction.rollback();
+            }
+        }
+
+        async del(id) {
+            // 先删除签章oss地址
+            const info = await this.getDataById(id);
+            if (info.sign_path) {
+                await this.ctx.app.fujianOss.delete(this.ctx.app.config.fujianOssFolder + info.sign_path);
+            }
+            // 删除单位
+            return await this.db.delete(this.tableName, { id });
+        }
+    }
+    return ConstructionUnit;
+};

+ 5 - 7
app/service/deal_bills.js

@@ -222,14 +222,12 @@ module.exports = app => {
                 const nd = { id: this.uuid.v4(), tender_id: this.ctx.tender.id };
                 nd.code = d.code;
                 nd.order = d.order;
-                if (d.name) nd.name = d.name;
-                if (d.unit) nd.unit = d.unit;
-                if (d.unit_price) nd.unit_price = this.ctx.helper.round(d.unit_price, info.decimal.up);
+                nd.name = d.name || '';
+                nd.unit = d.unit || '';
+                nd.unit_price = d.unit_price ? this.ctx.helper.round(d.unit_price, info.decimal.up) : 0;
                 const precision = this.ctx.helper.findPrecision(info.precision, d.unit);
-                if (d.quantity) {
-                    nd.quantity = this.ctx.helper.round(d.quantity, precision.value);
-                    nd.total_price = this.ctx.helper.mul(nd.unit_price, nd.quantity, info.decimal.tp);
-                }
+                nd.quantity = d.quantity ? this.ctx.helper.round(d.quantity, precision.value) : 0;
+                nd.total_price = this.ctx.helper.mul(nd.unit_price, nd.quantity, info.decimal.tp);
                 insertData.push(nd);
             }
             const result = await this.db.insert(this.tableName, insertData);

+ 12 - 0
app/service/ledger.js

@@ -757,6 +757,18 @@ module.exports = app => {
                 throw (err.stack ? '导入工程量数据出错': err);
             }
         }
+
+        async loadDataFromOss(tid, url) {
+            const data = await this.ctx.helper.loadLedgerDataFromOss(url);
+            const curData = await this.getAllDataByCondition({
+                columns: ['id', 'is_tp', 'gxby_status', 'gxby_limit', 'gxby_url', 'dagl_status', 'dagl_limit', 'dagl_url'],
+                where: { tender_id: tid }
+            });
+            this.ctx.helper.assignRelaData(data, [
+                { data: curData, fields: ['is_tp', 'gxby_status', 'gxby_limit', 'gxby_url', 'dagl_status', 'dagl_limit', 'dagl_url'], prefix: '', relaId: 'id' },
+            ]);
+            return data;
+        }
     }
 
     return Ledger;

+ 6 - 1
app/service/material.js

@@ -148,6 +148,7 @@ module.exports = app => {
                 s_order: data.s_order,
                 material_tax: this.ctx.session.sessionProject.page_show.openMaterialTax,
                 decimal: preMaterial && preMaterial.decimal ? preMaterial.decimal : JSON.stringify(materialConst.decimal),
+                is_new: 1,
             };
             const transaction = await this.db.beginTransaction();
             try {
@@ -174,7 +175,10 @@ module.exports = app => {
                     const preNotJoinList = await this.ctx.service.materialListNotjoin.getAllDataByCondition({ where: { tid: this.ctx.tender.id, mid: preMaterial.id } });
                     await this.ctx.service.materialListNotjoin.copyNewStageNotJoinList(transaction, preNotJoinList, newMaterial.id);
                     // 复制调差清单工料关联表
-                    await this.ctx.service.materialList.copyPreMaterialList(transaction, preMaterial, newMaterial);
+                    // await this.ctx.service.materialList.copyPreMaterialList(transaction, preMaterial, newMaterial);
+                    await this.ctx.service.materialList.copyPreMaterialList2(transaction, data.material_list, preNotJoinList, newMaterial);
+                    // 新增或删除list_gcl表
+                    await this.ctx.service.materialListGcl.insertOrDelGcl(transaction, data.insertGclList, data.removeGclList, newMaterial.id);
                     // 修改本期应耗数量值和有效价差,需要剔除不参与调差的清单数据,并返回总金额
                     const [m_tp, m_tax_tp] = await this.ctx.service.materialBills.updateNewMaterial(transaction, this.ctx.tender.id, newMaterial.id, this.ctx, newMaterial.stage_id, JSON.parse(newMaterial.decimal));
                     // 修改现行价格指数,并返回调差基数json
@@ -225,6 +229,7 @@ module.exports = app => {
                 await transaction.delete(this.ctx.service.materialAudit.tableName, { mid: id });
                 await transaction.delete(this.ctx.service.materialBills.tableName, { mid: id });
                 await transaction.delete(this.ctx.service.materialList.tableName, { mid: id });
+                await transaction.delete(this.ctx.service.materialListGcl.tableName, { mid: id });
                 await transaction.delete(this.ctx.service.materialListNotjoin.tableName, { mid: id });
                 await transaction.delete(this.ctx.service.materialBillsHistory.tableName, { mid: id });
                 await transaction.delete(this.ctx.service.materialFile.tableName, { mid: id });

+ 116 - 20
app/service/material_list.js

@@ -292,6 +292,38 @@ module.exports = app => {
             return copyMLArray.length !== 0 ? await transaction.insert(this.tableName, copyMLArray) : true;
         }
 
+        /**
+         * 复制上一期并生成新一期清单工料关联,计算新一期小计值
+         * @param transaction
+         * @param preMaterial
+         * @param newMid
+         * @return {Promise<void>}
+         */
+        async copyPreMaterialList2(transaction, materialListData, notJoinList, newMaterial) {
+            if (materialListData && materialListData.length > 0) {
+                const copyMLArray = [];
+                for (const ml of materialListData) {
+                    const is_join = this._.find(notJoinList, { gcl_id: ml.gcl_id, xmj_id: ml.xmj_id, mx_id: ml.mx_id });
+                    const newMaterialList = {
+                        tid: newMaterial.tid,
+                        order: ml.order,
+                        mid: newMaterial.id,
+                        mb_id: ml.mb_id,
+                        gcl_id: ml.gcl_id,
+                        xmj_id: ml.xmj_id,
+                        mx_id: ml.mx_id,
+                        gather_qty: ml.gather_qty,
+                        quantity: ml.quantity ? ml.quantity : 0,
+                        expr: ml.expr ? ml.expr : '',
+                        is_join: is_join ? 0 : 1,
+                        in_time: new Date(),
+                    };
+                    copyMLArray.push(newMaterialList);
+                }
+                return copyMLArray.length !== 0 ? await transaction.insert(this.tableName, copyMLArray) : true;
+            }
+        }
+
 
         /**
          * 添加工料清单关联(多清单对应)
@@ -304,6 +336,7 @@ module.exports = app => {
             const transaction = await this.db.beginTransaction();
             try {
                 const list = [];
+                const listGcl = [];
                 // const delList = [];
                 // const mb_idList = [];
                 for (const xmj of datas.xmjs) {
@@ -320,31 +353,53 @@ module.exports = app => {
                         //     delList.push(mlInfo.id);
                         //     mb_idList.push(mb);
                         // }
-                        const newLists = {
-                            tid: this.ctx.tender.id,
-                            order: this.ctx.material.order,
-                            mid: this.ctx.material.id,
-                            mb_id: mb,
-                            gcl_id: xmj.gcl_id,
-                            xmj_id: xmj.xmj_id,
-                            mx_id: xmj.mx_id,
-                            gather_qty: xmj.gather_qty,
-                            in_time: new Date(),
-                            is_join: xmj.is_join,
-                        };
-                        list.push(newLists);
+                        if (xmj.gather_qty) {
+                            const newLists = {
+                                tid: this.ctx.tender.id,
+                                order: this.ctx.material.order,
+                                mid: this.ctx.material.id,
+                                mb_id: mb,
+                                gcl_id: xmj.gcl_id,
+                                xmj_id: xmj.xmj_id,
+                                mx_id: xmj.mx_id,
+                                gather_qty: xmj.gather_qty,
+                                in_time: new Date(),
+                                is_join: xmj.is_join,
+                            };
+                            list.push(newLists);
+                        }
+                        if (this._.findIndex(listGcl, { gcl_id: xmj.gcl_id, mb_id: mb }) === -1) {
+                            const newListGcl = {
+                                tid: this.ctx.tender.id,
+                                order: this.ctx.material.order,
+                                mid: this.ctx.material.id,
+                                mb_id: mb,
+                                gcl_id: xmj.gcl_id,
+                                quantity: 0,
+                                expr: '',
+                            };
+                            listGcl.push(newListGcl);
+                        }
                     }
                 }
+                // 维护list_gcl表
+
 
                 // 删除工料清单关联
                 // if (delList.length > 0) await transaction.delete(this.tableName, { id: delList });
                 // 新增工料清单关联
                 if (list.length > 0) {
                     const result = await transaction.insert(this.tableName, list);
-                    if (result.affectedRows === 0) {
+                    if (result.affectedRows === 0 ) {
                         throw '新增工料数据失败';
                     }
                 }
+                if (listGcl.length > 0) {
+                    const result2 = await transaction.insert(this.ctx.service.materialListGcl.tableName, listGcl);
+                    if (result2.affectedRows === 0) {
+                        throw '新增工料关联数据失败';
+                    }
+                }
                 if (checklist) {
                     await this.ctx.service.materialChecklist.updateHadBills(transaction, checklist.id, checklist.had_bills);
                 }
@@ -356,7 +411,7 @@ module.exports = app => {
                 //     }
                 // }
                 await transaction.commit();
-                return await this.getMaterialData(this.ctx.tender.id, this.ctx.material.id);
+                return checklist ? await this.ctx.service.materialListGcl.getAllDataByCondition({ where: { tid: this.ctx.tender.id } }) : await this.getMaterialData(this.ctx.tender.id, this.ctx.material.id);
             } catch (err) {
                 await transaction.rollback();
                 throw err;
@@ -375,8 +430,13 @@ module.exports = app => {
             const transaction = await this.db.beginTransaction();
             try {
                 // 判断是否可删
+                const listGcl = [];
                 for (const xmj of datas.xmjs) {
                     await transaction.delete(this.tableName, { tid: this.ctx.tender.id, mid: this.ctx.material.id, mb_id: datas.mb_id, gcl_id: xmj.gcl_id, xmj_id: xmj.xmj_id, mx_id: xmj.mx_id });
+                    if (this._.indexOf(listGcl, xmj.gcl_id) === -1) {
+                        await transaction.delete(this.service.materialListGcl.tableName, { tid: this.ctx.tender.id, mid: this.ctx.material.id, mb_id: datas.mb_id, gcl_id: xmj.gcl_id });
+                        listGcl.push(xmj.gcl_id);
+                    }
                 }
                 // await transaction.delete(this.tableName, { id });
                 await this.calcQuantityByML(transaction, datas.mb_id);
@@ -385,7 +445,7 @@ module.exports = app => {
                 }
                 await transaction.commit();
                 // console.log(datas);
-                return await this.getMaterialData(this.ctx.tender.id, this.ctx.material.id);
+                return checklist ? await this.ctx.service.materialListGcl.getAllDataByCondition({ where: { tid: this.ctx.tender.id } }) : await this.getMaterialData(this.ctx.tender.id, this.ctx.material.id);
             } catch (err) {
                 await transaction.rollback();
                 throw err;
@@ -398,7 +458,7 @@ module.exports = app => {
          * @param {int} order 期数
          * @return {void}
          */
-        async saves(datas) {
+        async saves(datas, checklist = false) {
             if (!this.ctx.tender || !this.ctx.material) {
                 throw '数据错误';
             }
@@ -407,6 +467,8 @@ module.exports = app => {
             try {
                 const mb_id = datas.mb_id;
                 const updateDatas = [];
+                const updateListGcl = [];
+                const listGcl = [];
                 for (const xmj of datas.xmjs) {
                     const udata = {
                         row: {
@@ -423,11 +485,27 @@ module.exports = app => {
                         },
                     };
                     updateDatas.push(udata);
+                    if (this._.indexOf(listGcl, xmj.gcl_id) === -1) {
+                        listGcl.push(xmj.gcl_id);
+                        updateListGcl.push({
+                            row: {
+                                expr: datas.expr,
+                                quantity: datas.quantity,
+                            },
+                            where: {
+                                tid: this.ctx.tender.id,
+                                mid: this.ctx.material.id,
+                                mb_id,
+                                gcl_id: xmj.gcl_id,
+                            },
+                        });
+                    }
                 }
                 if (updateDatas.length > 0) await transaction.updateRows(this.tableName, updateDatas);
+                if (updateListGcl.length > 0) await transaction.updateRows(this.service.materialListGcl.tableName, updateListGcl);
                 await this.calcQuantityByML(transaction, mb_id);
                 await transaction.commit();
-                return await this.getMaterialData(this.ctx.tender.id, this.ctx.material.id);
+                return checklist ? await this.ctx.service.materialListGcl.getAllDataByCondition({ where: { tid: this.ctx.tender.id } }) : await this.getMaterialData(this.ctx.tender.id, this.ctx.material.id);
             } catch (err) {
                 await transaction.rollback();
                 throw err;
@@ -439,7 +517,7 @@ module.exports = app => {
          * @param {Object} data 工料内容
          * @return {void}
          */
-        async savePastes(datas) {
+        async savePastes(datas, checklist = false) {
             if (!this.ctx.tender || !this.ctx.material) {
                 throw '数据错误';
             }
@@ -450,6 +528,8 @@ module.exports = app => {
 
                 for (const data of datas.pasteData) {
                     const updateDatas = [];
+                    const updateListGcl = [];
+                    const listGcl = [];
                     for (const xmj of datas.xmjs) {
                         const udata = {
                             row: {
@@ -466,12 +546,28 @@ module.exports = app => {
                             },
                         };
                         updateDatas.push(udata);
+                        if (this._.indexOf(listGcl, xmj.gcl_id) === -1) {
+                            listGcl.push(xmj.gcl_id);
+                            updateListGcl.push({
+                                row: {
+                                    expr: data.expr,
+                                    quantity: data.quantity,
+                                },
+                                where: {
+                                    tid: this.ctx.tender.id,
+                                    mid: this.ctx.material.id,
+                                    mb_id: data.mb_id,
+                                    gcl_id: xmj.gcl_id,
+                                },
+                            });
+                        }
                     }
                     if (updateDatas.length > 0) await transaction.updateRows(this.tableName, updateDatas);
+                    if (updateListGcl.length > 0) await transaction.updateRows(this.service.materialListGcl.tableName, updateListGcl);
                     await this.calcQuantityByML(transaction, data.mb_id);
                 }
                 await transaction.commit();
-                return await this.getMaterialData(this.ctx.tender.id, this.ctx.material.id);
+                return checklist ? await this.ctx.service.materialListGcl.getAllDataByCondition({ where: { tid: this.ctx.tender.id } }) : await this.getMaterialData(this.ctx.tender.id, this.ctx.material.id);
             } catch (err) {
                 await transaction.rollback();
                 throw err;

+ 68 - 0
app/service/material_list_gcl.js

@@ -0,0 +1,68 @@
+'use strict';
+/**
+ * 清单设置 数据模型
+ * @author LanJianRong
+ * @date 2020/6/30
+ * @version
+ */
+
+module.exports = app => {
+    class MaterialListGcl extends app.BaseService {
+        /**
+         * 构造函数
+         *
+         * @param {Object} ctx - egg全局变量
+         * @return {void}
+         */
+        constructor(ctx) {
+            super(ctx);
+            this.tableName = 'material_list_gcl';
+        }
+
+        async setData(mid, data) {
+            if (!this.ctx.tender) {
+                throw '数据错误';
+            }
+            const transaction = await this.db.beginTransaction();
+            try {
+                const insertArray = [];
+                for (const d of data) {
+                    insertArray.push({
+                        tid: this.ctx.tender.id,
+                        mid,
+                        order: d.order,
+                        gcl_id: d.gcl_id,
+                        mb_id: d.mb_id,
+                        quantity: d.quantity,
+                        expr: d.expr,
+                    });
+                }
+                if (insertArray.length > 0) await transaction.insert(this.tableName, insertArray);
+                await transaction.update(this.ctx.service.material.tableName, { id: mid, is_new: 1 });
+                await transaction.commit();
+                return true;
+            } catch (err) {
+                console.log(err);
+                await transaction.rollback();
+                throw err;
+            }
+        }
+
+        async insertOrDelGcl(transaction, insertGclList, removeGclList, mid) {
+            if (insertGclList && insertGclList.length > 0) {
+                for (const gcl of insertGclList) {
+                    gcl.tid = this.ctx.tender.id;
+                    gcl.mid = mid;
+                }
+                await transaction.insert(this.tableName, insertGclList);
+            }
+            if (removeGclList && removeGclList.length > 0) {
+                for (const gcl of removeGclList) {
+                    await transaction.delete(this.tableName, { id: gcl.id });
+                }
+            }
+        }
+    }
+    return MaterialListGcl;
+};
+

+ 61 - 0
app/service/pos.js

@@ -343,6 +343,53 @@ module.exports = app => {
             }
         }
 
+        async _insertPosData(tid, data) {
+            if (!data.length) return;
+
+            let porder = data[0].porder, lid = data[0].lid;
+            for (const d of data) {
+                this._completeInsertPosData(tid, d);
+                porder = Math.min(porder, d.porder);
+            }
+
+            const transaction = await this.db.beginTransaction();
+            try {
+                this.initSqlBuilder();
+                this.sqlBuilder.setAndWhere('lid', {
+                    value: this.db.escape(lid),
+                    operate: '=',
+                });
+                this.sqlBuilder.setAndWhere('porder', {
+                    value: porder,
+                    operate: '>=',
+                });
+                this.sqlBuilder.setUpdateData('porder', {
+                    value: data.length,
+                    selfOperate: '+',
+                });
+                const [sql, sqlParam] = this.sqlBuilder.build(this.tableName, 'update');
+                const result = await transaction.query(sql, sqlParam);
+                await transaction.insert(this.tableName, data);
+                await transaction.commit();
+            } catch (err) {
+                await transaction.rollback();
+                throw err;
+            }
+
+            this.initSqlBuilder();
+            this.sqlBuilder.setAndWhere('lid', {
+                value: this.db.escape(lid),
+                operate: '=',
+            });
+            this.sqlBuilder.setAndWhere('porder', {
+                value: porder,
+                operate: '>=',
+            });
+            const [sql, sqlParam] = this.sqlBuilder.build(this.tableName);
+            const update = await this.db.query(sql, sqlParam);
+            return { pos: update }
+        }
+
         /**
          * 保存部位明细数据
          * @param data
@@ -361,6 +408,8 @@ module.exports = app => {
                     }
                 case 'delete':
                     return await this._deletePosData(tid, data.updateData);
+                case 'insert':
+                    return await this._insertPosData(tid, data.updateData);
             }
         }
 
@@ -454,6 +503,18 @@ module.exports = app => {
         async deletePosData(transaction, tid, lid) {
             await transaction.delete(this.tableName, {tid: tid, lid: lid});
         }
+
+        async loadDataFromOss(tid, url) {
+            const data = await this.ctx.helper.loadLedgerDataFromOss(url);
+            const curData = await this.getAllDataByCondition({
+                columns: ['id', 'gxby_status', 'gxby_limit', 'gxby_url', 'dagl_status', 'dagl_limit', 'dagl_url'],
+                where: { tid: tid }
+            });
+            this.ctx.helper.assignRelaData(data, [
+                { data: curData, fields: ['gxby_status', 'gxby_limit', 'gxby_url', 'dagl_status', 'dagl_limit', 'dagl_url'], prefix: '', relaId: 'id' },
+            ]);
+            return data;
+        }
     }
 
     return Pos;

+ 9 - 2
app/service/project_account.js

@@ -758,9 +758,8 @@ module.exports = app => {
          * @param {Object} data - 筛选表单中的get数据
          * @return {void}
          */
-        searchFilter(data, projectId) {
+        searchFilter(data, projectId, columns = ['id', 'account', 'name', 'company', 'role', 'mobile', 'auth_mobile', 'telephone', 'enable', 'is_admin', 'account_group', 'bind']) {
             this.initSqlBuilder();
-            const columns = ['id', 'account', 'name', 'company', 'role', 'mobile', 'auth_mobile', 'telephone', 'enable', 'is_admin', 'account_group', 'bind'];
             this.sqlBuilder.columns = columns;
 
             this.sqlBuilder.setAndWhere('project_id', {
@@ -768,6 +767,14 @@ module.exports = app => {
                 operate: '=',
             });
 
+            // 单位名称筛选
+            if (data.company !== undefined && data.company !== '') {
+                this.sqlBuilder.setAndWhere('company', {
+                    value: this.db.escape(data.company),
+                    operate: '=',
+                });
+            }
+
             // 名字筛选
             if (data.keyword !== undefined && data.keyword !== '') {
                 this.sqlBuilder.setNewOrWhere([{

+ 6 - 0
app/service/report.js

@@ -269,6 +269,12 @@ module.exports = app => {
                     case 'mem_union_data':
                         rst[filter] = [];
                         break;
+                    case 'mem_import_change':
+                        rst[filter] = await service.reportMemory.getImportChangeData(params.tender_id, params.stage_id, memFieldKeys[filter]);
+                        break;
+                    case 'mem_import_change_bills':
+                        rst[filter] = await service.reportMemory.getImportChangeBillsData(params.tender_id, params.stage_id, memFieldKeys[filter]);
+                        break;
                     case 'mem_change':
                         rst[filter] = await service.reportMemory.getChangeData(params.tender_id, params.stage_id, memFieldKeys[filter]);
                         break;

+ 63 - 0
app/service/report_memory.js

@@ -840,6 +840,68 @@ module.exports = app => {
             }
         }
 
+        async _generateImportChange(sid) {
+            if (this.importChange !== null) return;
+            const self = this;
+            try {
+                const ctx = this.ctx;
+                const change = await this.ctx.service.stageImportChange.getImportChange(sid);
+                for (const c of change) {
+                    const types = ctx.helper._.map(c.type.split(','), function (t) {
+                        return self._getChangeConstName(changeConst.type, ctx.helper._.toInteger(t));
+                    });
+                    c.type = types.join(';');
+                    c.class = this._getChangeConstName(changeConst.class, c.class);
+                    c.quality = this._getChangeConstName(changeConst.quality, c.quality);
+                    c.charge = this._getChangeConstName(changeConst.charge, c.charge);
+                }
+
+                change.sort(function (a, b) {
+                    return a.code.localeCompare(b.code);
+                });
+                const changeBills = [];
+                for (const c of change) {
+                    const bills = await this.ctx.service.changeAuditList.getAllDataByCondition({ where: { cid: c.cid } });
+                    for (const b of bills) {
+                        b.o_qty = b.oamount;
+                        b.c_qty = b.camount;
+                        b.s_qty = b.samount ? parseFloat(b.samount) : 0;
+                        b.sp_qty = b.spamount;
+                    }
+
+                    bills.sort(function (a, b) {
+                        return ctx.helper.compareCode(a.code, b.code);
+                    });
+                    changeBills.push(...bills);
+                }
+                this.importChange = {change: change, bills: changeBills};
+            } catch(err) {
+                this.ctx.helper.log(err);
+                throw err;
+                this.importChange = {change: [], bills: []};
+            }
+        }
+
+        async getImportChangeData(tid, sid, fields) {
+            try {
+                await this._generateImportChange(sid);
+                return this.importChange.change;
+            } catch (err) {
+                return [];
+            }
+        }
+
+        async getImportChangeBillsData(tid, sid, fields) {
+            try {
+                await this.ctx.service.tender.checkTender(tid);
+
+                await this._generateImportChange(tid);
+                return this.importChange.bills;
+            } catch (err) {
+                return [];
+            }
+        }
+
         async _generateChangeApply(tid) {
             if (!!this.changeApplyData) return;
             const helper = this.ctx.helper;
@@ -1380,6 +1442,7 @@ module.exports = app => {
             for (const c of this.changeInfo) {
                 c.cur_used = usedChangesId.indexOf(c.cid) >= 0;
                 c.used_ratio = c.used_pt;
+                c.is_import = 0;
             }
             return this.changeInfo;
         }

+ 47 - 0
app/service/revise_pos.js

@@ -114,6 +114,53 @@ module.exports = app => {
             return {pos: data};
         }
 
+        async insertPos(tid, rid, data) {
+            if (!data.length) return;
+
+            let porder = data[0].porder, lid = data[0].lid;
+            for (const d of data) {
+                this._completeInsertPosData(tid, rid, d);
+                porder = Math.min(porder, d.porder);
+            }
+
+            const transaction = await this.db.beginTransaction();
+            try {
+                this.initSqlBuilder();
+                this.sqlBuilder.setAndWhere('lid', {
+                    value: this.db.escape(lid),
+                    operate: '=',
+                });
+                this.sqlBuilder.setAndWhere('porder', {
+                    value: porder,
+                    operate: '>=',
+                });
+                this.sqlBuilder.setUpdateData('porder', {
+                    value: data.length,
+                    selfOperate: '+',
+                });
+                const [sql, sqlParam] = this.sqlBuilder.build(this.tableName, 'update');
+                const result = await transaction.query(sql, sqlParam);
+                await transaction.insert(this.tableName, data);
+                await transaction.commit();
+            } catch (err) {
+                await transaction.rollback();
+                throw err;
+            }
+
+            this.initSqlBuilder();
+            this.sqlBuilder.setAndWhere('lid', {
+                value: this.db.escape(lid),
+                operate: '=',
+            });
+            this.sqlBuilder.setAndWhere('porder', {
+                value: porder,
+                operate: '>=',
+            });
+            const [sql, sqlParam] = this.sqlBuilder.build(this.tableName);
+            const update = await this.db.query(sql, sqlParam);
+            return { pos: update }
+        }
+
         async updatePos(tid, data) {
             if (data.sgfh_qty !== undefined || data.sjcl_qty !== undefined || data.qtcl_qty !== undefined) {
                 const op = await this.getDataById(data.id);

+ 14 - 2
app/service/stage.js

@@ -226,6 +226,7 @@ module.exports = app => {
                     const tpData = await this.ctx.service.stageBills.getSumTotalPrice(stage);
                     stage.contract_tp = tpData.contract_tp;
                     stage.qc_tp = tpData.qc_tp;
+                    stage.tp =this.ctx.helper.add(stage.contract_tp, stage.qc_tp);
                     const tp = await this.ctx.service.stagePay.getSpecialTotalPrice(stage);
                     stage.yf_tp = tp.yf;
                     stage.sf_tp = tp.sf;
@@ -440,6 +441,16 @@ module.exports = app => {
             }
         }
 
+        async getChangeSubtotal(stage) {
+            const result = {};
+            const bg = await this.ctx.service.stageChange.getSubtotal(stage);
+            const importBg = await this.ctx.service.stageImportChange.getSubtotal(stage);
+            result.common = this.ctx.helper.add(bg.common, importBg.common);
+            result.great = this.ctx.helper.add(bg.great, importBg.great);
+            result.more = this.ctx.helper.add(bg.more, importBg.more);
+            return result;
+        }
+
         /**
          * 获取 当期的 计算基数
          * @return {Promise<any>}
@@ -447,9 +458,9 @@ module.exports = app => {
         async getStagePayCalcBase(stage, tenderInfo) {
             const calcBase = JSON.parse(JSON.stringify(payConst.calcBase));
             const param = tenderInfo.deal_param;
+            const sum = await this.ctx.service.stageBills.getSumTotalPrice(stage);
+            const bg = await this.getChangeSubtotal(stage);
             for (const cb of calcBase) {
-                const sum = await this.ctx.service.stageBills.getSumTotalPrice(stage);
-                const bg = await this.ctx.service.stageChange.getQualityTotalPrice(stage);
                 switch (cb.code) {
                     case 'htj':
                         cb.value = param.contractPrice;
@@ -533,6 +544,7 @@ module.exports = app => {
                 await transaction.delete(this.ctx.service.stageAudit.tableName, { sid: id });
                 await transaction.delete(this.ctx.service.stageBills.tableName, { sid: id });
                 await transaction.delete(this.ctx.service.stageChange.tableName, { sid: id });
+                await transaction.delete(this.ctx.service.stageImportChange.tableName, { sid: id });
                 await transaction.delete(this.ctx.service.stagePos.tableName, { sid: id });
                 await transaction.delete(this.ctx.service.stageDetail.tableName, { sid: id });
                 await transaction.delete(this.ctx.service.stagePosFinal.tableName, { sid: id });

+ 7 - 2
app/service/stage_bills.js

@@ -460,8 +460,6 @@ module.exports = app => {
                 for (const u of result.update) {
                     const sb = stageBills.find(x => { return x.lid === u.lid; });
                     if (!sb || sb.times !== this.ctx.stage.curTimes || sb.order !== this.ctx.stage.curOrder) {
-                        u.qc_qty = sb ? sb.qc_qty : null;
-                        u.qc_tp = sb ? sb.qc_tp : null;
                         u.postil = sb ? sb.postil : null;
                         u.tid = this.ctx.tender.id;
                         u.sid = this.ctx.stage.id;
@@ -474,10 +472,17 @@ module.exports = app => {
                         updateStageBills.push(u);
                     }
                 }
+                for (const cd of result.qc_detail) {
+                    cd.tid = this.ctx.tender.id;
+                    cd.sid = this.ctx.stage.id;
+                    cd.import_lid = lid;
+                }
 
                 const his = await this.ctx.service.sumLoadHistory.saveStageHistory(this.ctx.tender.id, this.ctx.stage.id, lid, tenders, result.errors, cover);
                 if (updateStageBills.length > 0) await conn.updateRows(this.tableName, updateStageBills);
                 if (insertStageBills.length > 0) await conn.insert(this.tableName, insertStageBills);
+                await conn.delete(this.ctx.service.stageImportChange.tableName, { import_lid: lid, sid: this.ctx.stage.id });
+                if (result.qc_detail.length > 0) await conn.insert(this.ctx.service.stageImportChange.tableName, result.qc_detail);
                 await conn.commit();
                 return { curStageData: result.update, sumLoadHis: his };
             } catch (err) {

+ 18 - 6
app/service/stage_change.js

@@ -308,7 +308,10 @@ module.exports = app => {
         }
 
         async getFinalUsedData(tid, cid) {
-            const stage = this.ctx.service.stage.getLastestStage(tid, true);
+            const stage = await this.ctx.service.stage.getLastestStage(tid, true);
+            if (!stage) { // 防止未创建期时调用
+                return [];
+            }
             if (stage.status === audit.stage.status.checked) {
                 const sql = 'SELECT scf.* ' +
                     '  FROM ' + this.ctx.service.stageChangeFinal.tableName + ' scf ' +
@@ -322,8 +325,8 @@ module.exports = app => {
                     '  LEFT JOIN ' + this.ctx.service.stage.tableName + ' s ON scf.sid = s.id' +
                     '  WHERE scf.tid = ? And scf.cid = ? And s.order < ?';
                 const pre = await this.db.query(preSql, [tid, cid, stage.order]);
-                const sql = 'SELECT * FROM ' + this.tableName + ' WHERE sid = ?';
-                const curAll = await this.db.query(sql, [stage.id]);
+                const sql = 'SELECT * FROM ' + this.tableName + ' WHERE sid = ? and cid = ?';
+                const curAll = await this.db.query(sql, [stage.id, cid]);
                 const cur = this.ctx.helper.filterLastestData(curAll, ['lid', 'pid', 'cid', 'cbid'], 'stimes', 'sorder');
                 return [...pre, ...cur];
             }
@@ -360,7 +363,7 @@ module.exports = app => {
          * @return {Promise<void>}
          */
         async getStageUsedChangeId(sid) {
-            const sql = 'SELECT cid, qty FROM ' + this.tableName + ' WHERE sid = ?';
+            const sql = 'SELECT lid, pid, cid, cbid, qty, stimes, sorder FROM ' + this.tableName + ' WHERE sid = ?';
             const curAll = await this.db.query(sql, [sid]);
             const cur = this.ctx.helper.filterLastestData(curAll, ['lid', 'pid', 'cid', 'cbid'], 'stimes', 'sorder');
             return this._.map(this._.filter(cur, 'qty'), 'cid');
@@ -371,6 +374,15 @@ module.exports = app => {
             return this.ctx.helper.filterLastestData(data, ['lid', 'pid', 'cid', 'cbid'], 'stimes', 'sorder');
         }
 
+        async getSumLoadFinalData(sid) {
+            const sql = 'Select cf.tid, cf.sid, cf.lid, cf.pid, cf.cid, cf.cbid, cf.qty, cf.stimes, cf.sorder, c.code As c_code' +
+                '  FROM ' + this.tableName + ' cf' +
+                '  Left Join ' + this.ctx.service.change.tableName + ' c ON cf.cid = c.cid' +
+                '  Where cf.sid = ?';
+            const result = await this.db.query(sql, [sid]);
+            return this.ctx.helper.filterLastestData(result, ['lid', 'pid', 'cbid'], 'stimes', 'sorder');
+        }
+
         async _getTender(stage) {
             if (this.ctx.tender) return this.ctx.tender;
             const tender = { id: stage.tid };
@@ -379,12 +391,12 @@ module.exports = app => {
             return tender;
         }
 
-        async getQualityTotalPrice(stage) {
+        async getSubtotal(stage) {
             const helper = this.ctx.helper;
             const tender = await this._getTender(stage);
             const sql = 'SELECT sc.*, c.quality FROM ' + this.tableName + ' sc' +
                 '  LEFT JOIN ' + this.ctx.service.change.tableName + ' c ON sc.cid = c.cid' +
-                '  WHERE sid = ?';
+                '  WHERE sid = ? ' + (stage.readOnly ? ` and (stimes < ${stage.curTimes} or (stimes = ${stage.curTimes} and sorder <= ${stage.curOrder}))` : '');
             let data = await this.db.query(sql, [stage.id]);
             data = helper.filterLastestData(data, ['lid', 'pid', 'cbid'], 'stimes', 'sorder');
             const bqData = [];

+ 2 - 3
app/service/stage_change_final.js

@@ -84,11 +84,10 @@ module.exports = app => {
         }
 
         async getSumLoadFinalData(sid) {
-            const sql = 'Select cf.lid, cf.cid, sum(cf.qty) as qty, c.code As c_code' +
+            const sql = 'Select cf.tid, cf.sid, cf.lid, cf.cid, cf.cbid, cf.qty, c.code As c_code' +
                 '  FROM ' + this.tableName + ' cf' +
                 '  Left Join ' + this.ctx.service.change.tableName + ' c ON cf.cid = c.cid' +
-                '  Where cf.sid = ?' +
-                '  Group By cf.lid, cf.cid';
+                '  Where cf.sid = ?';
             return await this.db.query(sql, [sid]);
         }
     }

+ 220 - 0
app/service/stage_import_change.js

@@ -0,0 +1,220 @@
+'use strict';
+
+/**
+ * 期 - 引入 变更数据
+ *
+ * @author Mai
+ * @date
+ * @version
+ */
+
+const changeConst = require('../const/change');
+
+module.exports = app => {
+
+    class StageImportChange extends app.BaseService {
+
+        /**
+         * 构造函数
+         *
+         * @param {Object} ctx - egg全局变量
+         * @return {void}
+         */
+        constructor(ctx) {
+            super(ctx);
+            this.tableName = 'stage_import_change';
+        }
+
+        async getImportLid(sid) {
+            const sql = 'Select lid, 1 as is_import From ' + this.tableName + ' Where sid = ? Group By lid';
+            return await this.db.query(sql, [sid]);
+        }
+
+        /**
+         * 获取最底层项目节(importLid)下导入过变更数据的台账id
+         * @param sid
+         * @param importLid
+         * @returns {Promise<void>}
+         */
+        async getLeafXmjImportLid(sid, importLid) {
+            const sql = 'Select lid, 1 as is_import From ' + this.tableName + ' Where sid = ? and import_lid = ? Group By lid';
+            return await this.db.query(sql, [sid, importLid]);
+        }
+
+        async getImportChange(sid) {
+            const sql =
+                `SELECT c.*, t.name as t_name FROM ${this.ctx.service.change.tableName} c` +
+                `  LEFT JOIN ${this.ctx.service.tender.tableName} t ON c.tid = t.id` +
+                `  WHERE c.cid IN ( SELECT rela_cid FROM ${this.tableName} WHERE sid = ? GROUP BY rela_cid)` +
+                '  Order By c.tid, c.sin_time';
+            return await this.db.query(sql, [sid]);
+        }
+
+        async getStageImportData(stage) {
+            const sql = 'SELECT scf.*, ' +
+                '    oc.p_code As c_code, oc.new_code As c_new_code, oc.quality, ocb.code as b_code, ocb.name, ocb.unit' +
+                '  FROM ' + this.tableName + ' scf ' +
+                '  LEFT JOIN ' + this.ctx.service.change.tableName + ' oc ON scf.rela_cid = oc.cid ' +
+                '  LEFT JOIN ' + this.ctx.service.changeAuditList.tableName + ' ocb ON scf.rela_cbid = ocb.id ' +
+                '  WHERE scf.sid = ?';
+            return await this.db.query(sql, [stage.id]);
+        }
+
+        async getEndStageImportData(stage) {
+            const sql = 'SELECT scf.*, ' +
+                '    oc.p_code As c_code, oc.new_code As c_new_code, oc.quality, ocb.code as b_code, ocb.name, ocb.unit' +
+                '  FROM ' + this.tableName + ' scf ' +
+                '  LEFT JOIN ' + this.ctx.service.stage.tableName + ' s ON scf.sid = s.id' +
+                '  LEFT JOIN ' + this.ctx.service.change.tableName + ' oc ON scf.rela_cid = oc.cid ' +
+                '  LEFT JOIN ' + this.ctx.service.changeAuditList.tableName + ' ocb ON scf.rela_cbid = ocb.id ' +
+                '  WHERE scf.tid = ? And s.order <= ?';
+            return await this.db.query(sql, [stage.tid, stage.order]);
+        }
+
+        async getLeafXmjStageImportData(stage, lid) {
+            const sql = 'SELECT scf.*, ' +
+                '    oc.p_code As c_code, oc.new_code As c_new_code, oc.quality, ocb.code as b_code, ocb.name, ocb.unit' +
+                '  FROM ' + this.tableName + ' scf ' +
+                '  LEFT JOIN ' + this.ctx.service.change.tableName + ' oc ON scf.rela_cid = oc.cid ' +
+                '  LEFT JOIN ' + this.ctx.service.changeAuditList.tableName + ' ocb ON scf.rela_cbid = ocb.id ' +
+                '  WHERE scf.sid = ? and import_lid = ?';
+            return await this.db.query(sql, [stage.id, lid]);
+        }
+
+        async getLeafXmjImportChange(sid, importLid) {
+            const sql =
+                `SELECT c.*, t.name as t_name FROM ${this.ctx.service.change.tableName} c` +
+                `  LEFT JOIN ${this.ctx.service.tender.tableName} t ON c.tid = t.id` +
+                `  WHERE c.cid IN ( SELECT rela_cid FROM ${this.tableName} WHERE sid = ? and import_lid = ? GROUP BY rela_cid)` +
+                '  Order By c.tid, c.sin_time';
+            return await this.db.query(sql, [sid, importLid]);
+        }
+
+        async getChangeWithUsedInfo(stage, importLid) {
+            const change = importLid
+                ? await this.getLeafXmjImportChange(stage.id, importLid)
+                : await this.getImportChange(stage.id);
+            if (change.length === 0) return [];
+
+            // 引入变更,不计算已执行等????
+            // const changeBills = await this.ctx.service.changeAuditList.getAllDataByCondition({
+            //     where: { cid: change.map(x => { return x.cid; }) }
+            // });
+            // const changeBillsIndex = {}, changeBillsPart = {};
+            // for (const cb of changeBills) {
+            //     changeBillsIndex[cb.id] = cb;
+            //     if (!changeBillsPart[cb.cid]) changeBillsPart[cb.cid] = [];
+            //     changeBillsPart[cb.cid].push(cb);
+            // }
+            // const stageChangeBills = await this.getEndStageImportChange(stage);
+            // for (const scb of stageChangeBills) {
+            //     if (!scb.rela_qty) continue;
+            //
+            //     const cb = changeBillsIndex[scb.rela_cbid];
+            //     if (cb) cb.used_qty = this.ctx.helper.add(cb.used_qty, scb.rela_qty);
+            // }
+            // for (const cid in changeBillsPart) {
+            //     const c = change.find(x => { return x.cid === cid });
+            //     if (!c) continue;
+            //
+            //     for (const cb of changeBillsPart[cid]) {
+            //         cb.tp = this.ctx.helper.mul(cb.spamount, cb.unit_price, c.tp_decimal || this.ctx.tender.info.decimal.tp);
+            //         cb.used_tp = this.ctx.helper.mul(cb.used_qty, cb.unit_price, this.ctx.tender.info.decimal.tp);
+            //
+            //         c.used_tp = this.ctx.helper.add(c.used_tp, cb.used_tp);
+            //         if (cb.spamount > 0) {
+            //             c.p_tp = this.ctx.helper.add(c.p_tp, cb.tp);
+            //             c.p_used_tp = this.ctx.helper.add(c.p_used_tp, cb.used_tp);
+            //         } else if (cb.spamount < 0){
+            //             c.n_tp = this.ctx.helper.add(c.n_tp, cb.tp);
+            //             c.n_used_tp = this.ctx.helper.add(c.n_used_tp, cb.used_tp);
+            //         }
+            //     }
+            //     c.used_pt = c.total_price ? this.ctx.helper.mul(this.ctx.helper.div(c.used_tp, c.total_price, 4), 100) : 0;
+            //     c.p_used_pt = c.p_tp ? this.ctx.helper.mul(this.ctx.helper.div(c.p_used_tp, c.p_tp, 4), 100) : 0;
+            //     c.n_used_pt = c.n_tp ? this.ctx.helper.mul(this.ctx.helper.div(c.n_used_tp, c.n_tp, 4), 100) : 0;
+            // }
+            return change;
+        }
+
+        /**
+         * 获取 变更令 - 变更清单 使用情况
+         * @param {Number} sid - 查询期id
+         * @param {uuid} cid - 变更令id
+         * @return {Promise<void>}
+         */
+        async getChangeUsedData(sid, cid) {
+            return await this.getAllDataByCondition({ where: { sid, rela_cid: cid } });
+        }
+
+        /**
+         * 获取 变更令 - 变更清单 使用情况
+         * @param {Number} sid - 查询期id
+         * @param {uuid} cid - 变更令id
+         * @return {Promise<void>}
+         */
+        async getChangeEndUsedData(tid, cid) {
+            const sql = 'SELECT scf.* ' +
+                '  FROM ' + this.tableName + ' scf ' +
+                '  LEFT JOIN ' + this.ctx.service.stage.tableName + ' s ON scf.sid = s.id' +
+                '  WHERE scf.tid = ? And scf.rela_cid = ? And s.order <= ?';
+            const result = await this.db.query(sql, [tid, cid, this.ctx.stage.order]);
+            return result;
+        }
+
+        async getChangeFinalUsedData(tid, cid) {
+            const stage = this.ctx.service.stage.getLastestStage(tid, true);
+            const sql = 'SELECT scf.* ' +
+                '  FROM ' + this.tableName + ' scf ' +
+                '  LEFT JOIN ' + this.ctx.service.stage.tableName + ' s ON scf.sid = s.id' +
+                '  WHERE scf.tid = ? And scf.rela_cid = ? And s.order <= ?';
+            const result = await this.db.query(sql, [tid, cid, stage.order]);
+            return result;
+        }
+
+        async getStageUsedChangeId(sid) {
+            const sql = 'SELECT rela_cid FROM ' + this.tableName + ' WHERE sid = ? Group By rela_cid';
+            const cur = await this.db.query(sql, [sid]);
+            return this._.map(cur, 'rela_cid');
+        }
+
+        async _getTender(stage) {
+            if (this.ctx.tender) return this.ctx.tender;
+            const tender = { id: stage.tid };
+            tender.data = await this.ctx.service.tender.getTender(stage.tid);
+            tender.info = await this.service.tenderInfo.getTenderInfo(tender.id);
+            return tender;
+        }
+
+        // 分类汇总变更金额
+        async getSubtotal(stage) {
+            const helper = this.ctx.helper;
+            const tender = await this._getTender(stage);
+            const sql = 'SELECT sc.*, c.quality, c.name FROM ' + this.tableName + ' sc' +
+                '  LEFT JOIN ' + this.ctx.service.change.tableName + ' c ON sc.rela_cid = c.cid' +
+                '  WHERE sid = ?';
+            const data = await this.db.query(sql, [stage.id]);
+            const bqData = [];
+            for (const d of data) {
+                if (!d.rela_qty) continue;
+                let bd = bqData.find(x => { return x.lid === d.lid && x.quality === d.quality; });
+                if (!bd) {
+                    const bills = await this.db.get(this.ctx.service.ledger.departTableName(tender.id), { id: d.lid });
+                    if (!bills) continue;
+                    bd = { lid: d.lid, quality: d.quality, unit_price: bills.unit_price, qty: d.rela_qty, cid: d.rela_cid, name: d.name };
+                    bqData.push(bd);
+                }
+                const tp = this.ctx.helper.mul(d.rela_qty, bd.unit_price, tender.info.decimal.tp);
+                bd.tp = this.ctx.helper.add(bd.tp, tp);
+            }
+            const result = {};
+            result.common = helper.sum(helper._.map(bqData.filter(x => {return x.quality === changeConst.quality.common.value; }), 'tp'));
+            result.more = helper.sum(helper._.map(bqData.filter(x => {return x.quality === changeConst.quality.more.value; }), 'tp'));
+            result.great = helper.sum(helper._.map(bqData.filter(x => {return x.quality === changeConst.quality.great.value; }), 'tp'));
+            return result;
+        }
+    }
+
+    return StageImportChange;
+
+};

+ 2 - 2
app/service/stage_rela.js

@@ -19,7 +19,7 @@ class srCache {
     }
 
     async _getCacheOrgTp() {
-        const bg = await this.ctx.service.stageChange.getQualityTotalPrice(this.stage);
+        const bg = await this.ctx.service.stageChange.getSubtotal(this.stage);
         const gcl100 = await this.ctx.service.stageBills.getSumTotalPriceGcl(this.stage, '^[^0-9]*1[0-9]{2}(-|$)');
         return {
             contract_tp: this.stage.contract_tp, qc_tp: this.stage.qc_tp,
@@ -290,7 +290,7 @@ module.exports = app => {
         }
 
         // async _getCacheOrgTp(stage) {
-        //     const bg = await this.ctx.service.stageChange.getQualityTotalPrice(stage);
+        //     const bg = await this.ctx.service.stageChange.getSubtotal(stage);
         //     const gcl100 = await this.ctx.service.stageBills.getSumTotalPriceGcl(stage, '^[^0-9]*1[0-9]{2}(-|$)');
         //     return {
         //         contract_tp: stage.contract_tp, qc_tp: stage.qc_tp,

+ 13 - 5
app/view/advance/detail.ejs

@@ -39,21 +39,29 @@
                                         <div class="input-group-append"><span class="input-group-text">%</span></div>
                                     </div>
                                 </td>
-                                <th width="150" class="text-center">本期金额</th>
+                                <th width="150" class="text-center">支付时间</th>
                                 <td class="text-right" width="405">
                                     <div class="input-group input-group-sm">
-                                        <input type="number" class="pay-input form-control nospin text-right" min="1" <%- isEdited  ? '' : 'disabled' %>
-                                            placeholder="请填写本期金额,将自动计算支付比例" data-type="1"
-                                            value="<%- cur_amount %>">
-                                        <div class="input-group-append"><span class="input-group-text">元</span></div>
+                                        <input data-language="zh" type="text" <%- ctx.session.sessionUser.is_admin ? '' : isEdited ? '' : 'disabled' %> data-date-format="yyyy-MM-dd" placeholder="点击选择时间" class="pay-date-input datepicker-here form-control" value="<%- ctx.helper.dateTran(advance.pay_time, 'YYYY/MM/DD') %>">
                                     </div>
                                 </td>
                             </tr>
                             <tr>
+                                <th class="text-center">本期金额</th>
+                                <td class="text-right" width="405">
+                                    <div class="input-group input-group-sm">
+                                        <input type="number" class="pay-input form-control nospin text-right" min="1" <%- isEdited  ? '' : 'disabled' %>
+                                               placeholder="请填写本期金额,将自动计算支付比例" data-type="1"
+                                               value="<%- cur_amount %>">
+                                        <div class="input-group-append"><span class="input-group-text">元</span></div>
+                                    </div>
+                                </td>
                                 <th class="text-center">截止上期</th>
                                 <td class="text-right" id="p_total1" width="405">
                                     <%- prev_amount %>元
                                 </td>
+                            </tr>
+                            <tr>
                                 <th class="text-center">截止本期金额</th>
                                 <td class="text-right" id="p_total2" width="405">
                                     <%- prev_total_amount %>元

+ 1 - 1
app/view/advance/modal_audit.ejs

@@ -26,7 +26,7 @@
                 </button>
             </div>
             <div class="modal-body">
-                <p>大小限制:30MB,支持office等文档格式、图片格式、压缩包格式</p>
+                <p>单个文件大小限制:30MB,支持office等文档格式、图片格式、压缩包格式</p>
                 <!-- <p><a href="javascript: void(0);" class="btn btn-primary" id="file-modal-target">选择文件</a></p> -->
                 <input type="file" id="file-modal" multiple="multiple">
             </div>

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

@@ -10,7 +10,7 @@
             </div>
             <div class="modal-body">
                 <div class="form-group">
-                    <label for="file-modal">大小限制:30MB,支持office等文档格式、图片格式、压缩包格式</label>
+                    <label for="file-modal">单个文件大小限制:30MB,支持office等文档格式、图片格式、压缩包格式</label>
                     <!-- <p><a href="javascript: void(0);" class="btn btn-primary" id="file-modal-target">选择文件</a></p> -->
                     <input type="file" id="file-modal" multiple="multiple">
                 </div>

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

@@ -920,7 +920,7 @@
                 </button>
             </div>
             <div class="modal-body">
-                <p>大小限制:30MB,支持office等文档格式、图片格式、压缩包格式</p>
+                <p>单个文件大小限制:30MB,支持office等文档格式、图片格式、压缩包格式</p>
                 <p><input value="选择文件" type="file" id="upload-file" multiple /></p>
             </div>
             <div class="modal-footer">

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

@@ -450,6 +450,7 @@
     };
     let changeInfo = Object.assign({}, back_changeInfo);
     let changeUsedData = JSON.parse(unescape('<%- escape(JSON.stringify(changeUsedData)) %>'));
+    console.log(changeUsedData);
 </script>
 <script src="/public/js/change_information_set.js?202001181"></script>
 <script src="/public/js/change_audit.js"></script>
@@ -466,6 +467,7 @@
     const aidList = _.map(auditList2, 'uid');
     aidList.splice(0, 1);
     let changeUsedData = JSON.parse(unescape('<%- escape(JSON.stringify(changeUsedData)) %>'));
+    // console.log(changeUsedData);
     const changeLedgerList = JSON.parse(unescape('<%- escape(JSON.stringify(changeLedgerList)) %>'));
     const changePosList = JSON.parse(unescape('<%- escape(JSON.stringify(changePosList)) %>'));
 </script>

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

@@ -940,7 +940,7 @@
                 </button>
             </div>
             <div class="modal-body">
-                <p>大小限制:30MB,支持office等文档格式、图片格式、压缩包格式</p>
+                <p>单个文件大小限制:30MB,支持office等文档格式、图片格式、压缩包格式</p>
                 <p><input value="选择文件" type="file" id="upload-file" multiple /></p>
             </div>
             <div class="modal-footer">

+ 4 - 0
app/view/change/plan_information.ejs

@@ -107,6 +107,10 @@
                                 <th width="" class="text-center" style="vertical-align: middle">方案描述</th>
                                 <td colspan="3"><textarea class="form-control form-control-sm" data-name="memo" <% if (change.readOnly) { %>readonly<% } %> rows="3"><%- change.memo %></textarea></td>
                             </tr>
+                            <tr>
+                                <th width="" class="text-center" style="vertical-align: middle">工程量数量计算式</th>
+                                <td colspan="3"><textarea class="form-control form-control-sm" data-name="expr" <% if (change.readOnly) { %>readonly<% } %> rows="3"><%- change.expr %></textarea></td>
+                            </tr>
                         </table>
                         <h5 id="qingdan" class="d-inline-block">变更清单</h5>
                         <% if (change.status === auditConst.status.checked) { %>

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

@@ -10,7 +10,7 @@
             </div>
             <div class="modal-body">
                 <div class="form-group">
-                    <label for="file-modal">大小限制:30MB,支持office等文档格式、图片格式、压缩包格式</label>
+                    <label for="file-modal">单个文件大小限制:30MB,支持office等文档格式、图片格式、压缩包格式</label>
                     <!-- <p><a href="javascript: void(0);" class="btn btn-primary" id="file-modal-target">选择文件</a></p> -->
                     <input type="file" id="file-modal" multiple="multiple">
                 </div>

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

@@ -9,7 +9,7 @@
                 </button>
             </div>
             <div class="modal-body">
-                <p>大小限制:30MB,支持office等文档格式、图片格式、压缩包格式</p>
+                <p>单个文件大小限制:30MB,支持office等文档格式、图片格式、压缩包格式</p>
                 <!-- <p><a href="javascript: void(0);" class="btn btn-primary" id="file-modal-target">选择文件</a></p> -->
                 <input type="file" id="file-modal" multiple="multiple">
             </div>

+ 46 - 23
app/view/datacollect/index.ejs

@@ -93,27 +93,35 @@
                                 <div class="height-20 mb-2">
                                     <div class="card text-center bg-dark text-white ml-2 mr-2 py-2 height-100">
                                         <div class="card-body card-per-body">
-                                            <h5 class="card-title card-case-title data_total_change_price">0.00</h5>
-                                            <p class="card-text text-muted">累计变更金额</p>
+                                            <h5 class="card-title card-case-title data_total_checked_change_price">0.00</h5>
+                                            <p class="card-text text-muted">已批复变更金额</p>
                                         </div>
                                     </div>
                                 </div>
                                 <div class="height-20 mb-2">
                                     <div class="card text-center bg-dark text-white ml-2 mr-2 py-2 height-100">
                                         <div class="card-body card-per-body">
-                                            <h5 class="card-title card-case-title data_total_stage_price">0.00</h5>
-                                            <p class="card-text text-muted">累计完成金额</p>
+                                            <h5 class="card-title card-case-title data_total_after_change_price">0.00</h5>
+                                            <p class="card-text text-muted">变更后金额</p>
                                         </div>
                                     </div>
                                 </div>
-                                <div class="height-20">
-                                    <div class="card text-center bg-dark text-white ml-2 mr-2 height-100">
+                                <div class="height-20 mb-2">
+                                    <div class="card text-center bg-dark text-white ml-2 mr-2 py-2 height-100">
                                         <div class="card-body card-per-body">
-                                            <h5 class="card-title card-case-title data_total_material_price">0.00</h5>
-                                            <p class="card-text text-muted">材料调差</p>
+                                            <h5 class="card-title card-case-title data_total_stage_price">0.00</h5>
+                                            <p class="card-text text-muted">累计完成金额</p>
                                         </div>
                                     </div>
                                 </div>
+                                <!--<div class="height-20">-->
+                                    <!--<div class="card text-center bg-dark text-white ml-2 mr-2 height-100">-->
+                                        <!--<div class="card-body card-per-body">-->
+                                            <!--<h5 class="card-title card-case-title data_total_material_price">0.00</h5>-->
+                                            <!--<p class="card-text text-muted">材料调差</p>-->
+                                        <!--</div>-->
+                                    <!--</div>-->
+                                <!--</div>-->
                             </div>
                             <!--饼图-->
                             <div class="left-chart">
@@ -358,8 +366,8 @@
                                         <div class="col-4 pl-0">
                                             <div class="card text-center bg-dark text-white height-100">
                                                 <div class="card-body card-per-body">
-                                                    <h5 class="card-title card-case-title data_total_change_price">0.00</h5>
-                                                    <p class="card-text text-muted">累计变更金额</p>
+                                                    <h5 class="card-title card-case-title data_total_checked_change_price">0.00</h5>
+                                                    <p class="card-text text-muted">已批复变更金额</p>
                                                 </div>
                                             </div>
                                         </div>
@@ -397,27 +405,35 @@
                                         <div class="col-4 pl-0">
                                             <div class="card text-center bg-dark text-white mb-2 height-100">
                                                 <div class="card-body card-per-body">
-                                                    <h5 class="card-title card-case-title data_total_stage_price">0.00</h5>
-                                                    <p class="card-text card-case-text text-muted">累计完成金额</p>
+                                                    <h5 class="card-title card-case-title data_total_after_change_price">0.00</h5>
+                                                    <p class="card-text text-muted">变更后金额</p>
                                                 </div>
                                             </div>
                                         </div>
                                         <div class="col-4 pl-0">
                                             <div class="card text-center bg-dark text-white height-100">
                                                 <div class="card-body card-per-body">
-                                                    <h5 class="card-title card-case-title data_total_advance_price">0.00</h5>
-                                                    <p class="card-text card-case-text text-muted">预付款</p>
+                                                    <h5 class="card-title card-case-title data_total_stage_price">0.00</h5>
+                                                    <p class="card-text card-case-text text-muted">累计完成金额</p>
                                                 </div>
                                             </div>
                                         </div>
                                         <div class="col-4 pl-0">
                                             <div class="card text-center bg-dark text-white height-100">
                                                 <div class="card-body card-per-body">
-                                                    <h5 class="card-title card-case-title data_total_material_price">0.00</h5>
-                                                    <p class="card-text card-case-text text-muted">材料调差</p>
+                                                    <h5 class="card-title card-case-title data_total_advance_price">0.00</h5>
+                                                    <p class="card-text card-case-text text-muted">预付款</p>
                                                 </div>
                                             </div>
                                         </div>
+                                        <!--<div class="col-4 pl-0">-->
+                                            <!--<div class="card text-center bg-dark text-white height-100">-->
+                                                <!--<div class="card-body card-per-body">-->
+                                                    <!--<h5 class="card-title card-case-title data_total_material_price">0.00</h5>-->
+                                                    <!--<p class="card-text card-case-text text-muted">材料调差</p>-->
+                                                <!--</div>-->
+                                            <!--</div>-->
+                                        <!--</div>-->
                                     </div>
                                 </div>
                                 <div class="right-big-chart-content mr-3">
@@ -1331,10 +1347,12 @@
 
         function setData(tenderList, categoryIndex = 0) {
             let total_price = 0;
-            let total_change_price = 0;
+            // let total_change_price = 0;
+            let total_checked_change_price = 0;
+            let total_after_change_price = 0;
             let total_stage_price = 0;
             let total_advance_price = 0;
-            let total_material_price = 0;
+            // let total_material_price = 0;
             let month_stage_num = 0;
             let month_change_num = 0;
             let month_revise_num = 0;
@@ -1368,9 +1386,11 @@
             const chart_option4_data = [];
             for(const t of tenderList) {
                 total_price = ZhCalc.add(total_price, t.total_price);
-                total_change_price = ZhCalc.add(total_change_price, t.end_qc_tp);
+                // total_change_price = ZhCalc.add(total_change_price, t.end_qc_tp);
+                total_checked_change_price = ZhCalc.add(total_checked_change_price, t.change_tp);
+                total_after_change_price = ZhCalc.add(total_after_change_price, ZhCalc.add(t.total_price, t.change_tp));
                 total_stage_price = ZhCalc.add(total_stage_price, t.end_gather_tp);
-                total_material_price = ZhCalc.add(total_material_price, t.material_tp);
+                // total_material_price = ZhCalc.add(total_material_price, t.material_tp);
                 total_advance_price = ZhCalc.add(total_advance_price, t.advance_tp);
                 month_stage_num = ZhCalc.add(month_stage_num, t.month_stage_num);
                 month_change_num = ZhCalc.add(month_change_num, t.month_change_num);
@@ -1421,13 +1441,16 @@
             }
             // console.log(tenderList);
             $('.data_tender_num').text(tenderList.length);
-            const rate = total_price && total_change_price ? ZhCalc.round(ZhCalc.div(total_stage_price, ZhCalc.add(total_price, total_change_price)) * 100, 2) : 0;
+            // const rate = total_price && total_change_price ? ZhCalc.round(ZhCalc.div(total_stage_price, ZhCalc.add(total_price, total_change_price)) * 100, 2) : 0;
+            const rate = total_stage_price && total_after_change_price ? ZhCalc.round(ZhCalc.div(total_stage_price, total_after_change_price) * 100, 2) : 0;
             $('.data_tender_rate').text(rate ? rate + '%' : '0%');
             $('.data_total_price').text(formatMoney(total_price));
-            $('.data_total_change_price').text(formatMoney(total_change_price));
+            // $('.data_total_change_price').text(formatMoney(total_change_price));
+            $('.data_total_checked_change_price').text(formatMoney(total_checked_change_price));
+            $('.data_total_after_change_price').text(formatMoney(total_after_change_price));
             $('.data_total_stage_price').text(formatMoney(total_stage_price));
             $('.data_total_advance_price').text(formatMoney(total_advance_price));
-            $('.data_total_material_price').text(formatMoney(total_material_price));
+            // $('.data_total_material_price').text(formatMoney(total_material_price));
             $('.month_stage_num').text(month_stage_num);
             $('.month_change_num').text(month_change_num);
             $('.month_revise_num').text(month_revise_num);

+ 1 - 1
app/view/layout/layout.ejs

@@ -79,7 +79,7 @@
     }
     const userID = <%- ctx.session.sessionUser.accountId %>;
     let user = '<%= ctx.session.sessionUser.name %>';
-    <% if (ctx.app.config.is_debug) { %>
+    <% if (ctx.session.sessionUser.loginStatus) { %>
     const is_debug = true;
     <% } %>
 </script>

+ 27 - 1
app/view/ledger/explode.ejs

@@ -113,7 +113,7 @@
                     <div id="std-gcl" class="tab-pane tab-select-show">
                     </div>
                     <div id="deal-bills" class="tab-pane tab-select-show">
-                        <div class="sjs-bar-4">
+                        <div class="sjs-bar-4 d-flex">
                             <div class="pb-1">
                                 <% if (dealBillsPermission) { %>
                                 <a href="#upload-deal" data-toggle="modal" data-target="#upload-deal" class="btn btn-sm btn-primary">上传签约清单</a>
@@ -123,6 +123,32 @@
                                 <a href="#db-full-code" data-toggle="modal" data-target="#db-full-code" class="btn btn-sm btn-primary">转换长编号</a>
                                 <a href="/tender/<%- ctx.tender.id %>/deal/download/签约清单.xlsx" class="btn btn-sm btn-primary" style="display: none">下载签约清单</a>
                             </div>
+                            <div class="ml-auto mr-1">
+                                <div class="dropdown">
+                                    <button class="btn btn-sm btn-outline-secondary dropdown-toggle" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
+                                        <i class="fa fa-search"></i>
+                                    </button>
+                                    <div class="dropdown-menu dropdown-menu-right">
+                                        <div class="px-2 border-primary border-1 d-flex" style="width: 300px">
+                                            <div class="d-inline-block">
+                                                <div class="input-group input-group-sm">
+                                                    <input type="text" class="form-control" id="dbs-keyword" placeholder="输入编号/名称查找">
+                                                    <div class="input-group-append" >
+                                                        <span class="input-group-text" id="dbs-count">结果:0</span>
+                                                    </div>
+                                                    <div class="input-group-append" >
+                                                        <button class="btn btn-outline-secondary" id="dbs-pre" type="button" title="上一个"><i class="fa fa-angle-double-left"></i></button>
+                                                        <button class="btn btn-outline-secondary" id="dbs-next" type="button" title="下一个"><i class="fa fa-angle-double-right"></i></button>
+                                                    </div>
+                                                </div>
+                                            </div>
+                                            <div class="d-inline-block">
+                                                <button class="btn btn-light text-danger btn-sm ml-1" id="dbs-clear" type="button"><i class="fa fa-remove"></i></button>
+                                            </div>
+                                        </div>
+                                    </div>
+                                </div>
+                            </div>
                         </div>
                         <div id="deal-bills-spread" class="sjs-sh-4">
                         </div>

+ 16 - 2
app/view/ledger/explode_modal.ejs

@@ -85,7 +85,21 @@
                         </div>
                     </div>
                     <div class="col-6">
-                        <h6>签约清单</h6>
+                        <div class="d-inline-flex">
+                            <h6 class="mr-2 mt-1">签约清单</h6>
+                            <div class="mx-2 mb-2">
+                                <div class="input-group input-group-sm">
+                                    <input type="text" class="form-control" id="batch-dbs-keyword" placeholder="输入编号/名称查找">
+                                    <div class="input-group-append" >
+                                        <span class="input-group-text" id="batch-dbs-count">结果:0</span>
+                                    </div>
+                                    <div class="input-group-append" >
+                                        <button class="btn btn-outline-secondary" id="batch-dbs-pre" type="button" title="上一个"><i class="fa fa-angle-double-left"></i></button>
+                                        <button class="btn btn-outline-secondary" id="batch-dbs-next" type="button" title="下一个"><i class="fa fa-angle-double-right"></i></button>
+                                    </div>
+                                </div>
+                            </div>
+                        </div>
                         <div class="batch-r">
                         </div>
                     </div>
@@ -359,7 +373,7 @@
             </div>
             <div class="modal-body">
                 <div class="form-group">
-                    <label for="formGroupExampleInput">大小限制:30MB,支持<span data-toggle="tooltip" data-placement="bottom" title="" data-original-title="doc,docx,xls,xlsx,ppt,pptx,pdf">office等文档格式</span>、<span data-toggle="tooltip" data-placement="bottom" title="" data-original-title="jpg,png,bmp">图片格式</span>、<span data-toggle="tooltip" data-placement="bottom" title="" data-original-title="rar,zip">压缩包格式</span></label>
+                    <label for="formGroupExampleInput">单个文件大小限制:30MB,支持<span data-toggle="tooltip" data-placement="bottom" title="" data-original-title="doc,docx,xls,xlsx,ppt,pptx,pdf">office等文档格式</span>、<span data-toggle="tooltip" data-placement="bottom" title="" data-original-title="jpg,png,bmp">图片格式</span>、<span data-toggle="tooltip" data-placement="bottom" title="" data-original-title="rar,zip">压缩包格式</span></label>
                     <input type="file" class="" id="upload-file" multiple>
                 </div>
             </div>

+ 1 - 1
app/view/material/checklist.ejs

@@ -68,5 +68,5 @@
     const stage_order = <%- material.order %>;
     const materialID = <%- material.id %>;
     const materialDecimal = JSON.parse(unescape('<%- escape(JSON.stringify(material.decimal)) %>'));
-    let materialChecklistData, materialListData, notJoinList, ledger, curLedgerData, pos, curPosData, gclGatherData;
+    let materialChecklistData, notJoinList, ledger, curLedgerData, pos, curPosData, gclGatherData, gclList;
 </script>

+ 1 - 1
app/view/material/file_modal.ejs

@@ -9,7 +9,7 @@
       </button>
         </div>
         <div class="modal-body">
-          <p>大小限制:30MB,支持office等文档格式、图片格式、压缩包格式</p>
+          <p>单个文件大小限制:30MB,支持office等文档格式、图片格式、压缩包格式</p>
                   <p>
                       <input id="upload-fujian-file" type="file" multiple="multiple">
                       <!-- <a href="javascript: void(0);" id="upload-fujian" class="btn btn-primary">选择文件</a> -->

+ 6 - 0
app/view/material/index.ejs

@@ -92,12 +92,18 @@
     </div>
 </div>
 <script src="/public/js/sub_menu.js"></script>
+<script src="/public/js/decimal.min.js"></script>
+<script src="/public/js/zh_calc.js"></script>
+<script src="/public/js/path_tree.js"></script>
+<script src="/public/js/gcl_gather.js"></script>
 <script src="/public/js/datepicker/datepicker.min.js"></script>
 <script src="/public/js/datepicker/datepicker.zh.js"></script>
 
 <script>
     const tenderId = '<%- ctx.tender.id %>';
     const auditConst = JSON.parse('<%- auditConst2 %>');
+    const preUrl = JSON.parse(unescape('<%- escape(JSON.stringify(preUrl)) %>'));
+    const lastMaterialListNum = parseInt('<%- lastMaterialList ? lastMaterialList.length : 0 %>');
     $.subMenu({
         menu: '#sub-menu', miniMenu: '#sub-mini-menu', miniMenuList: '#mini-menu-list',
         toMenu: '#to-menu', toMiniMenu: '#to-mini-menu',

+ 2 - 1
app/view/material/list.ejs

@@ -55,7 +55,7 @@
                             </li>
                             <li class="ml-auto">
                             <% if (!material.readOnly || material.status === auditConst.status.checked) { %>
-                            <button class="btn btn-sm btn-primary" id="user_all_material">应用调差工料至其他清单明细</button>
+                            <!--<button class="btn btn-sm btn-primary" id="user_all_material">应用调差工料至其他清单明细</button>-->
                             <% } %>
                             </li>
                         </ul>
@@ -81,6 +81,7 @@
     const readOnly = <%- material.readOnly %>;
     const stage_order = <%- material.order %>;
     const materialID = <%- material.id %>;
+    const tenderID = <%- tender.id %>;
     const materialDecimal = JSON.parse(unescape('<%- escape(JSON.stringify(material.decimal)) %>'));
     const openMaterialChecklist = parseInt(<%- ctx.session.sessionProject.page_show.openMaterialChecklist %>);
     let materialListData, materialChecklistData, notJoinList, ledger, curLedgerData, pos, curPosData, gclGatherData;

+ 20 - 2
app/view/material/modal.ejs

@@ -33,12 +33,30 @@
             <div class="modal-footer">
                 <input type="hidden" name="_csrf_j" value="<%= ctx.csrf %>" />
                 <input type="hidden" name="s_order" value="" id="s_order" />
+                <input type="hidden" name="list" value="" id="insertList" />
                 <button type="button" class="btn btn-secondary btn-sm" data-dismiss="modal">关闭</button>
-                <button type="submit" id="addMaterial" class="btn btn-primary btn-sm">确定添加</button>
+                <button type="button" id="addMaterial" class="btn btn-primary btn-sm">确定添加</button>
             </div>
         </form>
     </div>
 </div>
+<!--正在修订提示-->
+<div class="modal fade" id="okedit" data-backdrop="static">
+    <div class="modal-dialog " role="document" >
+        <div class="modal-content">
+            <div class="modal-header">
+                <h5 class="modal-title">提示</h5>
+            </div>
+            <div class="modal-body">
+                <h5>正在生成新一期调差清单数据中,请等待...</h5>
+                <div class="progress">
+                    <div id="material-progress" class="progress-bar" role="progressbar" style="width: 0%;" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100">0%</div>
+                </div>
+            </div>
+        </div>
+    </div>
+</div>
+<!--<script type="text/javascript">$('#okedit').modal('show');</script>-->
 <% } %>
 <% if (materials && materials.length >= 1) { %>
 <!--删除期-->
@@ -114,4 +132,4 @@
     </div>
 </div>
 <script src="/public/js/moment/moment.min.js"></script>
-<script src="/public/js/measure_material.js"></script>
+<script src="/public/js/measure_material.js?202204191"></script>

+ 14 - 2
app/view/profile/sign.ejs

@@ -2,7 +2,7 @@
 <div class="panel-content">
     <div class="panel-title">
         <div class="title-main">
-            <h2>签字管理</h2>
+            <h2>签字/签章管理</h2>
         </div>
     </div>
     <div class="content-wrap">
@@ -11,7 +11,7 @@
                 <% if (ctx.session.sessionProject.page_show.openNetCaSign) { %>
                 <nav class="nav nav-tabs m-3" role="tablist">
                     <a class="nav-item nav-link" href="/profile/sign/netca">Ukey认证签名</a>
-                    <a class="nav-item nav-link active" href="/profile/sign">签字设置</a>
+                    <a class="nav-item nav-link active" href="/profile/sign">签字/签章设置</a>
                 </nav>
                 <% } %>
                 <div class="row m-0">
@@ -40,6 +40,12 @@
                             </div>
                             <button type="button" class="btn btn-danger btn-sm" id="delete-sign">移除签名</button>
                             <div class="form-group">
+                                <label for="stamp">上传签章</label>
+                                <input type="file" class="form-control-file" id="stamp-upload">
+                                <small class="form-text text-danger">图片大小为600x300,格式PNG透明背景。</small>
+                            </div>
+                            <button type="button" class="btn btn-sm btn-danger" id="delete-stamp">移除签章</button>
+                            <div class="form-group">
                                 <label>签名图预览</label>
                                 <div>
                                     <div class="position-relative">
@@ -49,6 +55,11 @@
                                                 <img src="/public/upload/sign/<%= accountData.sign_path %>" width="90">
                                             <% } %>
                                         </div>
+                                        <div class="position-absolute fixed-top" id="stamp-show" style="left:300px;top:260px">
+                                            <% if (accountData.stamp_path) { %>
+                                                <img src="<%- fujianOssPath + accountData.stamp_path %>" width="90">
+                                            <% } %>
+                                        </div>
                                     </div>
                                 </div>
                             </div>
@@ -61,5 +72,6 @@
 </div>
 <script type="text/javascript">
     const csrf = '<%= ctx.csrf %>';
+    const fujianOssPath = JSON.parse(unescape('<%- escape(JSON.stringify(fujianOssPath)) %>'));
 </script>
 <script type="text/javascript" src="/public/js/profile.js"></script>

+ 16 - 0
app/view/report/archive_popup.ejs

@@ -0,0 +1,16 @@
+  <!--删除归档报表-->
+  <div class="modal fade" id="del-archive" data-backdrop="static">
+    <div class="modal-dialog" role="document">
+        <div class="modal-content">
+            <div class="modal-header">
+                <h5 class="modal-title">删除归档报表</h5>
+            </div>
+            <div class="modal-body" id="disp_archive_info_body">
+            </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-danger" data-dismiss="modal" id="delete_file">确定删除</button>
+            </div>
+        </div>
+    </div>
+  </div>

+ 12 - 0
app/view/report/index_archive.ejs

@@ -76,6 +76,18 @@
                                     历史归档
                                 </div>
                             </div>
+                            <% if(ctx.session.sessionUser.is_admin) { %>
+                                <div class="panel">
+                                    <div class="panel-body" id="delete_div">
+                                        <div class="btn-group mr-1" role="group">
+                                            <button class="btn btn-outline-danger btn-sm" type="button" data-toggle="modal" data-target="#del-archive" id="delete_archive_btn">
+                                                <i class="fa fa-remove"></i><br>
+                                                删除归档
+                                            </button>
+                                        </div>
+                                    </div>
+                                </div>
+                            <% } %>
                         </div>
                     </div>
                     <div class="print-view form-view">

+ 5 - 5
app/view/report/rpt_all_popup.ejs

@@ -342,7 +342,7 @@
 </div>
 <!--选择标段-->
 <div class="modal fade" id="gather-select" data-backdrop="static">
-    <div class="modal-dialog modal-lg" role="document">
+    <div class="modal-dialog modal-lgx" role="document">
         <div class="modal-content">
             <div class="modal-header">
                 <h5 class="modal-title" id="gather-select-title">选择标段</h5>
@@ -352,14 +352,14 @@
             </div>
             <div class="modal-body">
                 <div class="row">
-                    <div class="col-6">
+                    <div class="col-7">
                         <h5>可选标段 </h5>
-                        <div class="modal-height-300" id="gather-source-spread" style="height: 330px">
+                        <div class="modal-height-500" id="gather-source-spread">
                         </div>
                     </div>
-                    <div class="col-6">
+                    <div class="col-5">
                         <h5>已选标段 </h5>
-                        <div class="modal-height-300" id="gather-result-spread">
+                        <div class="modal-height-500" id="gather-result-spread">
                         </div>
                         <div class="mt-1" id="gather-by-month" style="width: 60%" name="gather-type">
                             <div class="input-group input-group-sm">

+ 20 - 0
app/view/report/stage_archive_modal.ejs

@@ -0,0 +1,20 @@
+<% if(ctx.session.sessionUser.is_admin) { %>
+<!--删除归档报表 因归档页面要引用这个界面,layout没法再多render一个ejs,只能在这加了-->
+<div class="modal fade" id="del-archive" data-backdrop="static">
+    <div class="modal-dialog" role="document">
+        <div class="modal-content">
+            <div class="modal-header">
+                <h5 class="modal-title">删除归档报表</h5>
+            </div>
+            <div class="modal-body" id="disp_archive_info_body">
+            </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-danger" data-dismiss="modal" id="delete_file">确定删除</button>
+            </div>
+        </div>
+    </div>
+</div>
+<% } %>
+
+<% include ../stage/audit_modal.ejs %>

+ 1 - 1
app/view/revise/index.ejs

@@ -46,7 +46,7 @@
                     <% for (const lr of ledgerRevise) { %>
                     <tr>
                         <td><%- lr.corder %></td>
-                        <td><%- lr.in_time ? ctx.moment(lr.in_time).format('YYYY-MM-DD') : '' %></td>
+                        <td><%- lr.in_time ? ctx.moment(lr.in_time).format('YYYY-MM-DD HH:mm:ss') : '' %></td>
                         <td><%- lr.user_name %></td>
                         <% if (!lr.valid) {%>
                         <td class="text-danger">

+ 28 - 3
app/view/revise/info.ejs

@@ -143,9 +143,34 @@
                     <div id="std-gcl" class="tab-pane">
                     </div>
                     <div id="deal-bills" class="tab-pane">
-                        <div class="sjs-bar">
-                            签约清单
-                            <a href="/tender/<%- ctx.tender.id %>/deal/download/签约清单.xlsx" class="btn btn-sm btn-primary" style="display: none">下载签约清单</a>
+                        <div class="sjs-bar d-flex">
+                            <div class="pb-1">签约清单</div>
+                            <div class="ml-auto mr-1">
+                                <div class="dropdown">
+                                    <button class="btn btn-sm btn-outline-secondary dropdown-toggle" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
+                                        <i class="fa fa-search"></i>
+                                    </button>
+                                    <div class="dropdown-menu dropdown-menu-right">
+                                        <div class="px-2 border-primary border-1 d-flex" style="width: 300px">
+                                            <div class="d-inline-block">
+                                                <div class="input-group input-group-sm">
+                                                    <input type="text" class="form-control" id="dbs-keyword" placeholder="输入编号/名称查找">
+                                                    <div class="input-group-append" >
+                                                        <span class="input-group-text" id="dbs-count">结果:0</span>
+                                                    </div>
+                                                    <div class="input-group-append" >
+                                                        <button class="btn btn-outline-secondary" id="dbs-pre" type="button" title="上一个"><i class="fa fa-angle-double-left"></i></button>
+                                                        <button class="btn btn-outline-secondary" id="dbs-next" type="button" title="下一个"><i class="fa fa-angle-double-right"></i></button>
+                                                    </div>
+                                                </div>
+                                            </div>
+                                            <div class="d-inline-block">
+                                                <button class="btn btn-light text-danger btn-sm ml-1" id="dbs-clear" type="button"><i class="fa fa-remove"></i></button>
+                                            </div>
+                                        </div>
+                                    </div>
+                                </div>
+                            </div>
                         </div>
                         <div id="deal-bills-spread" class="sjs-sh">
                         </div>

+ 15 - 1
app/view/revise/info_modal.ejs

@@ -28,7 +28,21 @@
                         </div>
                     </div>
                     <div class="col-6">
-                        <h6>签约清单</h6>
+                        <div class="d-inline-flex">
+                            <h6 class="mr-2 mt-1">签约清单</h6>
+                            <div class="mx-2 mb-2">
+                                <div class="input-group input-group-sm">
+                                    <input type="text" class="form-control" id="batch-dbs-keyword" placeholder="输入编号/名称查找">
+                                    <div class="input-group-append" >
+                                        <span class="input-group-text" id="batch-dbs-count">结果:0</span>
+                                    </div>
+                                    <div class="input-group-append" >
+                                        <button class="btn btn-outline-secondary" id="batch-dbs-pre" type="button" title="上一个"><i class="fa fa-angle-double-left"></i></button>
+                                        <button class="btn btn-outline-secondary" id="batch-dbs-next" type="button" title="下一个"><i class="fa fa-angle-double-right"></i></button>
+                                    </div>
+                                </div>
+                            </div>
+                        </div>
                         <div class="batch-r">
                         </div>
                     </div>

+ 55 - 40
app/view/setting/user.ejs

@@ -4,9 +4,9 @@
         <div class="title-main">
             <h2>账号管理
                 <% if (projectData.max_user === 0 || projectData.max_user > user_total) { %>
-                <a href="#ver" data-toggle="modal" data-target="#add-user" class="btn btn-primary btn-sm pull-right">添加账号</a>
+                    <a href="#ver" data-toggle="modal" data-target="#add-user" class="btn btn-primary btn-sm pull-right">添加账号</a>
                 <% } else { %>
-                <a href="#add-unpass" data-toggle="modal" data-target="#add-unpass" class="btn btn-primary btn-sm pull-right">添加账号(受限)</a>
+                    <a href="#add-unpass" data-toggle="modal" data-target="#add-unpass" class="btn btn-primary btn-sm pull-right">添加账号(受限)</a>
                 <% } %>
             </h2>
         </div>
@@ -17,9 +17,11 @@
                 <nav class="nav nav-tabs m-3" role="tablist">
                     <a class="nav-item nav-link active" href="/setting/user">账号列表</a>
                     <a class="nav-item nav-link" href="/setting/user/permission/set">账号权限</a>
+                    <a class="nav-item nav-link" href="/setting/user/unit">参建单位</a>
                     <div class="ml-auto">
                         <form class="input-group input-group-sm" method="get">
                             <input type="text" class="form-control" value="<%- keyword %>" name="keyword" placeholder="账号/姓名/单位/手机 搜索" aria-label="账号/姓名/单位/手机 搜索" aria-describedby="button-addon2">
+                            <input type="hidden" class="form-control" value="<%- company %>" name="company">
                             <div class="input-group-append">
                                 <button class="btn btn-outline-primary" type="submit" id="button-addon2"><i class="fa fa-search"></i></button>
                             </div>
@@ -27,44 +29,56 @@
                     </div>
                 </nav>
                 <div class="tab-content m-3">
-                    <div id="user-list" class="tab-pane active">
-                        <table class="table table-hover table-bordered table-sm">
-                            <thead>
-                            <tr>
-                                <th>账号</th>
-                                <th>姓名</th>
-                                <th>单位</th>
-                                <th>角色/职位</th>
-                                <th>手机</th>
-                                <th>电话</th>
-                                <th class="text-center">操作</th></tr>
-                            </thead>
-                            <tbody>
-                            <% for (const account of accountData) { %>
-                                <tr <% if (account.enable !== 1) { %> class="table-danger"<% } %>>
-                                    <td><%= account.account %><% if (account.is_admin === 1) { %> <span data-toggle="tooltip" data-placement="bottom" title="" data-original-title="管理员"><i class="fa fa-user-circle-o"></i></span><% } %></td>
-                                    <td><%= account.name %></td>
-                                    <td><%= account.company %></td>
-                                    <td><%= account.role %></td>
-                                    <td><%= account.mobile %><% if (account.bind === 1) { %>(已绑定第三方平台)<% } %></td>
-                                    <td><%= account.telephone %></td>
-                                    <td class="text-center"><a href="#edit-user" data-account="<%= JSON.stringify(account) %>" data-toggle="modal" data-target="#edit-user" class="btn btn-sm btn-outline-primary">编辑</a>
-                                        <% if (account.is_admin !== 1) { %>
-                                            <% if (account.enable !== 1) { %>
-                                                <!--<a href="" class="btn btn-sm btn-outline-success account-switch-btn" data-account="<%= account.id %>">启用</a>-->
-                                            <% } else { %>
-                                                <!--<a href="" class="btn btn-sm btn-outline-danger account-switch-btn" data-account="<%= account.id %>">停用</a>-->
-                                            <% } %>
-                                        <% } %>
-                                        <% if (account.bind === 1) { %>
-                                            <a href="#unlink-user" data-toggle="modal" data-target="#unlink-user" class="btn btn-sm btn-outline-dark unlink-user" data-account="<%= account.id %>" >解绑</a>
-                                        <% } %>
-                                    </td>
-                                </tr>
-                            <% } %>
-                            </tbody>
-                        </table>
-                        <% include ../layout/page.ejs %>
+                        <div id="user-list" class="tab-pane active">
+                        <div class="row">
+                            <div class="col-3">
+                                <div class="list-group">
+                                    <a href="/setting/user?company=" class="list-group-item list-group-item-action <% if (!company) { %>active<% } %>">全部</a>
+                                    <% for (const u of unitList) { %>
+                                    <a href="/setting/user?company=<%- u.name %>" class="list-group-item list-group-item-action <% if (company === u.name) { %>active<% } %>"><%- u.name %></a>
+                                    <% } %>
+                                </div>
+                            </div>
+                            <div class="col-9">
+                                <table class="table table-hover table-bordered table-sm">
+                                    <thead>
+                                    <tr>
+                                        <th>账号</th>
+                                        <th>姓名</th>
+                                        <th>单位</th>
+                                        <th>角色/职位</th>
+                                        <th>手机</th>
+                                        <th>电话</th>
+                                        <th class="text-center">操作</th></tr>
+                                    </thead>
+                                    <tbody>
+                                    <% for (const account of accountData) { %>
+                                        <tr <% if (account.enable !== 1) { %> class="table-danger"<% } %>>
+                                            <td><%= account.account %><% if (account.is_admin === 1) { %> <span data-toggle="tooltip" data-placement="bottom" title="" data-original-title="管理员"><i class="fa fa-user-circle-o"></i></span><% } %></td>
+                                            <td><%= account.name %></td>
+                                            <td><%= account.company %></td>
+                                            <td><%= account.role %></td>
+                                            <td><%= account.mobile %><% if (account.bind === 1) { %>(已绑定第三方平台)<% } %></td>
+                                            <td><%= account.telephone %></td>
+                                            <td class="text-center"><a href="#edit-user" data-account="<%= JSON.stringify(account) %>" data-toggle="modal" data-target="#edit-user" class="btn btn-sm btn-outline-primary">编辑</a>
+                                                <% if (account.is_admin !== 1) { %>
+                                                    <% if (account.enable !== 1) { %>
+                                                        <!--<a href="" class="btn btn-sm btn-outline-success account-switch-btn" data-account="<%= account.id %>">启用</a>-->
+                                                    <% } else { %>
+                                                        <!--<a href="" class="btn btn-sm btn-outline-danger account-switch-btn" data-account="<%= account.id %>">停用</a>-->
+                                                    <% } %>
+                                                <% } %>
+                                                <% if (account.bind === 1) { %>
+                                                    <a href="#unlink-user" data-toggle="modal" data-target="#unlink-user" class="btn btn-sm btn-outline-dark unlink-user" data-account="<%= account.id %>" >解绑</a>
+                                                <% } %>
+                                            </td>
+                                        </tr>
+                                    <% } %>
+                                    </tbody>
+                                </table>
+                                <% include ../layout/page.ejs %>
+                            </div>
+                        </div>
                     </div>
                 </div>
             </div>
@@ -73,6 +87,7 @@
 </div>
 <script>
     const accountList = JSON.parse(unescape('<%- escape(JSON.stringify(accountData)) %>'));
+    const unitList = JSON.parse(unescape('<%- escape(JSON.stringify(unitList)) %>'));
 </script>
 <script src="/public/js/setting.js"></script>
 <script>autoFlashHeight();</script>

+ 34 - 20
app/view/setting/user_modal.ejs

@@ -7,15 +7,15 @@
             </div>
             <form method="post" action="/setting/user/add?_csrf_j=<%= ctx.csrf %>" onsubmit="return checkUserForm('add');">
             <div class="modal-body">
-                <div class="form-group">
-                    <label><b class="text-danger">*</b>账号组</label>
-                    <select class="form-control form-control-sm" name="account_group">
-                        <option value="0">请选择</option>
-                        <% for (const dw in accountGroup) { %>
-                        <option value="<%= dw %>"><%= accountGroup[dw] %></option>
-                        <% } %>
-                    </select>
-                </div>
+                <!--<div class="form-group">-->
+                    <!--<label><b class="text-danger">*</b>账号组</label>-->
+                    <!--<select class="form-control form-control-sm" name="account_group">-->
+                        <!--<option value="0">请选择</option>-->
+                        <!--<% for (const dw in accountGroup) { %>-->
+                        <!--<option value="<%= dw %>"><%= accountGroup[dw] %></option>-->
+                        <!--<% } %>-->
+                    <!--</select>-->
+                <!--</div>-->
                 <div class="form-group">
                     <label>登录账号<b class="text-danger">*</b></label>
                     <input class="form-control form-control-sm" name="account" placeholder="支持英文数字组合" type="text">
@@ -39,7 +39,14 @@
                 </div>
                 <div class="form-group">
                     <label>单位名称<b class="text-danger">*</b></label>
-                    <input class="form-control form-control-sm" name="company" value="" type="text">
+                    <!--<input class="form-control form-control-sm" name="company" value="" type="text">-->
+                    <input value="7" name="account_group" type="hidden">
+                    <select class="form-control form-control-sm" name="company">
+                        <option>请选择</option>
+                        <% for (const u of unitList) { %>
+                            <option><%- u.name %></option>
+                        <% } %>
+                    </select>
                 </div>
                 <div class="form-group">
                     <label>角色/职位<b class="text-danger">*</b></label>
@@ -71,15 +78,15 @@
             </div>
             <form method="post" action="/setting/user/update?_csrf_j=<%= ctx.csrf %>" onsubmit="return checkUserForm('update');">
             <div class="modal-body">
-                <div class="form-group">
-                    <label>账号组<b class="text-danger">*</b></label>
-                    <select class="form-control form-control-sm" name="account_group">
-                        <option value="0">请选择</option>
-                        <% for (const dw in accountGroup) { %>
-                            <option value="<%= dw %>"><%= accountGroup[dw] %></option>
-                        <% } %>
-                    </select>
-                </div>
+                <!--<div class="form-group">-->
+                    <!--<label>账号组<b class="text-danger">*</b></label>-->
+                    <!--<select class="form-control form-control-sm" name="account_group">-->
+                        <!--<option value="0">请选择</option>-->
+                        <!--<% for (const dw in accountGroup) { %>-->
+                            <!--<option value="<%= dw %>"><%= accountGroup[dw] %></option>-->
+                        <!--<% } %>-->
+                    <!--</select>-->
+                <!--</div>-->
                 <div class="form-group">
                     <label>登录账号<b class="text-danger">*</b></label>
                     <input class="form-control form-control-sm" value="" name="account" readonly placeholder="支持英文数字组合" type="text">
@@ -109,7 +116,14 @@
                 </div>
                 <div class="form-group">
                     <label>单位名称<b class="text-danger">*</b></label>
-                    <input class="form-control form-control-sm" value="" name="company" type="text">
+                    <!--<input class="form-control form-control-sm" value="" name="company" type="text">-->
+                    <input value="" name="account_group" type="hidden">
+                    <select class="form-control form-control-sm" name="company">
+                        <option>请选择</option>
+                        <% for (const u of unitList) { %>
+                            <option><%- u.name %></option>
+                        <% } %>
+                    </select>
                 </div>
                 <div class="form-group">
                     <label>职位名称<b class="text-danger">*</b></label>

+ 62 - 40
app/view/setting/user_permission.ejs

@@ -4,9 +4,9 @@
         <div class="title-main">
             <h2>账号管理
                 <% if (projectData.max_user === 0 || projectData.max_user > user_total) { %>
-                <a href="#ver" data-toggle="modal" data-target="#add-user" class="btn btn-primary btn-sm pull-right">添加账号</a>
+                    <a href="#ver" data-toggle="modal" data-target="#add-user" class="btn btn-primary btn-sm pull-right">添加账号</a>
                 <% } else { %>
-                <a href="#add-unpass" data-toggle="modal" data-target="#add-unpass" class="btn btn-primary btn-sm pull-right">添加账号(受限)</a>
+                    <a href="#add-unpass" data-toggle="modal" data-target="#add-unpass" class="btn btn-primary btn-sm pull-right">添加账号(受限)</a>
                 <% } %>
             </h2>
         </div>
@@ -17,50 +17,72 @@
                 <nav class="nav nav-tabs m-3" role="tablist">
                     <a class="nav-item nav-link" href="/setting/user">账号列表</a>
                     <a class="nav-item nav-link active" href="/setting/user/permission/set">账号权限</a>
+                    <a class="nav-item nav-link" href="/setting/user/unit">参建单位</a>
+                    <div class="ml-auto">
+                        <form class="input-group input-group-sm" method="get">
+                            <input type="text" class="form-control" value="<%- keyword %>" name="keyword" placeholder="账号/姓名/单位/手机 搜索" aria-label="账号/姓名/单位/手机 搜索" aria-describedby="button-addon2">
+                            <input type="hidden" class="form-control" value="<%- company %>" name="company">
+                            <div class="input-group-append">
+                                <button class="btn btn-outline-primary" type="submit" id="button-addon2"><i class="fa fa-search"></i></button>
+                            </div>
+                        </form>
+                    </div>
                 </nav>
                 <div class="tab-content m-3">
                     <div id="user-purview" class="tab-pane active">
-                        <table class="table table-hover table-bordered table-sm">
-                            <thead>
-                            <tr>
-                                <th>账号</th>
-                                <th>姓名</th>
-                                <th>单位</th>
-                                <th>角色/职位</th>
-                                <th>协作办公</th>
-                                <th>权限</th>
-                            </thead>
-                            <tbody>
-                            <% for (const account of accountData) { %>
-                                <tr>
-                                    <td><%= account.account %><% if (account.is_admin === 1) { %> <span data-toggle="tooltip" data-placement="bottom" title="" data-original-title="管理员"><i class="fa fa-user-circle-o"></i></span><% } %></td>
-                                    <td><%= account.name %></td>
-                                    <td><%= account.company %></td>
-                                    <td><%= account.role %></td>
-                                    <td><% if (account.cooperation === 1) { %>启用<% } %></td>
-                                    <td>
-                                        <% if (account.permission !== '' && account.permission !== null) { %>
-                                            <% const accountPermission = JSON.parse(account.permission); %>
-                                            <% for(const ap in accountPermission) { %>
-                                                <% if (permission[ap].type === 'checkbox') { %>
-                                                    <% for (const tc in permission[ap].children) { %>
-                                                        <% if (accountPermission[ap].indexOf(permission[ap].children[tc].value.toString()) !== -1) { %>
-                                                            <%= permission[ap].children[tc].title %>
+                        <div class="row">
+                            <div class="col-3">
+                                <div class="list-group">
+                                    <a href="/setting/user/permission/set?company=" class="list-group-item list-group-item-action <% if (!company) { %>active<% } %>">全部</a>
+                                    <% for (const u of unitList) { %>
+                                        <a href="/setting/user/permission/set?company=<%- u.name %>" class="list-group-item list-group-item-action <% if (company === u.name) { %>active<% } %>"><%- u.name %></a>
+                                    <% } %>
+                                </div>
+                            </div>
+                            <div class="col-9">
+                                <table class="table table-hover table-bordered table-sm">
+                                    <thead>
+                                    <tr>
+                                        <th>账号</th>
+                                        <th>姓名</th>
+                                        <th>单位</th>
+                                        <th>角色/职位</th>
+                                        <th>协作办公</th>
+                                        <th>权限</th>
+                                    </thead>
+                                    <tbody>
+                                    <% for (const account of accountData) { %>
+                                        <tr>
+                                            <td><%= account.account %><% if (account.is_admin === 1) { %> <span data-toggle="tooltip" data-placement="bottom" title="" data-original-title="管理员"><i class="fa fa-user-circle-o"></i></span><% } %></td>
+                                            <td><%= account.name %></td>
+                                            <td><%= account.company %></td>
+                                            <td><%= account.role %></td>
+                                            <td><% if (account.cooperation === 1) { %>启用<% } %></td>
+                                            <td>
+                                                <% if (account.permission !== '' && account.permission !== null) { %>
+                                                    <% const accountPermission = JSON.parse(account.permission); %>
+                                                    <% for(const ap in accountPermission) { %>
+                                                        <% if (permission[ap].type === 'checkbox') { %>
+                                                            <% for (const tc in permission[ap].children) { %>
+                                                                <% if (accountPermission[ap].indexOf(permission[ap].children[tc].value.toString()) !== -1) { %>
+                                                                    <%= permission[ap].children[tc].title %>
+                                                                <% } %>
+                                                            <% } %>
+                                                        <% } else if (permission[ap].type === 'radio') { %>
+                                                            <% if (parseInt(accountPermission[ap]) === permission[ap].children[0].value) { %>
+                                                                <%= permission[ap].title %>
+                                                            <% } %>
                                                         <% } %>
                                                     <% } %>
-                                                <% } else if (permission[ap].type === 'radio') { %>
-                                                    <% if (parseInt(accountPermission[ap]) === permission[ap].children[0].value) { %>
-                                                        <%= permission[ap].title %>
-                                                    <% } %>
                                                 <% } %>
-                                            <% } %>
-                                        <% } %>
-                                        <a href="#edit-user2" data-account="<%= JSON.stringify(account) %>" data-toggle="modal" data-target="#edit-user2" class="btn btn-sm btn-outline-primary pull-right">编辑</a></td>
-                                </tr>
-                            <% } %>
-                            </tbody>
-                        </table>
-                        <% include ../layout/page.ejs %>
+                                                <a href="#edit-user2" data-account="<%= JSON.stringify(account) %>" data-toggle="modal" data-target="#edit-user2" class="btn btn-sm btn-outline-primary pull-right">编辑</a></td>
+                                        </tr>
+                                    <% } %>
+                                    </tbody>
+                                </table>
+                                <% include ../layout/page.ejs %>
+                            </div>
+                        </div>
                     </div>
                 </div>
             </div>

+ 16 - 10
app/view/setting/user_permission_modal.ejs

@@ -7,15 +7,15 @@
             </div>
             <form method="post" action="/setting/user/add?_csrf_j=<%= ctx.csrf %>" onsubmit="return checkUserForm('add');">
             <div class="modal-body">
-                <div class="form-group">
-                    <label><b class="text-danger">*</b>账号组</label>
-                    <select class="form-control form-control-sm" name="account_group">
-                        <option value="0">请选择</option>
-                        <% for (const dw in accountGroup) { %>
-                        <option value="<%= dw %>"><%= accountGroup[dw] %></option>
-                        <% } %>
-                    </select>
-                </div>
+                <!--<div class="form-group">-->
+                    <!--<label><b class="text-danger">*</b>账号组</label>-->
+                    <!--<select class="form-control form-control-sm" name="account_group">-->
+                        <!--<option value="0">请选择</option>-->
+                        <!--<% for (const dw in accountGroup) { %>-->
+                        <!--<option value="<%= dw %>"><%= accountGroup[dw] %></option>-->
+                        <!--<% } %>-->
+                    <!--</select>-->
+                <!--</div>-->
                 <div class="form-group">
                     <label>登录账号<b class="text-danger">*</b></label>
                     <input class="form-control form-control-sm" name="account" placeholder="支持英文数字组合" type="text">
@@ -39,7 +39,13 @@
                 </div>
                 <div class="form-group">
                     <label>单位名称<b class="text-danger">*</b></label>
-                    <input class="form-control form-control-sm" name="company" value="" type="text">
+                    <input value="7" name="account_group" type="hidden">
+                    <select class="form-control form-control-sm" name="company">
+                        <option>请选择</option>
+                        <% for (const u of unitList) { %>
+                            <option><%- u.name %></option>
+                        <% } %>
+                    </select>
                 </div>
                 <div class="form-group">
                     <label>角色/职位<b class="text-danger">*</b></label>

+ 139 - 0
app/view/setting/user_unit.ejs

@@ -0,0 +1,139 @@
+<% include ./sub_menu.ejs %>
+<div class="panel-content">
+    <div class="panel-title">
+        <div class="title-main">
+            <h2>账号管理
+                <a href="#add-company" data-toggle="modal" data-target="#add-company" class="btn btn-primary btn-sm pull-right">添加单位</a>
+                <% if (projectData.max_user === 0 || projectData.max_user > user_total) { %>
+                <a href="#ver" data-toggle="modal" data-target="#add-user" class="btn btn-primary btn-sm pull-right mr-2">添加账号</a>
+                <% } else { %>
+                <a href="#add-unpass" data-toggle="modal" data-target="#add-unpass" class="btn btn-primary btn-sm pull-right mr-2">添加账号(受限)</a>
+                <% } %>
+            </h2>
+        </div>
+    </div>
+    <div class="content-wrap">
+        <div class="c-body">
+            <div class="sjs-height-0">
+                <nav class="nav nav-tabs m-3" role="tablist">
+                    <a class="nav-item nav-link" href="/setting/user">账号列表</a>
+                    <a class="nav-item nav-link" href="/setting/user/permission/set">账号权限</a>
+                    <a class="nav-item nav-link active" href="/setting/user/unit">参建单位</a>
+                </nav>
+                <div class="tab-content m-3">
+                    <div id="unit-list" class="tab-pane active">
+                        <div class="row">
+                            <div class="col-6">
+                                <table class="table table-hover table-bordered table-sm">
+                                    <thead>
+                                    <tr>
+                                        <th>序号</th>
+                                        <th>单位名称</th>
+                                        <th>账号数</th>
+                                        <th>类型</th>
+                                        <th class="text-center">备注</th></tr>
+                                    </thead>
+                                    <tbody id="unit_list" style="cursor: pointer">
+                                    <% if (unitList.length > 0) { %>
+                                    <% for (const [index, u] of unitList.entries()) { %>
+                                    <tr <% if (index === 0) { %>class="table-warning" <% } %> data-id="<%- u.id %>">
+                                        <td width="50px"><%- index+1 %></td>
+                                        <td ><a href="javascript:void(0)"><%- u.name %></a></td>
+                                        <td width="80px"><%- u.account_num %></td>
+                                        <td width="100px"><%- accountGroup[u.type] %></td>
+                                        <td width="150px"><%- u.basic %></td>
+                                    </tr>
+                                    <% } %>
+                                    <% } %>
+                                    </tbody>
+                                </table>
+                            </div>
+                            <% if (unitList.length > 0) { %>
+                            <div class="col-6">
+                                <div>
+                                    <div class="row px-3">
+                                        <div>
+                                            <legend>单位详情</legend>
+                                        </div>
+                                        <div class="ml-auto">
+                                            <a href="#del-company" data-toggle="modal" data-target="#del-company" id="del-modal-btn" class="btn btn-outline-danger btn-sm btn-block">删除单位</a>
+                                        </div>
+                                    </div>
+                                    <table class="table table-hover table-bordered table-sm" id="one_unit">
+                                        <tr>
+                                            <th width="120">单位名称</th>
+                                            <td width="200"><input class="form-control form-control-sm" type="text" data-name="name" id="unit_name" value="<%- unitList[0].name %>" placeholder=""></td>
+                                            <th width="120">法人代表</th>
+                                            <td width="200"><input class="form-control form-control-sm" type="text" data-name="corporation" id="unit_corporation" value="<%- unitList[0].corporation %>" placeholder=""></td>
+                                        </tr>
+                                        <tr>
+                                            <th>企业信用代码</th>
+                                            <td><input class="form-control form-control-sm" type="text" data-name="credit_code" id="unit_credit_code" value="<%- unitList[0].credit_code %>" placeholder=""></td>
+                                            <th>类型</th>
+                                            <td>
+                                                <select class="form-control form-control-sm" id="unit_type" data-name="type">
+                                                    <option value="0">请选择</option>
+                                                    <% for (const dw in accountGroup) { %>
+                                                        <option value="<%= dw %>" <% if (unitList[0].type === parseInt(dw)) { %>selected<% } %>><%- accountGroup[dw] %></option>
+                                                    <% } %>
+                                                </select>
+                                            </td>
+                                        </tr>
+                                        <tr>
+                                            <th>电话</th>
+                                            <td><input class="form-control form-control-sm" type="text" data-name="tel" id="unit_tel" value="<%- unitList[0].tel %>" placeholder=""></td>
+                                            <th>网站</th>
+                                            <td><input class="form-control form-control-sm" type="text" data-name="website" id="unit_website" value="<%- unitList[0].website %>" placeholder=""></td>
+                                        </tr>
+                                        <tr>
+                                            <th>所属地区</th>
+                                            <td><input class="form-control form-control-sm" type="text" data-name="region" id="unit_region" value="<%- unitList[0].region %>" placeholder=""></td>
+                                            <th>地址</th>
+                                            <td><input class="form-control form-control-sm" type="text" data-name="address" id="unit_address" value="<%- unitList[0].address %>" placeholder=""></td>
+                                        </tr>
+                                        <tr>
+                                            <th>备注</th>
+                                            <td colspan="3">
+                                                <textarea class="form-control form-control-sm" id="unit_basic" data-name="basic" value="<%- unitList[0].basic %>" rows="2"></textarea>
+                                            </td>
+                                        </tr>
+                                    </table>
+                                </div>
+                                <div <% if (unitList[0].sign_path) { %>style="display: none" <% } %> id="upload-sign" class="form-group">
+                                    <label for="sign-upload">上传签章</label>
+                                    <input type="file" class="form-control-file" id="sign-upload">
+                                    <small class="form-text text-danger">图片大小为500x500px,格式PNG透明背景。</small>
+                                </div>
+                                <button <% if (!unitList[0].sign_path) { %>style="display: none" <% } %> type="button" class="btn btn-sm btn-danger" id="delete-sign">移除签章</button>
+                                <div class="form-group">
+                                    <label>签章图预览</label>
+                                    <div>
+                                        <div class="position-relative">
+                                            <img src="/public/images/baobiao3.png">
+                                            <div class="position-absolute fixed-top" id="sign-show" style="left:290px;top:210px">
+                                                <% if (unitList[0].sign_path) { %>
+                                                    <img src="<%- fujianOssPath + unitList[0].sign_path %>" width="120">
+                                                <% } %>
+                                            </div>
+                                        </div>
+                                    </div>
+                                </div>
+                            </div>
+                            <% } %>
+                        </div>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </div>
+</div>
+<script>
+</script>
+<script src="/public/js/setting.js"></script>
+<script>
+    const unitList = JSON.parse(unescape('<%- escape(JSON.stringify(unitList)) %>'));
+    const accountGroup = JSON.parse(unescape('<%- escape(JSON.stringify(accountGroup)) %>'));
+    const fujianOssPath = JSON.parse(unescape('<%- escape(JSON.stringify(fujianOssPath)) %>'));
+    let oneUnit = unitList.length > 0 ? unitList[0] : null;
+</script>
+<script>autoFlashHeight();</script>

+ 137 - 0
app/view/setting/user_unit_modal.ejs

@@ -0,0 +1,137 @@
+<!--弹出添加账号-->
+<div class="modal fade" id="add-user" data-backdrop="static">
+    <div class="modal-dialog" role="document">
+        <div class="modal-content">
+            <div class="modal-header">
+                <h5 class="modal-title">添加账号</h5>
+            </div>
+            <form method="post" action="/setting/user/add?_csrf_j=<%= ctx.csrf %>" onsubmit="return checkUserForm('add');">
+            <div class="modal-body">
+                <!--<div class="form-group">-->
+                    <!--<label><b class="text-danger">*</b>账号组</label>-->
+                    <!--<select class="form-control form-control-sm" name="account_group">-->
+                        <!--<option value="0">请选择</option>-->
+                        <!--<% for (const dw in accountGroup) { %>-->
+                        <!--<option value="<%= dw %>"><%= accountGroup[dw] %></option>-->
+                        <!--<% } %>-->
+                    <!--</select>-->
+                <!--</div>-->
+                <div class="form-group">
+                    <label>登录账号<b class="text-danger">*</b></label>
+                    <input class="form-control form-control-sm" name="account" placeholder="支持英文数字组合" type="text">
+                    <input value="" class="account-check" type="hidden">
+                    <div class="invalid-feedback">
+                        该账号已存在。
+                    </div>
+                </div>
+                <div class="form-group">
+                    <label>登录密码<b class="text-danger">*</b></label>
+                    <div class="input-group">
+                        <input type="text" name="password" class="form-control form-control-sm" placeholder="密码支持英文数字及符号">
+                        <div class="input-group-append">
+                            <button id="rand-password" class="btn btn-outline-secondary btn-sm" type="button">随机密码</button>
+                        </div>
+                    </div>
+                </div>
+                <div class="form-group">
+                    <label>姓名<b class="text-danger">*</b></label>
+                    <input class="form-control form-control-sm" name="name" value="" type="text">
+                </div>
+                <div class="form-group">
+                    <label>单位名称<b class="text-danger">*</b></label>
+                    <input value="7" name="account_group" type="hidden">
+                    <select class="form-control form-control-sm" name="company">
+                        <option value="0">请选择</option>
+                        <% for (const u of unitList) { %>
+                            <option><%- u.name %></option>
+                        <% } %>
+                    </select>
+                </div>
+                <div class="form-group">
+                    <label>角色/职位<b class="text-danger">*</b></label>
+                    <input class="form-control form-control-sm" name="role" value="" type="text">
+                </div>
+                <div class="form-group">
+                    <label>手机</label>
+                    <input class="form-control form-control-sm" name="mobile" value="" type="number">
+                </div>
+                <div class="form-group">
+                    <label>电话</label>
+                    <input class="form-control form-control-sm" name="telephone" placeholder="格式000-0000000" type="text">
+                </div>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-secondary btn-sm" data-dismiss="modal">关闭</button>
+                <button type="submit" class="btn btn-primary btn-sm">确定添加</button>
+            </div>
+            </form>
+        </div>
+    </div>
+</div>
+<!--弹出账号受限-->
+<div class="modal" tabindex="-1" role="dialog" id="add-unpass">
+    <div class="modal-dialog" role="document">
+        <div class="modal-content">
+            <div class="modal-header">
+                <h5 class="modal-title">无法添加账号</h5>
+                <button type="button" class="close" data-dismiss="modal" aria-label="Close">
+                    <span aria-hidden="true">&times;</span>
+                </button>
+            </div>
+            <div class="modal-body">
+                <p>账号已超过最大账号数,无法添加新账号。</p>
+                <p>当前限制账号总数:<b><%= projectData.max_user %></b></p>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-secondary btn-sm" data-dismiss="modal">关闭</button>
+            </div>
+        </div>
+    </div>
+</div>
+<div class="modal fade" id="add-company" data-backdrop="static">
+    <div class="modal-dialog" role="document">
+        <form method="post" class="modal-content" action="/setting/user/unit/add?_csrf_j=<%= ctx.csrf %>" onsubmit="return checkUnitForm();">
+            <div class="modal-header">
+                <h5 class="modal-title">添加单位</h5>
+            </div>
+            <div class="modal-body">
+                <div class="form-group">
+                    <label>单位名称<b class="text-danger">*</b></label>
+                    <input class="form-control form-control-sm" name="name" placeholder="请输入单位名称" value="" type="text">
+                </div>
+                <div class="form-group">
+                    <label>类型<b class="text-danger">*</b></label>
+                    <select class="form-control form-control-sm" name="type">
+                        <option value="0">请选择</option>
+                        <% for (const dw in accountGroup) { %>
+                            <option value="<%= dw %>"><%- accountGroup[dw] %></option>
+                        <% } %>
+                    </select>
+                </div>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-sm btn-secondary" data-dismiss="modal">关闭</button>
+                <button type="submit" class="btn btn-sm btn-sm btn-primary">确定添加</button>
+            </div>
+        </form>
+    </div>
+</div>
+<!--删除单位-->
+<div class="modal fade" id="del-company" data-backdrop="static">
+    <div class="modal-dialog" role="document">
+        <div class="modal-content">
+            <div class="modal-header">
+                <h5 class="modal-title">删除单位</h5>
+            </div>
+            <div class="modal-body">
+                <h6 class="del-btn">确认删除当前单位?</h6>
+                <h6 class="not-del-btn">当前单位下存在账号,不允许删除。</h6>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-sm btn-secondary del-btn" data-dismiss="modal">取消</button>
+                <button type="button" class="btn btn-sm btn-danger del-btn" id="delete-unit">确定删除</button>
+                <button type="button" class="btn btn-sm btn-secondary not-del-btn" data-dismiss="modal">关闭</button>
+            </div>
+        </div>
+    </div>
+</div>

+ 2 - 0
app/view/stage/audit_modal.ejs

@@ -801,6 +801,8 @@
 </div>
 <% } %>
 <% } %>
+
+
 <% include ../shares/check_data_modal.ejs %>
 <script type="text/javascript">
     const csrf = '<%= ctx.csrf %>';

+ 2 - 2
app/view/stage/deal_modal.ejs

@@ -296,7 +296,7 @@
             </div>
             <div class="modal-body">
                 <div class="form-group">
-                    <label for="formGroupExampleInput">大小限制:10MB,支持<span data-toggle="tooltip" data-placement="bottom" title="doc,docx,xls,xlsx,ppt,pptx,pdf">office等文档格式</span>、<span data-toggle="tooltip" data-placement="bottom" title="jpg,png,bmp">图片格式</span>、<span data-toggle="tooltip" data-placement="bottom" title="rar,zip">压缩包格式</span></label>
+                    <label for="formGroupExampleInput">单个文件大小限制:10MB,支持<span data-toggle="tooltip" data-placement="bottom" title="doc,docx,xls,xlsx,ppt,pptx,pdf">office等文档格式</span>、<span data-toggle="tooltip" data-placement="bottom" title="jpg,png,bmp">图片格式</span>、<span data-toggle="tooltip" data-placement="bottom" title="rar,zip">压缩包格式</span></label>
                     <input type="file" class="form-control-file">
                 </div>
                 <div class="modal-height-500">
@@ -315,4 +315,4 @@
             </div>
         </div>
     </div>
-</div>
+</div>

+ 2 - 2
app/view/stage/modal.ejs

@@ -206,7 +206,7 @@
             </div>
             <div class="modal-body">
                 <div class="form-group">
-                    <label for="formGroupExampleInput">大小限制:30MB,支持<span data-toggle="tooltip" data-placement="bottom" title="" data-original-title="doc,docx,xls,xlsx,ppt,pptx,pdf">office等文档格式</span>、<span data-toggle="tooltip" data-placement="bottom" title="" data-original-title="jpg,png,bmp">图片格式</span>、<span data-toggle="tooltip" data-placement="bottom" title="" data-original-title="rar,zip">压缩包格式</span></label>
+                    <label for="formGroupExampleInput">单个文件大小限制:30MB,支持<span data-toggle="tooltip" data-placement="bottom" title="" data-original-title="doc,docx,xls,xlsx,ppt,pptx,pdf">office等文档格式</span>、<span data-toggle="tooltip" data-placement="bottom" title="" data-original-title="jpg,png,bmp">图片格式</span>、<span data-toggle="tooltip" data-placement="bottom" title="" data-original-title="rar,zip">压缩包格式</span></label>
                     <input type="file" class="" id="upload-file" multiple>
                 </div>
             </div>
@@ -601,7 +601,7 @@
             <div class="modal-body">
                 <div class="form-group">
                     <% if (!ctx.tender.isTourist || (ctx.tender.isTourist && ctx.tender.touristPermission.file) || stage.filePermission) { %>
-                    <label for="formGroupExampleInput">大小限制:30MB,支持<span data-toggle="tooltip" data-placement="bottom" title="doc,docx,xls,xlsx,ppt,pptx,pdf">office等文档格式</span>、<span data-toggle="tooltip" data-placement="bottom" title="jpg,png,bmp">图片格式</span>、<span data-toggle="tooltip" data-placement="bottom" title="rar,zip">压缩包格式</span></label>
+                    <label for="formGroupExampleInput">单个文件大小限制:30MB,支持<span data-toggle="tooltip" data-placement="bottom" title="doc,docx,xls,xlsx,ppt,pptx,pdf">office等文档格式</span>、<span data-toggle="tooltip" data-placement="bottom" title="jpg,png,bmp">图片格式</span>、<span data-toggle="tooltip" data-placement="bottom" title="rar,zip">压缩包格式</span></label>
                     <input type="file" class="" id="upload-im-file" multiple>
                     <% } %>
                 </div>

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

@@ -77,7 +77,7 @@
             <div class="modal-body">
                 <% if (uploadPermission) { %>
                 <div class="form-group">
-                    <label for="formGroupExampleInput">大小限制:30MB,支持<span data-toggle="tooltip" data-placement="bottom" title="doc,docx,xls,xlsx,ppt,pptx,pdf">office等文档格式</span>、<span data-toggle="tooltip" data-placement="bottom" title="jpg,png,bmp">图片格式</span>、<span data-toggle="tooltip" data-placement="bottom" title="rar,zip">压缩包格式</span></label>
+                    <label for="formGroupExampleInput">单个文件大小限制:30MB,支持<span data-toggle="tooltip" data-placement="bottom" title="doc,docx,xls,xlsx,ppt,pptx,pdf">office等文档格式</span>、<span data-toggle="tooltip" data-placement="bottom" title="jpg,png,bmp">图片格式</span>、<span data-toggle="tooltip" data-placement="bottom" title="rar,zip">压缩包格式</span></label>
                     <input type="file" class="" id="upload-file" multiple>
                 </div>
                 <% } %>

+ 1 - 1
app/view/stage_extra/bonus_modal.ejs

@@ -8,7 +8,7 @@
             <div class="modal-body">
                 <% if (ctx.stage.filePermission) { %>
                 <div class="form-group" id="upload-file-panel">
-                    <label for="formGroupExampleInput">大小限制:30MB,支持<span data-toggle="tooltip" data-placement="bottom" title="doc,docx,xls,xlsx,ppt,pptx,pdf">office等文档格式</span>、<span data-toggle="tooltip" data-placement="bottom" title="jpg,png,bmp">图片格式</span>、<span data-toggle="tooltip" data-placement="bottom" title="rar,zip">压缩包格式</span></label>
+                    <label for="formGroupExampleInput">单个文件大小限制:30MB,支持<span data-toggle="tooltip" data-placement="bottom" title="doc,docx,xls,xlsx,ppt,pptx,pdf">office等文档格式</span>、<span data-toggle="tooltip" data-placement="bottom" title="jpg,png,bmp">图片格式</span>、<span data-toggle="tooltip" data-placement="bottom" title="rar,zip">压缩包格式</span></label>
                     <input type="file" class="" id="upload-file" multiple>
                 </div>
                 <% } %>

+ 2 - 2
app/view/stage_rela/detail_modal.ejs

@@ -29,7 +29,7 @@
             <div class="modal-body">
                 <div class="form-group">
                     <% if (!ctx.tender.isTourist || (ctx.tender.isTourist && ctx.tender.touristPermission.file) || stage.filePermission) { %>
-                    <label for="formGroupExampleInput">大小限制:30MB,支持<span data-toggle="tooltip" data-placement="bottom" title="doc,docx,xls,xlsx,ppt,pptx,pdf">office等文档格式</span>、<span data-toggle="tooltip" data-placement="bottom" title="jpg,png,bmp">图片格式</span>、<span data-toggle="tooltip" data-placement="bottom" title="rar,zip">压缩包格式</span></label>
+                    <label for="formGroupExampleInput">单个文件大小限制:30MB,支持<span data-toggle="tooltip" data-placement="bottom" title="doc,docx,xls,xlsx,ppt,pptx,pdf">office等文档格式</span>、<span data-toggle="tooltip" data-placement="bottom" title="jpg,png,bmp">图片格式</span>、<span data-toggle="tooltip" data-placement="bottom" title="rar,zip">压缩包格式</span></label>
                     <input type="file" class="" id="upload-im-file" multiple>
                     <% } %>
                 </div>
@@ -48,4 +48,4 @@
             </div>
         </div>
     </div>
-</div>
+</div>

+ 65 - 2
builder_report_index_define.js

@@ -44,7 +44,8 @@ const advance_pay = {
         { name: '截止本期金额', field: 'prev_total_amount', type: dataType.currency },
         { name: '创建时间', field: 'create_time', type: dataType.str },
         { name: '备注', field: 'remark', type: dataType.str },
-        { name: '结束时间', field: 'end_time', type: dataType.str },
+        { name: '结束时间', field: 'end_time', type: dataType.time },
+        { name: '支付时间', field: 'pay_time', type: dataType.time },
     ],
 };
 const ledger_cooperation = {
@@ -327,6 +328,64 @@ const change_audit = {
     ],
 };
 
+const import_change = {
+    name: '变更令(mem_import_change)',
+    remark: '',
+    id: 83,
+    key: 'mem_import_change',
+    prefix: '变更令',
+    cols: [
+        { name: '变更令id', field: 'cid', type: dataType.str },
+        { name: '变更令号', field: 'code', type: dataType.str },
+        { name: '批复文号', field: 'w_code', type: dataType.str },
+        { name: '批复编号', field: 'p_code', type: dataType.str },
+        { name: '变更名称', field: 'name', type: dataType.str },
+        { name: '桩号', field: 'peg', type: dataType.str },
+        { name: '原设计图名称', field: 'org_name', type: dataType.str },
+        { name: '原图号', field: 'org_code', type: dataType.str },
+        { name: '变更设计图名称', field: 'new_name', type: dataType.str },
+        { name: '变更图号', field: 'new_code', type: dataType.str },
+        { name: '工程变更理由及内容', field: 'content', type: dataType.str },
+        { name: '工程变更合同依据', field: 'basis', type: dataType.str },
+        { name: '变更工程量数量计算式', field: 'expr', type: dataType.str },
+        { name: '备注', field: 'memo', type: dataType.str },
+        { name: '变更类型', field: 'type', type: dataType.str },
+        { name: '变更类别', field: 'class', type: dataType.int },
+        { name: '变更性质', field: 'quality', type: dataType.int },
+        { name: '变更提出单位', field: 'company', type: dataType.str },
+        { name: '费用承担方', field: 'charge', type: dataType.int },
+        { name: '金额', field: 'total_price', type: dataType.currency },
+        { name: '变更状态发生时间(时间戳)', field: 'cin_time', type: dataType.str },
+        { name: '完成审批时间(时间戳)', field: 'sin_time', type: dataType.str },
+    ],
+};
+const import_change_bills = {
+    name: '变更清单(mem_import_change_bills)',
+    remark: '',
+    id: 84,
+    key: 'mem_import_change_bills',
+    prefix: '变更清单',
+    cols: [
+        { name: '变更令id', field: 'cid', type: dataType.str },
+        { name: '签约清单id或台账id', field: 'lid', type: dataType.str },
+        { name: '清单编号', field: 'code', type: dataType.str },
+        { name: '名称', field: 'name', type: dataType.str },
+        { name: '单位', field: 'unit', type: dataType.str },
+        { name: '单价', field: 'unit_price', type: dataType.currency },
+        { name: '原-数量', field: 'o_qty', type: dataType.currency },
+        { name: '变更-数量', field: 'c_qty', type: dataType.currency },
+        { name: '审批变更后-数量', field: 's_qty', type: dataType.currency },
+        { name: '部位明细', field: 'bwmx', type: dataType.str },
+        { name: '变更详情', field: 'detail', type: dataType.str },
+        { name: '项目节编号', field: 'xmj_code', type: dataType.str },
+        { name: '细目', field: 'xmj_jldy', type: dataType.str },
+        { name: '单位工程', field: 'xmj_dwgc', type: dataType.str },
+        { name: '分部工程', field: 'xmj_fbgc', type: dataType.str },
+        { name: '分项工程', field: 'xmj_fxgc', type: dataType.str },
+        { name: '审批流程中读取数量', field: 'sp_qty', type: dataType.currency },
+    ],
+};
+
 const change_project = {
     name: '变更立项(mem_change_project)',
     remark: '',
@@ -483,6 +542,7 @@ const change_plan = {
         { name: '方案描述', field: 'memo', type: dataType.str },
         { name: '金额', field: 'total_price', type: dataType.currency },
         { name: '小数位数设置JSON', field: 'decimal', type: dataType.int },
+        { name: '计算式', field: 'expr', type: dataType.str },
     ],
 };
 const change_plan_audit = {
@@ -545,6 +605,9 @@ const change_plan_bills = {
         { name: '金额_8', field: 'tp_8', type: dataType.currency },
         { name: '数量_9', field: 'qty_9', type: dataType.currency },
         { name: '金额_9', field: 'tp_9', type: dataType.currency },
+        { name: '新增单价', field: 'new_up', type: dataType.int},
+        { name: 'ex_memo1', field: 'ex_memo1', type: dataType.str},
+        { name: 'ex_memo2', field: 'ex_memo2', type: dataType.str},
     ]
 };
 
@@ -2499,7 +2562,7 @@ const defines = [
     month_progress,
     stage_bills, stage_bills_compare,
     stage_jgcl, stage_bonus, stage_other, stage_safe_prod, stage_temp_land,
-    change, change_bills, change_audit,
+    change, change_bills, change_audit, import_change, import_change_bills,
     change_apply, change_apply_audit, change_apply_bills,
     change_plan, change_plan_audit, change_plan_bills,
     change_project, change_project_audit,

+ 0 - 0
config/config.default.js


Certains fichiers n'ont pas été affichés car il y a eu trop de fichiers modifiés dans ce diff