Explorar o código

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

TonyKang %!s(int64=3) %!d(string=hai) anos
pai
achega
f76c42fe6e
Modificáronse 73 ficheiros con 3507 adicións e 656 borrados
  1. 1 1
      app/const/map.js
  2. 1 0
      app/const/tender_info.js
  3. 34 7
      app/controller/material_controller.js
  4. 1 1
      app/controller/measure_controller.js
  5. 2 2
      app/controller/revise_controller.js
  6. 3 3
      app/controller/schedule_controller.js
  7. 2 2
      app/controller/spss_controller.js
  8. 110 10
      app/controller/stage_controller.js
  9. 111 0
      app/controller/stage_extra_controller.js
  10. 2 4
      app/controller/stage_rela_controller.js
  11. 27 0
      app/controller/wap_controller.js
  12. 2 2
      app/lib/rptCustomData.js
  13. 62 20
      app/lib/stage_im.js
  14. 37 4
      app/lib/sum_load.js
  15. 2 2
      app/lib/tender_info.js
  16. 1 1
      app/middleware/change_audit_check.js
  17. 65 0
      app/public/js/change_information_set.js
  18. 26 18
      app/public/js/material.js
  19. 37 9
      app/public/js/material_exponent.js
  20. 46 24
      app/public/js/material_list.js
  21. 452 0
      app/public/js/se_safe_prod.js
  22. 441 0
      app/public/js/se_temp_land.js
  23. 2 1
      app/public/js/spreadjs_rela/spreadjs_zh.js
  24. 6 5
      app/public/js/sr_detail.js
  25. 211 23
      app/public/js/stage.js
  26. 2 3
      app/public/js/stage_gather.js
  27. 141 4
      app/public/js/stage_im.js
  28. 11 2
      app/public/js/zh_calc.js
  29. 16 0
      app/router.js
  30. 3 1
      app/service/change.js
  31. 4 3
      app/service/change_audit.js
  32. 5 4
      app/service/pos.js
  33. 7 2
      app/service/project.js
  34. 9 1
      app/service/report.js
  35. 53 4
      app/service/report_memory.js
  36. 4 4
      app/service/rpt_gather_memory.js
  37. 2 2
      app/service/rpt_stage_sum_memory.js
  38. 19 1
      app/service/stage.js
  39. 12 0
      app/service/stage_audit.js
  40. 82 114
      app/service/stage_bills.js
  41. 1 1
      app/service/stage_bills_final.js
  42. 0 2
      app/service/stage_bonus.js
  43. 5 4
      app/service/stage_change.js
  44. 141 0
      app/service/stage_detail_att.js
  45. 1 1
      app/service/stage_jgcl.js
  46. 8 4
      app/service/stage_pay.js
  47. 33 32
      app/service/stage_pos.js
  48. 9 9
      app/service/stage_rela.js
  49. 291 0
      app/service/stage_safe_prod.js
  50. 2 0
      app/service/stage_shoufang.js
  51. 19 2
      app/service/stage_shoufang_att.js
  52. 266 0
      app/service/stage_temp_land.js
  53. 11 3
      app/view/change/information_modal.ejs
  54. 1 6
      app/view/material/list.ejs
  55. 11 3
      app/view/setting/fun.ejs
  56. 1 1
      app/view/setting/user.ejs
  57. 1 1
      app/view/setting/user_permission.ejs
  58. 4 0
      app/view/stage/detail_modal.ejs
  59. 1 0
      app/view/stage/gather.ejs
  60. 9 134
      app/view/stage/index.ejs
  61. 47 6
      app/view/stage/modal.ejs
  62. 42 0
      app/view/stage_extra/safe_prod.ejs
  63. 2 0
      app/view/stage_extra/sub_menu_list.ejs
  64. 42 0
      app/view/stage_extra/temp_land.ejs
  65. 4 4
      app/view/stage_rela/detail.ejs
  66. 8 0
      app/view/tender/detail_modal.ejs
  67. 1 1
      app/view/wap/shoufangupload.ejs
  68. 80 13
      builder_report_index_define.js
  69. 1 1
      config/config.default.js
  70. 40 0
      config/web.js
  71. 50 144
      sql/update.sql
  72. 278 0
      sql/update20211015.sql
  73. 43 0
      test/app/temp_test.test.js

+ 1 - 1
app/const/map.js

@@ -18,7 +18,7 @@ const map = [
     { province: '重庆', lng: 106.780, lat: 28.666, office: 8, level: 15 },
     { province: '广西', lng: 109.980, lat: 24.484, office: 5, level: 15 },
     { province: '广东', lng: 113.795, lat: 23.218, office: 4, level: 15 },
-    { province: '江西', lng: 118.366, lat: 27.071, office: 6, level: 15 },
+    { province: '江西', lng: 114.340, lat: 27.329, office: 6, level: 15 },
     { province: '浙江', lng: 120.792, lat: 30.073, office: 10, level: 15 },
     { province: '山东', lng: 119.059, lat: 36.552, office: 11, level: 15 },
 ];

+ 1 - 0
app/const/tender_info.js

@@ -162,6 +162,7 @@ const defaultInfo = {
         over: true,
     },
     fun_rela: {
+        hintOver: true,
         sum_load: {
             ignoreParent: false,
         },

+ 34 - 7
app/controller/material_controller.js

@@ -333,7 +333,8 @@ module.exports = app => {
                     }
                 }
                 // 取所有已被调用的工料清单表
-                renderData.materialListData = await ctx.service.materialList.getAllDataByCondition({ tid: ctx.tender.id, mid: ctx.material.id });
+                const materialListData = await ctx.service.materialList.getAllDataByCondition({ where: { tid: ctx.tender.id, mid: ctx.material.id } });
+                renderData.materialListData = ctx.helper._.uniqBy(materialListData, 'mb_id');
                 // 基数
                 // if (!ctx.material.readOnly) {
                 const stage_list = await ctx.service.stage.getStageMsgByStageId(ctx.material.stage_id);
@@ -388,16 +389,16 @@ module.exports = app => {
                     }
                 }
                 // 取所有已被调用的工料清单表
-                renderData.materialListData = await ctx.service.materialList.getMaterialData(ctx.tender.id, ctx.material.id);
-                renderData.materialNotJoinListData = await ctx.service.materialListNotjoin.getAllDataByCondition({ where: { tid: ctx.tender.id, mid: ctx.material.id } });
+                // renderData.materialListData = await ctx.service.materialList.getMaterialData(ctx.tender.id, ctx.material.id);
+                // renderData.materialNotJoinListData = await ctx.service.materialListNotjoin.getAllDataByCondition({ where: { tid: ctx.tender.id, mid: ctx.material.id } });
                 renderData.materialType = JSON.stringify(materialConst);
                 renderData.jsFiles = this.app.jsFiles.common.concat(this.app.jsFiles.material.list);
                 // 获取清单数据
-                renderData.ledger = await ctx.service.ledger.getData(ctx.tender.id);
-                renderData.pos = await ctx.service.pos.getPosData({ tid: ctx.tender.id });
+                // renderData.ledger = await ctx.service.ledger.getData(ctx.tender.id);
+                // renderData.pos = await ctx.service.pos.getPosData({ tid: ctx.tender.id });
                 // 获取所选期数据并合并相加同类清单项
-                renderData.curLedgerData = await ctx.service.stageBills.getStagesData(ctx.tender.id, ctx.material.stage_id);
-                renderData.curPosData = await ctx.service.stagePos.getStagesData(ctx.tender.id, ctx.material.stage_id);
+                // renderData.curLedgerData = await ctx.service.stageBills.getStagesData(ctx.tender.id, ctx.material.stage_id);
+                // renderData.curPosData = await ctx.service.stagePos.getStagesData(ctx.tender.id, ctx.material.stage_id, 'list');
                 await this.layout('material/list.ejs', renderData, 'material/list_modal.ejs');
             } catch (err) {
                 this.log(err);
@@ -406,6 +407,32 @@ module.exports = app => {
         }
 
         /**
+         * 调差清单页面 (Get)
+         * @param {Object} ctx - egg全局变量
+         * @return {Promise<void>}
+         */
+        async loadListsData(ctx) {
+            try {
+                // const data = JSON.parse(ctx.request.body.data);
+                // const filter = data.filter.split(';');
+                const responseData = { err: 0, msg: '', data: {} };
+                // 取所有已被调用的工料清单表
+                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, ctx.material.stage_id);
+                responseData.data.curPosData = await ctx.service.stagePos.getStagesData(ctx.tender.id, ctx.material.stage_id, 'list');
+                ctx.body = responseData;
+            } catch (err) {
+                this.log(err);
+                ctx.body = { err: 1, msg: err.toString(), data: null };
+            }
+        }
+
+        /**
          * 指数调差页面 (Get)
          * @param {Object} ctx - egg全局变量
          * @return {Promise<void>}

+ 1 - 1
app/controller/measure_controller.js

@@ -220,7 +220,7 @@ module.exports = app => {
                     for (const order of data.stages) {
                         const data = { order, bills: [], pos: [] };
                         const stage = await this.ctx.service.stage.getDataByCondition({ tid: ctx.tender.id, order });
-                        data.bills = await ctx.service.stageBills.getLastestStageData(ctx.tender.id, stage.id);
+                        data.bills = await ctx.service.stageBills.getLastestStageData2(ctx.tender.id, stage.id);
                         data.pos = await ctx.service.stagePos.getLastestStageData2(ctx.tender.id, stage.id);
                         result.stages.push(data);
                     }

+ 2 - 2
app/controller/revise_controller.js

@@ -955,10 +955,10 @@ module.exports = app => {
         async _loadLastStageBillsData(ctx) {
             let curStageData;
             if (ctx.lastStage.readOnly) {
-                curStageData = await ctx.service.stageBills.getAuditorStageData(ctx.tender.id,
+                curStageData = await ctx.service.stageBills.getAuditorStageData2(ctx.tender.id,
                     ctx.lastStage.id, ctx.lastStage.curTimes, ctx.lastStage.curOrder);
             } else {
-                curStageData = await ctx.service.stageBills.getLastestStageData(ctx.tender.id, ctx.lastStage.id);
+                curStageData = await ctx.service.stageBills.getLastestStageData2(ctx.tender.id, ctx.lastStage.id);
             }
             const preStageData = ctx.lastStage.order > 1
                 ? await ctx.service.stageBillsFinal.getFinalData(ctx.tender.data, ctx.lastStage.order - 1)

+ 3 - 3
app/controller/schedule_controller.js

@@ -247,7 +247,7 @@ module.exports = app => {
                     });
                     // let preStageData;
                     // 当前操作人查看最新数据,其他人查看历史数据
-                    const curStageData = await ctx.service.stageBills.getLastestStageData(ctx.tender.id, stageInfo.id);
+                    const curStageData = await ctx.service.stageBills.getLastestStageData2(ctx.tender.id, stageInfo.id);
                     // // 查询截止上期数据
                     // if (stageInfo.order > 1) {
                     //     preStageData = await ctx.service.stageBillsFinal.getFinalData(ctx.tender.data, stageInfo.order - 1);
@@ -277,7 +277,7 @@ module.exports = app => {
             });
             let preStageData;
             // 当前操作人查看最新数据,其他人查看历史数据
-            const curStageData = await ctx.service.stageBills.getLastestStageData(ctx.tender.id, stageInfo.id);
+            const curStageData = await ctx.service.stageBills.getLastestStageData2(ctx.tender.id, stageInfo.id);
             // 查询截止上期数据
             if (stageInfo.order > 1) {
                 preStageData = await ctx.service.stageBillsFinal.getFinalData(ctx.tender.data, stageInfo.order - 1);
@@ -307,7 +307,7 @@ module.exports = app => {
                     tid: ctx.tender.id,
                     order: scheduleStage[0].order,
                 });
-                const finalStageData = await ctx.service.stageBills.getLastestStageData(ctx.tender.id, finalStageInfo.id);
+                const finalStageData = await ctx.service.stageBills.getLastestStageData2(ctx.tender.id, finalStageInfo.id);
                 if (finalStageInfo.order > 1) {
                     prefinalStageData = await ctx.service.stageBillsFinal.getFinalData(ctx.tender.data, finalStageInfo.order - 1);
                 } else {

+ 2 - 2
app/controller/spss_controller.js

@@ -89,8 +89,8 @@ module.exports = app => {
         async _getStageData(tid, sorder) {
             const data = await this._getTzData(tid, true);
             const stage = await this._checkStage(tid, sorder);
-            const bills = await this.ctx.service.stageBills.getAuditorStageData(tid, stage.id, stage.curTimes, stage.curOrder);
-            const pos = await this.ctx.service.stagePos.getAuditorStageData(tid, stage.id, stage.curTimes, stage.curOrder);
+            const bills = await this.ctx.service.stageBills.getAuditorStageData2(tid, stage.id, stage.curTimes, stage.curOrder);
+            const pos = await this.ctx.service.stagePos.getAuditorStageData2(tid, stage.id, stage.curTimes, stage.curOrder);
             data.stage = {
                 sid: stage.id, sorder: stage.order, curTimes: stage.curTimes, curOrder: stage.curOrder,
                 bills: bills, pos: pos

+ 110 - 10
app/controller/stage_controller.js

@@ -203,7 +203,9 @@ module.exports = app => {
                 }
                 renderData.sfData = sfData;
                 // 收方单附件删除权限
-                renderData.sfAttDelPower = ctx.session.sessionUser.accountId === ctx.stage.user_id || ctx.helper._.find(ctx.stage.auditors2, { aid: ctx.session.sessionUser.accountId }) !== -1;
+                renderData.sfAttDelPower = ctx.session.sessionUser.accountId === ctx.stage.user_id || ctx.helper._.findIndex(ctx.stage.auditors2, { aid: ctx.session.sessionUser.accountId }) !== -1;
+                const projectFunInfo = await this.ctx.service.project.getFunRela(ctx.session.sessionProject.id);
+                renderData.hintOver = projectFunInfo.hintOver && ctx.tender.info.fun_rela.hintOver;
                 await this.layout('stage/index.ejs', renderData, 'stage/modal.ejs');
             } catch (err) {
                 this.log(err);
@@ -240,9 +242,9 @@ module.exports = app => {
                 preStageData;
             // 当前操作人查看最新数据,其他人查看历史数据
             if (ctx.stage.readOnly) {
-                curStageData = await ctx.service.stageBills.getAuditorStageData(ctx.tender.id, ctx.stage.id, ctx.stage.curTimes, ctx.stage.curOrder);
+                curStageData = await ctx.service.stageBills.getAuditorStageData2(ctx.tender.id, ctx.stage.id, ctx.stage.curTimes, ctx.stage.curOrder);
             } else {
-                curStageData = await ctx.service.stageBills.getLastestStageData(ctx.tender.id, ctx.stage.id);
+                curStageData = await ctx.service.stageBills.getLastestStageData2(ctx.tender.id, ctx.stage.id);
                 const surplus = this._checkUniqData(curStageData, 'lid');
                 if (surplus.length > 0) {
                     await ctx.service.stageBills.deleteById(surplus);
@@ -251,7 +253,6 @@ module.exports = app => {
             // 查询截止上期数据
             if (ctx.stage.order > 1) {
                 preStageData = await ctx.service.stageBillsFinal.getFinalData(ctx.tender.data, ctx.stage.order - 1);
-                // renderData.preStageData = await ctx.service.stageBills.getEndStageData(ctx.tender.id, ctx.stage.order - 1);
             } else {
                 preStageData = [];
             }
@@ -283,7 +284,6 @@ module.exports = app => {
             // console.time('pre');
             if (ctx.stage.order > 1) {
                 preStageData = await ctx.service.stagePosFinal.getFinalData(ctx.tender.data, ctx.stage.order - 1);
-                // responseData.data.preStageData = await ctx.service.stagePos.getEndStageData(ctx.tender.id, ctx.stage.order - 1);
             } else {
                 preStageData = [];
             }
@@ -327,6 +327,7 @@ module.exports = app => {
                             break;
                         case 'detail':
                             responseData.data.detailData = await this._getStageDetailData(ctx);
+                            responseData.data.detailAtt = await this.ctx.service.stageDetailAtt.getStageData(ctx.stage.id, ctx.stage.im_type);
                             break;
                         case 'change':
                             responseData.data.changeData = await this._getStageChangeData(ctx);
@@ -418,7 +419,6 @@ module.exports = app => {
                 // console.time('pre');
                 if (ctx.stage.order > 1) {
                     preStageData = await ctx.service.stagePosFinal.getFinalData(ctx.tender.data, ctx.stage.order - 1);
-                    // responseData.data.preStageData = await ctx.service.stagePos.getEndStageData(ctx.tender.id, ctx.stage.order - 1);
                 } else {
                     preStageData = [];
                 }
@@ -608,7 +608,7 @@ module.exports = app => {
                     result.ledger = await ctx.service.ledger.getData(ctx.tender.id);
                     result.pos = await ctx.service.pos.getPosData({ tid: ctx.tender.id });
                     if (ctx.stage.readOnly) {
-                        const curStage = await ctx.service.stageBills.getAuditorStageData(ctx.tender.id, ctx.stage.id, ctx.stage.curTimes, ctx.stage.curOrder);
+                        const curStage = await ctx.service.stageBills.getAuditorStageData2(ctx.tender.id, ctx.stage.id, ctx.stage.curTimes, ctx.stage.curOrder);
                         this.ctx.helper.assignRelaData(result.ledger, [
                             { data: curStage, fields: ['contract_qty', 'contract_tp', 'qc_qty', 'qc_tp'], prefix: '', relaId: 'lid' },
                         ]);
@@ -617,7 +617,7 @@ module.exports = app => {
                             { data: curPosStage, fields: ['contract_qty', 'qc_qty'], prefix: '', relaId: 'pid' },
                         ]);
                     } else {
-                        const curStage = await ctx.service.stageBills.getLastestStageData(ctx.tender.id, ctx.stage.id);
+                        const curStage = await ctx.service.stageBills.getLastestStageData2(ctx.tender.id, ctx.stage.id);
                         this.ctx.helper.assignRelaData(result.ledger, [
                             { data: curStage, fields: ['contract_qty', 'contract_tp', 'qc_qty', 'qc_tp'], prefix: '', relaId: 'lid' },
                         ]);
@@ -742,6 +742,101 @@ module.exports = app => {
             }
         }
 
+        async uploadImFile(ctx) {
+            let stream;
+            try {
+                const parts = ctx.multipart({ autoFields: true });
+                let index = 0;
+                const create_time = Date.parse(new Date()) / 1000;
+                stream = await parts();
+                const uploadFiles = [];
+                while (stream !== undefined) {
+                    if (!stream.filename) throw '未发现上传文件!';
+
+                    const fileInfo = path.parse(stream.filename);
+                    const dirName = path.join('app', 'public', 'upload', this.ctx.tender.id.toString(), 'im');
+                    const fileName = `${ctx.session.sessionUser.accountId}_${create_time}_${index}${fileInfo.ext}`;
+
+                    // 保存文件
+                    await ctx.helper.saveStreamFile(stream, path.join(this.app.baseDir, dirName, fileName));
+                    await sendToWormhole(stream);
+
+                    // 插入到stage_pay对应的附件列表中
+                    uploadFiles.push({
+                        file_id: this.ctx.app.uuid.v4(),
+                        filename: fileInfo.name,
+                        fileext: fileInfo.ext,
+                        filesize: Array.isArray(parts.field.size) ? parts.field.size[index] : parts.field.size,
+                        filepath: path.join(dirName, fileName),
+                        uid: ctx.session.sessionUser.accountId,
+                        in_time: moment(create_time * 1000).format('YYYY-MM-DD'),
+                        renew: ctx.stage.status === auditConst.status.checked,
+                    });
+                    ++index;
+                    if (Array.isArray(parts.field.size) && index < parts.field.size.length) {
+                        stream = await parts();
+                    } else {
+                        stream = undefined;
+                    }
+                }
+                const baseInfo = JSON.parse(parts.field.base);
+                const result = await ctx.service.stageDetailAtt.addFiles(baseInfo, uploadFiles);
+                ctx.body = { err: 0, mgs: '', data: result };
+            } catch (err) {
+                console.log(err);
+                // 失败需要消耗掉stream 以防卡死
+                if (stream) await sendToWormhole(stream);
+                ctx.log(err);
+                ctx.ajaxErrorBody(err, '上传附件失败');
+            }
+        }
+
+        async deleteImFile(ctx) {
+            try {
+                const data = JSON.parse(ctx.request.body.data);
+                if (!data.uuid || !data.file_id) throw '数据错误';
+
+                const result = await ctx.service.stageDetailAtt.delFiles(data.uuid, data.file_id);
+                ctx.body = { err: 0, msg: '', data: result };
+            } catch (err) {
+                ctx.log(err);
+                ctx.ajaxErrorBody(err, '删除附件失败');
+            }
+        }
+
+        async downloadImFile(ctx) {
+            try {
+                const uuid = ctx.query.im_id;
+                const file_id = ctx.query.file_id;
+                if (!uuid || !file_id) throw '数据错误';
+
+                const fileInfo = await ctx.service.stageDetailAtt.getFiles(uuid, file_id);
+                if (!fileInfo) throw '文件不存在';
+
+                const fileName = path.join(this.app.baseDir, fileInfo.filepath);
+                // 解决中文无法下载问题
+                const userAgent = (ctx.request.header['user-agent'] || '').toLowerCase();
+                let disposition = '';
+                if (userAgent.indexOf('msie') >= 0 || userAgent.indexOf('chrome') >= 0) {
+                    disposition = 'attachment; filename=' + encodeURIComponent(fileInfo.filename + fileInfo.fileext);
+                } else if (userAgent.indexOf('firefox') >= 0) {
+                    disposition = 'attachment; filename*="utf8\'\'' + encodeURIComponent(fileInfo.filename + fileInfo.fileext) + '"';
+                } else {
+                    /* safari等其他非主流浏览器只能自求多福了 */
+                    disposition = 'attachment; filename=' + new Buffer(fileInfo.filename + fileInfo.fileext).toString('binary');
+                }
+                ctx.response.set({
+                    'Content-Type': 'application/octet-stream',
+                    'Content-Disposition': disposition,
+                    'Content-Length': fileInfo.filesize,
+                });
+                ctx.body = await fs.createReadStream(fileName);
+            } catch (err) {
+                ctx.log(err);
+                ctx.postError(err, '下载文件失败');
+            }
+        }
+
         async _updateStageCache(ctx, payCalculator) {
             await ctx.service.stage.update({
                 check_calc: false,
@@ -1206,6 +1301,8 @@ module.exports = app => {
                 await this._getStageAuditViewData(ctx);
                 const renderData = await this._getDefaultRenderData(ctx);
                 [renderData.gclSpread, renderData.leafXmjSpread] = this._getGatherSpreadSetting();
+                const projectFunInfo = await this.ctx.service.project.getFunRela(ctx.session.sessionProject.id);
+                renderData.hintOver = projectFunInfo.hintOver && ctx.tender.info.fun_rela.hintOver;
 
                 renderData.jsFiles = this.app.jsFiles.common.concat(this.app.jsFiles.stage.gather);
                 await this.layout('stage/gather.ejs', renderData, 'stage/gather_modal.ejs');
@@ -1257,8 +1354,8 @@ module.exports = app => {
                     const data = { order, bills: [], pos: [] };
                     const compareTimes = ctx.stage.status === auditConst.status.checkNo && !ctx.stage.readOnly
                         ? ctx.stage.curTimes - 1 : ctx.stage.curTimes;
-                    data.bills = await ctx.service.stageBills.getAuditorStageData(ctx.tender.id, ctx.stage.id, compareTimes, order);
-                    data.pos = await ctx.service.stagePos.getAuditorStageData(ctx.tender.id, ctx.stage.id, compareTimes, order);
+                    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);
                 }
                 ctx.body = { err: 0, msg: '', data: result };
@@ -1887,6 +1984,9 @@ module.exports = app => {
          */
         async saveShoufang(ctx) {
             try {
+                if (ctx.session.sessionUser.accountId !== ctx.stage.user_id) {
+                    throw '没有权限操作收方单';
+                }
                 const data = JSON.parse(ctx.request.body.data);
                 const responseData = { err: 0, msg: '', data: {} };
                 switch (data.type) {

+ 111 - 0
app/controller/stage_extra_controller.js

@@ -199,6 +199,117 @@ module.exports = app => {
             }
         }
 
+        /**
+         * 安全生产(Get)
+         * @param {Object} ctx - egg全局变量
+         */
+        async safeProd(ctx) {
+            try {
+                const renderData = {
+                    jsFiles: this.app.jsFiles.common.concat(this.app.jsFiles.stageExtra.safeProd)
+                };
+                await this.layout('stage_extra/safe_prod.ejs', renderData);
+            } catch (err) {
+                ctx.helper.log(err);
+            }
+        }
+
+        /**
+         * 获取 安全生产 数据 (Ajax)
+         * @param ctx
+         * @returns {Promise<void>}
+         */
+        async loadSafeProd (ctx) {
+            try {
+                const data = await ctx.service.stageSafeProd.getStageData(ctx.stage);
+                const preData = await ctx.service.stageSafeProd.getPreStageData(ctx.stage.order);
+                for (const d of data) {
+                    const pd = this.ctx.helper._.find(preData, {uuid: d.uuid});
+                    if (pd) {
+                        d.pre_qty = pd.qty;
+                        d.pre_tp = pd.tp;
+                    }
+                }
+                ctx.body = {err: 0, msg: '', data: data};
+            } catch (error) {
+                ctx.helper.log(error);
+                ctx.body = this.ajaxErrorBody(error, '获取数据失败,请刷新');
+            }
+
+        }
+
+        /**
+         * 提交 安全生产 数据 (Ajax)
+         * @param ctx
+         * @returns {Promise<void>}
+         */
+        async updateSafeProd (ctx) {
+            try {
+                const data = JSON.parse(ctx.request.body.data);
+                const result = await ctx.service.stageSafeProd.updateDatas(data);
+                ctx.body = { err: 0, msg: '', data: result };
+            } catch (error) {
+                ctx.helper.log(error);
+                ctx.body = this.ajaxErrorBody(error, '提交数据失败,请重试');
+            }
+        }
+
+
+        /**
+         * 临时用地(Get)
+         * @param {Object} ctx - egg全局变量
+         */
+        async tempLand(ctx) {
+            try {
+                const renderData = {
+                    jsFiles: this.app.jsFiles.common.concat(this.app.jsFiles.stageExtra.tempLand)
+                };
+                await this.layout('stage_extra/temp_land.ejs', renderData);
+            } catch (err) {
+                ctx.helper.log(err);
+            }
+        }
+
+        /**
+         * 获取 临时用地 数据 (Ajax)
+         * @param ctx
+         * @returns {Promise<void>}
+         */
+        async loadTempLand (ctx) {
+            try {
+                const data = await ctx.service.stageTempLand.getStageData(ctx.stage);
+                const preData = await ctx.service.stageTempLand.getPreStageData(ctx.stage.order);
+                for (const d of data) {
+                    const pd = this.ctx.helper._.find(preData, {uuid: d.uuid});
+                    if (pd) {
+                        d.pre_qty = pd.qty;
+                        d.pre_tp = pd.tp;
+                    }
+                }
+                ctx.body = {err: 0, msg: '', data: data};
+            } catch (error) {
+                ctx.helper.log(error);
+                ctx.body = this.ajaxErrorBody(error, '获取数据失败,请刷新');
+            }
+
+        }
+
+        /**
+         * 提交 临时用地 数据 (Ajax)
+         * @param ctx
+         * @returns {Promise<void>}
+         */
+        async updateTempLand (ctx) {
+            try {
+                const data = JSON.parse(ctx.request.body.data);
+                const result = await ctx.service.stageTempLand.updateDatas(data);
+                ctx.body = { err: 0, msg: '', data: result };
+            } catch (error) {
+                ctx.helper.log(error);
+                ctx.body = this.ajaxErrorBody(error, '提交数据失败,请重试');
+            }
+        }
+
         async uploadFile(ctx) {
             let stream;
             try {

+ 2 - 4
app/controller/stage_rela_controller.js

@@ -144,11 +144,9 @@ module.exports = app => {
             const endStageData = await ctx.service.stageRelaPosFinal.getAllDataByCondition({
                 where: {sid: ctx.stage.id, rela_tid: relaStage.rela_tid},
             });
-            // const curStageData = await ctx.service.stagePos.getLastestStageData2(relaStage.rela_tid, relaStage.rela_sid);
-            // const endStageData = await ctx.service.stagePosFinal.getFinalData({id: relaStage.rela_tid}, relaStage.rela_sid);
             this.ctx.helper.assignRelaData(posData, [
-                { data: curStageData, fields: ['contract_qty', 'contract_expr', 'contract_tp', 'qc_qty', 'qc_tp', 'postil'], prefix: '', relaId: 'lid' },
-                { data: endStageData, fields: ['contract_qty', 'contract_tp', 'qc_qty', 'qc_tp', ], prefix: 'end_', relaId: 'lid' },
+                { data: curStageData, fields: ['contract_qty', 'contract_expr', 'contract_tp', 'qc_qty', 'qc_tp', 'postil'], prefix: '', relaId: 'pid' },
+                { data: endStageData, fields: ['contract_qty', 'contract_tp', 'qc_qty', 'qc_tp', ], prefix: 'end_', relaId: 'pid' },
             ]);
             return posData;
         }

+ 27 - 0
app/controller/wap_controller.js

@@ -677,6 +677,33 @@ module.exports = app => {
         }
 
         /**
+         * 编辑附件
+         * @param {Object} ctx - egg全局变量
+         * @return {void}
+         */
+        async shoufangEditFile(ctx) {
+            const responseData = {
+                err: 0,
+                msg: '',
+                data: '',
+            };
+            try {
+
+                const data = JSON.parse(ctx.request.body.data);
+                const fileInfo = await ctx.service.stageShoufangAtt.getDataById(data.id);
+                if (!fileInfo || !Object.keys(fileInfo).length) {
+                    throw '该文件不存在';
+                }
+                await ctx.service.stageShoufangAtt.edit(data);
+            } catch (err) {
+                responseData.err = 1;
+                responseData.msg = err;
+            }
+
+            ctx.body = responseData;
+        }
+
+        /**
          * 下载附件
          * @param {Object} ctx - egg全局变量
          * @return {void}

+ 2 - 2
app/lib/rptCustomData.js

@@ -234,7 +234,7 @@ class jhHelper {
         // 加载截止上期/本期
         let billsData = await this.ctx.service.ledger.getData(tender.id);
         if (filterGcl) billsData = billsData.filter(x => { return x.b_code && x.is_leaf });
-        const curStage = await this.ctx.service.stageBills.getLastestStageData(tender.id, stage.id);
+        const curStage = await this.ctx.service.stageBills.getLastestStageData2(tender.id, stage.id);
         const preStage = stage.order > 1 ? await this.ctx.service.stageBillsFinal.getFinalData(tender, stage.order - 1) : [];
         const loadData = [
             { data: curStage, fields: this.billsQueryField, prefix: '', relaId: 'lid' },
@@ -243,7 +243,7 @@ class jhHelper {
         for (const dc of gsDefine.defaultCompare) {
             const auditor = auditors[dc];
             if (!auditor) continue;
-            const auditorStage = await this.ctx.service.stageBills.getAuditorStageData(tender.id, stage.id, auditor.times, auditor.order);
+            const auditorStage = await this.ctx.service.stageBills.getAuditorStageData2(tender.id, stage.id, auditor.times, auditor.order);
             loadData.push({ data: auditorStage, fields: this.billsQueryField, prefix: `r${dc}_`, relaId: 'lid' });
         }
         helper.assignRelaData(billsData, loadData);

+ 62 - 20
app/lib/stage_im.js

@@ -69,13 +69,13 @@ class StageIm {
     async _loadMainData() {
         const billsData = await this.ctx.service.ledger.getData(this.ctx.tender.id);
         if (this.ctx.stage.readOnly) {
-            const curStage = await this.ctx.service.stageBills.getAuditorStageData(this.ctx.tender.id,
+            const curStage = await this.ctx.service.stageBills.getAuditorStageData2(this.ctx.tender.id,
                 this.ctx.stage.id, this.ctx.stage.curTimes, this.ctx.stage.curOrder);
             this.ctx.helper.assignRelaData(billsData, [
                 { data: curStage, fields: ['contract_qty', 'contract_tp', 'qc_qty', 'qc_tp'], prefix: '', relaId: 'lid' },
             ]);
         } else {
-            const curStage = await this.ctx.service.stageBills.getLastestStageData(this.ctx.tender.id, this.ctx.stage.id);
+            const curStage = await this.ctx.service.stageBills.getLastestStageData2(this.ctx.tender.id, this.ctx.stage.id);
             this.ctx.helper.assignRelaData(billsData, [
                 { data: curStage, fields: ['contract_qty', 'contract_tp', 'qc_qty', 'qc_tp'], prefix: '', relaId: 'lid' },
             ]);
@@ -310,23 +310,53 @@ class StageIm {
 
     _checkCustomDetail(im) {
         const self = this;
-        const cd = this._.find(this.details, function(d) {
-            return im.lid === d.lid &&
-                (!im.code || im.code === d.code) &&
-                (!im.name || im.name === d.name) &&
-                (!im.unit || im.unit === d.unit) &&
-                self.ctx.helper.checkZero(self.ctx.helper.sub(im.unit_price, d.unit_price)) &&
-                (!im.pid || im.pid === d.pid) &&
-                (!im.pos_name || im.pos_name === d.pos_name);
-        });
+        let cd;
+        switch (this.stage.im_type) {
+            case imType.tz.value:
+                cd = this._.find(this.details, function (d) {
+                    return im.lid === d.lid &&
+                        (!im.code || im.code === d.code) &&
+                        (!im.name || im.name === d.name) &&
+                        (!im.unit || im.unit === d.unit) &&
+                        (!im.pid || im.pid === d.pid) &&
+                        (!im.pos_name || im.pos_name === d.pos_name);
+                });
+                break;
+            case imType.zl.value:
+                cd = this._.find(this.details, function (d) {
+                    return im.lid === d.lid &&
+                        (!im.code || im.code === d.code) &&
+                        (!im.name || im.name === d.name) &&
+                        (!im.unit || im.unit === d.unit) &&
+                        self.ctx.helper.checkZero(self.ctx.helper.sub(im.unit_price, d.unit_price)) &&
+                        (!im.pid || im.pid === d.pid) &&
+                        (!im.pos_name || im.pos_name === d.pos_name);
+                });
+                break;
+            case imType.bw.value:
+                cd = this._.find(this.details, function (d) {
+                    return im.lid === d.lid &&
+                        (!im.code || im.code === d.code) &&
+                        (!im.name || im.name === d.name) &&
+                        (!im.unit || im.unit === d.unit) &&
+                        self.ctx.helper.checkZero(self.ctx.helper.sub(im.unit_price, d.unit_price)) &&
+                        (!im.pid || im.pid === d.pid) &&
+                        (!im.pos_name || im.pos_name === d.pos_name);
+                });
+                break;
+            case imType.bb.value:
+                cd = this._.find(this.details, function (d) {
+                    return im.lid === d.lid &&
+                        (!im.name || im.name === d.name) &&
+                        (!im.unit || im.unit === d.unit) &&
+                        (!im.pid || im.pid === d.pid) &&
+                        (!im.pos_name || im.pos_name === d.pos_name);
+                });
+                break;
+        }
         if (cd) {
-            im.custom_define = im.custom_define !== null
-                ? (cd.custom_define ? cd.custom_define.split(',') : [])
-                : this.imFields;
-            this._.assignInWith(im, cd, function(oV, sV, key) {
-                return im.custom_define.indexOf(key) > -1 ? sV : oV;
-            });
-            if (im.code === '101-1-b') console.log(im.calc_memo);
+            im.custom_define = im.custom_define !== null ? (cd.custom_define ? cd.custom_define.split(',') : []) : this.imFields;
+            this._.assignInWith(im, cd, function(oV, sV, key) { return im.custom_define.indexOf(key) > -1 ? sV : oV; });
         }
     }
 
@@ -619,9 +649,15 @@ class StageIm {
         im.qc_jl = 0;
         for (const b of im.gclBills) {
             im.contract_jl = this.ctx.helper.add(im.contract_jl, this.ctx.helper.mul(b.contract_jl, b.unit_price, tp_decimal));
+            im.pre_contract_jl = this.ctx.helper.add(im.pre_contract_jl, this.ctx.helper.mul(b.pre_contract_jl, b.unit_price, tp_decimal));
+            im.end_contract_jl = this.ctx.helper.add(im.end_contract_jl, this.ctx.helper.mul(b.end_contract_jl, b.unit_price, tp_decimal));
             im.qc_jl = this.ctx.helper.add(im.qc_jl, this.ctx.helper.mul(b.qc_jl, b.unit_price, tp_decimal));
+            im.pre_qc_jl = this.ctx.helper.add(im.pre_qc_jl, this.ctx.helper.mul(b.pre_qc_jl, b.unit_price, tp_decimal));
+            im.end_qc_jl = this.ctx.helper.add(im.end_qc_jl, this.ctx.helper.mul(b.end_qc_jl, b.unit_price, tp_decimal));
         }
         im.jl = this.ctx.helper.add(im.contract_jl, im.qc_jl);
+        im.pre_jl = this.ctx.helper.add(im.pre_contract_jl, im.pre_qc_jl);
+        im.end_jl = this.ctx.helper.add(im.end_contract_jl, im.end_qc_jl);
     }
     _getBwBillsIm(node, peg, index, bw) {
         const im = {
@@ -694,6 +730,10 @@ class StageIm {
 
                 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);
+                imDefault.pre_contract_jl = this.ctx.helper.add(imDefault.pre_contract_jl, p.pre_contract_qty);
+                imDefault.pre_qc_jl = this.ctx.helper.add(imDefault.pre_qc_jl, p.pre_qc_qty);
+                imDefault.end_contract_jl = this.ctx.helper.add(imDefault.end_contract_jl, p.end_contract_qty);
+                imDefault.end_qc_jl = this.ctx.helper.add(imDefault.end_qc_jl, p.end_qc_qty);
                 imDefault.used = true;
 
                 this._addBwBillsGclBills(imDefault, p);
@@ -701,6 +741,8 @@ class StageIm {
         }
         if (imDefault.used) {
             imDefault.jl = this.ctx.helper.add(imDefault.contract_jl, imDefault.qc_jl);
+            imDefault.pre_jl = this.ctx.helper.add(imDefault.pre_contract_jl, imDefault.pre_qc_jl);
+            imDefault.end_jl = this.ctx.helper.add(imDefault.end_contract_jl, imDefault.end_qc_jl);
             imDefault.id = this.ImData.length + 1;
             this.ImData.push(imDefault);
         }
@@ -993,7 +1035,7 @@ class StageIm {
         // 生成数据(需要缓存,并清理缓存)
         const pre = (this.stage.im_pre && this.stage.im_pre !== '') ? this.stage.im_pre + this.splitChar : '';
         for (const [i, im] of this.ImData.entries()) {
-            im.im_code = pre + this._getNumberFormat(this.stage.order, 2) + this.splitChar + this._getNumberFormat(i + 1, 3);
+            im.im_code = pre + this._getNumberFormat(this.stage.order, 2) + this.splitChar + this._getNumberFormat(i + this.stage.im_start_num, 3);
             if (im.gclBills) {
                 for (const b of im.gclBills) {
                     b.im_code = im.im_code;
@@ -1032,7 +1074,7 @@ class StageIm {
         // 生成数据(需要缓存,并清理缓存)
         const pre = (this.stage.im_pre && this.stage.im_pre !== '') ? this.stage.im_pre + this.splitChar : '';
         for (const [i, im] of this.ImData.entries()) {
-            im.im_code = pre + this._getNumberFormat(this.stage.order, 2) + this.splitChar + this._getNumberFormat(i + 1, 3);
+            im.im_code = pre + this._getNumberFormat(this.stage.order, 2) + this.splitChar + this._getNumberFormat(i + this.stage.im_start_num, 3);
             if (im.gclBills) {
                 for (const b of im.gclBills) {
                     b.im_code = im.im_code;

+ 37 - 4
app/lib/sum_load.js

@@ -134,6 +134,21 @@ class loadGclBaseTree {
     gather(source, parent) {}
     getUpdateData() {}
 
+    resortChildrenByCode(node) {
+        const helper = this.ctx.helper;
+        if (!node.children || node.children.length === 0) return;
+        node.children.sort((x, y) => {
+            return helper.compareCode(x.b_code, y.b_code);
+        });
+        for (const [i, c] of node.children.entries()) {
+            this.resortChildrenByCode(c);
+            c.order = i + 1;
+        }
+    }
+    resortByCode() {
+        this.resortChildrenByCode(this.parent);
+    }
+
     recursiveCalculate(dealBills, node) {
         if (node.children && node.children.length > 0) {
             for (const child of node.children) {
@@ -250,7 +265,14 @@ class updateReviseGclTree extends loadGclBaseTree {
             if (this.items.length > 0) result.update.push({id: this.parent.id, ledger_id: this.parent.ledger_id, is_leaf: 0});
         } else {
             for (const bn of this.baseNodes) {
-                if (bn.children && bn.children.length > 0) continue;
+                if (bn.children && bn.children.length > 0) {
+                    if (bn.order !== bn.org_order) {
+                        result.update.push({
+                            id: bn.id, ledger_id: bn.ledger_id, order: bn.order,
+                        });
+                    }
+                    continue;
+                }
 
                 if (bn.deal_qty < bn.org_deal_qty || bn.sjcl_qty < bn.org_sjcl_qty || bn.qtcl_qty < bn.org_qtcl_qty || bn.sgfh_qty < bn.org_sgfh_qty) {
                     result.errors.push({
@@ -258,13 +280,22 @@ class updateReviseGclTree extends loadGclBaseTree {
                         b_code: bn.b_code, name: bn.name, unit: bn.unit,
                         deal_qty: bn.deal_qty, sgfh_qty: bn.sgfh_qty, sjcl_qty: bn.sjcl_qty, qtcl_qty: bn.qtcl_qty, qty: bn.quantity, type: 'less',
                     });
+                    if (bn.order !== bn.org_order) {
+                        result.update.push({
+                            id: bn.id, ledger_id: bn.ledger_id, order: bn.order,
+                        });
+                    }
                 } else if (bn.sjcl_qty !== bn.org_sjcl_qty || bn.qtcl_qty !== bn.org_qtcl_qty || bn.sgfh_qty !== bn.org_sgfh_qty) {
                     result.update.push({
-                        id: bn.id, ledger_id: bn.ledger_id, unit_price: bn.unit_price || 0,
+                        id: bn.id, ledger_id: bn.ledger_id, unit_price: bn.unit_price || 0, order: bn.order,
                         deal_qty: bn.deal_qty, deal_tp: bn.deal_tp || 0,
                         sgfh_qty: bn.sgfh_qty, sjcl_qty: bn.sjcl_qty, qtcl_qty: bn.qtcl_qty, quantity: bn.quantity,
                         sgfh_tp: bn.sgfh_tp || 0, sjcl_tp: bn.sjcl_tp || 0, qtcl_tp: bn.qtcl_tp || 0, total_price: bn.total_price || 0,
-                    })
+                    });
+                } else if (bn.order !== bn.org_order) {
+                    result.update.push({
+                        id: bn.id, ledger_id: bn.ledger_id, order: bn.order,
+                    });
                 }
             }
         }
@@ -411,6 +442,7 @@ class sumLoad {
                 this.recusiveLoadGatherGcl(top, null);
             }
         }
+        this.loadTree.resortByCode();
         const dealBills = await this.ctx.service.dealBills.getAllDataByCondition({ where: { tender_id: this.ctx.tender.id } });
         this.loadTree.calculateAll(dealBills);
         return this.loadTree;
@@ -441,6 +473,7 @@ class sumLoad {
                 this.recusiveLoadGatherGcl(top, null);
             }
         }
+        this.loadTree.resortByCode();
         const dealBills = await this.ctx.service.dealBills.getAllDataByCondition({ where: { tender_id: this.ctx.tender.id } });
         this.loadTree.calculateAll(dealBills);
         return this.loadTree;
@@ -486,7 +519,7 @@ class sumLoad {
             const billsData = await this.ctx.service.ledger.getData(tender.tid);
             const stage = await this.ctx.service.stage.getDataByCondition({tid: tender.tid, order: tender.stage});
             if (!stage) throw '选择的期不存在';
-            const curStageData = await this.ctx.service.stageBills.getLastestStageData(tender.tid, stage.id);
+            const curStageData = await this.ctx.service.stageBills.getLastestStageData2(tender.tid, stage.id);
             const curStageChange = await this.ctx.service.stageChangeFinal.getSumLoadFinalData(stage.id);
             this._loadCurStageAndChange(billsData, curStageData, curStageChange);
             const billsTree = new Ledger.billsTree(this.ctx, {

+ 2 - 2
app/lib/tender_info.js

@@ -42,8 +42,8 @@ class TenderInfo {
         const billsData = await this.ctx.service.ledger.getData(this.tender.id);
         if (this.stage) {
             const curStage = this.stage.readOnly
-                ? await this.ctx.service.stageBills.getAuditorStageData(this.tender.id, this.stage.id, this.stage.curTimes, this.stage.curOrder)
-                : await this.ctx.service.stageBills.getLastestStageData(this.tender.id, this.stage.id);
+                ? await this.ctx.service.stageBills.getAuditorStageData2(this.tender.id, this.stage.id, this.stage.curTimes, this.stage.curOrder)
+                : await this.ctx.service.stageBills.getLastestStageData2(this.tender.id, this.stage.id);
 
             const preStage = this.stage.order > 1 ? await this.ctx.service.stageBillsFinal.getFinalData(this.tender, this.stage.order - 1) : [];
             this.ctx.helper.assignRelaData(billsData, [

+ 1 - 1
app/middleware/change_audit_check.js

@@ -33,7 +33,7 @@ module.exports = options => {
             if ((change.status === status.uncheck || change.status === status.back || change.status === status.revise) && this.tender.info.shenpi.change !== shenpiConst.sp_status.sqspr) {
                 const shenpi_status = this.tender.info.shenpi.change;
                 // 进一步比较审批流是否与审批流程设置的相同,不同则替换为固定审批流或固定的终审
-                const auditList = yield this.service.changeAudit.getAllDataByCondition({ where: { cid: change.cid, times: change.times } });
+                const auditList = yield this.service.changeAudit.getAllDataByCondition({ where: { cid: change.cid, times: change.times }, orders: [['usort', 'asc']] });
                 auditList.shift();
                 const auditIdList = _.map(auditList, 'uid');
                 if (shenpi_status === shenpiConst.sp_status.gdspl) {

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

@@ -664,6 +664,7 @@ $(document).ready(() => {
         $('#code-input').val('');
         $('#code-input').siblings('a').hide();
         $('#code-list').html(codeHtml);
+        checkSelectAll();
     });
 
     // 右边项目节选择
@@ -721,6 +722,7 @@ $(document).ready(() => {
                 $('#table-list-select tr[data-index="' + index + '"]').attr('data-bwmx', '');
             }
         }
+        checkSelectAll();
     });
 
     // 添加空白清单or签约清单
@@ -776,6 +778,7 @@ $(document).ready(() => {
         $('#code-input').val('');
         $('#code-input').siblings('a').hide();
         $('#code-list').html('');
+        $('#code-select-all').prop('checked', false);
     });
 
     $('#code-input').on('valuechange', function (e, previous) {
@@ -786,6 +789,7 @@ $(document).ready(() => {
             $(this).siblings('a').hide();
         }
         makeCodeTable($(this).val());
+        checkSelectAll();
     });
 
     $('.remove-btn').on('click', function () {
@@ -798,6 +802,53 @@ $(document).ready(() => {
         } else {
             makeCodeTable();
         }
+        checkSelectAll();
+    });
+    // 全选及取消
+    $('#code-select-all').click(function () {
+        // 全选checkbox
+        let index = $('#code-list').attr('data-index');
+        if (index) {
+            if ($(this).is(':checked')){
+                $('#code-list tr').each(function () {
+                    if ($(this).css('display') !== 'none') {
+                        $(this).find('input').prop('checked', true);
+                    }
+                })
+            } else {
+                $('#code-list tr').each(function () {
+                    if ($(this).css('display') !== 'none' && $(this).find('input').prop('disabled') !== true) {
+                        $(this).find('input').prop('checked', false);
+                    }
+                });
+            }
+            // 判断还有无选中项目节编号
+            if ($('#code-list input').is(':checked')) {
+                // 去除部分data-detail值
+                let data_bwmx = [];
+                $('#code-list input:checked').each(function () {
+                    const tr = $(this).parents('tr');
+                    const length = tr.children('td').length;
+                    const gcl_id = tr.attr('gcl_id');
+                    const bwmx = length === 8 ?
+                        tr.children('td').eq(0).text() + '!_!' +
+                        tr.children('td').eq(1).text() + '!_!' +
+                        tr.children('td').eq(2).text() + '!_!' +
+                        tr.children('td').eq(3).text() + '!_!' +
+                        tr.children('td').eq(4).text() + '!_!' + gcl_id + '!_!' +
+                        (tr.children('td').eq(5).text() !== '' ? tr.children('td').eq(5).text() : tr.children('td').eq(1).text()) : '0';
+                    const quantity = tr.attr('quantity');
+                    const de_qu = bwmx + '*;*' + quantity;
+                    data_bwmx.push(de_qu);
+                });
+                data_bwmx = data_bwmx.join('$#$');
+                $('#table-list-select tr[data-index="' + index + '"]').attr('data-bwmx', data_bwmx);
+                $('#table-list-select tr[data-index="' + index + '"]').addClass('table-success');
+            } else {
+                $('#table-list-select tr[data-index="' + index + '"]').removeClass('table-success');
+                $('#table-list-select tr[data-index="' + index + '"]').attr('data-bwmx', '');
+            }
+        }
     });
 
     // 记录变更信息操作
@@ -869,6 +920,16 @@ $(document).ready(() => {
         toastr.success('已还原到上次保存状态');
     });
 });
+function checkSelectAll() {
+    let check = $('#code-list tr').length > 0 ? true : false;
+    $('#code-list tr').each(function () {
+        if ($(this).css('display') !== 'none' && !$(this).find('input').is(':checked')) {
+            check = false;
+        }
+    });
+    $('#code-select-all').prop('checked', check);
+}
+
 function checkChangeFrom() {
     let returnFlag = false;
     // 表单判断
@@ -920,6 +981,10 @@ function tableDataRemake(changeListData) {
     $('#table-list-select tr').removeClass('table-success');
     $('#table-list-select tr').attr('data-bwmx', '');
     $('#code-list').html('');
+    $('#code-list').attr('data-index', '');
+    $('#code-input').val('');
+    $('#code-select-all').prop('checked', false);
+    $('#code-input').siblings('a').hide();
     // 根据已添加的清单显示
     if (changeList.length > 0 && changeList[0]) {
         const removeList = [];

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

@@ -354,7 +354,7 @@ $(document).ready(() => {
                     return;
                 }
                 // 未改变值则不提交
-                const validText = is_numeric(info.editingText) ? parseFloat(info.editingText) : (info.editingText ? trimInvalidChar(info.editingText) : null);
+                let validText = is_numeric(info.editingText) ? parseFloat(info.editingText) : (info.editingText ? trimInvalidChar(info.editingText) : null);
                 const orgValue = select[col.field];
                 if (orgValue == validText || ((!orgValue || orgValue === '') && (validText === ''))) {
                     SpreadJsObj.reLoadRowData(info.sheet, info.row);
@@ -367,11 +367,13 @@ $(document).ready(() => {
                         SpreadJsObj.reLoadRowData(info.sheet, info.row);
                         return;
                     }
-                    const num = parseFloat(validText);
+                    let num = parseFloat(validText);
                     if (validText !== null && (num < 0 || !/^\d+(\.\d{1,3})?$/.test(num))) {
-                        toastr.error('请输入大于0并且小于3位小数的浮点数');
-                        SpreadJsObj.reLoadRowData(info.sheet, info.row);
-                        return;
+                        toastr.warning('已保留3位小数');
+                        validText = ZhCalc.round(num, 3);
+                        // toastr.error('请输入大于0并且小于3位小数的浮点数');
+                        // SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                        // return;
                     }
                 }
                 if (col.field === 'msg_tp') {
@@ -382,9 +384,11 @@ $(document).ready(() => {
                     }
                     const num = parseFloat(validText);
                     if (validText !== null && (num < 0 || !/^\d+(\.\d{1,3})?$/.test(num))) {
-                        toastr.error('请输入大于0并且小于3位小数的浮点数');
-                        SpreadJsObj.reLoadRowData(info.sheet, info.row);
-                        return;
+                        toastr.warning('已保留3位小数');
+                        validText = ZhCalc.round(num, 3);
+                        // toastr.error('请输入大于0并且小于3位小数的浮点数');
+                        // SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                        // return;
                     }
                 }
                 if (col.field === 'm_up_risk') {
@@ -482,7 +486,7 @@ $(document).ready(() => {
                 codeError: {type: 'error', msg: '编号为纯数字时,不能为小数'},
                 numberExpr: {type: 'error', msg: '不能粘贴其它非数字类型字符'},
                 riskCan: {type: 'error', msg: '只能粘贴0-100的正整数'},
-                numberCan: {type: 'error', msg: '请粘贴大于0并且小于3位小数的浮点数'},
+                numberCan: {type: 'warning', msg: '已保留3位小数'},
             };
             const range = info.cellRange;
             const sortData = info.sheet.zh_data || [];
@@ -553,8 +557,9 @@ $(document).ready(() => {
                         if (colSetting.field === 'basic_price' || colSetting.field === 'msg_tp') {
                             if (validText !== null && (num < 0 || !/^\d+(\.\d{1,3})?$/.test(num))) {
                                 toastMessageUniq(getPasteHint(hint.numberCan, hintRow));
-                                bPaste = false;
-                                continue;
+                                validText = ZhCalc.round(num, 3);
+                                // bPaste = false;
+                                // continue;
                             }
                         } else if (colSetting.field === 'm_up_risk' || colSetting.field === 'm_down_risk') {
                             if (validText !== null && (num < 0 || num > 100 || !/^\d+$/.test(num))) {
@@ -753,7 +758,7 @@ $(document).ready(() => {
                 const select = SpreadJsObj.getSelectObject(info.sheet);
                 const col = info.sheet.zh_setting.cols[info.col];
                 // 未改变值则不提交
-                const validText = is_numeric(info.editingText) ? parseFloat(info.editingText) : (info.editingText ? trimInvalidChar(info.editingText) : null);
+                let validText = is_numeric(info.editingText) ? parseFloat(info.editingText) : (info.editingText ? trimInvalidChar(info.editingText) : null);
                 const orgValue = select[col.field];
                 if (orgValue == validText || ((!orgValue || orgValue === '') && (validText === ''))) {
                     SpreadJsObj.reLoadRowData(info.sheet, info.row);
@@ -778,9 +783,11 @@ $(document).ready(() => {
                     }
                     const num = parseFloat(validText);
                     if (validText !== null && (num < 0 || !/^\d+(\.\d{1,3})?$/.test(num))) {
-                        toastr.error('请输入大于0并且小于3位小数的浮点数');
-                        SpreadJsObj.reLoadRowData(info.sheet, info.row);
-                        return;
+                        // toastr.error('请输入大于0并且小于3位小数的浮点数');
+                        // SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                        // return;
+                        toastr.warning('已保留3位小数');
+                        validText = ZhCalc.round(num, 3);
                     }
                     select[col.field] = validText;
 
@@ -834,7 +841,7 @@ $(document).ready(() => {
             const hint = {
                 cellError: {type: 'error', msg: '粘贴内容超出了表格范围'},
                 numberExpr: {type: 'error', msg: '不能粘贴其它非数字类型字符'},
-                numberCan: {type: 'error', msg: '请粘贴大于0并且小于3位小数的浮点数'},
+                numberCan: {type: 'warning', msg: '已保留3位小数'},
             };
             const range = info.cellRange;
             const sortData = info.sheet.zh_data || [];
@@ -884,8 +891,9 @@ $(document).ready(() => {
                         }
                         if (validText !== null && (num < 0 || !/^\d+(\.\d{1,3})?$/.test(num))) {
                             toastMessageUniq(getPasteHint(hint.numberCan, hintRow));
-                            bPaste = false;
-                            continue;
+                            validText = ZhCalc.round(num, 3);
+                            // bPaste = false;
+                            // continue;
                         }
                     }
                     materialMonthData[colSetting.field] = validText;

+ 37 - 9
app/public/js/material_exponent.js

@@ -146,7 +146,7 @@ $(document).ready(() => {
                     return;
                 }
                 // 未改变值则不提交
-                const validText = is_numeric(info.editingText) ? parseFloat(info.editingText) : (info.editingText ? trimInvalidChar(info.editingText) : null);
+                let validText = is_numeric(info.editingText) ? parseFloat(info.editingText) : (info.editingText ? trimInvalidChar(info.editingText) : null);
                 const orgValue = select[col.field];
                 if (orgValue == validText || ((!orgValue || orgValue === '') && (validText === ''))) {
                     SpreadJsObj.reLoadRowData(info.sheet, info.row);
@@ -159,11 +159,20 @@ $(document).ready(() => {
                         SpreadJsObj.reLoadRowData(info.sheet, info.row);
                         return;
                     }
-                    const num = parseFloat(validText);
-                    if (validText !== null && (num < 0 || !/^(\d{1,10}|\d{1,7}\.\d{1,3})?$/.test(num))) {
+                    const num = parseFloat(validText.toPrecision(10));
+                    if (validText !== null && num < 0) {
                         toastr.error('请输入10位以内有效数字并且小于3位小数的浮点数');
                         SpreadJsObj.reLoadRowData(info.sheet, info.row);
                         return;
+                    } else if (validText !== null) {
+                        if(!Number.isInteger(num) && !/^(\d{1,10}\.\d{1,3})?$/.test(num)) {
+                            toastr.warning('已保留3位小数');
+                        } else if (validText.toString().length > 11) {
+                            toastr.warning('已保留10位有效数字');
+                        }
+                        // SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                        // return;
+                        validText = ZhCalc.round(num, 3);
                     }
                 }
                 if (col.field === 'weight_num') {
@@ -172,11 +181,15 @@ $(document).ready(() => {
                         SpreadJsObj.reLoadRowData(info.sheet, info.row);
                         return;
                     }
-                    const num = parseFloat(validText);
-                    if (validText !== null && (num < 0 || num >= 1 || !/^\d+(\.\d{1,3})?$/.test(num))) {
+                    let num = parseFloat(validText);
+                    if (validText !== null && (num < 0 || num >= 1)) {
                         toastr.error('请输入0~1范围内并且小于3位小数的浮点数');
                         SpreadJsObj.reLoadRowData(info.sheet, info.row);
                         return;
+                    } else if (validText !== null && num > 0 && num < 1 && !/^\d+(\.\d{1,3})?$/.test(num)) {
+                        toastr.warning('已保留3位小数');
+                        validText = ZhCalc.round(num, 3);
+                        num = ZhCalc.round(num, 3);
                     }
                     const total_weight = ZhCalc.add(ZhCalc.sub(_.sumBy(materialExponentData, 'weight_num'), parseFloat(orgValue)), num);
                     if (total_weight > 1) {
@@ -297,6 +310,8 @@ $(document).ready(() => {
                 numberExpr: {type: 'error', msg: '不能粘贴其它非数字类型字符'},
                 numberCan: {type: 'error', msg: '请粘贴10位以内有效数字并且小于3位小数的浮点数'},
                 numberCan2: {type: 'error', msg: '请粘贴0~1范围内并且小于3位小数的浮点数'},
+                numberCan3: {type: 'warning', msg: '已保留3位小数'},
+                numberCan4: {type: 'warning', msg: '已保留10位有效数字'},
                 weightNumberCan: {type: 'error', msg: '粘贴的加权系数总和不能大于1'},
             };
             const range = info.cellRange;
@@ -344,11 +359,20 @@ $(document).ready(() => {
                             bPaste = false;
                             continue;
                         }
-                        const num = parseFloat(validText);
-                        if (validText !== null && (num < 0 || !/^(\d{1,10}|\d{1,7}\.\d{1,3})?$/.test(num))) {
+                        const num = parseFloat(validText.toPrecision(10));
+                        if (validText !== null && num < 0) {
                             toastMessageUniq(getPasteHint(hint.numberCan, hintRow));
                             bPaste = false;
                             continue;
+                        } else if (validText !== null) {
+                            if(!Number.isInteger(num) && !/^(\d{1,10}\.\d{1,3})?$/.test(num)) {
+                                toastMessageUniq(getPasteHint(hint.numberCan3, hintRow));
+                            } else if (validText.toString().length > 11) {
+                                toastMessageUniq(getPasteHint(hint.numberCan4, hintRow));
+                            }
+                            // SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                            // return;
+                            validText = ZhCalc.round(num, 3);
                         }
                     }
                     if (colSetting.field === 'weight_num') {
@@ -357,11 +381,15 @@ $(document).ready(() => {
                             bPaste = false;
                             continue;
                         }
-                        const num = parseFloat(validText);
-                        if (validText !== null && (num < 0 || num >= 1 || !/^\d+(\.\d{1,3})?$/.test(num))) {
+                        let num = parseFloat(validText);
+                        if (validText !== null && (num < 0 || num >= 1)) {
                             toastMessageUniq(getPasteHint(hint.numberCan2, hintRow));
                             bPaste = false;
                             continue;
+                        } else if (validText !== null && num > 0 && num < 1 && !/^\d+(\.\d{1,3})?$/.test(num)) {
+                            toastMessageUniq(getPasteHint(hint.numberCan3, hintRow));
+                            validText = ZhCalc.round(num, 3);
+                            num = ZhCalc.round(num, 3);
                         }
                         const total_weight = ZhCalc.add(ZhCalc.sub(_.sumBy(materialExponentData, 'weight_num'), parseFloat(orgValue)), num);
                         if (total_weight > 1) {

+ 46 - 24
app/public/js/material_list.js

@@ -153,24 +153,20 @@ $(document).ready(() => {
         font: '12px 微软雅黑',
         readOnly: true,
     };
-    // 解析清单汇总数据
-    gclGatherModel.loadLedgerData(ledger, curLedgerData);
-    gclGatherModel.loadPosData(pos, curPosData);
-    let gclGatherData = gclGatherModel.gatherGclData().filter(item => {
-        return item.qc_qty || item.contract_qty
-    });
-    calculateJiaCha(gclGatherData);
-
     // let gclGatherData = gclGatherModel.gatherGclData()
     // 获取项目节数据
     function loadLeafXmjData(iGclRow) {
         const gcl = gclGatherData[iGclRow];
         if (gcl) {
+            gcl.leafXmjs = gcl.leafXmjs.filter(item => {
+                return item.qc_qty || item.contract_qty
+            });
             for (const [index, xmj] of gcl.leafXmjs.entries()) {
                 const jiacha = calcOneBQJC(xmj);
                 gcl.leafXmjs[index].jiacha = jiacha !== 0 ? ZhCalc.round(jiacha, 2) : null;
             }
             SpreadJsObj.loadSheetData(leafXmjSpread.getActiveSheet(), SpreadJsObj.DataType.Data, gcl.leafXmjs);
+
             // 对清单调差工料table的单位数量进行改变
             materialSpreadSetting.cols[materialSpreadSetting.cols.length - 2].title = '|' + gcl.unit + '数量 �';
             // SpreadJsObj.initSheet(materialSpread.getActiveSheet(), materialSpreadSetting);
@@ -205,6 +201,32 @@ $(document).ready(() => {
         font: '12px 微软雅黑',
         readOnly: true,
     };
+    // 加载清单数据 - 暂时统一加载,如有需要,切换成动态加载并缓存
+    postData(window.location.pathname + '/load', {}, function (result) {
+        ledger = result.ledger;
+        curLedgerData = result.curLedgerData;
+        pos = result.pos;
+        curPosData = result.curPosData;
+        materialListData = result.materialListData;
+        notJoinList = result.materialNotJoinListData;
+        // 解析清单汇总数据
+        gclGatherModel.loadLedgerData(ledger, curLedgerData);
+        gclGatherModel.loadPosData(pos, curPosData);
+        gclGatherData = gclGatherModel.gatherGclData().filter(item => {
+            return item.qc_qty || item.contract_qty
+        });
+        calculateJiaCha(gclGatherData);
+        SpreadJsObj.initSheet(leafXmjSpread.getActiveSheet(), leafXmjSpreadSetting);
+        // 加载清单数据
+        SpreadJsObj.loadSheetData(ledgerSpread.getActiveSheet(), SpreadJsObj.DataType.Data, gclGatherData);
+        loadLeafXmjData(0);
+        loadMaterialData(0, 0);
+        const sheet = materialSpread.getActiveSheet();
+        sheet.suspendPaint();
+        sheet.setCellType(1, 3, new TipCellType(), spreadNS.SheetArea.colHeader);
+        sheet.resumePaint();
+        checkNotJoinMaterialData();
+    });
     // const leafXmjCol = {
     //     getValue: {
     //         jiacha: function (data) {
@@ -222,9 +244,6 @@ $(document).ready(() => {
     //     }
     // };
     // SpreadJsObj.initSpreadSettingEvents(leafXmjSpreadSetting, leafXmjCol);
-    SpreadJsObj.initSheet(leafXmjSpread.getActiveSheet(), leafXmjSpreadSetting);
-    // 加载清单数据
-    SpreadJsObj.loadSheetData(ledgerSpread.getActiveSheet(), SpreadJsObj.DataType.Data, gclGatherData);
 
     // 调差清单工料table
     const materialSpread = SpreadJsObj.createNewSpread($('#material-spread')[0]);
@@ -283,12 +302,12 @@ $(document).ready(() => {
     }
 
     // SpreadJsObj.locateTreeNode(ledgerSpread.getActiveSheet(), )
-    loadLeafXmjData(0);
-    loadMaterialData(0, 0);
-    const sheet = materialSpread.getActiveSheet();
-    sheet.suspendPaint();
-    sheet.setCellType(1, 3, new TipCellType(), spreadNS.SheetArea.colHeader);
-    sheet.resumePaint();
+    // loadLeafXmjData(0);
+    // loadMaterialData(0, 0);
+    // const sheet = materialSpread.getActiveSheet();
+    // sheet.suspendPaint();
+    // sheet.setCellType(1, 3, new TipCellType(), spreadNS.SheetArea.colHeader);
+    // sheet.resumePaint();
     // 不参与调差数据值变灰
     function checkNotJoinMaterialData() {
         const sheet = ledgerSpread.getActiveSheet();
@@ -304,7 +323,7 @@ $(document).ready(() => {
             }
         }
     }
-    checkNotJoinMaterialData();
+    // checkNotJoinMaterialData();
     // 对添加工料表格赋值
     function changeMaterialTable() {
         $('#materialBills tr').removeClass('table-secondary');
@@ -572,9 +591,11 @@ $(document).ready(() => {
                     }
                     const num = parseFloat(exprQuantity.quantity);
                     if (num < 0 || !/^\d+(\.\d{1,6})?$/.test(num)) {
-                        toastr.error('数量值必须大于0并且小于6位小数的浮点数');
-                        SpreadJsObj.reLoadRowData(info.sheet, info.row);
-                        return;
+                        // toastr.error('数量值必须大于0并且小于6位小数的浮点数');
+                        // SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                        // return;
+                        toastr.warning('已保留6位小数');
+                        exprQuantity.quantity = ZhCalc.round(num, 6);
                     }
                     // 更新至服务器
                     const ledgerSheet = ledgerSpread.getActiveSheet();
@@ -625,7 +646,7 @@ $(document).ready(() => {
                 const hint = {
                     cellError: {type: 'error', msg: '粘贴内容超出了表格范围'},
                     numberExpr: {type: 'error', msg: '不能粘贴其它非数字类型字符'},
-                    numberCan: {type: 'error', msg: '请粘贴大于0并且小于6位小数的浮点数'},
+                    numberCan: {type: 'warning', msg: '已保留6位小数'},
                 };
                 const range = info.cellRange;
                 const sortData = info.sheet.zh_data || [];
@@ -682,8 +703,9 @@ $(document).ready(() => {
                         const num = parseFloat(exprQuantity.quantity);
                         if (num < 0 || !/^\d+(\.\d{1,6})?$/.test(num)) {
                             toastMessageUniq(getPasteHint(hint.numberCan, hintRow));
-                            bPaste = false;
-                            continue;
+                            // bPaste = false;
+                            // continue;
+                            exprQuantity.quantity = ZhCalc.round(num, 6);
                         }
                         // materialData[colSetting.field] = validText;
                         materialData.expr = exprQuantity.expr;

+ 452 - 0
app/public/js/se_safe_prod.js

@@ -0,0 +1,452 @@
+'use strict';
+
+/**
+ * 安全生产
+ *
+ * @author Mai
+ * @date 2021/10/21
+ * @version
+ */
+
+$(document).ready(() => {
+    autoFlashHeight();
+
+    const safeSpreadSetting = {
+        cols: [
+            {title: '名称', colSpan: '1', rowSpan: '2', field: 'name', hAlign: 0, width: 350, formatter: '@'},
+            {title: '单位', colSpan: '1', rowSpan: '2', field: 'unit', hAlign: 1, width: 60, formatter: '@', cellType: 'unit'},
+            {title: '单价', colSpan: '1', rowSpan: '2', field: 'unit_price', hAlign: 2, width: 60, type: 'Number'},
+            {title: '计划|数量', colSpan: '2|1', rowSpan: '1|1', field: 'quantity', hAlign: 2, width: 60, type: 'Number'},
+            {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'total_price', hAlign: 2, width: 60, type: 'Number', readOnly: true},
+            {title: '本期|数量', colSpan: '2|1', rowSpan: '1|1', field: 'qty', hAlign: 2, width: 60, type: 'Number'},
+            {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'tp', hAlign: 2, width: 60, type: 'Number', readOnly: true},
+            {title: '截止本期|数量', colSpan: '2|1', rowSpan: '1|1', field: 'end_qty', hAlign: 2, width: 60, type: 'Number', readOnly: true},
+            {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'end_tp', hAlign: 2, width: 60, type: 'Number', readOnly: true},
+            {title: '备注', colSpan: '1', rowSpan: '2', field: 'memo', hAlign: 0, width: 180, formatter: '@', cellType: 'ellipsisAutoTip'}
+        ],
+        emptyRows: readOnly ? 0 : 3,
+        headRows: 2,
+        headRowHeight: [25, 25],
+        defaultRowHeight: 21,
+        headerFont: '12px 微软雅黑',
+        font: '12px 微软雅黑',
+        readOnly: readOnly,
+        localCache: {
+            key: 'stage-extra-temp',
+            colWidth: true,
+        },
+        getColor: function (sheet, data, row, col, defaultColor) {
+            if (!data || !data.quantity) return defaultColor;
+
+            return data.quantity >= 0
+                ? data.end_qty > data.quantity ? '#f8d7da' : defaultColor
+                : data.end_qty < data.quantity ? '#f8d7da' : defaultColor;
+        }
+    };
+
+    const safeSpread = SpreadJsObj.createNewSpread($('#safe-prod-spread')[0]);
+    const safeSheet = safeSpread.getActiveSheet();
+    if (thousandth) sjsSettingObj.setTpThousandthFormat(safeSpreadSetting);
+    SpreadJsObj.initSheet(safeSheet, safeSpreadSetting);
+
+    $.subMenu({
+        menu: '#sub-menu', miniMenu: '#sub-mini-menu', miniMenuList: '#mini-menu-list',
+        toMenu: '#to-menu', toMiniMenu: '#to-mini-menu',
+        key: 'menu.1.0.0',
+        miniHint: '#sub-mini-hint', hintKey: 'menu.hint.1.0.1',
+        callback: function (info) {
+            if (info.mini) {
+                $('.panel-title').addClass('fluid');
+                $('#sub-menu').removeClass('panel-sidebar');
+            } else {
+                $('.panel-title').removeClass('fluid');
+                $('#sub-menu').addClass('panel-sidebar');
+            }
+            autoFlashHeight();
+            safeSpread.refresh();
+        }
+    });
+
+    class SeSafe {
+        constructor () {
+            this.data = [];
+        }
+        resortData() {
+            this.data.sort(function (a, b) {
+                return a.order - b.order;
+            });
+        }
+        calculateAll() {
+            for (const d of this.data) {
+                d.end_qty = ZhCalc.add(d.pre_qty, d.qty);
+                d.end_tp = ZhCalc.add(d.pre_tp, d.tp);
+            }
+        }
+        loadDatas(datas) {
+            this.data = datas;
+            this.calculateAll();
+            this.resortData();
+        }
+        loadUpdateData(updateData) {
+            if (updateData.add) {
+                for (const a of updateData.add) {
+                    this.data.push(a);
+                }
+            }
+            if (updateData.update) {
+                for (const u of updateData.update) {
+                    const d = this.data.find(function (x) {
+                        return u.id === x.id;
+                    });
+                    if (d) {
+                        _.assign(d, u);
+                    } else {
+                        this.data.push(d);
+                    }
+                }
+            }
+            if (updateData.del) {
+                _.remove(this.data, function (d) {
+                    return updateData.del.indexOf(d.id) >= 0;
+                });
+            }
+            this.calculateAll();
+            this.resortData();
+        }
+        sum () {
+            const result = {
+                total_price: 0,
+                tp: 0,
+                end_tp: 0,
+            };
+            for (const d of this.data) {
+                result.total_price = ZhCalc.add(result.total_price, d.total_price);
+                result.tp = ZhCalc.add(result.tp, d.tp);
+                result.end_tp = ZhCalc.add(result.end_tp, d.end_tp);
+            }
+            return result;
+        }
+    }
+    const seSafeObj = new SeSafe();
+    const refreshSum = function () {
+        const sum = seSafeObj.sum();
+        const html = [];
+        const getTrHtml = function (name, value) {
+            return '<tr><td>' + name + '</td><td class="text-right">' + (!checkZero(value) ? value : '') + ' </td></tr>';
+        };
+        html.push(getTrHtml('金额', sum.total_price));
+        html.push(getTrHtml('本期金额', sum.tp));
+        html.push(getTrHtml('截止本期金额', sum.end_tp));
+        $('#sum').html(html.join(' '));
+    };
+
+    postData(window.location.pathname + '/load', null, function (result) {
+        seSafeObj.loadDatas(result);
+        SpreadJsObj.loadSheetData(safeSheet, SpreadJsObj.DataType.Data, seSafeObj.data);
+        refreshSum();
+    });
+
+    if (!readOnly) {
+        const seSafeOprObj = {
+            /**
+             * 删除按钮响应事件
+             * @param sheet
+             */
+            deletePress: function (sheet) {
+                if (!sheet.zh_setting || readOnly) return;
+
+                const sortData = sheet.zh_data;
+                const datas = [];
+                const sels = sheet.getSelections();
+                if (!sels || !sels[0]) return;
+
+                for (let iRow = sels[0].row; iRow < sels[0].row + sels[0].rowCount; iRow++) {
+                    let bDel = false;
+                    const node = sortData[iRow];
+                    if (node) {
+                        const data = {id: node.id};
+                        for (let iCol = sels[0].col; iCol < sels[0].col + sels[0].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;
+                                bDel = true;
+                            }
+                        }
+                        if (bDel) {
+                            datas.push(data);
+                        }
+                    }
+                }
+                if (datas.length > 0) {
+                    postData(window.location.pathname + '/update', {update: datas}, function (result) {
+                        seSafeObj.loadUpdateData(result);
+                        SpreadJsObj.reLoadSheetData(safeSheet);
+                        refreshSum();
+                    }, function () {
+                        SpreadJsObj.reLoadSheetData(safeSheet);
+                    });
+                }
+            },
+            delete: function (sheet) {
+                if (!sheet.zh_setting || readOnly) return;
+
+                const sortData = sheet.zh_data;
+                const datas = [];
+                const sels = sheet.getSelections();
+                if (!sels || !sels[0]) return;
+                const hint = {
+                    isOld: {type: 'warning', msg: '该数据已计量,不可删除'},
+                    invalidDel: {type: 'warning', msg: '该数据为往期新增,只有原报可删除'},
+                };
+
+                for (let iRow = sels[0].row, iLen = sels[0].row + sels[0].rowCount; iRow < iLen; iRow++) {
+                    const node = sortData[iRow];
+                    if (node.pre_used || !checkZero(node.end_tp)) {
+                        toastMessageUniq(hint.isOld);
+                        continue;
+                    } else {
+                        if (node.add_sid !== stageId && stageUserId !== userID) {
+                            toastMessageUniq(hint.invalidDel);
+                            continue;
+                        }
+                        datas.push(node.id);
+                    }
+                }
+                if (datas.length > 0) {
+                    postData(window.location.pathname + '/update', {del: datas}, function (result) {
+                        seSafeObj.loadUpdateData(result);
+                        SpreadJsObj.reLoadSheetData(safeSheet);
+                        refreshSum();
+                    }, function () {
+                        SpreadJsObj.reLoadSheetData(safeSheet);
+                    });
+                }
+            },
+            editEnded: function (e, info) {
+                if (!info.sheet.zh_setting || !info.sheet.zh_data) return;
+
+                const node = info.sheet.zh_data[info.row];
+                const col = info.sheet.zh_setting.cols[info.col];
+                const data = {};
+
+                if (node) {
+                    data.update = {};
+                    data.update.id = node.id;
+
+                    const oldValue = node ? node[col.field] : null;
+                    const newValue = trimInvalidChar(info.editingText);
+                    if (oldValue == info.editingText || ((!oldValue || oldValue === '') && (newValue === ''))) {
+                        SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                        return;
+                    }
+                    data.update[col.field] = newValue;
+                } else {
+                    if (col.field !== 'name') {
+                        toastr.warning('请先输入名称');
+                        SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                        return;
+                    }
+                    data.add = {};
+                    data.add.order = info.row + 1;
+                    data.add.name = trimInvalidChar(info.editingText);
+                }
+
+                postData(window.location.pathname + '/update', data, function (result) {
+                    seSafeObj.loadUpdateData(result);
+                    SpreadJsObj.reLoadSheetData(info.sheet);
+                    refreshSum();
+                }, function () {
+                    SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                });
+            },
+            editStarting(e, info) {
+                if (!info.sheet.zh_setting || !info.sheet.zh_data) {
+                    info.cancel = true;
+                    return;
+                }
+
+                const col = info.sheet.zh_setting.cols[info.col];
+                const node = info.sheet.zh_data[info.row];
+                if (!node) return;
+
+                switch (col.field) {
+                    case 'unit':
+                    case 'unit_price':
+                    case 'quantity':
+                        info.cancel = readOnly || node.pre_used;
+                        break;
+                }
+            },
+            clipboardPasting(e, info) {
+                const setting = info.sheet.zh_setting, sortData = info.sheet.zh_data;
+                info.cancel = true;
+
+                if (!setting || !sortData) return;
+                const pasteData = info.pasteData.html
+                    ? SpreadJsObj.analysisPasteHtml(info.pasteData.html)
+                    : (info.pasteData.text === ''
+                        ? SpreadJsObj.Clipboard.getAnalysisPasteText()
+                        : SpreadJsObj.analysisPasteText(info.pasteData.text));
+                const hint = {
+                    name: {type: 'warning', msg: '名称不可为空,已过滤'},
+                };
+
+                const uDatas = [], iDatas = [];
+                for (let iRow = 0; iRow < info.cellRange.rowCount; iRow++) {
+                    const curRow = info.cellRange.row + iRow;
+                    const node = sortData[curRow];
+
+                    let bPaste = false;
+                    const data = {};
+                    for (let iCol = 0; iCol < info.cellRange.colCount; iCol++) {
+                        const curCol = info.cellRange.col + iCol;
+                        const colSetting = setting.cols[curCol];
+                        const value = trimInvalidChar(pasteData[iRow][iCol]);
+
+                        if (colSetting.field === 'name' && (!value || value === '')) {
+                            toastMessageUniq(hint.name);
+                            break;
+                        }
+                        if (colSetting.type === 'Number') {
+                            const num = _.toNumber(value);
+                            if (num) {
+                                data[colSetting.field] = num;
+                                bPaste = true;
+                            }
+                        } else {
+                            data[colSetting.field] = value;
+                            bPaste = true;
+                        }
+                    }
+                    if (bPaste) {
+                        if (node) {
+                            data.id = node.id;
+                            uDatas.push(data);
+                        } else {
+                            data.order = curRow + 1;
+                            iDatas.push(data);
+                        }
+                    }
+                }
+                const updateData = {};
+                if (uDatas.length > 0) updateData.update = uDatas;
+                if (iDatas.length > 0) updateData.add = iDatas;
+                if (uDatas.length > 0 || iDatas.length > 0) {
+                    postData(window.location.pathname + '/update', updateData, function (result) {
+                        seSafeObj.loadUpdateData(result);
+                        SpreadJsObj.reLoadSheetData(info.sheet);
+                        refreshSum();
+                    });
+                } else {
+                    SpreadJsObj.reLoadSheetData(info.sheet);
+                }
+            },
+            upMove: function () {
+                const sels = safeSheet.getSelections(), sortData = safeSheet.zh_data;
+                const node = sortData[sels[0].row];
+                const preNode = sortData[sels[0].row - 1];
+                const data = [
+                    {id: node.id, order: preNode.order},
+                    {id: preNode.id, order: node.order}
+                ];
+                postData(window.location.pathname + '/update', {update: data}, function (result) {
+                    seSafeObj.loadUpdateData(result);
+                    SpreadJsObj.reLoadRowsData(safeSheet, [sels[0].row, sels[0].row - 1]);
+                    safeSheet.setSelection(sels[0].row - 1, sels[0].col, sels[0].rowCount, sels[0].colCount);
+                });
+            },
+            downMove: function () {
+                const sels = safeSheet.getSelections(), sortData = safeSheet.zh_data;
+                const node = sortData[sels[0].row];
+                const nextNode = sortData[sels[0].row + 1];
+                const data = [
+                    {id: node.id, order: nextNode.order},
+                    {id: nextNode.id, order: node.order}
+                ];
+                postData(window.location.pathname + '/update', {update: data}, function (result) {
+                    seSafeObj.loadUpdateData(result);
+                    SpreadJsObj.reLoadRowsData(safeSheet, [sels[0].row, sels[0].row + 1]);
+                    safeSheet.setSelection(sels[0].row + 1, sels[0].col, sels[0].rowCount, sels[0].colCount);
+                });
+            }
+        };
+        safeSheet.bind(spreadNS.Events.EditEnded, seSafeOprObj.editEnded);
+        safeSheet.bind(spreadNS.Events.EditStarting, seSafeOprObj.editStarting);
+        safeSheet.bind(spreadNS.Events.ClipboardPasting, seSafeOprObj.clipboardPasting);
+        SpreadJsObj.addDeleteBind(safeSpread, seSafeOprObj.deletePress);
+        $.contextMenu({
+            selector: '#safe-prod-spread',
+            build: function ($trigger, e) {
+                const target = SpreadJsObj.safeRightClickSelection($trigger, e, safeSpread);
+                return target.hitTestType === spreadNS.SheetArea.viewport || target.hitTestType === spreadNS.SheetArea.rowHeader;
+            },
+            items: {
+                del: {
+                    name: '删除',
+                    icon: 'fa-remove',
+                    callback: function (key, opt) {
+                        seSafeOprObj.delete(safeSheet);
+                    },
+                    disabled: function (key, opt) {
+                        const sels = safeSheet.getSelections();
+                        if (!sels || !sels[0]) return true;
+
+                        const row = sels[0].row;
+                        const node = seSafeObj.data[row];
+                        return node === undefined || node === null;
+                    },
+                    visible: function (key, opt) {
+                        return !readOnly;
+                    }
+                },
+                sprDel: '------------',
+                upMove: {
+                    name: '上移',
+                    icon: 'fa-arrow-up',
+                    callback: function (key, opt) {
+                        seSafeOprObj.upMove();
+                    },
+                    disabled: function (key, opt) {
+                        const sels = safeSheet.getSelections();
+                        if (!sels || !sels[0] || sels[0].row === 0) return true;
+
+                        const row = sels[0].row;
+                        const node = seSafeObj.data[row];
+                        return node === undefined || node === null;
+                    },
+                    visible: function (key, opt) {
+                        return !readOnly;
+                    }
+                },
+                downMove: {
+                    name: '下移',
+                    icon: 'fa-arrow-down',
+                    callback: function (key, opt) {
+                        seSafeOprObj.downMove();
+                    },
+                    disabled: function (key, opt) {
+                        const sels = safeSheet.getSelections();
+                        if (!sels || !sels[0] || sels[0].row >= seSafeObj.data.length - 1) return true;
+
+                        const row = sels[0].row;
+                        const node = seSafeObj.data[row];
+                        return node === undefined || node === null;
+                    },
+                    visible: function (key, opt) {
+                        return !readOnly;
+                    }
+                }
+            },
+        })
+    }
+
+    $('#exportExcel').click(function () {
+        SpreadExcelObj.exportSimpleXlsxSheet(safeSpreadSetting, seSafeObj.data, $('.sidebar-title').attr('data-original-title') + "-安全生产.xlsx");
+    });
+});

+ 441 - 0
app/public/js/se_temp_land.js

@@ -0,0 +1,441 @@
+'use strict';
+
+/**
+ * 临时占地
+ *
+ * @author Mai
+ * @date 2021/10/21
+ * @version
+ */
+
+$(document).ready(() => {
+    autoFlashHeight();
+
+    const tempSpreadSetting = {
+        cols: [
+            {title: '名称', colSpan: '1', rowSpan: '2', field: 'name', hAlign: 0, width: 350, formatter: '@'},
+            {title: '单位', colSpan: '1', rowSpan: '2', field: 'unit', hAlign: 1, width: 60, formatter: '@', cellType: 'unit'},
+            {title: '单价', colSpan: '1', rowSpan: '2', field: 'unit_price', hAlign: 2, width: 60, type: 'Number'},
+            {title: '本期|数量', colSpan: '2|1', rowSpan: '1|1', field: 'qty', hAlign: 2, width: 60, type: 'Number'},
+            {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'tp', hAlign: 2, width: 60, type: 'Number', readOnly: true},
+            {title: '截止本期|数量', colSpan: '2|1', rowSpan: '1|1', field: 'end_qty', hAlign: 2, width: 60, type: 'Number', readOnly: true},
+            {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'end_tp', hAlign: 2, width: 60, type: 'Number', readOnly: true},
+            {title: '备注', colSpan: '1', rowSpan: '2', field: 'memo', hAlign: 0, width: 180, formatter: '@', cellType: 'ellipsisAutoTip'}
+        ],
+        emptyRows: readOnly ? 0 : 3,
+        headRows: 2,
+        headRowHeight: [25, 25],
+        defaultRowHeight: 21,
+        headerFont: '12px 微软雅黑',
+        font: '12px 微软雅黑',
+        readOnly: readOnly,
+        localCache: {
+            key: 'stage-extra-temp',
+            colWidth: true,
+        }
+    };
+
+    const TempSpread = SpreadJsObj.createNewSpread($('#temp-land-spread')[0]);
+    const tempSheet = TempSpread.getActiveSheet();
+    if (thousandth) sjsSettingObj.setTpThousandthFormat(tempSpreadSetting);
+    SpreadJsObj.initSheet(tempSheet, tempSpreadSetting);
+
+    $.subMenu({
+        menu: '#sub-menu', miniMenu: '#sub-mini-menu', miniMenuList: '#mini-menu-list',
+        toMenu: '#to-menu', toMiniMenu: '#to-mini-menu',
+        key: 'menu.1.0.0',
+        miniHint: '#sub-mini-hint', hintKey: 'menu.hint.1.0.1',
+        callback: function (info) {
+            if (info.mini) {
+                $('.panel-title').addClass('fluid');
+                $('#sub-menu').removeClass('panel-sidebar');
+            } else {
+                $('.panel-title').removeClass('fluid');
+                $('#sub-menu').addClass('panel-sidebar');
+            }
+            autoFlashHeight();
+            TempSpread.refresh();
+        }
+    });
+
+    class SeTemp {
+        constructor () {
+            this.data = [];
+        }
+        resortData() {
+            this.data.sort(function (a, b) {
+                return a.order - b.order;
+            });
+        }
+        calculateAll() {
+            for (const d of this.data) {
+                d.end_qty = ZhCalc.add(d.pre_qty, d.qty);
+                d.end_tp = ZhCalc.add(d.pre_tp, d.tp);
+            }
+        }
+        loadDatas(datas) {
+            this.data = datas;
+            this.calculateAll();
+            this.resortData();
+        }
+        loadUpdateData(updateData) {
+            if (updateData.add) {
+                for (const a of updateData.add) {
+                    this.data.push(a);
+                }
+            }
+            if (updateData.update) {
+                for (const u of updateData.update) {
+                    const d = this.data.find(function (x) {
+                        return u.id === x.id;
+                    });
+                    if (d) {
+                        _.assign(d, u);
+                    } else {
+                        this.data.push(d);
+                    }
+                }
+            }
+            if (updateData.del) {
+                _.remove(this.data, function (d) {
+                    return updateData.del.indexOf(d.id) >= 0;
+                });
+            }
+            this.calculateAll();
+            this.resortData();
+        }
+        sum () {
+            const result = {
+                total_price: 0,
+                tp: 0,
+                end_tp: 0,
+            };
+            for (const d of this.data) {
+                result.total_price = ZhCalc.add(result.total_price, d.total_price);
+                result.tp = ZhCalc.add(result.tp, d.tp);
+                result.end_tp = ZhCalc.add(result.end_tp, d.end_tp);
+            }
+            return result;
+        }
+    }
+    const seTempObj = new SeTemp();
+    const refreshSum = function () {
+        const sum = seTempObj.sum();
+        const html = [];
+        const getTrHtml = function (name, value) {
+            return '<tr><td>' + name + '</td><td class="text-right">' + (!checkZero(value) ? value : '') + ' </td></tr>';
+        };
+        html.push(getTrHtml('本期金额', sum.tp));
+        html.push(getTrHtml('截止本期金额', sum.end_tp));
+        $('#sum').html(html.join(' '));
+    };
+
+    postData(window.location.pathname + '/load', null, function (result) {
+        seTempObj.loadDatas(result);
+        SpreadJsObj.loadSheetData(tempSheet, SpreadJsObj.DataType.Data, seTempObj.data);
+        refreshSum();
+    });
+
+    if (!readOnly) {
+        const seTempOprObj = {
+            /**
+             * 删除按钮响应事件
+             * @param sheet
+             */
+            deletePress: function (sheet) {
+                if (!sheet.zh_setting || readOnly) return;
+
+                const sortData = sheet.zh_data;
+                const datas = [];
+                const sels = sheet.getSelections();
+                if (!sels || !sels[0]) return;
+
+                for (let iRow = sels[0].row; iRow < sels[0].row + sels[0].rowCount; iRow++) {
+                    let bDel = false;
+                    const node = sortData[iRow];
+                    if (node) {
+                        const data = {id: node.id};
+                        for (let iCol = sels[0].col; iCol < sels[0].col + sels[0].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;
+                                bDel = true;
+                            }
+                        }
+                        if (bDel) {
+                            datas.push(data);
+                        }
+                    }
+                }
+                if (datas.length > 0) {
+                    postData(window.location.pathname + '/update', {update: datas}, function (result) {
+                        seTempObj.loadUpdateData(result);
+                        SpreadJsObj.reLoadSheetData(tempSheet);
+                        refreshSum();
+                    }, function () {
+                        SpreadJsObj.reLoadSheetData(tempSheet);
+                    });
+                }
+            },
+            delete: function (sheet) {
+                if (!sheet.zh_setting || readOnly) return;
+
+                const sortData = sheet.zh_data;
+                const datas = [];
+                const sels = sheet.getSelections();
+                if (!sels || !sels[0]) return;
+                const hint = {
+                    isOld: {type: 'warning', msg: '该数据已计量,不可删除'},
+                    invalidDel: {type: 'warning', msg: '该数据为往期新增,只有原报可删除'},
+                };
+
+                for (let iRow = sels[0].row, iLen = sels[0].row + sels[0].rowCount; iRow < iLen; iRow++) {
+                    const node = sortData[iRow];
+                    if (node.pre_used || !checkZero(node.end_tp)) {
+                        toastMessageUniq(hint.isOld);
+                        continue;
+                    } else {
+                        if (node.add_sid !== stageId && stageUserId !== userID) {
+                            toastMessageUniq(hint.invalidDel);
+                            continue;
+                        }
+                        datas.push(node.id);
+                    }
+                }
+                if (datas.length > 0) {
+                    postData(window.location.pathname + '/update', {del: datas}, function (result) {
+                        seTempObj.loadUpdateData(result);
+                        SpreadJsObj.reLoadSheetData(tempSheet);
+                        refreshSum();
+                    }, function () {
+                        SpreadJsObj.reLoadSheetData(tempSheet);
+                    });
+                }
+            },
+            editEnded: function (e, info) {
+                if (!info.sheet.zh_setting || !info.sheet.zh_data) return;
+
+                const node = info.sheet.zh_data[info.row];
+                const col = info.sheet.zh_setting.cols[info.col];
+                const data = {};
+
+                if (node) {
+                    data.update = {};
+                    data.update.id = node.id;
+
+                    const oldValue = node ? node[col.field] : null;
+                    const newValue = trimInvalidChar(info.editingText);
+                    if (oldValue == info.editingText || ((!oldValue || oldValue === '') && (newValue === ''))) {
+                        SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                        return;
+                    }
+                    data.update[col.field] = newValue;
+                } else {
+                    if (col.field !== 'name') {
+                        toastr.warning('请先输入名称');
+                        SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                        return;
+                    }
+                    data.add = {};
+                    data.add.order = info.row + 1;
+                    data.add.name = trimInvalidChar(info.editingText);
+                }
+
+                postData(window.location.pathname + '/update', data, function (result) {
+                    seTempObj.loadUpdateData(result);
+                    SpreadJsObj.reLoadSheetData(info.sheet);
+                    refreshSum();
+                }, function () {
+                    SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                });
+            },
+            editStarting(e, info) {
+                if (!info.sheet.zh_setting || !info.sheet.zh_data) {
+                    info.cancel = true;
+                    return;
+                }
+
+                const col = info.sheet.zh_setting.cols[info.col];
+                const node = info.sheet.zh_data[info.row];
+                if (!node) return;
+
+                switch (col.field) {
+                    case 'unit':
+                    case 'unit_price':
+                        info.cancel = readOnly || node.pre_used;
+                        break;
+                }
+            },
+            clipboardPasting(e, info) {
+                const setting = info.sheet.zh_setting, sortData = info.sheet.zh_data;
+                info.cancel = true;
+
+                if (!setting || !sortData) return;
+                const pasteData = info.pasteData.html
+                    ? SpreadJsObj.analysisPasteHtml(info.pasteData.html)
+                    : (info.pasteData.text === ''
+                        ? SpreadJsObj.Clipboard.getAnalysisPasteText()
+                        : SpreadJsObj.analysisPasteText(info.pasteData.text));
+                const hint = {
+                    name: {type: 'warning', msg: '名称不可为空,已过滤'},
+                };
+
+                const uDatas = [], iDatas = [];
+                for (let iRow = 0; iRow < info.cellRange.rowCount; iRow++) {
+                    const curRow = info.cellRange.row + iRow;
+                    const node = sortData[curRow];
+
+                    let bPaste = false;
+                    const data = {};
+                    for (let iCol = 0; iCol < info.cellRange.colCount; iCol++) {
+                        const curCol = info.cellRange.col + iCol;
+                        const colSetting = setting.cols[curCol];
+                        const value = trimInvalidChar(pasteData[iRow][iCol]);
+
+                        if (colSetting.field === 'name' && (!value || value === '')) {
+                            toastMessageUniq(hint.name);
+                            break;
+                        }
+                        if (colSetting.type === 'Number') {
+                            const num = _.toNumber(value);
+                            if (num) {
+                                data[colSetting.field] = num;
+                                bPaste = true;
+                            }
+                        } else {
+                            data[colSetting.field] = value;
+                            bPaste = true;
+                        }
+                    }
+                    if (bPaste) {
+                        if (node) {
+                            data.id = node.id;
+                            uDatas.push(data);
+                        } else {
+                            data.order = curRow + 1;
+                            iDatas.push(data);
+                        }
+                    }
+                }
+                const updateData = {};
+                if (uDatas.length > 0) updateData.update = uDatas;
+                if (iDatas.length > 0) updateData.add = iDatas;
+                if (uDatas.length > 0 || iDatas.length > 0) {
+                    postData(window.location.pathname + '/update', updateData, function (result) {
+                        seTempObj.loadUpdateData(result);
+                        SpreadJsObj.reLoadSheetData(info.sheet);
+                        refreshSum();
+                    });
+                } else {
+                    SpreadJsObj.reLoadSheetData(info.sheet);
+                }
+            },
+            upMove: function () {
+                const sels = tempSheet.getSelections(), sortData = tempSheet.zh_data;
+                const node = sortData[sels[0].row];
+                const preNode = sortData[sels[0].row - 1];
+                const data = [
+                    {id: node.id, order: preNode.order},
+                    {id: preNode.id, order: node.order}
+                ];
+                postData(window.location.pathname + '/update', {update: data}, function (result) {
+                    seTempObj.loadUpdateData(result);
+                    SpreadJsObj.reLoadRowsData(tempSheet, [sels[0].row, sels[0].row - 1]);
+                    tempSheet.setSelection(sels[0].row - 1, sels[0].col, sels[0].rowCount, sels[0].colCount);
+                });
+            },
+            downMove: function () {
+                const sels = tempSheet.getSelections(), sortData = tempSheet.zh_data;
+                const node = sortData[sels[0].row];
+                const nextNode = sortData[sels[0].row + 1];
+                const data = [
+                    {id: node.id, order: nextNode.order},
+                    {id: nextNode.id, order: node.order}
+                ];
+                postData(window.location.pathname + '/update', {update: data}, function (result) {
+                    seTempObj.loadUpdateData(result);
+                    SpreadJsObj.reLoadRowsData(tempSheet, [sels[0].row, sels[0].row + 1]);
+                    tempSheet.setSelection(sels[0].row + 1, sels[0].col, sels[0].rowCount, sels[0].colCount);
+                });
+            }
+        };
+        tempSheet.bind(spreadNS.Events.EditEnded, seTempOprObj.editEnded);
+        tempSheet.bind(spreadNS.Events.EditStarting, seTempOprObj.editStarting);
+        tempSheet.bind(spreadNS.Events.ClipboardPasting, seTempOprObj.clipboardPasting);
+        SpreadJsObj.addDeleteBind(TempSpread, seTempOprObj.deletePress);
+        $.contextMenu({
+            selector: '#temp-land-spread',
+            build: function ($trigger, e) {
+                const target = SpreadJsObj.safeRightClickSelection($trigger, e, TempSpread);
+                return target.hitTestType === spreadNS.SheetArea.viewport || target.hitTestType === spreadNS.SheetArea.rowHeader;
+            },
+            items: {
+                del: {
+                    name: '删除',
+                    icon: 'fa-remove',
+                    callback: function (key, opt) {
+                        seTempOprObj.delete(tempSheet);
+                    },
+                    disabled: function (key, opt) {
+                        const sels = tempSheet.getSelections();
+                        if (!sels || !sels[0]) return true;
+
+                        const row = sels[0].row;
+                        const node = seTempObj.data[row];
+                        return node === undefined || node === null;
+                    },
+                    visible: function (key, opt) {
+                        return !readOnly;
+                    }
+                },
+                sprDel: '------------',
+                upMove: {
+                    name: '上移',
+                    icon: 'fa-arrow-up',
+                    callback: function (key, opt) {
+                        seTempOprObj.upMove();
+                    },
+                    disabled: function (key, opt) {
+                        const sels = tempSheet.getSelections();
+                        if (!sels || !sels[0] || sels[0].row === 0) return true;
+
+                        const row = sels[0].row;
+                        const node = seTempObj.data[row];
+                        return node === undefined || node === null;
+                    },
+                    visible: function (key, opt) {
+                        return !readOnly;
+                    }
+                },
+                downMove: {
+                    name: '下移',
+                    icon: 'fa-arrow-down',
+                    callback: function (key, opt) {
+                        seTempOprObj.downMove();
+                    },
+                    disabled: function (key, opt) {
+                        const sels = tempSheet.getSelections();
+                        if (!sels || !sels[0] || sels[0].row >= seTempObj.data.length - 1) return true;
+
+                        const row = sels[0].row;
+                        const node = seTempObj.data[row];
+                        return node === undefined || node === null;
+                    },
+                    visible: function (key, opt) {
+                        return !readOnly;
+                    }
+                }
+            },
+        })
+    }
+
+    $('#exportExcel').click(function () {
+        SpreadExcelObj.exportSimpleXlsxSheet(tempSpreadSetting, seTempObj.data, $('.sidebar-title').attr('data-original-title') + "-临时占地.xlsx");
+    });
+});

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

@@ -93,13 +93,14 @@ const SpreadJsObj = {
         return spread;
     },
     addCutEvents: function (spread, fun) {
+        spread.cutFun = fun;
         const cut = spreadNS.Commands.copy.execute;
         spreadNS.Commands.cut.execute = function (context, options, isUndo) {
             const sheet = context.getActiveSheet();
             const sels = sheet.getSelections();
             const sel = sels ? sels[0] : null;
             if (sel) {
-                return fun(sheet, sel, function () {
+                return context.cutFun(sheet, sel, function (isUndo) {
                     cut(context, options, isUndo);
                 });
             }

+ 6 - 5
app/public/js/sr_detail.js

@@ -545,7 +545,7 @@ $(document).ready(() => {
         SpreadJsObj.resetTopAndSelect(spSpread.getActiveSheet());
         // 加载中间计量
         stageIm.init(relaStage, imType, tenderInfo.decimal);
-        stageIm.loadData(result.ledgerData, result.posData, result.detailData, result.changeData);
+        stageIm.loadData4Rela(result.ledgerData, result.posData, result.detailData, result.changeData);
     }, null, true);
     spSpread.bind(spreadNS.Events.SelectionChanged, stagePosSpreadObj.selectionChanged);
 
@@ -780,6 +780,7 @@ $(document).ready(() => {
 
             this.spread.bind(spreadNS.Events.SelectionChanged, this.detailObj.selectionChanged);
 
+            this._initImTypeSetRela();
             this._initLocateRela();
             this.reBuildImData();
         }
@@ -810,14 +811,14 @@ $(document).ready(() => {
                 const jlCol = self.spreadSetting.cols.find(function (x) {return x.field === 'jl'});
                 jlCol.title = '本期计量金额';
                 SpreadJsObj.reLoadSheetHeader(self.sheet);
-                $('#type-title-contract').text('本期合同计量金额');
-                $('#type-title-qc').text('本期变更计量金额');
+                $('[name=type-title-contract]').text('本期合同计量金额');
+                $('[name=type-title-qc]').text('本期变更计量金额');
             } else {
                 const jlCol = self.spreadSetting.cols.find(function (x) {return x.field === 'jl'});
                 jlCol.title = '本期计量数量';
                 SpreadJsObj.reLoadSheetHeader(self.sheet);
-                $('#type-title-contract').text('本期合同计量数量');
-                $('#type-title-qc').text('本期变更计量数量');
+                $('[name=type-title-contract]').text('本期合同计量数量');
+                $('[name=type-title-qc]').text('本期变更计量数量');
             }
             if (relaStage.im_type === imType.bb.value || relaStage.im_type === imType.bw.value) {
                 $('#show-jldy').parent().show();

+ 211 - 23
app/public/js/stage.js

@@ -676,7 +676,7 @@ $(document).ready(() => {
                 if (def && def.color) return def.color;
             }
 
-            return checkUtils.billsOver(data, checkTzMeasureType(), stagePos) ? '#f8d7da' : defaultColor;
+            return hintOver && checkUtils.billsOver(data, checkTzMeasureType(), stagePos) ? '#f8d7da' : defaultColor;
         } else {
             return defaultColor;
         }
@@ -770,7 +770,7 @@ $(document).ready(() => {
             }
         }
         if (checkTzMeasureType()) {
-            return checkUtils.posOver(data)  ? '#f8d7da' : defaultColor;
+            return hintOver && checkUtils.posOver(data)  ? '#f8d7da' : defaultColor;
         }
     };
     sjsSettingObj.setGridSelectStyle(posSpreadSetting);
@@ -847,6 +847,9 @@ $(document).ready(() => {
     });
 
     const stageTreeSpreadObj = {
+        cut: function (sheet, sel, callback) {
+            callback(true);
+        },
         loadExprToInput(sheet) {
             const sel = sheet.getSelections()[0];
             const col = sheet.zh_setting.cols[sel.col], cell = sheet.getCell(sel.row, sel.col);
@@ -1396,6 +1399,7 @@ $(document).ready(() => {
     slSpread.bind(spreadNS.Events.TopRowChanged, stageTreeSpreadObj.topRowChanged);
     slSpread.bind(spreadNS.Events.EditStarting, stageTreeSpreadObj.editStarting);
     slSpread.bind(spreadNS.Events.ButtonClicked, stageTreeSpreadObj.buttonClicked);
+    SpreadJsObj.addCutEvents(slSpread, stageTreeSpreadObj.cut);
     SpreadJsObj.addDeleteBind(slSpread, stageTreeSpreadObj.deletePress);
     if (!readOnly) {
         $('#bills-expr').bind('change onblur', function () {
@@ -1571,9 +1575,12 @@ $(document).ready(() => {
             shoufangdanSpr: '---',
             shoufangdan: {
                 name: '生成收方单',
+                visible: function(key, opt) {
+                   if (cur_uid === stage.user_id) return true;
+                },
                 disabled: function (key, opt) {
                     const node = SpreadJsObj.getSelectObject(slSpread.getActiveSheet());
-                    if (!node || !node.b_code || !node.is_leaf) return true;
+                    if (!node || !node.b_code || !node.is_leaf || cur_uid !== stage.user_id) return true;
                 },
                 callback: function (key, opt) {
                     const node = SpreadJsObj.getSelectObject(slSpread.getActiveSheet());
@@ -2049,7 +2056,7 @@ $(document).ready(() => {
         SpreadJsObj.resetTopAndSelect(spSpread.getActiveSheet());
         // 加载中间计量
         stageIm.init(stage, imType, tenderInfo.decimal);
-        stageIm.loadData(result.ledgerData, result.posData, result.detailData, result.changeData);
+        stageIm.loadData(result.ledgerData, result.posData, result.detailData, result.changeData, result.detailAtt);
 
         errorList.loadHisErrorData();
         checkList.loadHisCheckData();
@@ -2206,9 +2213,12 @@ $(document).ready(() => {
             },
             'shoufangdan': {
                 name: '生成收方单',
+                visible: function(key, opt) {
+                    if (cur_uid === stage.user_id) return true;
+                },
                 disabled: function (key, opt) {
                     const node = SpreadJsObj.getSelectObject(spSpread.getActiveSheet());
-                    if (!node) return true;
+                    if (!node || cur_uid !== stage.user_id) return true;
                 },
                 callback: function (key, opt) {
                     const node = SpreadJsObj.getSelectObject(spSpread.getActiveSheet());
@@ -2463,6 +2473,7 @@ $(document).ready(() => {
     class Detail {
         constructor (obj) {
             const self = this;
+
             this.spreadSetting = {
                 cols: [
                     {title: '编号', colSpan: '1', rowSpan: '1', field: 'code', hAlign: 0, width: 80, formatter: '@', readOnly: true},
@@ -2472,6 +2483,12 @@ $(document).ready(() => {
                         title: stage.im_type === imType.tz.value ? '本期计量金额' : '本期计量数量',
                         colSpan: '1', rowSpan: '1', field: 'jl', hAlign: 2, width: 85, formatter: '@', readOnly: true
                     },
+                    {
+                        title: '附件', colSpan: '1', rowSpan: '1', field: 'attachment', hAlign: 0, width: 60, readOnly: true, cellType: 'imageBtn',
+                        normalImg: '#rela-file-icon', hoverImg: '#rela-file-hover', getValue: function (data) {
+                            return data.attachment ? data.attachment.length : 0;
+                        }
+                    },
                 ],
                 headRows: 1,
                 emptyRows: 0,
@@ -2482,6 +2499,10 @@ $(document).ready(() => {
                 font: '12px 微软雅黑',
                 readOnly: readOnly,
                 selectedBackColor: '#fffacd',
+                imageClick: function (data) {
+                    self.makeAttTable(data);
+                    $('#im-file').modal('show');
+                }
             };
             this.spread = SpreadJsObj.createNewSpread(obj[0]);
             this.sheet = this.spread.getActiveSheet();
@@ -2515,6 +2536,7 @@ $(document).ready(() => {
                                 updateData.unit_price = data.unit_price;
                                 updateData.pid = data.pid;
                                 updateData.pos_name = data.pos_name;
+                                updateData.im_type = data.im_type;
                             }
                             if (data.custom_define.indexOf('doc_code') === -1) {
                                 updateData.custom_define = data.custom_define;
@@ -2561,6 +2583,7 @@ $(document).ready(() => {
                                     updateData.unit_price = data.unit_price;
                                     updateData.pid = data.pid;
                                     updateData.pos_name = data.pos_name;
+                                    updateData.im_type = data.im_type;
                                 }
                                 if (data.custom_define.indexOf('doc_code') === -1) {
                                     updateData.custom_define = data.custom_define;
@@ -2633,6 +2656,7 @@ $(document).ready(() => {
                                 updateData.unit_price = data.unit_price;
                                 updateData.pid = data.pid;
                                 updateData.pos_name = data.pos_name;
+                                updateData.im_type = data.im_type;
                             }
                             if (regRst) {
                                 updateData.doc_code = text.substr(0, regRst.index) + (parseInt(regRst[0]) + iRow + 1);
@@ -2669,10 +2693,92 @@ $(document).ready(() => {
             this._initImTypeSetRela();
             this._initModifyDetail();
             this._initLocateRela();
+            this._initAttRela();
             // 草图相关
             this._initImageRela();
             this.reBuildImData();
         }
+        makeAttTable (data) {
+            let html = [];
+            if (data.attachment) {
+                for (const att of data.attachment) {
+                    const delHtml = (parseInt(att.uid) === userID && (att.renew || stage.status !== auditConst.status.checked))
+                        ? '<a class="delete-att text-danger" href="javascript:void(0);" data-imid="'+ data.att_uuid +'" data-attid="'+ att.file_id +'" title="删除"><i class="fa fa-remove "></i></a>'
+                        : '';
+                    html.push('<tr><td style="width: 200px">' + att.filename + att.fileext + '</td><td>' + att.username + '</td><td>' + att.in_time + '</td>',
+                        '<td><a href="'+ window.location.pathname + '/im-file/download?im_id='+ data.att_uuid +'&file_id='+ att.file_id +'" title="下载"><i class="fa fa-download "></i></a> ',
+                        delHtml, '</td></tr>');
+                }
+            }
+            $('#im-attList').html(html.join(''));
+        };
+        _initAttRela() {
+            // 上传附件
+            const self = this;
+            $('#upload-im-file').change(function () {
+                const files = this.files;
+                const sels = self.sheet.getSelections();
+                const select = SpreadJsObj.getSelectObject(self.sheet);
+                if (!select) return;
+
+                const formData = new FormData();
+                const baseInfo = {};
+                if (select.att_uuid) {
+                    baseInfo.uuid = select.att_uuid;
+                } else {
+                    baseInfo.im_type = select.im_type;
+                    baseInfo.lid = select.lid;
+                    baseInfo.pid = select.pid;
+                    baseInfo.code = select.code;
+                    baseInfo.name = select.name;
+                    baseInfo.unit = select.unit;
+                    baseInfo.unit_price = select.unit_price;
+                    baseInfo.pos_name = select.pos_name;
+                }
+                formData.append('base', JSON.stringify(baseInfo));
+
+                for (const file of files) {
+                    if (file === undefined) {
+                        toast('未选择上传文件!', 'error');
+                        return false;
+                    }
+                    const filesize = file.size;
+                    if (filesize > 30 * 1024 * 1024) {
+                        toast('存在上传文件大小过大!', 'error');
+                        return false;
+                    }
+                    const fileext = '.' + file.name.toLowerCase().split('.').splice(-1)[0];
+                    if (whiteList.indexOf(fileext) === -1) {
+                        toast('只能上传指定格式的附件!', 'error');
+                        return false;
+                    }
+                    formData.append('size', filesize);
+                    formData.append('file[]', file);
+                }
+                postDataWithFile(window.location.pathname + '/im-file/upload', formData, function (data) {
+                    stageIm.loadUpdateDetailAtt(data);
+                    SpreadJsObj.reLoadRowData(self.sheet, sels[0].row);
+                    self.makeAttTable(select);
+                    $('#upload-im-file').val('');
+                });
+            });
+
+            // 删除附件
+            $('body').on('click', '.delete-att' ,function () {
+                const sels = self.sheet.getSelections();
+                const select = SpreadJsObj.getSelectObject(self.sheet);
+                if (!select) return;
+
+                const uuid = $(this).attr('data-imid');
+                const file_id = $(this).attr('data-attid');
+
+                postData(window.location.pathname + '/im-file/del', { uuid, file_id }, function (result) {
+                    stageIm.loadUpdateDetailAtt(result);
+                    SpreadJsObj.reLoadRowData(self.sheet, sels[0].row);
+                    self.makeAttTable(select);
+                });
+            });
+        }
         _initImTypeSetRela() {
             const self = this;
             const gatherConfirmPopover = {
@@ -2700,14 +2806,14 @@ $(document).ready(() => {
                 const jlCol = self.spreadSetting.cols.find(function (x) {return x.field === 'jl'});
                 jlCol.title = '本期计量金额';
                 SpreadJsObj.reLoadSheetHeader(self.sheet);
-                $('#type-title-contract').text('本期合同计量金额');
-                $('#type-title-qc').text('本期变更计量金额');
+                $('[name=type-title-contract]').text('本期合同计量金额');
+                $('[name=type-title-qc]').text('本期变更计量金额');
             } else {
                 const jlCol = self.spreadSetting.cols.find(function (x) {return x.field === 'jl'});
                 jlCol.title = '本期计量数量';
                 SpreadJsObj.reLoadSheetHeader(self.sheet);
-                $('#type-title-contract').text('本期合同计量数量');
-                $('#type-title-qc').text('本期变更计量数量');
+                $('[name=type-title-contract]').text('本期合同计量数量');
+                $('[name=type-title-qc]').text('本期变更计量数量');
             }
             if (stage.im_type === imType.bb.value || stage.im_type === imType.bw.value) {
                 $('#show-jldy').parent().show();
@@ -2759,6 +2865,7 @@ $(document).ready(() => {
                     $('i', obj).remove();
                 }
                 $('#im-pre').val(stage.im_pre ? stage.im_pre : '');
+                $('#im-start-num').val(stage.im_start_num ? stage.im_start_num : '');
                 const typeArr = $('div[name="im-type"]');
                 for (const t of typeArr) {
                     if (parseInt($(t).attr('im-type')) === stage.im_type) {
@@ -2770,28 +2877,38 @@ $(document).ready(() => {
             });
             // 提交 中间计量模式
             $('#choose-ok').click(() => {
+                let startNum;
+                try {
+                    startNum = parseInt($('#im-start-num').val());
+                    if (!startNum || startNum <= 0) throw '起始编号请输入正整数';
+                } catch (err) {
+                    toastr.error("起始编号请输入正整数");
+                    return;
+                }
                 const chooseType = _.find($('div[name="im-type"]', '#im-type'), function (it) {
                     return it.style.cursor !== 'pointer';
                 });
                 const data = {
                     im_type: parseInt($(chooseType).attr('im-type')),
                     im_pre: $('#im-pre').val(),
+                    im_start_num: startNum,
                 };
                 postData(window.location.pathname + '/detail/build', data, function (result) {
                     stage.im_type = data.im_type;
                     stage.im_pre = data.im_pre;
+                    stage.im_start_num = data.im_start_num;
                     if (stage.im_type === imType.tz.value || stage.im_type === imType.bb.value) {
                         const jlCol = self.spreadSetting.cols.find(function (x) {return x.field === 'jl'});
                         jlCol.title = '本期计量金额';
                         SpreadJsObj.reLoadSheetHeader(self.sheet);
-                        $('#type-title-contract').text('本期合同计量金额');
-                        $('#type-title-qc').text('本期变更计量金额');
+                        $('[name=type-title-contract]').text('本期合同计量金额');
+                        $('[name=type-title-qc]').text('本期变更计量金额');
                     } else {
                         const jlCol = self.spreadSetting.cols.find(function (x) {return x.field === 'jl'});
                         jlCol.title = '本期计量数量';
                         SpreadJsObj.reLoadSheetHeader(self.sheet);
-                        $('#type-title-contract').text('本期合同计量数量');
-                        $('#type-title-qc').text('本期变更计量数量');
+                        $('[name=type-title-contract]').text('本期合同计量数量');
+                        $('[name=type-title-qc]').text('本期变更计量数量');
                     }
                     if (stage.im_type === imType.bb.value || stage.im_type === imType.bw.value) {
                         $('#show-jldy').parent().show();
@@ -3044,6 +3161,7 @@ $(document).ready(() => {
                     updateData.unit_price = data.unit_price;
                     updateData.pid = data.pid;
                     updateData.pos_name = data.pos_name;
+                    updateData.im_type = data.im_type;
                     updateData.custom_define = [];
                 }
                 let infoUpdate = false;
@@ -3255,6 +3373,7 @@ $(document).ready(() => {
                     updateData.unit_price = data.unit_price;
                     updateData.pid = data.pid;
                     updateData.pos_name = data.pos_name;
+                    updateData.im_type = data.im_type;
                 }
                 if (items.length > 0) {
                     const itemInfo = [];
@@ -4345,16 +4464,83 @@ $(document).ready(() => {
         $('#shoufang-flie-list').html(html);
         postData(window.location.pathname + '/shoufang/file', { sfid: id }, function (result) {
             for (const att of result) {
-                html += `<tr><td>${att.filename}${att.fileext}</td><td>${moment(att.in_time).format('YYYY-MM-DD HH:mm')}</td><td>` +
-                    `<a href="${att.filepath}" target="_blank"><i class="fa fa-download"></i></a>` + makeDelHtml(att.id) +`</td></tr>`;
+                html += `<tr><td>${att.filename}${att.fileext} ` + makeEditHtml(att.id, att.filename, att.fileext, att.extra_upload) +
+                    `<td>${moment(att.in_time).format('YYYY-MM-DD HH:mm')}</td><td>` +
+                    `<a href="${att.filepath}" target="_blank"><i data-toggle="tooltip" data-placement="left" data-original-title="下载" class="fa fa-download"></i></a>` + makeViewHtml(att.viewpath) + makeDelHtml(att.id, att.extra_upload) +`</td></tr>`;
             }
             $('#shoufang-flie-list').html(html);
-            $('[data-toggle="tooltip"]').tooltip();
+            $('[data-toggle="tooltip"]').tooltip()
         });
 
-        function makeDelHtml(fid) {
-            return sfAttDelPower ? '<a href="javascript:void(0)" data-sfid="'+ id +'" data-id="'+ fid +'" title="删除" class="del-shoufang-att text-danger ml-3"><i data-toggle="tooltip" data-placement="left" data-original-title="删除" class="fa fa-remove"></i></a>' : '';
+        function makeViewHtml(viewpath) {
+            return viewpath ? `<a href="${viewpath}" target="_blank" class="ml-1"><i data-toggle="tooltip" data-placement="left" data-original-title="预览" class="fa fa-eye"></i></a>` : '';
         }
+
+        function makeDelHtml(fid, extra_upload) {
+            return sfAttDelPower &&
+            (stage.status !== auditConst.status.checked || (stage.status === auditConst.status.checked && extra_upload)) ?
+                '<a href="javascript:void(0)" data-sfid="'+ id +'" data-id="'+ fid +'" title="删除" class="del-shoufang-att pull-right text-danger mr-2">' +
+                '<i data-toggle="tooltip" data-placement="left" data-original-title="删除" class="fa fa-remove"></i></a>' : '';
+        }
+    });
+    function makeEditHtml(fid, filename, fileext, extra_upload) {
+        return sfAttDelPower &&
+        (stage.status !== auditConst.status.checked || (stage.status === auditConst.status.checked && extra_upload)) ?
+            '<a href="javascript:void(0)" data-filename="' + filename + '" data-fileext="'+ fileext +'" data-id="'+ fid +'" class="edit-shoufang-att ml-1">' +
+            '<i data-toggle="tooltip" data-placement="left" data-original-title="修改文件名" class="fa fa-pencil"></i></a></td>' : ''
+    }
+    // 修改收方单附件文件名
+    $('body').on('click', '.edit-shoufang-att', function () {
+        $(this).children('i').tooltip('hide');
+        const fid = $(this).data('id');
+        const filename = $(this).data('filename');
+        const fileext = $(this).data('fileext');
+        const html = `<div class="input-group input-group-sm">
+            <input type="text" class="form-control" value="${filename}" placeholder="输入文件名" aria-describedby="button-${fid}" style="width:50px">
+            <div class="input-group-append" id="button-${fid}">
+                <button class="btn btn-outline-primary shoufang-att-confirm-btn" data-filename="${filename}" data-fileext="${fileext}" data-fid="${fid}" type="button">确认</button>
+                <button class="btn btn-outline-secondary shoufang-att-cancel-btn" data-filename="${filename}" data-fileext="${fileext}" data-fid="${fid}" type="button">取消</button>
+            </div>
+            </div>`;
+        $(this).parents('td').html(html);
+    });
+    // 确认修改文件名
+    $('body').on('click', '.shoufang-att-confirm-btn', function () {
+        const validText = $.trim($(this).parents('td').find('input').val());
+        const orgValue = $.trim($(this).data('filename'));
+        const fileext = $(this).data('fileext');
+        const fid = $(this).data('fid');
+        if (!validText) {
+            toastr.error('文件名不能为空');
+            return;
+        }
+        // const reg = /^[0-9a-zA-Z\-_\u4e00-\u9fa5]+$/;
+        if(validText.indexOf('.') !== -1 || validText.indexOf(' ') !== -1) {
+            toastr.error('文件名中不能包含.字符和空格');
+            return;
+        }
+        if(validText == orgValue) {
+            const html = `${orgValue}${fileext} ` + makeEditHtml(fid, orgValue, fileext, 1);
+            $(this).parents('td').html(html);
+            return;
+        }
+        const data = {
+            id: fid,
+            filename: validText,
+        };
+        const _self = $(this);
+        postData('/wap/shoufang/editfile', data, function (result) {
+            const html = `${validText}${fileext} ` + makeEditHtml(fid, validText, fileext, 1);
+            _self.parents('td').html(html);
+        });
+    });
+    // 取消修改文件名
+    $('body').on('click', '.shoufang-att-cancel-btn', function () {
+        const fid = $(this).data('fid');
+        const filename = $(this).data('filename');
+        const fileext = $(this).data('fileext');
+        const html = `${filename}${fileext} ` + makeEditHtml(fid, filename, fileext, 1);
+        $(this).parents('td').html(html);
     });
     // 删除收方单附件
     $('body').on('click', '.del-shoufang-att', function () {
@@ -4432,12 +4618,14 @@ function getShouFangList(currPageNum = 1) {
             '<a href="javascript:void(0);" data-id="'+ att.id +'" class="show-shoufang-node mr-1" data-toggle="tooltip" data-placement="left" data-original-title="定位"><i class="fa fa-crosshairs"></i></a> ' +
             '<a href="javascript:void(0);" data-id="'+ att.id +'" class="show-shoufang-report mr-1"><i data-toggle="tooltip" data-placement="left" data-original-title="预览" class="fa fa-eye"></i></a> ' +
             // '<a href="javascript:void(0);"><i data-toggle="tooltip" data-placement="left" data-original-title="下载" class="fa fa-download"></i></a> ' +
-            makeDelHtml(att.id) +
+            makeDelHtml(att.id, att.extra_upload) +
             '</td></tr>';
     }
 
-    function makeDelHtml(id) {
-        return cur_uid === stage.user_id ? '<a href="javascript:void(0);" data-id="'+ id +'" data-toggle="modal" data-target="#shoufangdelete" class="ml-3 text-danger del-shoufang">' +
+    function makeDelHtml(id, extra_upload) {
+        return cur_uid === stage.user_id &&
+        (stage.status !== auditConst.status.checked || (stage.status === auditConst.status.checked && extra_upload)) ?
+            '<a href="javascript:void(0);" data-id="'+ id +'" data-toggle="modal" data-target="#shoufangdelete" class="ml-3 text-danger del-shoufang">' +
             '<i data-toggle="tooltip" data-placement="left" data-original-title="删除"  class="fa fa-remove"></i></a>' : '';
     }
     $('#shoufang-table').html(html);
@@ -4465,13 +4653,13 @@ function makeReportData(sfid) {
     replace_key_params['KEY_子目号及子目名称'] = ledger_code + ' ' + lData.name;
     replace_key_params['KEY_桩号及工程部位'] = pos_name;
     const req_params = {
-        rpt_tpl_id: window.location.host === '127.0.0.1:7002' || window.location.host === 'jlqa.smartcost.com.cn:7002' ? 1784 : 2138, // 1784(qa的,外网是2138)
+        rpt_tpl_id: window.location.host === '127.0.0.1:7002' || window.location.host === 'jlqa.smartcost.com.cn:7002' ? 1784 : (window.location.host === 'jluat.smartcost.com.cn' ? 315 : 2138), // 1784(qa的,外网是2138)
         pageSize: 'A4',
         project_id: tender.project_id,
         tender_id: tender.id,
         stage_id: stage.id,
     };
-    const rpt_name = tender.name + '-第' + stage.order + '期-' + (pos_name ? pos_name : ledger_code + '-' + lData.name);
+    const rpt_name = tender.name + '-第' + stage.order + '期-' + ledger_code + (pos_name ? '/' + pos_name : '-' + lData.name);
     const qrCodePath = sfInfo.qrcode;
     return [qrCodePath, replace_key_params, req_params, rpt_name];
 }

+ 2 - 3
app/public/js/stage_gather.js

@@ -55,9 +55,8 @@ $(document).ready(function () {
     const gclSpread = SpreadJsObj.createNewSpread($('#gcl-spread')[0]);
     const gclSheet = gclSpread.getActiveSheet();
     gclSpreadSetting.getColor = function (sheet, data, row, col, defaultColor) {
-        return data 
-            ? (data.overRange ? '#f8d7da' : data.differ ? '#FFE699' : defaultColor)
-            : defaultColor;
+        if (!data) return defaultColor;
+        return data.overRange && hintOver ? '#f8d7da' : data.differ ? '#FFE699' : defaultColor;
     };
     if (thousandth) sjsSettingObj.setTpThousandthFormat(gclSpreadSetting);
     SpreadJsObj.initSheet(gclSpread.getActiveSheet(), gclSpreadSetting);

+ 141 - 4
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, ImData, pre, orgImData;
+    let stage, imType, decimal, details, changes, detailsAtt, ImData, pre, orgImData;
     const gsTreeSetting = {
         id: 'ledger_id',
         pid: 'ledger_pid',
@@ -66,7 +66,7 @@ const stageIm = (function () {
         }
     }
 
-    function loadData (ledger, pos, stageDetail, stageChange) {
+    function loadData (ledger, pos, stageDetail, stageChange, stageDetailAtt) {
         gsTree.loadDatas(ledger);
         treeCalc.calculateAll(gsTree);
 
@@ -77,6 +77,22 @@ const stageIm = (function () {
         details = stageDetail;
 
         changes = stageChange;
+
+        detailsAtt = stageDetailAtt;
+    }
+
+    function loadData4Rela(ledger, pos, stageDetail, stageChange, stageDetailAtt) {
+        gsTree.loadDatas(ledger);
+        treeCalc.calculateAll(gsTree);
+
+        gsPos.loadDatas(pos);
+
+        initCheck();
+        details = stageDetail;
+
+        changes = stageChange;
+
+        detailsAtt = stageDetailAtt;
     }
 
     // function loadData (ledger, curStage, pos, curPosStage, stageDetail) {
@@ -274,6 +290,82 @@ const stageIm = (function () {
         im.calc_img_org = detail.calc_img_org;
         im.calc_img_remark = detail.calc_img_remark;
     }
+    function loadAtt(im, att) {
+        im.att_uuid = att.uuid;
+        im.attachment = att.attachment;
+    }
+
+    function findTzRela(rela, data) {
+        return _.find(rela, function (d) {
+            return data.lid === d.lid &&
+                (!data.code || data.code === d.code) &&
+                (!data.name || data.name === d.name) &&
+                (!data.unit || data.unit === d.unit) &&
+                (!data.pid || data.pid === d.pid) &&
+                (!data.pos_name || data.pos_name === d.pos_name);
+        });
+    }
+    function findZlRela(rela, data) {
+        return _.find(rela, function (d) {
+            return data.lid === d.lid &&
+                (!data.code || data.code === d.code) &&
+                (!data.name || data.name === d.name) &&
+                (!data.unit || data.unit === d.unit) &&
+                checkZero(ZhCalc.sub(data.unit_price, d.unit_price)) &&
+                (!data.pid || data.pid === d.pid) &&
+                (!data.pos_name || data.pos_name === d.pos_name);
+        });
+    }
+
+    function findBwRela(rela, data) {
+        return _.find(rela, function (d) {
+            return data.lid === d.lid &&
+                (!data.code || data.code === d.code) &&
+                (!data.name || data.name === d.name) &&
+                (!data.unit || data.unit === d.unit) &&
+                checkZero(ZhCalc.sub(data.unit_price, d.unit_price)) &&
+                (!data.pid || data.pid === d.pid) &&
+                (!data.pos_name || data.pos_name === d.pos_name);
+        });
+    }
+
+    function findBbRela(rela, data) {
+        return _.find(rela, function (d) {
+            return data.lid === d.lid &&
+                (!data.name || data.name === d.name) &&
+                (!data.unit || data.unit === d.unit) &&
+                (!data.pid || data.pid === d.pid) &&
+                (!data.pos_name || data.pos_name === d.pos_name);
+        });
+    }
+
+    function checkTzCustomDetail(im) {
+        const cd = findTzRela(details, im);
+        if (cd) loadCustomDetail(im, cd);
+        const att = findTzRela(detailsAtt, im);
+        if (att) loadAtt(im, att);
+    }
+
+    function checkZlCustomDetail(im) {
+        const cd = findZlRela(details, im);
+        if (cd) loadCustomDetail(im, cd);
+        const att = findZlRela(detailsAtt, im);
+        if (att) loadAtt(im, att);
+    }
+
+    function checkBwCustomDetail(im) {
+        const cd = findBwRela(details, im);
+        if (cd) loadCustomDetail(im, cd);
+        const att = findBwRela(detailsAtt, im);
+        if (att) loadAtt(im, att);
+    }
+
+    function checkBbCustomDetail(im) {
+        const cd = findBbRela(details, im);
+        if (cd) loadCustomDetail(im, cd);
+        const att = findBbRela(detailsAtt, im);
+        if (att) loadAtt(im, att);
+    }
 
     function checkCustomDetail(im) {
         const cd = _.find(details, function (d) {
@@ -502,6 +594,7 @@ const stageIm = (function () {
                 lIndex: nodeIndex,
                 custom_define: [],
                 source: [{id: node.ledger_id, code: node.code, b_code: node.b_code}],
+                im_type: imType.tz.value,
             };
             if (stage.im_gather && node.check) {
                 im.bw = getZlGatherBw(node, peg);
@@ -559,6 +652,7 @@ const stageIm = (function () {
                 changes: [], gclBills: [],
                 custom_define: [],
                 source: [{id: node.ledger_id, code: node.code, b_code: node.b_code}],
+                im_type: imType.bb.value,
             };
             for (const p of posterity) {
                 if (p.children && p.children.length > 0) continue;
@@ -582,6 +676,7 @@ const stageIm = (function () {
                                 changes: [], gclBills: [],
                                 custom_define: [],
                                 source: [],
+                                im_type: imType.bb.value,
                             };
                             nodeImData.push(im);
                         }
@@ -724,6 +819,7 @@ const stageIm = (function () {
                     lIndex: nodeIndex,
                     custom_define: [],
                     source: [],
+                    im_type: imType.zl.value,
                 };
                 if (stage.im_gather && node.check) {
                     im.bw = getZlGatherBw(node, peg);
@@ -773,6 +869,7 @@ const stageIm = (function () {
                         lIndex: nodeIndex,
                         custom_define: [],
                         source: [{id: p.ledger_id, code: p.code, b_code: p.b_code}],
+                        im_type: imType.bw.value,
                     };
                     im.calc_memo = '本期计量:' + (checkZero(im.jl) ? 0 : im.jl) + ' ' + im.unit;
                     ImData.push(im);
@@ -800,6 +897,7 @@ const stageIm = (function () {
                     lIndex: gsTree.getNodeIndex(node),
                     custom_define: [],
                     source: [{id: p.ledger_id, code: p.code, b_code: p.b_code}],
+                    im_type: imType.bw.value,
                 };
                 im.calc_memo = '本期计量:' + (checkZero(im.jl) ? 0 : im.jl) + ' ' + im.unit;
                 ImData.push(im);
@@ -859,14 +957,19 @@ const stageIm = (function () {
             }
             getCalcMemo(im);
             getChangeInfo(im);
-            checkCustomDetail(im);
+            switch (stage.im_type) {
+                case imType.tz.value: checkTzCustomDetail(im); break;
+                case imType.zl.value: checkZlCustomDetail(im); break;
+                case imType.bw.value: checkBwCustomDetail(im); break;
+                case imType.bb.value: checkBbCustomDetail(im); break;
+            }
         }
     }
 
     function _buildImCode() {
         pre = (stage.im_pre && stage.im_pre !== '') ? stage.im_pre + splitChar : '';
         for (const [i, im] of ImData.entries()) {
-            im.im_code = pre + getNumberFormat(stage.order, 2) + splitChar + getNumberFormat(i + 1, 3);
+            im.im_code = pre + getNumberFormat(stage.order, 2) + splitChar + getNumberFormat(i + stage.im_start_num, 3);
         }
     }
 
@@ -955,6 +1058,38 @@ const stageIm = (function () {
         }
     }
 
+    function loadUpdateDetailAtt (data) {
+        const datas = data instanceof Array ? data : [data];
+        for (const d of datas) {
+            const detail = _.find(detailsAtt, {uuid: d.uuid});
+            if (detail) {
+                _.assignInWith(detail, d, function (oV, sV) {
+                    return !_.isUndefined(sV) ? sV : oV;
+                });
+            } else {
+                detailsAtt.push(d);
+            }
+            let imData = _.find(ImData, {lid: d.lid, att_uuid: d.uuid});
+            if (!imData) {
+                switch (stage.im_type) {
+                    case imType.tz.value:
+                        imData = findTzRela(ImData, d);
+                        break;
+                    case imType.zl.value:
+                        imData = findZlRela(ImData, d);
+                        break;
+                    case imType.bw.value:
+                        imData = findBwRela(ImData, d);
+                        break;
+                    case imType.bb.value:
+                        imData = findBbRela(ImData, d);
+                        break;
+                }
+            }
+            if (imData) loadAtt(imData, d);
+        }
+    }
+
     function loadUpdateLedgerData(data, refreshNodes) {
         gsTree.loadPostStageData(data);
         if (data.change) {
@@ -1040,8 +1175,10 @@ const stageIm = (function () {
         init,
         initCheck,
         loadData,
+        loadData4Rela,
         buildImData,
         loadUpdateDetailData,
+        loadUpdateDetailAtt,
         loadUpdateLedgerData,
         loadUpdatePosData,
         loadUpdateChangeData,

+ 11 - 2
app/public/js/zh_calc.js

@@ -78,6 +78,14 @@ const ZhCalc = (function () {
         //return zhBaseCalc.add(num1 ? num1 : 0, num2 ? num2: 0);
         return num1 ? (num2 ? zhBaseCalc.add(num1, num2) : num1) : num2;
     };
+
+    function sum(array) {
+        let result = 0;
+        for (const a of array) {
+            result = ZhCalc.add(result, a);
+        }
+        return result;
+    }
     /**
      * 减法 num1 - num2
      * @param num1
@@ -219,7 +227,7 @@ const ZhCalc = (function () {
             const getResult = function (num1, num2, opera) {
                 switch (opera) {
                     case '+':
-                        return add(num1, num1);
+                        return add(num1, num2);
                     case '-':
                         return sub(num1, num2);
                     case '*':
@@ -264,6 +272,7 @@ const ZhCalc = (function () {
                     return Number(expr);
                 } else {
                     const rpnArr = this.parse2Rpn(expr);
+                    console.log(rpnArr);
                     const result = this.evalRpn(rpnArr);
                     return result === Infinity ? null : result;
                 }
@@ -273,5 +282,5 @@ const ZhCalc = (function () {
         },
     };
 
-    return {add, sub, mul, div, round, isNonZero: zhBaseCalc.isNonZero, calcExpr: ExprCalc}
+    return {add, sum, sub, mul, div, round, isNonZero: zhBaseCalc.isNonZero, calcExpr: ExprCalc}
 })();

+ 16 - 0
app/router.js

@@ -260,6 +260,9 @@ module.exports = app => {
     app.post('/tender/:id/measure/stage/:order/use-change', sessionAuth, tenderCheck, uncheckTenderCheck, stageCheck, 'stageController.useChange');
     app.post('/tender/:id/measure/stage/:order/check', sessionAuth, tenderCheck, uncheckTenderCheck, stageCheck, 'stageController.check');
     app.post('/tender/:id/measure/stage/:order/save/cooperation', sessionAuth, tenderCheck, uncheckTenderCheck, stageCheck, 'stageController.saveCooperationData');
+    app.post('/tender/:id/measure/stage/:order/im-file/upload', sessionAuth, tenderCheck, uncheckTenderCheck, stageCheck, 'stageController.uploadImFile');
+    app.post('/tender/:id/measure/stage/:order/im-file/del', sessionAuth, tenderCheck, uncheckTenderCheck, stageCheck, 'stageController.deleteImFile');
+    app.get('/tender/:id/measure/stage/:order/im-file/download', sessionAuth, tenderCheck, uncheckTenderCheck, stageCheck, 'stageController.downloadImFile');
 
     // 计量附件
     app.post('/tender/:id/measure/stage/:order/upload/file', sessionAuth, tenderCheck, uncheckTenderCheck, stageCheck, 'stageController.uploadFile');
@@ -312,18 +315,29 @@ module.exports = app => {
     app.post('/tender/:id/measure/stage/:order/compare/load', sessionAuth, tenderCheck, uncheckTenderCheck, stageCheck, 'stageController.compareAuditor');
 
     // 附加功能
+    // 甲供材料
     app.get('/tender/:id/measure/stage/:order/extra/jgcl', sessionAuth, tenderCheck, uncheckTenderCheck, stageCheck, 'stageExtraController.jgcl');
     app.post('/tender/:id/measure/stage/:order/extra/jgcl/load', sessionAuth, tenderCheck, uncheckTenderCheck, stageCheck, 'stageExtraController.loadJgcl');
     app.post('/tender/:id/measure/stage/:order/extra/jgcl/update', sessionAuth, tenderCheck, uncheckTenderCheck, stageCheck, 'stageExtraController.updateJgcl');
+    // 奖罚金
     app.get('/tender/:id/measure/stage/:order/extra/bonus', sessionAuth, tenderCheck, uncheckTenderCheck, stageCheck, 'stageExtraController.bonus');
     app.post('/tender/:id/measure/stage/:order/extra/bonus/load', sessionAuth, tenderCheck, uncheckTenderCheck, stageCheck, 'stageExtraController.loadBonus');
     app.post('/tender/:id/measure/stage/:order/extra/bonus/update', sessionAuth, tenderCheck, uncheckTenderCheck, stageCheck, 'stageExtraController.updateBonus');
+    // 其他
     app.get('/tender/:id/measure/stage/:order/extra/other', sessionAuth, tenderCheck, uncheckTenderCheck, stageCheck, 'stageExtraController.other');
     app.post('/tender/:id/measure/stage/:order/extra/other/load', sessionAuth, tenderCheck, uncheckTenderCheck, stageCheck, 'stageExtraController.loadOther');
     app.post('/tender/:id/measure/stage/:order/extra/other/update', sessionAuth, tenderCheck, uncheckTenderCheck, stageCheck, 'stageExtraController.updateOther');
     app.post('/tender/:id/measure/stage/:order/extra/upload/file', sessionAuth, tenderCheck, uncheckTenderCheck, stageCheck, 'stageExtraController.uploadFile');
     app.get('/tender/:id/measure/stage/:order/extra/download/file', sessionAuth, tenderCheck, uncheckTenderCheck, stageCheck, 'stageExtraController.downloadFile');
     app.post('/tender/:id/measure/stage/:order/extra/delete/file', sessionAuth, tenderCheck, uncheckTenderCheck, stageCheck, 'stageExtraController.deleteFile');
+    // 安全生产
+    app.get('/tender/:id/measure/stage/:order/extra/safeProd', sessionAuth, tenderCheck, uncheckTenderCheck, stageCheck, 'stageExtraController.safeProd');
+    app.post('/tender/:id/measure/stage/:order/extra/safeProd/load', sessionAuth, tenderCheck, uncheckTenderCheck, stageCheck, 'stageExtraController.loadSafeProd');
+    app.post('/tender/:id/measure/stage/:order/extra/safeProd/update', sessionAuth, tenderCheck, uncheckTenderCheck, stageCheck, 'stageExtraController.updateSafeProd');
+    // 临时用地
+    app.get('/tender/:id/measure/stage/:order/extra/tempLand', sessionAuth, tenderCheck, uncheckTenderCheck, stageCheck, 'stageExtraController.tempLand');
+    app.post('/tender/:id/measure/stage/:order/extra/tempLand/load', sessionAuth, tenderCheck, uncheckTenderCheck, stageCheck, 'stageExtraController.loadTempLand');
+    app.post('/tender/:id/measure/stage/:order/extra/tempLand/update', sessionAuth, tenderCheck, uncheckTenderCheck, stageCheck, 'stageExtraController.updateTempLand');
 
     // 关联台账
     app.get('/tender/:id/measure/stage/:order/rela', sessionAuth, tenderCheck, uncheckTenderCheck, stageCheck, 'stageRelaController.index');
@@ -438,6 +452,7 @@ module.exports = app => {
     // 调差清单
     app.get('/tender/:id/measure/material/:order/list', sessionAuth, tenderCheck, uncheckTenderCheck, materialCheck, 'materialController.list');
     app.post('/tender/:id/measure/material/:order/list/save', sessionAuth, tenderCheck, uncheckTenderCheck, materialCheck, 'materialController.saveListsData');
+    app.post('/tender/:id/measure/material/:order/list/load', sessionAuth, tenderCheck, uncheckTenderCheck, materialCheck, 'materialController.loadListsData');
 
     // 附件
     app.get('/tender/:id/measure/material/:order/file', sessionAuth, tenderCheck, uncheckTenderCheck, materialCheck, 'materialController.file');
@@ -547,6 +562,7 @@ module.exports = app => {
     app.get('/wap/shoufang/upload', 'wapController.shoufangUpload');
     app.post('/wap/shoufang/upfile', 'wapController.shoufangUpFile');
     app.post('/wap/shoufang/delfile', 'wapController.shoufangDeleteFile');
+    app.post('/wap/shoufang/editfile', 'wapController.shoufangEditFile');
     app.get('/wap/shoufang/download/file/:fid', 'wapController.shoufangDownloadFile');
 
     // 决策大屏

+ 3 - 1
app/service/change.js

@@ -320,13 +320,15 @@ module.exports = app => {
                 case 0: // 包含你的所有变更令
                     const sql =
                         'SELECT count(*) AS count FROM ?? AS a WHERE a.tid = ? AND ' +
-                        '(a.uid = ? OR a.cid IN (SELECT b.cid FROM ?? AS b WHERE b.uid = ? AND a.times = b.times GROUP BY b.cid))';
+                        '(a.uid = ? OR (a.status != ? AND a.cid IN (SELECT b.cid FROM ?? AS b WHERE b.uid = ? AND a.times = b.times GROUP BY b.cid)) OR a.status = ? )';
                     const sqlParam = [
                         this.tableName,
                         tenderId,
                         this.ctx.session.sessionUser.accountId,
+                        audit.flow.status.uncheck,
                         this.ctx.service.changeAudit.tableName,
                         this.ctx.session.sessionUser.accountId,
+                        audit.flow.status.checked,
                     ];
                     const result = await this.db.query(sql, sqlParam);
                     return result[0].count;

+ 4 - 3
app/service/change_audit.js

@@ -156,7 +156,7 @@ module.exports = app => {
                 case 2:// 待重新上报
                 case 9:// 待修订
                     sql = 'SELECT * FROM ?? WHERE ' +
-                        'cid = ? AND times = ? GROUP BY usite';
+                        'cid = ? AND times = ? GROUP BY usite ORDER BY usort asc';
                     sqlParam = [this.tableName, change.cid,
                         change.times];
                     break;
@@ -173,7 +173,7 @@ module.exports = app => {
                 case 6: // 审批中
                     sql = 'SELECT * FROM (SELECT MAX(usort) as ust FROM ?? ' +
                         'WHERE cid = ? and times = ? GROUP BY usite ) as b ' +
-                        'JOIN ?? as a ON a.usort = b.ust WHERE cid = ? and times = ? ORDER BY usite';
+                        'JOIN ?? as a ON a.usort = b.ust WHERE cid = ? and times = ? ORDER BY usite asc';
                     sqlParam = [this.tableName, change.cid, change.times, this.tableName, change.cid, change.times];
                     // sql = 'SELECT * FROM ?? WHERE ' +
                     //     'cid = ? AND times = ? ORDER BY usort';
@@ -606,7 +606,8 @@ module.exports = app => {
                     status: auditConst.status.checking,
                 };
                 await transaction.update(this.ctx.service.change.tableName, updateData, options);
-
+                // 清空audit_amount
+                if(times > 1) await transaction.update(this.ctx.service.changeAuditList.tableName, { audit_amount: null }, { where: { cid: cid } });
                 // 添加短信通知-需要审批提醒功能
                 const sms = new SMS(this.ctx);
                 const code = await sms.contentChange(this.ctx.change.code);

+ 5 - 4
app/service/pos.js

@@ -36,23 +36,24 @@ module.exports = app => {
         }
 
         async getPosDataWithAddStageOrder(condition) {
+            if (!condition.tid) throw '查询计量单元缺少必要信息';
             const sql = 'SELECT p.id, p.tid, p.lid, p.name, p.quantity, p.position, p.drawing_code,' +
                 '    p.sgfh_qty, p.sjcl_qty, p.qtcl_qty, p.porder, p.add_stage, p.add_times, p.add_user, s.order As add_stage_order,' +
                 '    p.sgfh_expr, p.sjcl_expr, p.qtcl_expr, p.real_qty, p.gxby_status, p.dagl_status, p.dagl_url,' +
                 '    p.gxby_limit, p.dagl_limit, p.ex_memo1, p.ex_memo2, p.ex_memo3' +
-                '  FROM ' + this.tableName + ' p ' +
+                '  FROM ' + this.departTableName(condition.tid) + ' p ' +
                 '  LEFT JOIN ' + this.ctx.service.stage.tableName + ' s' +
                 '  ON p.add_stage = s.id'
                 + this.ctx.helper.whereSql(condition, 'p');
             return await this.db.query(sql);
         }
 
-        async getPosDataByIds(ids) {
+        async getPosDataByIds(tid, ids) {
             if (ids instanceof Array && ids.length > 0) {
                 const sql = 'SELECT id, tid, lid, name, quantity, position, drawing_code,' +
                     '    sgfh_qty, sjcl_qty, qtcl_qty, add_stage, add_times, add_user, sgfh_expr, sjcl_expr, qtcl_expr, real_qty,' +
                     '    dagl_status, dagl_url, gxby_status, gxby_limit, dagl_limit, ex_memo1, ex_memo2, ex_memo3' +
-                    '  FROM ' + this.tableName +
+                    '  FROM ' + this.departTableName(tid) +
                     '  WHERE id in (' + this.ctx.helper.getInArrStrSqlFilter(ids) + ')';
                 return await this.db.query(sql, []);
             } else {
@@ -70,7 +71,7 @@ module.exports = app => {
 
         async getPosDataByUnits(tenderId, units) {
             const sql = 'SELECT p.id, p.lid, p.sgfh_qty, p.sjcl_qty, p.qtcl_qty' +
-                '  FROM ' + this.tableName + ' p' +
+                '  FROM ' + this.departTableName(tenderId) + ' p' +
                 '  LEFT JOIN ' + this.ctx.service.ledger.tableName + ' b' +
                 '  ON p.lid = b.id ' +
                 '  WHERE p.tid = ? and b.unit IN (?)';

+ 7 - 2
app/service/project.js

@@ -10,6 +10,7 @@
 const imType = require('../const/tender').imType;
 const defaultFunRela = {
     banOver: true,
+    hintOver: true,
     imType: imType.zl.value,
 };
 const sjsRelaConst = require('../const/setting').sjsRela;
@@ -52,7 +53,8 @@ module.exports = app => {
                 case 'fun':
                     rule = {
                         imType: {type: 'enum', values: [imType.tz.value, imType.zl.value, imType.bb.value, imType.bw.value], required: true},
-                        banOver: {type: 'bool', required: true,}
+                        banOver: {type: 'bool', required: true,},
+                        hintOver: {type: 'bool', required: true,}
                     };
                     break;
                 default:
@@ -157,7 +159,10 @@ module.exports = app => {
 
         async updateFunRela(id, data) {
             const result = await this.db.update(this.tableName, {
-                id: id, fun_rela: JSON.stringify({banOver: data.banOver, imType: data.imType}),
+                id: id, fun_rela: JSON.stringify({
+                    banOver: data.banOver, hintOver: data.hintOver,
+                    imType: data.imType
+                }),
             });
             return result.affectedRows === 1;
         }

+ 9 - 1
app/service/report.js

@@ -62,7 +62,7 @@ module.exports = app => {
                             runnableKey.push(filter);
                             break;
                         case 'stage_bills':
-                            runnableRst.push(service.stageBills.getLastestStageData(params.tender_id, params.stage_id));
+                            runnableRst.push(service.stageBills.getLastestStageData2(params.tender_id, params.stage_id));
                             runnableKey.push(filter);
                             break;
                         case 'stage_bills_final':
@@ -132,6 +132,14 @@ module.exports = app => {
                             runnableRst.push(service.reportMemory.getStageOther(params.tender_id, params.stage_id, memFieldKeys[filter]));
                             runnableKey.push(filter);
                             break;
+                        case 'mem_stage_safe_prod':
+                            runnableRst.push(service.reportMemory.getStageSafeProd(params.tender_id, params.stage_id, memFieldKeys[filter]));
+                            runnableKey.push(filter);
+                            break;
+                        case 'mem_stage_temp_land':
+                            runnableRst.push(service.reportMemory.getStageTempLand(params.tender_id, params.stage_id, memFieldKeys[filter]));
+                            runnableKey.push(filter);
+                            break;
                         case 'mem_gather_stage_bills':
                             runnableRst.push(service.rptGatherMemory.getGatherStageBills(memFieldKeys[filter],
                                 customDefine.gather_select, customSelect ? customSelect.gather_select : null));

+ 53 - 4
app/service/report_memory.js

@@ -374,13 +374,13 @@ module.exports = app => {
                 }
                 if (this._checkFieldsExist(fields, billsFields.stage)) {
                     if (this.ctx.stage.readOnly) {
-                        const curStage = await this.ctx.service.stageBills.getAuditorStageData(this.ctx.tender.id,
+                        const curStage = await this.ctx.service.stageBills.getAuditorStageData2(this.ctx.tender.id,
                             this.ctx.stage.id, this.ctx.stage.curTimes, this.ctx.stage.curOrder);
                         this.ctx.helper.assignRelaData(billsData, [
                             {data: curStage, fields: ['contract_qty', 'contract_tp', 'contract_expr', 'qc_qty', 'qc_tp', 'postil'], prefix: '', relaId: 'lid'}
                         ]);
                     } else {
-                        const curStage = await this.ctx.service.stageBills.getLastestStageData(this.ctx.tender.id, this.ctx.stage.id);
+                        const curStage = await this.ctx.service.stageBills.getLastestStageData2(this.ctx.tender.id, this.ctx.stage.id);
                         this.ctx.helper.assignRelaData(billsData, [
                             {data: curStage, fields: ['contract_qty', 'contract_tp', 'contract_expr', 'qc_qty', 'qc_tp', 'postil'], prefix: '', relaId: 'lid'}
                         ]);
@@ -828,6 +828,10 @@ module.exports = app => {
                         d.pre_deduct_qty = pd.deduct_qty;
                         d.pre_deduct_tp = pd.deduct_tp;
                     }
+                    d.end_arrive_qty = this.ctx.helper.add(d.pre_arrive_qty, d.arrive_qty);
+                    d.end_arrive_tp = this.ctx.helper.add(d.pre_arrive_tp, d.arrive_tp);
+                    d.end_deduct_qty = this.ctx.helper.add(d.pre_deduct_qty, d.deduct_qty);
+                    d.end_deduct_tp = this.ctx.helper.add(d.pre_deduct_tp, d.deduct_tp);
                 }
                 return data;
             } catch (err) {
@@ -859,6 +863,51 @@ module.exports = app => {
                     if (pd) {
                         d.pre_tp = pd.tp;
                     }
+                    d.end_tp = this.ctx.helper.add(d.pre_tp, d.tp);
+                }
+                return data;
+            } catch (err) {
+                return [];
+            }
+        }
+
+        async getStageSafeProd(tid, sid, fields) {
+            try {
+                await this.ctx.service.tender.checkTender(tid);
+                await this.ctx.service.stage.checkStage(sid);
+
+                const data = await this.ctx.service.stageSafeProd.getStageData(this.ctx.stage);
+                const preData = await this.ctx.service.stageSafeProd.getPreStageData(this.ctx.stage.order);
+                for (const d of data) {
+                    const pd = this.ctx.helper._.find(preData, {uuid: d.uuid});
+                    if (pd) {
+                        d.pre_qty = pd.qty;
+                        d.pre_tp = pd.tp;
+                    }
+                    d.end_qty = this.ctx.helper.add(d.pre_qty, d.qty);
+                    d.end_tp = this.ctx.helper.add(d.pre_tp, d.tp);
+                }
+                return data;
+            } catch (err) {
+                return [];
+            }
+        }
+
+        async getStageTempLand(tid, sid, fields) {
+            try {
+                await this.ctx.service.tender.checkTender(tid);
+                await this.ctx.service.stage.checkStage(sid);
+
+                const data = await this.ctx.service.stageTempLand.getStageData(this.ctx.stage);
+                const preData = await this.ctx.service.stageTempLand.getPreStageData(this.ctx.stage.order);
+                for (const d of data) {
+                    const pd = this.ctx.helper._.find(preData, {uuid: d.uuid});
+                    if (pd) {
+                        d.pre_qty = pd.qty;
+                        d.pre_tp = pd.tp;
+                    }
+                    d.end_qty = this.ctx.helper.add(d.pre_qty, d.qty);
+                    d.end_tp = this.ctx.helper.add(d.pre_tp, d.tp);
                 }
                 return data;
             } catch (err) {
@@ -999,8 +1048,8 @@ module.exports = app => {
                     calcFields.push('s' + stage.order + '_');
 
                     const curStage = stage.readOnly
-                        ? await this.ctx.service.stageBills.getAuditorStageData(this.ctx.tender.id, stage.id, stage.curTimes, stage.curOrder)
-                        : await this.ctx.service.stageBills.getLastestStageData(this.ctx.tender.id, stage.id);
+                        ? await this.ctx.service.stageBills.getAuditorStageData2(this.ctx.tender.id, stage.id, stage.curTimes, stage.curOrder)
+                        : await this.ctx.service.stageBills.getLastestStageData2(this.ctx.tender.id, stage.id);
                     this.ctx.helper.assignRelaData(billsData, [
                         {data: curStage, fields: ['contract_qty', 'contract_tp', 'contract_expr', 'qc_qty', 'qc_tp'], prefix: 's' + stage.order + '_', relaId: 'lid'}
                     ]);

+ 4 - 4
app/service/rpt_gather_memory.js

@@ -281,7 +281,7 @@ module.exports = app => {
             if (stage) {
                 await this.ctx.service.stage.doCheckStage(stage);
                 if (stage.readOnly) {
-                    const curStage = await this.ctx.service.stageBills.getAuditorStageData(tender.id,
+                    const curStage = await this.ctx.service.stageBills.getAuditorStageData2(tender.id,
                         stage.id, stage.curTimes, stage.curOrder);
                     this.ctx.helper.assignRelaData(billsData, [
                         {
@@ -292,7 +292,7 @@ module.exports = app => {
                         }
                     ]);
                 } else {
-                    const curStage = await this.ctx.service.stageBills.getLastestStageData(tender.id, stage.id);
+                    const curStage = await this.ctx.service.stageBills.getLastestStageData2(tender.id, stage.id);
                     this.ctx.helper.assignRelaData(billsData, [
                         {
                             data: curStage,
@@ -380,7 +380,7 @@ module.exports = app => {
             for (const stage of stages) {
                 await this.ctx.service.stage.doCheckStage(stage);
                 if (stage.readOnly) {
-                    const curStage = await this.ctx.service.stageBills.getAuditorStageData(tender.id,
+                    const curStage = await this.ctx.service.stageBills.getAuditorStageData2(tender.id,
                         stage.id, stage.curTimes, stage.curOrder);
                     sumAssignRelaData(billsIndexData, [{
                         data: curStage,
@@ -389,7 +389,7 @@ module.exports = app => {
                         relaId: 'lid'
                     }]);
                 } else {
-                    const curStage = await this.ctx.service.stageBills.getLastestStageData(tender.id, stage.id);
+                    const curStage = await this.ctx.service.stageBills.getLastestStageData2(tender.id, stage.id);
                     sumAssignRelaData(billsIndexData, [{
                         data: curStage,
                         fields: ['contract_qty', 'contract_tp', 'qc_qty', 'qc_tp'],

+ 2 - 2
app/service/rpt_stage_sum_memory.js

@@ -178,8 +178,8 @@ module.exports = app => {
                 defaultData[prefix + 'order'] = stage.order;
 
                 const curStage = stage.readOnly
-                    ? await this.ctx.service.stageBills.getAuditorStageData(this.ctx.tender.id, stage.id, stage.curTimes, stage.curOrder)
-                    : await this.ctx.service.stageBills.getLastestStageData(this.ctx.tender.id, stage.id);
+                    ? await this.ctx.service.stageBills.getAuditorStageData2(this.ctx.tender.id, stage.id, stage.curTimes, stage.curOrder)
+                    : await this.ctx.service.stageBills.getLastestStageData2(this.ctx.tender.id, stage.id);
                 this.ctx.helper.assignRelaData(billsData, [
                     {data: curStage, fields: ['contract_qty', 'contract_tp', 'contract_expr', 'qc_qty', 'qc_tp'], prefix: prefix, relaId: 'lid', defaultData: defaultData}
                 ]);

+ 19 - 1
app/service/stage.js

@@ -373,6 +373,10 @@ module.exports = app => {
                     if (!jgclResult) throw '初始化甲供材料数据失败';
                     const otherResult = await this.ctx.service.stageOther.addInitialStageData(newStage, preStage, transaction);
                     if (!otherResult) throw '初始化其他台账数据失败';
+                    const safeResult = await this.ctx.service.stageSafeProd.addInitialStageData(newStage, preStage, transaction);
+                    if (!safeResult) throw '初始化其他台账数据失败';
+                    const tempResult = await this.ctx.service.stageTempLand.addInitialStageData(newStage, preStage, transaction);
+                    if (!tempResult) throw '初始化其他台账数据失败';
                 }
                 // 新增期拷贝报表相关配置/签名角色 等
                 if (preStage) {
@@ -427,7 +431,7 @@ module.exports = app => {
         async buildDetailData(tenderId, order, data) {
             const conn = await this.db.beginTransaction();
             try {
-                await conn.update(this.tableName, { im_type: data.im_type, im_pre: data.im_pre }, { where: { tid: tenderId, order } });
+                await conn.update(this.tableName, { im_type: data.im_type, im_pre: data.im_pre , im_start_num: data.im_start_num }, { where: { tid: tenderId, order } });
                 // to do 生成中间计量数据
                 await conn.commit();
             } catch (err) {
@@ -536,6 +540,8 @@ module.exports = app => {
                 await transaction.delete(this.ctx.service.stageRela.tableName, { sid: id });
                 await transaction.delete(this.ctx.service.stageRelaBills.tableName, { sid: id });
                 await transaction.delete(this.ctx.service.stageRelaBillsFinal.tableName, { sid: id });
+                await transaction.delete(this.ctx.service.stageRelaIm.tableName, { sid: id });
+                await transaction.delete(this.ctx.service.stageRelaImBills.tableName, { sid: id });
                 // 删除计量合同支付附件
                 const payList = await this.ctx.service.stagePay.getAllDataByCondition({ where: { sid: id } });
                 if (payList) {
@@ -578,6 +584,18 @@ module.exports = app => {
                 await transaction.delete(this.ctx.service.stageOther.tableName, { sid: id });
                 // 同步删除进度里所选的期
                 await transaction.delete(this.ctx.service.scheduleStage.tableName, { tid: stageInfo.tid, order: stageInfo.order });
+                const detailAtt = await this.ctx.service.stageDetailAtt.getAllDataByCondition({ where: { sid: id } });
+                if (detailAtt && detailAtt.length > 0) {
+                    for (const da of detailAtt) {
+                        da.attachment = da.attachment ? JSON.parse(da.attachment) : [];
+                        for (const daa of da.attachment) {
+                            if (fs.existsSync(path.join(this.app.baseDir, daa.filepath))) {
+                                await fs.unlinkSync(path.join(this.app.baseDir, daa.filepath));
+                            }
+                        }
+                    }
+                }
+                await transaction.delete(this.ctx.service.stageDetailAtt.tableName, { sid: id });
                 // 重算进度计量总金额
                 await this.ctx.service.scheduleStage.calcStageSjTp(transaction, stageInfo.tid);
                 // 删除收方单及附件

+ 12 - 0
app/service/stage_audit.js

@@ -280,6 +280,8 @@ module.exports = app => {
                 await this.ctx.service.stageJgcl.updateHistory(this.ctx.stage, transaction);
                 await this.ctx.service.stageBonus.updateHistory(this.ctx.stage, transaction);
                 await this.ctx.service.stageOther.updateHistory(this.ctx.stage, transaction);
+                await this.ctx.service.stageSafeProd.updateHistory(this.ctx.stage, transaction);
+                await this.ctx.service.stageTempLand.updateHistory(this.ctx.stage, transaction);
                 // 更新期数据
                 const tpData = await this.ctx.service.stageBills.getSumTotalPrice(this.ctx.stage);
                 this.ctx.stage.tp_history.push({
@@ -406,6 +408,8 @@ module.exports = app => {
                     await this.ctx.service.stageJgcl.updateHistory(this.ctx.stage, transaction);
                     await this.ctx.service.stageBonus.updateHistory(this.ctx.stage, transaction);
                     await this.ctx.service.stageOther.updateHistory(this.ctx.stage, transaction);
+                    await this.ctx.service.stageSafeProd.updateHistory(this.ctx.stage, transaction);
+                    await this.ctx.service.stageTempLand.updateHistory(this.ctx.stage, transaction);
                     // 流程至下一审批人
                     await transaction.update(this.tableName, {
                         id: nextAudit.id,
@@ -608,6 +612,8 @@ module.exports = app => {
                 await this.ctx.service.stageJgcl.updateHistory(this.ctx.stage, transaction);
                 await this.ctx.service.stageBonus.updateHistory(this.ctx.stage, transaction);
                 await this.ctx.service.stageOther.updateHistory(this.ctx.stage, transaction);
+                await this.ctx.service.stageSafeProd.updateHistory(this.ctx.stage, transaction);
+                await this.ctx.service.stageTempLand.updateHistory(this.ctx.stage, transaction);
 
                 // 添加短信通知-审批退回提醒功能
                 // const mobile_array = [];
@@ -763,6 +769,8 @@ module.exports = app => {
                 await this.ctx.service.stageJgcl.updateHistory(this.ctx.stage, transaction);
                 await this.ctx.service.stageBonus.updateHistory(this.ctx.stage, transaction);
                 await this.ctx.service.stageOther.updateHistory(this.ctx.stage, transaction);
+                await this.ctx.service.stageSafeProd.updateHistory(this.ctx.stage, transaction);
+                await this.ctx.service.stageTempLand.updateHistory(this.ctx.stage, transaction);
 
                 // 多人协同数据确认删除本人和上一个审批人确认状态
                 await this.ctx.service.cooperationConfirm.delBycheckNoPre(audit.aid, this.ctx.stage, transaction);
@@ -911,6 +919,8 @@ module.exports = app => {
                 await this.ctx.service.stageJgcl.updateHistory4CheckAgain(this.ctx.stage, transaction);
                 await this.ctx.service.stageBonus.updateHistory4CheckAgain(this.ctx.stage, transaction);
                 await this.ctx.service.stageOther.updateHistory4CheckAgain(this.ctx.stage, transaction);
+                await this.ctx.service.stageSafeProd.updateHistory4CheckAgain(this.ctx.stage, transaction);
+                await this.ctx.service.stageTempLand.updateHistory4CheckAgain(this.ctx.stage, transaction);
 
                 // 本期结束
                 // 生成截止本期数据 final数据
@@ -1227,6 +1237,8 @@ module.exports = app => {
             await this.ctx.service.stageJgcl.deleteStageTimesData(sid, times, transaction);
             await this.ctx.service.stageOther.deleteStageTimesData(sid, times, transaction);
             await this.ctx.service.stageBonus.deleteStageTimesData(sid, times, transaction);
+            await this.ctx.service.stageSafeProd.deleteStageTimesData(sid, times, transaction);
+            await this.ctx.service.stageTempLand.deleteStageTimesData(sid, times, transaction);
         }
 
         /**

+ 82 - 114
app/service/stage_bills.js

@@ -33,32 +33,32 @@ module.exports = app => {
          * @param {Number|Array} lid - 台账节点id(可以为空)
          * @return {Promise<*>}
          */
-        async getLastestStageData(tid, sid, lid) {
-            let lidSql = '',
-                result;
-            if (lid) {
-                if (lid instanceof Array) {
-                    lidSql = lid.length > 0 ? ' And lid in (' + this.ctx.helper.getInArrStrSqlFilter(lid) + ')' : '';
-                } else {
-                    lidSql = ' And lid in (' + this.db.escape(lid) + ')';
-                }
-            }
-            const sql = 'SELECT Bills.* FROM ' + this.departTableName(tid) + ' As Bills ' +
-                        '  INNER JOIN ( ' +
-                        '    SELECT MAX(`times` * ' + timesLen + ' + `order`) As `progress`, `lid`, `sid` From ' + this.departTableName(tid) +
-                        '      WHERE tid = ? And sid = ?' + lidSql +
-                        '      GROUP BY `lid`' +
-                        '  ) As MaxFilter ' +
-                        '  ON (Bills.times * ' + timesLen + ' + `order`) = MaxFilter.progress And Bills.lid = MaxFilter.lid And Bills.`sid` = MaxFilter.`sid`';
-            const sqlParam = [tid, sid];
-            if (!lid) {
-                return await this.db.query(sql, sqlParam);
-            } else if (lid instanceof Array) {
-                return await this.db.query(sql, sqlParam);
-            }
-            return await this.db.queryOne(sql, sqlParam);
-
-        }
+        // async getLastestStageData(tid, sid, lid) {
+        //     let lidSql = '',
+        //         result;
+        //     if (lid) {
+        //         if (lid instanceof Array) {
+        //             lidSql = lid.length > 0 ? ' And lid in (' + this.ctx.helper.getInArrStrSqlFilter(lid) + ')' : '';
+        //         } else {
+        //             lidSql = ' And lid in (' + this.db.escape(lid) + ')';
+        //         }
+        //     }
+        //     const sql = 'SELECT Bills.* FROM ' + this.departTableName(tid) + ' As Bills ' +
+        //                 '  INNER JOIN ( ' +
+        //                 '    SELECT MAX(`times` * ' + timesLen + ' + `order`) As `progress`, `lid`, `sid` From ' + this.departTableName(tid) +
+        //                 '      WHERE tid = ? And sid = ?' + lidSql +
+        //                 '      GROUP BY `lid`' +
+        //                 '  ) As MaxFilter ' +
+        //                 '  ON (Bills.times * ' + timesLen + ' + `order`) = MaxFilter.progress And Bills.lid = MaxFilter.lid And Bills.`sid` = MaxFilter.`sid`';
+        //     const sqlParam = [tid, sid];
+        //     if (!lid) {
+        //         return await this.db.query(sql, sqlParam);
+        //     } else if (lid instanceof Array) {
+        //         return await this.db.query(sql, sqlParam);
+        //     }
+        //     return await this.db.queryOne(sql, sqlParam);
+        //
+        // }
 
         /**
          * 查询 某期 某轮审批 某人数据
@@ -69,53 +69,61 @@ module.exports = app => {
          * @param {Number|Array} lid - 台账节点id(可以为空)
          * @return {Promise<*>}
          */
-        async getAuditorStageData(tid, sid, times, order, lid) {
-            const lidSql = lid ? ' And Bills.lid in (?)' : '';
-            const sql = 'SELECT Bills.* FROM ' + this.departTableName(tid) + ' As Bills ' +
-                '  INNER JOIN ( ' +
-                '    SELECT MAX(`times` * ' + timesLen + ' + `order`) As `progress`, `lid`, `tid`, `sid` From ' + this.departTableName(tid) +
-                '      WHERE (`times` < ? OR (`times` = ? AND `order` <= ?)) And tid = ? And sid = ?' + lidSql +
-                '      GROUP BY `lid`' +
-                '  ) As MaxFilter ' +
-                '  ON (Bills.times * ' + timesLen + ' + `order`) = MaxFilter.progress And Bills.lid = MaxFilter.lid' +
-                '    AND Bills.sid = MaxFilter.sid';
-            const sqlParam = [times, times, order, tid, sid];
-            if (!lid) {
-                return await this.db.query(sql, sqlParam);
-            } else if (lid instanceof Array) {
-                sqlParam.push(lid.join(', '));
-                return await this.db.query(sql, sqlParam);
+        // async getAuditorStageData(tid, sid, times, order, lid) {
+        //     const lidSql = lid ? ' And Bills.lid in (?)' : '';
+        //     const sql = 'SELECT Bills.* FROM ' + this.departTableName(tid) + ' As Bills ' +
+        //         '  INNER JOIN ( ' +
+        //         '    SELECT MAX(`times` * ' + timesLen + ' + `order`) As `progress`, `lid`, `tid`, `sid` From ' + this.departTableName(tid) +
+        //         '      WHERE (`times` < ? OR (`times` = ? AND `order` <= ?)) And tid = ? And sid = ?' + lidSql +
+        //         '      GROUP BY `lid`' +
+        //         '  ) As MaxFilter ' +
+        //         '  ON (Bills.times * ' + timesLen + ' + `order`) = MaxFilter.progress And Bills.lid = MaxFilter.lid' +
+        //         '    AND Bills.sid = MaxFilter.sid';
+        //     const sqlParam = [times, times, order, tid, sid];
+        //     if (!lid) {
+        //         return await this.db.query(sql, sqlParam);
+        //     } else if (lid instanceof Array) {
+        //         sqlParam.push(lid.join(', '));
+        //         return await this.db.query(sql, sqlParam);
+        //     }
+        //     sqlParam.push(lid);
+        //     return await this.db.queryOne(sql, sqlParam);
+        //
+        // }
+
+        _getFilterSql(where, asTable = '') {
+            let whereSql = '';
+            if (!where) return whereSql;
+            if (where.lid) {
+                if (where.lid instanceof Array) {
+                    whereSql += ' And ' + asTable + 'lid in ('  + this.ctx.helper.getInArrStrSqlFilter(where.lid) + ')';
+                } else if (typeof where.lid === "string") {
+                    whereSql += ' And ' + asTable + 'lid = ' + this.db.escape(where.lid);
+                }
             }
-            sqlParam.push(lid);
-            return await this.db.queryOne(sql, sqlParam);
-
+            return whereSql;
         }
 
         async getLastestStageData2(tid, sid, lid) {
-            let lidSql = '',
-                result;
-            if (lid) {
-                if (lid instanceof Array) {
-                    lidSql = lid.length > 0 ? ' And lid in (' + this.ctx.helper.getInArrStrSqlFilter(lid) + ')' : '';
-                } else {
-                    lidSql = ' And lid in (' + this.db.escape(lid) + ')';
-                }
-            }
-            const sql = 'SELECT Bills.* FROM ' + this.tableName + ' As Bills ' +
-                '  INNER JOIN ( ' +
-                '    SELECT MAX(`times` * ' + timesLen + ' + `order`) As `progress`, `lid`, `sid` From ' + this.tableName +
-                '      WHERE tid = ? And sid = ?' + lidSql +
-                '      GROUP BY `lid`' +
-                '  ) As MaxFilter ' +
-                '  ON (Bills.times * ' + timesLen + ' + `order`) = MaxFilter.progress And Bills.lid = MaxFilter.lid And Bills.`sid` = MaxFilter.`sid`';
-            const sqlParam = [tid, sid];
-            if (!lid) {
-                return await this.db.query(sql, sqlParam);
-            } else if (lid instanceof Array) {
-                return await this.db.query(sql, sqlParam);
-            }
-            return await this.db.queryOne(sql, sqlParam);
+            const filterSql = lid ? this._getFilterSql({lid}) : '';
+            const sql = 'Select * From ' + this.departTableName(tid) +
+                '  Where tid = ? and sid = ? ' + filterSql;
+
+            const result = await this.db.query(sql, [tid, sid]);
+            const stageBills = this.ctx.helper.filterLastestData(result, ['lid']);
+            if (!lid || lid instanceof Array) return stageBills;
+            return stageBills[0];
+        }
+
+        async getAuditorStageData2(tid, sid, times, order, lid) {
+            const filterSql = lid ? this._getFilterSql({lid}) : '';
+            const sql = 'Select * From ' + this.departTableName(tid) +
+                        '  Where tid = ? and sid = ? And (`times` < ? OR (`times` = ? AND `order` <= ?)) ' + filterSql;
 
+            const result = await this.db.query(sql, [tid, sid, times, times, order]);
+            const stageBills = this.ctx.helper.filterLastestData(result, ['lid']);
+            if (!lid || lid instanceof Array) return stageBills;
+            return stageBills[0];
         }
 
         async getStageUsedBills(tid, sid) {
@@ -132,46 +140,6 @@ module.exports = app => {
             return this._.map(this._.filter(stageBills, 'used'), 'lid');
         }
 
-        /**
-         * 获取截止本期数据
-         * @param {Number} tid - 标段id
-         * @param {Number} sorder - 截止期序号
-         * @param {String|Array[String]} lid - 台账id
-         * @returns {Promise<*>}
-         */
-        async getEndStageData(tid, sorder, lid) {
-            let lidSql = '';
-            if (lid) {
-                if (lid instanceof Array) {
-                    lidSql = lid.length > 0 ? ' And lid in (' + this.ctx.helper.getInArrStrSqlFilter(lid) + ')' : '';
-                } else {
-                    lidSql = ' And lid in (' + this.db.escape(lid) + ')';
-                }
-            }
-
-            const sql = 'SELECT Bills.tid, Bills.lid,' +
-                '  Sum(Bills.contract_qty) As contract_qty, Sum(Bills.contract_tp) As contract_tp,' +
-                '  Sum(Bills.qc_qty) As qc_qty, Sum(Bills.qc_tp) As qc_tp FROM ' + this.tableName + ' As Bills ' +
-                '  INNER JOIN ( ' +
-                '    SELECT MAX(`times` * ' + timesLen + ' + `order`) As `progress`, `lid`, `sid` From ' + this.tableName +
-                '      WHERE tid = ? ' + lidSql +
-                '      GROUP BY `lid`, `sid`' +
-                '  ) As MaxFilter ' +
-                '  ON (Bills.times * ' + timesLen + ' + `order`) = MaxFilter.progress And Bills.lid = MaxFilter.lid And Bills.`sid` = MaxFilter.`sid`' +
-                '  INNER JOIN ' + this.ctx.service.stage.tableName + ' As Stage' +
-                '  ON Bills.sid = Stage.id' +
-                '  WHERE Stage.order <= ?' +
-                '  GROUP BY `lid`';
-            const sqlParam = [tid, sorder];
-            if (!lid) {
-                return await this.db.query(sql, sqlParam);
-            } else if (lid instanceof Array) {
-                return await this.db.query(sql, sqlParam);
-            }
-            return await this.db.queryOne(sql, sqlParam);
-
-        }
-
         async getStageBills(tid, sid, lid) {
             const sql = 'SELECT Stage.*, Ledger.unit_price FROM ?? As Stage, ?? As Ledger ' +
                         '  Where Stage.tid = ?, Stage.sid = ?, Stage.lid = ?, Stage.lid = Ledger.id ' +
@@ -246,7 +214,7 @@ module.exports = app => {
             const transaction = await this.db.beginTransaction();
             try {
                 for (const d of datas) {
-                    const stageBills = await this.getLastestStageData(this.ctx.tender.id, this.ctx.stage.id, d.lid);
+                    const stageBills = await this.getLastestStageData2(this.ctx.tender.id, this.ctx.stage.id, d.lid);
                     const ledgerBills = await this.ctx.service.ledger.getDataById(d.lid);
                     if (!stageBills || stageBills.times !== this.ctx.stage.curTimes || stageBills.order !== this.ctx.stage.curOrder) {
                         await this._insertStageBillsData(transaction, d, stageBills, ledgerBills);
@@ -261,7 +229,7 @@ module.exports = app => {
                 await transaction.rollback();
                 throw err;
             }
-            return await this.getLastestStageData(this.ctx.tender.id, this.ctx.stage.id, this._.map(datas, 'lid'));
+            return await this.getLastestStageData2(this.ctx.tender.id, this.ctx.stage.id, this._.map(datas, 'lid'));
         }
 
         /**
@@ -299,7 +267,7 @@ module.exports = app => {
          */
         async calc(tid, sid, lid, transaction) {
             const info = this.ctx.tender.info;
-            const stageBills = await this.getLastestStageData(tid, sid, lid);
+            const stageBills = await this.getLastestStageData2(tid, sid, lid);
             const ledgerBills = await this.ctx.service.ledger.getDataById(lid);
             if (!ledgerBills) {
                 throw '提交数据错误';
@@ -334,7 +302,7 @@ module.exports = app => {
         }
 
         async updateStageBillsCalcType(data) {
-            const stageBills = await this.getLastestStageData(this.ctx.tender.id, this.ctx.stage.id, data.id);
+            const stageBills = await this.getLastestStageData2(this.ctx.tender.id, this.ctx.stage.id, data.id);
             const updateData = { contract_qty: null, contract_tp: null };
 
             const transaction = await this.db.beginTransaction();
@@ -356,7 +324,7 @@ module.exports = app => {
             }
 
             const bills = await this.ctx.service.ledger.getDataById(data.id);
-            const curStageData = await this.getLastestStageData(this.ctx.tender.id, this.ctx.stage.id, [data.id]);
+            const curStageData = await this.getLastestStageData2(this.ctx.tender.id, this.ctx.stage.id, [data.id]);
             return { bills: [bills], curStageData };
         }
 
@@ -487,7 +455,7 @@ module.exports = app => {
                 const result = loadTree.getUpdateData();
                 if (result.errors.length > 100) throw '您导入的数据存在大量数据错误,请您仔细检查';
 
-                const stageBills = await this.getLastestStageData(this.ctx.tender.id, this.ctx.stage.id);
+                const stageBills = await this.getLastestStageData2(this.ctx.tender.id, this.ctx.stage.id);
                 const updateStageBills = [], insertStageBills = [];
                 for (const u of result.update) {
                     const sb = stageBills.find(x => { return x.lid === u.lid; });

+ 1 - 1
app/service/stage_bills_final.js

@@ -76,7 +76,7 @@ module.exports = app => {
                 throw '数据错误';
             }
             if (stage.order > 1) {
-                const cur = await this.ctx.service.stageBills.getLastestStageData(tender.id, stage.id);
+                const cur = await this.ctx.service.stageBills.getLastestStageData2(tender.id, stage.id);
                 const pre = await this.getFinalData(tender, stage.order - 1);
                 if ((!cur || cur.length === 0) && (!pre || pre.length === 0)) return;
                 for (const c of cur) {

+ 0 - 2
app/service/stage_bonus.js

@@ -107,8 +107,6 @@ module.exports = app => {
             const datas = data instanceof Array ? data : [data];
             const orgDatas = await this.getAllDataByCondition({where: {sid: this.ctx.stage.id, id: this.ctx.helper._.map(datas, 'id')}});
             for (const od of orgDatas) {
-                console.log(od);
-                console.log(this.ctx.stage.id);
                 if (od.sid !== this.ctx.stage.id) throw '非本期新增数据,不可删除';
             }
             await this.db.delete(this.tableName, {id: datas});

+ 5 - 4
app/service/stage_change.js

@@ -188,14 +188,14 @@ module.exports = app => {
                 for (const c of updateChanges) {
                     await transaction.update(this.tableName, c);
                 }
-                const stageBills = await this.ctx.service.stageBills.getLastestStageData(this.ctx.tender.id, this.ctx.stage.id, bills.id);
+                const stageBills = await this.ctx.service.stageBills.getLastestStageData2(this.ctx.tender.id, this.ctx.stage.id, bills.id);
                 await this.ctx.service.stageBills.updateStageBillsQty(transaction, ledgerBills, stageBills, { qc_qty: billsQty });
                 await transaction.commit();
             } catch (err) {
                 await transaction.rollback();
                 throw err;
             }
-            const result = await this.ctx.service.stageBills.getLastestStageData(this.ctx.tender.id, this.ctx.stage.id, [bills.id]);
+            const result = await this.ctx.service.stageBills.getLastestStageData2(this.ctx.tender.id, this.ctx.stage.id, [bills.id]);
             return { bills: { curStageData: result } };
         }
 
@@ -274,7 +274,7 @@ module.exports = app => {
             // 获取返回数据
             try {
                 const data = { bills: {}, pos: {} };
-                data.bills.curStageData = await this.ctx.service.stageBills.getLastestStageData(this.ctx.tender.id, this.ctx.stage.id, [pos.lid]);
+                data.bills.curStageData = await this.ctx.service.stageBills.getLastestStageData2(this.ctx.tender.id, this.ctx.stage.id, [pos.lid]);
                 data.pos.curStageData = await this.ctx.service.stagePos.getLastestStageData2(this.ctx.tender.id, this.ctx.stage.id, { pid: pos.id });
                 return data;
             } catch (err) {
@@ -409,7 +409,8 @@ module.exports = app => {
             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 = ?';
-            const data = await this.db.query(sql, [stage.id]);
+            let data = await this.db.query(sql, [stage.id]);
+            data = helper.filterLastestData(data, ['lid', 'pid', 'cbid'], 'stimes', 'sorder');
             const bqData = [];
             for (const d of data) {
                 if (!d.qty) continue;

+ 141 - 0
app/service/stage_detail_att.js

@@ -0,0 +1,141 @@
+'use strict';
+
+/**
+ *
+ *
+ * @author Mai
+ * @date 2021/10/25
+ * @version
+ */
+
+const ImTypeConst = require('../const/tender').imType;
+
+module.exports = app => {
+    class StageDetailAtt extends app.BaseService {
+        /**
+         * 构造函数
+         *
+         * @param {Object} ctx - egg全局变量
+         * @return {void}
+         */
+        constructor(ctx) {
+            super(ctx);
+            this.tableName = 'stage_detail_attachment';
+        }
+
+        async getDetailAtt(field) {
+            if (!field.uuid && field.im_type === undefined) throw '数据错误';
+
+            if (field.uuid) {
+                return this.getDataByCondition({ uuid: field.uuid });
+            } else {
+                switch (field.im_type) {
+                    case ImTypeConst.zl.value:
+                    case ImTypeConst.bw.value:
+                        return this.getDataByCondition({
+                            im_type: field.im_type, sid: this.ctx.stage.id,
+                            lid: field.lid, code: field.code, name: field.name, unit: field.unit, unit_price: field.unit_price,
+                            pid: field.pid, pos_name: field.pos_name
+                        });
+                    case ImTypeConst.tz.value:
+                        return this.getDataByCondition({
+                            im_type: field.im_type, sid: this.ctx.stage.id,
+                            lid: field.lid, code: field.code, name: field.name, unit: field.unit,
+                            pid: field.pid, pos_name: field.pos_name
+                        });
+                    case ImTypeConst.bb.value:
+                        return this.getDataByCondition({
+                            im_type: field.im_type, sid: this.ctx.stage.id,
+                            lid: field.lid, name: field.name, unit: field.unit,
+                            pid: field.pid, pos_name: field.pos_name
+                        });
+                }
+            }
+        }
+
+        _analysisDetailAtt(data) {
+            if (data instanceof Array) {
+                for (const a of data) {
+                    a.attachment = a.attachment ? JSON.parse(a.attachment) : [];
+                }
+            } else {
+                data.attachment = data.attachment ? JSON.parse(data.attachment) : [];
+            }
+        }
+
+        async getUserTemp(uid) {
+            if (!this.userTemp[uid]) this.userTemp[uid] = await this.ctx.service.projectAccount.getAccountInfoById(uid);
+            return this.userTemp[uid];
+        }
+
+        async _complete4Output(data) {
+            this.userTemp = {};
+            const datas = data instanceof Array ? data : [data];
+            for (const d of datas) {
+                for (const a of d.attachment) {
+                    delete a.filepath;
+                    a.username = (await this.getUserTemp(a.uid)).name;
+                }
+            }
+        }
+
+        async getStageData(sid, im_type) {
+            const result = im_type !== undefined
+                ? await this.getAllDataByCondition({ where: { sid: sid, im_type: im_type } })
+                : await this.getAllDataByCondition({ where: { sid: sid } });
+            this._analysisDetailAtt(result);
+            await this._complete4Output(result);
+            return result;
+        }
+
+        async addFiles(field, files) {
+            const detailAtt = await this.getDetailAtt(field);
+            if (detailAtt) {
+                detailAtt && this._analysisDetailAtt(detailAtt);
+                for (const f of files) {
+                    detailAtt.attachment.unshift(f);
+                }
+                await this.db.update(this.tableName, { id: detailAtt.id, attachment: JSON.stringify(detailAtt.attachment) });
+                await this._complete4Output(detailAtt);
+                return detailAtt;
+            } else {
+                const insertData = {
+                    tid: this.ctx.tender.id, sid: this.ctx.stage.id, sorder: this.ctx.stage.order,
+                    uuid: this.uuid.v4(), im_type: field.im_type,
+                    lid: field.lid, code: field.code, name: field.name, unit: field.unit, unit_price: field.unit_price,
+                    pid: field.pid, pos_name: field.pos_name,
+                    attachment: JSON.stringify(files),
+                };
+                await this.db.insert(this.tableName, insertData);
+                this._analysisDetailAtt(insertData);
+                await this._complete4Output(insertData);
+                return insertData;
+            }
+        }
+
+        async delFiles(uuid, fileId) {
+            const detailAtt = await this.getDetailAtt({ uuid });
+            if (!detailAtt) throw '数据错误';
+            await this._analysisDetailAtt(detailAtt);
+
+            const index = detailAtt.attachment.findIndex(x => { return x.file_id === fileId });
+            if (index < 0) throw '不存在改文件';
+            if (detailAtt.attachment[index].uid !== this.ctx.session.sessionUser.accountId) throw '您无权删除该文件';
+
+            detailAtt.attachment.splice(index, 1);
+            await this.db.update(this.tableName, { id: detailAtt.id, attachment: JSON.stringify(detailAtt.attachment) });
+            await this._complete4Output(detailAtt);
+            return detailAtt;
+        }
+
+        async getFiles(uuid, fileId) {
+            const detailAtt = await this.getDetailAtt({ uuid });
+            if (!detailAtt) throw '数据错误';
+            await this._analysisDetailAtt(detailAtt);
+
+            return detailAtt.attachment.find(x => { return x.file_id === fileId });
+        }
+    }
+
+    return StageDetailAtt;
+};

+ 1 - 1
app/service/stage_jgcl.js

@@ -147,7 +147,7 @@ module.exports = app => {
                 // } else {
                 //     nd.unit_price = od.unit_price;
                 // }
-                const precision = this.ctx.helper.findPrecision(info.precision, d.unit);
+                const precision = this.ctx.helper.findPrecision(info.precision, d.unit || od.unit);
                 if (d.arrive_qty !== undefined) {
                     nd.arrive_qty = this.ctx.helper.round(d.arrive_qty, precision.value);
                     nd.arrive_tp = this.ctx.helper.mul(nd.unit_price, nd.arrive_qty, tpDecimal);

+ 8 - 4
app/service/stage_pay.js

@@ -38,10 +38,12 @@ module.exports = app => {
         async getAuditorStagePay(pid, sid, times, order) {
             const pidSql = pid instanceof Array ? ' And SP.`pid` in (' + pid.join(',') + ')' : 'And SP.`pid` = ' + pid;
             const sql = 'SELECT SP.*, P.`csorder`, P.`cstimes`, P.`csaorder`, P.`order`, P.uid, P.minus, P.ptype, P.sprice, P.sexpr, P.rprice, P.rexpr, P.is_yf, P.dl_type, P.dl_count, P.dl_tp_type, P.dl_tp ' +
-                '  FROM ?? As SP, ?? As P ' +
+                '  FROM ' + this.tableName + ' As SP ' +
+                '  LEFT JOIN ' + this.ctx.service.pay.tableName + ' As P ' +
+                '  ON SP.pid = P.id' +
                 '  WHERE SP.`sid` = ? AND SP.`stimes` = ? AND SP.`sorder` = ? AND SP.`pid` = P.`id` AND P.`valid`' + pidSql +
                 '  ORDER BY P.`order`';
-            const sqlParam = [this.tableName, this.ctx.service.pay.tableName, sid, times, order, pid];
+            const sqlParam = [sid, times, order, pid];
             if (pid instanceof Array) {
                 return await this.db.query(sql, sqlParam);
             } else {
@@ -61,10 +63,12 @@ module.exports = app => {
             const sql = 'SELECT SP.*,' +
                 '    P.`csorder`, P.`cstimes`, P.`csaorder`, P.`order`, P.uid, P.minus, P.ptype, P.sprice, P.sexpr,' +
                 '    P.rprice, P.rexpr, P.is_yf, P.dl_type, P.dl_count, P.dl_tp_type, P.dl_tp, P.name as pName ' +
-                '  FROM ?? As SP, ?? As P ' +
+                '  FROM ' + this.tableName + ' As SP ' +
+                '  LEFT JOIN ' + this.ctx.service.pay.tableName + ' As P ' +
+                '  ON SP.pid = P.id' +
                 '  WHERE SP.`sid` = ? AND SP.`stimes` = ? AND SP.`sorder` = ? AND SP.`pid` = P.`id` AND P.`valid`' +
                 '  ORDER BY P.`order`';
-            const sqlParam = [this.tableName, this.ctx.service.pay.tableName, sid, times, order];
+            const sqlParam = [sid, times, order];
             return await this.db.query(sql, sqlParam);
         }
 

+ 33 - 32
app/service/stage_pos.js

@@ -53,19 +53,19 @@ module.exports = app => {
          * @param {Number|Array} pid - 部位明细id(可以为空)
          * @returns {Promise<*>}
          */
-        async getLastestStageData(tid, sid, where) {
-            const filterSql = this._getPosFilterSql(where);
-            const sql = 'SELECT Pos.id, Pos.tid, Pos.sid, Pos.lid, Pos.pid, Pos.contract_qty, Pos.qc_qty, Pos.postil, Pos.times, Pos.order, Pos.contract_expr FROM ' +
-                '  (SELECT * FROM ' + this.tableName + ' WHERE tid = ? And sid = ?) As Pos ' +
-                '  INNER JOIN ( ' +
-                '    SELECT MAX(`times` * ' + timesLen + ' + `order`) As `flow`, `tid`, `sid`, `pid` From ' + this.tableName +
-                '      WHERE `tid` = ? And sid = ?' + filterSql +
-                '      GROUP BY `pid`' +
-                '  ) As MaxFilter ' +
-                '  ON (Pos.times * ' + timesLen + ' + Pos.order) = MaxFilter.flow And Pos.pid = MaxFilter.pid And Pos.sid = MaxFilter.sid';
-            const sqlParam = [tid, sid, tid, sid];
-            return await this.db.query(sql, sqlParam);
-        }
+        // async getLastestStageData(tid, sid, where) {
+        //     const filterSql = this._getPosFilterSql(where);
+        //     const sql = 'SELECT Pos.id, Pos.tid, Pos.sid, Pos.lid, Pos.pid, Pos.contract_qty, Pos.qc_qty, Pos.postil, Pos.times, Pos.order, Pos.contract_expr FROM ' +
+        //         '  (SELECT * FROM ' + this.tableName + ' WHERE tid = ? And sid = ?) As Pos ' +
+        //         '  INNER JOIN ( ' +
+        //         '    SELECT MAX(`times` * ' + timesLen + ' + `order`) As `flow`, `tid`, `sid`, `pid` From ' + this.tableName +
+        //         '      WHERE `tid` = ? And sid = ?' + filterSql +
+        //         '      GROUP BY `pid`' +
+        //         '  ) As MaxFilter ' +
+        //         '  ON (Pos.times * ' + timesLen + ' + Pos.order) = MaxFilter.flow And Pos.pid = MaxFilter.pid And Pos.sid = MaxFilter.sid';
+        //     const sqlParam = [tid, sid, tid, sid];
+        //     return await this.db.query(sql, sqlParam);
+        // }
         /**
          * 查询 某期 某轮审批 某审核人数据
          * @param {Number} tid - 标段id
@@ -75,19 +75,19 @@ module.exports = app => {
          * @param {Number|Array|Null} pid - 部位明细id - 为空则查询全部
          * @returns {Promise<*>}
          */
-        async getAuditorStageData(tid, sid, times, order, where) {
-            const filterSql = this._getPosFilterSql(where);
-            const sql = 'SELECT Pos.id, Pos.tid, Pos.sid, Pos.pid, Pos.lid, Pos.contract_qty, Pos.qc_qty, Pos.postil, Pos.times, Pos.order, Pos.contract_expr FROM ' +
-                '  (SELECT * FROM '+ this.tableName + ' WHERE tid = ? And sid = ?) As Pos ' +
-                '  INNER JOIN ( ' +
-                '    SELECT MAX(`times` * ' + timesLen + ' + `order`) As `flow`, `pid`, `sid` From ' + this.tableName +
-                '      WHERE (`times` < ? OR (`times` = ? AND `order` <= ?)) And tid = ? And sid = ?' + filterSql +
-                '      GROUP BY `pid`' +
-                '  ) As MaxFilter ' +
-                '  ON (Pos.times * ' + timesLen + ' + Pos.order) = MaxFilter.flow And Pos.pid = MaxFilter.pid And Pos.sid = MaxFilter.sid';
-            const sqlParam = [tid, sid, times, times, order, tid, sid];
-            return await this.db.query(sql, sqlParam);
-        }
+        // async getAuditorStageData(tid, sid, times, order, where) {
+        //     const filterSql = this._getPosFilterSql(where);
+        //     const sql = 'SELECT Pos.id, Pos.tid, Pos.sid, Pos.pid, Pos.lid, Pos.contract_qty, Pos.qc_qty, Pos.postil, Pos.times, Pos.order, Pos.contract_expr FROM ' +
+        //         '  (SELECT * FROM '+ this.tableName + ' WHERE tid = ? And sid = ?) As Pos ' +
+        //         '  INNER JOIN ( ' +
+        //         '    SELECT MAX(`times` * ' + timesLen + ' + `order`) As `flow`, `pid`, `sid` From ' + this.tableName +
+        //         '      WHERE (`times` < ? OR (`times` = ? AND `order` <= ?)) And tid = ? And sid = ?' + filterSql +
+        //         '      GROUP BY `pid`' +
+        //         '  ) As MaxFilter ' +
+        //         '  ON (Pos.times * ' + timesLen + ' + Pos.order) = MaxFilter.flow And Pos.pid = MaxFilter.pid And Pos.sid = MaxFilter.sid';
+        //     const sqlParam = [tid, sid, times, times, order, tid, sid];
+        //     return await this.db.query(sql, sqlParam);
+        // }
 
         _filterLastestData(stagePos) {
             const stagePosIndex = {};
@@ -255,7 +255,7 @@ module.exports = app => {
         async _updateStagePosData(data) {
             let bills, precision, updateBills = null;
             const datas = data instanceof Array ? data : [data];
-            const orgPos = await this.ctx.service.pos.getPosDataByIds(this._.map(datas, 'pid'));
+            const orgPos = await this.ctx.service.pos.getPosDataByIds(this.ctx.tender.id, this._.map(datas, 'pid'));
             const result = {ledger: [], pos: [], stageUpdate: true};
             const orgStagePos = await this.getLastestStageData2(this.ctx.tender.id, this.ctx.stage.id,
                 {pid: this._.map(datas, 'pid')});
@@ -439,7 +439,7 @@ module.exports = app => {
             }
             const updateBillsStage = [], insertBillsStage = [], info = this.ctx.tender.info;
             for (const b of bills) {
-                const stageBills = await this.ctx.service.stageBills.getLastestStageData(b.tender_id, this.ctx.stage.id, b.id);
+                const stageBills = await this.ctx.service.stageBills.getLastestStageData2(b.tender_id, this.ctx.stage.id, b.id);
 
                 const posStage = await this.getLastestStageData2(b.tender_id, this.ctx.stage.id, {lid: b.id});
                 let contract_qty = 0;
@@ -586,14 +586,14 @@ module.exports = app => {
                 if (refreshData.ledger && refreshData.ledger.length > 0) {
                     result.ledger.bills = await this.ctx.service.ledger.getDataByIds(refreshData.ledger);
                     if (refreshData.stageUpdate) {
-                        result.ledger.curStageData = await this.ctx.service.stageBills.getLastestStageData(this.ctx.tender.id, this.ctx.stage.id, refreshData.ledger);
+                        result.ledger.curStageData = await this.ctx.service.stageBills.getLastestStageData2(this.ctx.tender.id, this.ctx.stage.id, refreshData.ledger);
                     }
                 }
                 if (refreshData.pos) {
                     if (refreshData.isDeletePos) {
                         result.pos.pos = refreshData.pos;
                     } else if (refreshData.pos.length > 0) {
-                        result.pos.pos = await this.ctx.service.pos.getPosDataWithAddStageOrder({id: refreshData.pos});
+                        result.pos.pos = await this.ctx.service.pos.getPosDataWithAddStageOrder({tid: this.ctx.tender.id, id: refreshData.pos});
                         if (refreshData.stageUpdate) {
                             result.pos.curStageData = await this.getLastestStageData2(this.ctx.tender.id, this.ctx.stage.id, {pid: refreshData.pos});
                         }
@@ -663,9 +663,10 @@ module.exports = app => {
          * 多期清单数据整合 (材料调差调用)
          * @param {Number} tid - 标段id
          * @param {String} stage_id_list - 期id列表
+         * @param {String} comefrom - 来源(部分不调用计量不获取)
          * @returns {Promise<void>}
          */
-        async getStagesData(tid, stage_id_list) {
+        async getStagesData(tid, stage_id_list, comefrom = '') {
             const sids = stage_id_list.split(',');
             const result = [];
             for (const sid of sids) {
@@ -680,7 +681,7 @@ module.exports = app => {
                     if (rsp) {
                         rsp.contract_qty = this.ctx.helper.add(rsp.contract_qty, sp.contract_qty);
                         rsp.qc_qty = this.ctx.helper.add(rsp.qc_qty, sp.qc_qty);
-                    } else {
+                    } else if (!comefrom || (comefrom === 'list' && (sp.contract_qty || sp.qc_qty))) {
                         result.push({
                             id: sp.id, tid: sp.tid, lid: sp.lid, pid: sp.pid,
                             contract_qty: sp.contract_qty, qc_qty: sp.qc_qty,

+ 9 - 9
app/service/stage_rela.js

@@ -32,7 +32,7 @@ class srCache {
     async _loadBillsData() {
         const decimal = this.ctx.tender.info.decimal;
         const ledger = await this.ctx.service.ledger.getData(this.stage.tid);
-        const curBillsData = await this.ctx.service.stageBills.getLastestStageData(this.stage.tid, this.stage.id);
+        const curBillsData = await this.ctx.service.stageBills.getLastestStageData2(this.stage.tid, this.stage.id);
         const preStageBills = this.preRelaStage
             ? await this.ctx.service.stageRelaBillsFinal.getAllDataByCondition({ where: { sid: this.preRelaStage.sid, rela_tid: this.preRelaStage.rela_tid } })
             : [];
@@ -51,8 +51,8 @@ class srCache {
             }
             l.gather_qty = this.ctx.helper.add(l.contract_qty, l.qc_qty);
             l.gather_tp = this.ctx.helper.add(l.contract_tp, l.qc_tp);
-            l.pre_gather_qty = this.ctx.helper.add(l.pre_contract_qty, l.pre_contract_qty);
-            l.pre_gather_tp = this.ctx.helper.add(l.pre_contract_tp, l.pre_contract_tp);
+            l.pre_gather_qty = this.ctx.helper.add(l.pre_contract_qty, l.pre_qc_qty);
+            l.pre_gather_tp = this.ctx.helper.add(l.pre_contract_tp, l.pre_qc_tp);
             l.end_contract_qty = this.ctx.helper.add(l.pre_contract_qty, l.contract_qty);
             l.end_contract_tp = this.ctx.helper.add(l.pre_contract_tp, l.contract_tp);
             l.end_qc_qty = this.ctx.helper.add(l.pre_qc_qty, l.qc_qty);
@@ -88,7 +88,7 @@ class srCache {
         ]);
         for (const p of pos) {
             p.gather_qty = this.ctx.helper.add(p.contract_qty, p.qc_qty);
-            p.pre_gather_qty = this.ctx.helper.add(p.pre_contract_qty, p.qc_qty);
+            p.pre_gather_qty = this.ctx.helper.add(p.pre_contract_qty, p.pre_qc_qty);
             p.end_contract_qty = this.ctx.helper.add(p.contract_qty, p.pre_contract_qty);
             p.end_qc_qty = this.ctx.helper.add(p.qc_qty, p.pre_qc_qty);
             p.end_gather_qty = this.ctx.helper.add(p.gather_qty, p.pre_gather_qty);
@@ -206,7 +206,7 @@ class srCache {
                 calc_memo: i.calc_memo, calc_img_remark: i.calc_img_remark, calc_img: i.calc_img,
                 bgl_code: i.bgl_code, bgl_drawing_code: i.bgl_drawing_code,
                 jl: i.jl, contract_jl: i.contract_jl, qc_jl: i.qc_jl,
-                pre_jl: i.jl, pre_contract_jl: i.pre_contract_jl, pre_qc_jl: i.pre_qc_jl,
+                pre_jl: i.pre_jl, pre_contract_jl: i.pre_contract_jl, pre_qc_jl: i.pre_qc_jl,
                 end_jl: i.end_jl, end_contract_jl: i.end_contract_jl, end_qc_jl: i.end_qc_jl,
                 tp: i.tp, contract_tp: i.contract_tp, qc_tp: i.qc_tp,
                 pre_tp: i.pre_tp, pre_contract_tp: i.pre_contract_tp, pre_qc_tp: i.pre_qc_tp,
@@ -222,7 +222,7 @@ class srCache {
                 im_id: i.imid, bid: i.bid, im_code: i.im_code,
                 b_code: i.b_code, name: i.name, unit: i.unit, unit_price: i.unit_price,
                 jl: i.jl, contract_jl: i.contract_jl, qc_jl: i.qc_jl,
-                pre_jl: i.jl, pre_contract_jl: i.pre_contract_jl, pre_qc_jl: i.pre_qc_jl,
+                pre_jl: i.pre_jl, pre_contract_jl: i.pre_contract_jl, pre_qc_jl: i.pre_qc_jl,
                 end_jl: i.end_jl, end_contract_jl: i.end_contract_jl, end_qc_jl: i.end_qc_jl,
                 tp: i.tp, contract_tp: i.contract_tp, qc_tp: i.qc_tp,
                 pre_tp: i.pre_tp, pre_contract_tp: i.pre_contract_tp, pre_qc_tp: i.pre_qc_tp,
@@ -243,7 +243,7 @@ class srCache {
         const [gcl, gcl100] = this._getCacheBills();
         this._getCachePos();
         this.cache_tp = await this._getCacheTp(gcl, gcl100);
-        this._getCacheStageIm();
+        await this._getCacheStageIm();
     }
 };
 
@@ -303,7 +303,7 @@ module.exports = app => {
         // async _calculateRelaStage(stage, preRelaStage) {
         //     const result = {}, helper = this.ctx.helper, stageBills = [];
         //     const ledger = await this.ctx.service.ledger.getData(stage.tid);
-        //     const curBillsData = await this.ctx.service.stageBills.getLastestStageData(stage.tid, stage.id);
+        //     const curBillsData = await this.ctx.service.stageBills.getLastestStageData2(stage.tid, stage.id);
         //     helper.assignRelaData(ledger, [
         //         { data: curBillsData, fields: ['contract_qty', 'contract_tp', 'qc_qty', 'qc_tp', 'postil'], prefix: '', relaId: 'lid' },
         //     ]);
@@ -480,7 +480,7 @@ module.exports = app => {
                 await conn.delete(this.ctx.service.stageRelaPosFinal.tableName, {sid: rela.sid, rela_tid: rela.rela_tid});
                 await conn.delete(this.ctx.service.stageRelaIm.tableName, {sid: rela.sid, rela_tid: rela.rela_tid});
                 await conn.delete(this.ctx.service.stageRelaImBills.tableName, {sid: rela.sid, rela_tid: rela.rela_tid});
-                await conn.update(this.ctx.service.stage.tableName, { id: this.ctx.stage.id, check_calc: 1 });
+                // await conn.update(this.ctx.service.stage.tableName, { id: this.ctx.stage.id, check_calc: 1 });
                 await conn.commit();
             } catch (err) {
                 await conn.rollback();

+ 291 - 0
app/service/stage_safe_prod.js

@@ -0,0 +1,291 @@
+'use strict';
+
+/**
+ * 安全生产
+ *
+ * @author Mai
+ * @date 2021/10/21
+ * @version
+ */
+
+
+const auditConst = require('../const/audit').stage;
+module.exports = app => {
+    class StageSafeProd extends app.BaseService {
+        /**
+         * 构造函数
+         *
+         * @param {Object} ctx - egg全局变量
+         * @return {void}
+         */
+        constructor(ctx) {
+            super(ctx);
+            this.tableName = 'stage_safe_prod';
+        }
+
+        async getStageData(stage) {
+            const data = await this.getAllDataByCondition({where: { sid: stage.id }});
+            if (stage && stage.readOnly && !this.ctx.tender.isTourist && stage.status !== auditConst.status.checked) {
+                for (const d of data) {
+                    const his = d.shistory ? JSON.parse(d.shistory) : [];
+                    const h = this.ctx.helper._.find(his, {
+                        stimes: stage.curTimes, sorder: stage.curOrder
+                    });
+                    delete d.shistory;
+                    d.qty = h ? h.qty : null;
+                    d.tp = h ? h.tp : null;
+                }
+            }
+            return data;
+        }
+
+        async getPreStageData(sorder) {
+            const sql = 'SELECT o.uuid, Sum(o.qty) as qty, Sum(o.tp) as tp ' +
+                '  From ' + this.tableName + ' o ' +
+                '  LEFT JOIN ' + this.ctx.service.stage.tableName + ' s ON s.id = o.sid' +
+                '  WHERE s.order < ? And o.tid = ?' +
+                '  GROUP By uuid';
+            const sqlParam = [sorder, this.ctx.tender.id];
+            const data = await this.db.query(sql, sqlParam);
+            return data;
+        }
+
+        async getEndStageData(sorder) {
+            const sql = 'SELECT o.uuid, Sum(o.qty) as qty, Sum(o.tp) as tp ' +
+                '  From ' + this.tableName + ' o ' +
+                '  LEFT JOIN ' + this.ctx.service.stage.tableName + ' s ON s.id = o.sid' +
+                '  WHERE s.order <= ? And o.tid = ?' +
+                '  GROUP By uuid';
+            const sqlParam = [sorder, this.ctx.tender.id];
+            const data = await this.db.query(sql, sqlParam);
+            return data;
+        }
+
+        async _addDatas(data) {
+            const info = this.ctx.tender.info;
+            const tpDecimal = info.decimal.extra ? info.decimal.extraTp : info.decimal.tp;
+
+            const datas = data instanceof Array ? data : [data];
+            const insertData = [];
+            for (const d of datas) {
+                if (!d.name || !d.order) throw '新增其他数据,提交的数据错误';
+                const nd = {
+                    tid: this.ctx.tender.id,
+                    sid: this.ctx.stage.id,
+                    sorder: this.ctx.stage.order,
+                    uuid: this.uuid.v4(),
+                    add_sid: this.ctx.stage.id,
+                    add_uid: this.ctx.session.sessionUser.accountId,
+                    add_time: new Date(),
+                };
+                nd.name = d.name;
+                nd.order = d.order;
+                if (d.unit) nd.unit = d.unit;
+                if (d.unit_price) nd.unit_price = d.unit_price;
+                const precision = this.ctx.helper.findPrecision(this.ctx.tender.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, tpDecimal);
+                }
+                if (d.qty !== undefined) {
+                    nd.qty = this.ctx.helper.round(d.qty, precision.value);
+                    nd.tp = this.ctx.helper.mul(nd.unit_price, nd.qty, tpDecimal);
+                }
+                if (d.memo) nd.memo = d.memo;
+                insertData.push(nd);
+            }
+            await this.db.insert(this.tableName, insertData);
+            return await this.getAllDataByCondition({
+                where: { sid: this.ctx.stage.id, uuid: this.ctx.helper._.map(insertData, 'uuid') }
+            });
+        }
+
+        async _delDatas (data) {
+            const datas = data instanceof Array ? data : [data];
+            const orgDatas = await this.getAllDataByCondition({where: {sid: this.ctx.stage.id, id: this.ctx.helper._.map(datas, 'id')} });
+            for (const od of orgDatas) {
+                if (od.pre_used) throw '往期已经计量,不可删除';
+            }
+            await this.db.delete(this.tableName, {id: datas});
+            return datas;
+        }
+
+        async _updateDatas (data) {
+            const info = this.ctx.tender.info;
+            const tpDecimal = info.decimal.extra ? info.decimal.extraTp : info.decimal.tp;
+
+            const datas = data instanceof Array ? data : [data];
+            const orgDatas = await this.getAllDataByCondition({
+                where: { sid: this.ctx.stage.id, id: this.ctx.helper._.map(datas, 'id') }
+            });
+
+            const uDatas = [];
+            for (const d of datas) {
+                const od = this.ctx.helper._.find(orgDatas, {id: d.id});
+                if (!od) continue;
+
+                const nd = {id: od.id};
+                if (d.name !== undefined) nd.name = d.name;
+                if (d.unit !== undefined) {
+                    if (od.pre_used) throw '往期已使用,不可修改单位';
+                    nd.unit = d.unit;
+                }
+                if (d.unit_price !== undefined && od.pre_used) throw '往期已使用,不可修改单价';
+                nd.unit_price = d.unit_price !== undefined ? this.ctx.helper.round(d.unit_price, info.decimal.up) : od.unit_price;
+                const precision = this.ctx.helper.findPrecision(info.precision, d.unit || od.unit);
+                if (d.order !== undefined) nd.order = d.order;
+                if (d.quantity !== undefined) {
+                    if (od.pre_used) throw '往期已使用,不可修改数量';
+                    nd.quantity = this.ctx.helper.round(d.quantity, precision.value);
+                    nd.total_price = this.ctx.helper.mul(nd.unit_price, nd.quantity, tpDecimal);
+                } else if(d.unit_price !== undefined && !od.pre_used) {
+                    nd.total_price = this.ctx.helper.mul(nd.unit_price, od.quantity, tpDecimal);
+                }
+                if (d.qty !== undefined) {
+                    nd.qty = this.ctx.helper.round(d.qty, precision.value);
+                    nd.tp = this.ctx.helper.mul(nd.unit_price, nd.qty, tpDecimal);
+                } else if(d.unit_price !== undefined) {
+                    nd.tp = this.ctx.helper.mul(nd.unit_price, od.qty, tpDecimal);
+                }
+                if (d.memo !== undefined) nd.memo = d.memo;
+                uDatas.push(nd);
+            }
+            if (uDatas.length > 0) {
+                await this.db.updateRows(this.tableName, uDatas);
+                return uDatas;
+            } else {
+                return [];
+            }
+        }
+
+        async updateDatas(data) {
+            const result = {add: [], del: [], update: []};
+            try {
+                if (data.add) {
+                    result.add = await this._addDatas(data.add);
+                }
+                if (data.update) {
+                    result.update = await this._updateDatas(data.update);
+                }
+                if (data.del) {
+                    result.del = await this._delDatas(data.del);
+                }
+                return result;
+            } catch (err) {
+                throw err;
+            }
+        }
+
+        async updateHistory(stage, transaction) {
+            const datas = await this.getStageData(stage);
+            if (datas.length === 0) return;
+
+            const updateDatas = [];
+            const times = this.ctx.stage.curTimes, order = this.ctx.stage.curOrder;
+            for (const d of datas) {
+                const history = d.shistory && d.shistory !== '' ? JSON.parse(d.shistory) : [];
+                const his = history.find(function (x) {
+                    return x.stimes && x.stimes === times
+                        && x.sorder && x.sorder === order;
+                });
+                if (his) {
+                    his.qty = d.qty;
+                    his.tp = d.tp;
+                    if (d.sid === d.add_sid) {
+                        his.quantity = d.quantity;
+                        his.total_price = d.total_price;
+                    }
+                } else {
+                    const nHis = { stimes: this.ctx.stage.curTimes, sorder: this.ctx.stage.curOrder, qty: d.qty, tp: d.tp };
+                    if (d.sid === d.add_sid) {
+                        nHis.quantity = d.quantity;
+                        nHis.total_price = d.total_price;
+                    }
+                    history.push(nHis);
+                }
+                updateDatas.push({ id: d.id, shistory: JSON.stringify(history) });
+            }
+            await transaction.updateRows(this.tableName, updateDatas);
+        }
+
+        async updateHistory4CheckAgain(stage, transaction) {
+            const datas = await this.getStageData(stage);
+            if (datas.length === 0) return;
+
+            const updateDatas = [];
+            const times = this.ctx.stage.curTimes, order = this.ctx.stage.curOrder + 1;
+            for (const d of datas) {
+                const history = d.shistory && d.shistory !== '' ? JSON.parse(d.shistory) : [];
+                const his = history.find(function (x) {
+                    return x.stimes && x.stimes === times
+                        && x.sorder && x.sorder === order;
+                });
+                if (his) {
+                    his.qty = d.qty;
+                    his.tp = d.tp;
+                    if (d.sid === d.add_sid) {
+                        his.quantity = d.quantity;
+                        his.total_price = d.total_price;
+                    }
+                } else {
+                    const nHis = { stimes: times, sorder: order, qty: d.qty, tp: d.tp };
+                    if (d.sid === d.add_sid) {
+                        nHis.quantity = d.quantity;
+                        nHis.total_price = d.total_price;
+                    }
+                    history.push(nHis);
+                }
+                updateDatas.push({ id: d.id, shistory: JSON.stringify(history) });
+            }
+            await transaction.updateRows(this.tableName, updateDatas);
+        }
+
+        async addInitialStageData(stage, preStage, transaction) {
+            if (!stage || !preStage) {
+                throw '标段数据有误';
+            }
+            const preDatas = await this.getAllDataByCondition({
+                columns: ['tid', 'uuid', 'add_uid', 'add_sid', 'add_time', 'name', 'unit', 'unit_price', 'quantity', 'total_price', 'qty', 'order', 'memo', 'pre_used'],
+                where: { sid: preStage.id }
+            });
+            if (preDatas.length > 0) {
+                for (const pd of preDatas) {
+                    pd.pre_used = pd.pre_used || !this.ctx.helper.checkZero(pd.qty);
+                    delete pd.qty;
+                    pd.sid = stage.id;
+                    pd.sorder = stage.order;
+                }
+                const result = await transaction.insert(this.tableName, preDatas);
+                return result.affectedRows === preDatas.length;
+            } else {
+                return true;
+            }
+
+        }
+
+        async deleteStageTimesData(sid, times, transaction) {
+            const datas = await this.getAllDataByCondition({where: { sid: sid }});
+            if (datas.length === 0) return;
+
+            const updateDatas = [];
+            for (const d of datas) {
+                const history = d.shistory && d.shistory !== '' ? JSON.parse(d.shistory) : [];
+                const his = history.filter(function (x) {
+                    return x.stimes && x.stimes < times;
+                });
+                his.sort(function (x, y) {
+                    return (x.stimes * 1000 + x.sorder) - (y.stimes * 1000 + y.sorder);
+                });
+                updateDatas.push({
+                    id: d.id,
+                    shistory: JSON.stringify(his),
+                    qty: his.length > 0 ? his[his.length - 1].qty : null,
+                    tp: his.length > 0 ? his[his.length - 1].tp : null,
+                });
+            }
+            await transaction.updateRows(this.tableName, updateDatas);
+        }
+    }
+
+    return StageSafeProd;
+};

+ 2 - 0
app/service/stage_shoufang.js

@@ -12,6 +12,7 @@ const fs = require('fs');
 const path = require('path');
 const qr = require('qr-image');
 const sendToWormhole = require('stream-wormhole');
+const auditConst = require('../const/audit').stage;
 module.exports = app => {
     class stageShoufang extends app.BaseService {
         constructor(ctx) {
@@ -28,6 +29,7 @@ module.exports = app => {
                     sid: this.ctx.stage.id,
                     lid: datas.lid,
                     pid: datas.pid ? datas.pid : null,
+                    extra_upload: this.ctx.stage.status === auditConst.status.checked ? 1 : 0,
                     create_time: new Date(),
                 };
                 const result = await transaction.insert(this.tableName, data);

+ 19 - 2
app/service/stage_shoufang_att.js

@@ -11,6 +11,7 @@
 const archiver = require('archiver');
 const path = require('path');
 const fs = require('fs');
+const auditConst = require('../const/audit').stage;
 module.exports = app => {
     class StageShoufangAtt extends app.BaseService {
         /**
@@ -35,11 +36,25 @@ module.exports = app => {
             // const data = {
             // };
             // Object.assign(data, fileData);
+            const stageInfo = await this.ctx.service.stage.getDataById(fileData.sid);
+            fileData.extra_upload = stageInfo.status === auditConst.status.checked ? 1 : 0;
             const result = await this.db.insert(this.tableName, fileData);
             return result;
         }
 
         /**
+         * 修改附件
+         * @param {Object} postData - 表单信息
+         * @param {Object} fileData - 文件信息
+         * @param {int} uid - 上传者id
+         * @return {void}
+         */
+        async edit(data) {
+            const result = await this.db.update(this.tableName, data);
+            return result.affectedRows === 1;
+        }
+
+        /**
          * 添加附件
          * @param {Object} postData - 表单信息
          * @param {Object} fileData - 文件信息
@@ -82,8 +97,10 @@ module.exports = app => {
             const sqlParam = [this.tableName, sfid];
             const result = await this.db.query(sql, sqlParam);
             for (const att of result) {
-                if (!ctx.helper.canPreview(att.fileext)) att.filepath = `/wap/shoufang/download/file/${att.id}`;
-                else att.filepath = att.filepath.replace(/^app|\/app/, '');
+                // if (!ctx.helper.canPreview(att.fileext)) att.filepath = `/wap/shoufang/download/file/${att.id}`;
+                // else att.filepath = att.filepath.replace(/^app|\/app/, '');
+                if (ctx.helper.canPreview(att.fileext)) att.viewpath = att.filepath.replace(/^app|\/app/, '');
+                att.filepath = `/wap/shoufang/download/file/${att.id}`;
             }
             return result;
         }

+ 266 - 0
app/service/stage_temp_land.js

@@ -0,0 +1,266 @@
+'use strict';
+
+/**
+ * 临时占地
+ *
+ * @author Mai
+ * @date 2021/10/21
+ * @version
+ */
+
+
+const auditConst = require('../const/audit').stage;
+module.exports = app => {
+    class StageTempLand extends app.BaseService {
+        /**
+         * 构造函数
+         *
+         * @param {Object} ctx - egg全局变量
+         * @return {void}
+         */
+        constructor(ctx) {
+            super(ctx);
+            this.tableName = 'stage_temp_land';
+        }
+
+        async getStageData(stage) {
+            const data = await this.getAllDataByCondition({where: { sid: stage.id }});
+            if (stage && stage.readOnly && !this.ctx.tender.isTourist && stage.status !== auditConst.status.checked) {
+                for (const d of data) {
+                    const his = d.shistory ? JSON.parse(d.shistory) : [];
+                    const h = this.ctx.helper._.find(his, {
+                        stimes: stage.curTimes, sorder: stage.curOrder
+                    });
+                    delete d.shistory;
+                    d.qty = h ? h.qty : null;
+                    d.tp = h ? h.tp : null;
+                }
+            }
+            return data;
+        }
+
+        async getPreStageData(sorder) {
+            const sql = 'SELECT o.uuid, Sum(o.qty) as qty, Sum(o.tp) as tp ' +
+                '  From ' + this.tableName + ' o ' +
+                '  LEFT JOIN ' + this.ctx.service.stage.tableName + ' s ON s.id = o.sid' +
+                '  WHERE s.order < ? And o.tid = ?' +
+                '  GROUP By uuid';
+            const sqlParam = [sorder, this.ctx.tender.id];
+            const data = await this.db.query(sql, sqlParam);
+            return data;
+        }
+
+        async getEndStageData(sorder) {
+            const sql = 'SELECT o.uuid, Sum(o.qty) as qty, Sum(o.tp) as tp ' +
+                '  From ' + this.tableName + ' o ' +
+                '  LEFT JOIN ' + this.ctx.service.stage.tableName + ' s ON s.id = o.sid' +
+                '  WHERE s.order <= ? And o.tid = ?' +
+                '  GROUP By uuid';
+            const sqlParam = [sorder, this.ctx.tender.id];
+            const data = await this.db.query(sql, sqlParam);
+            return data;
+        }
+
+        async _addDatas(data) {
+            const info = this.ctx.tender.info;
+            const tpDecimal = info.decimal.extra ? info.decimal.extraTp : info.decimal.tp;
+
+            const datas = data instanceof Array ? data : [data];
+            const insertData = [];
+            for (const d of datas) {
+                if (!d.name || !d.order) throw '新增其他数据,提交的数据错误';
+                const nd = {
+                    tid: this.ctx.tender.id,
+                    sid: this.ctx.stage.id,
+                    sorder: this.ctx.stage.order,
+                    uuid: this.uuid.v4(),
+                    add_sid: this.ctx.stage.id,
+                    add_uid: this.ctx.session.sessionUser.accountId,
+                    add_time: new Date(),
+                };
+                nd.name = d.name;
+                nd.order = d.order;
+                if (d.unit) nd.unit = d.unit;
+                if (d.unit_price) nd.unit_price = d.unit_price;
+                const precision = this.ctx.helper.findPrecision(this.ctx.tender.info.precision, d.unit);
+                if (d.qty !== undefined) {
+                    nd.qty = this.ctx.helper.round(d.qty, precision.value);
+                    nd.tp = this.ctx.helper.mul(nd.unit_price, nd.qty, tpDecimal);
+                }
+                if (d.memo) nd.memo = d.memo;
+                insertData.push(nd);
+            }
+            await this.db.insert(this.tableName, insertData);
+            return await this.getAllDataByCondition({
+                where: { sid: this.ctx.stage.id, uuid: this.ctx.helper._.map(insertData, 'uuid') }
+            });
+        }
+
+        async _delDatas (data) {
+            const datas = data instanceof Array ? data : [data];
+            const orgDatas = await this.getAllDataByCondition({where: {sid: this.ctx.stage.id, id: this.ctx.helper._.map(datas, 'id')} });
+            for (const od of orgDatas) {
+                if (od.pre_used) throw '往期已经计量,不可删除';
+            }
+            await this.db.delete(this.tableName, {id: datas});
+            return datas;
+        }
+
+        async _updateDatas (data) {
+            const info = this.ctx.tender.info;
+            const tpDecimal = info.decimal.extra ? info.decimal.extraTp : info.decimal.tp;
+
+            const datas = data instanceof Array ? data : [data];
+            const orgDatas = await this.getAllDataByCondition({
+                where: { sid: this.ctx.stage.id, id: this.ctx.helper._.map(datas, 'id') }
+            });
+
+            const uDatas = [];
+            for (const d of datas) {
+                const od = this.ctx.helper._.find(orgDatas, {id: d.id});
+                if (!od) continue;
+
+                const nd = {id: od.id};
+                if (d.name !== undefined) {
+                    nd.name = d.name;
+                }
+                if (d.unit !== undefined) {
+                    if (od.pre_used) throw '往期已使用,不可修改单位';
+                    nd.unit = d.unit;
+                }
+                if (d.unit_price !== undefined && od.pre_used) throw '往期已使用,不可修改单价';
+                nd.unit_price = d.unit_price !== undefined ? this.ctx.helper.round(d.unit_price, info.decimal.up) : od.unit_price;
+                const precision = this.ctx.helper.findPrecision(info.precision, d.unit || od.unit);
+                if (d.order !== undefined) nd.order = d.order;
+                if (d.qty !== undefined) {
+                    nd.qty = this.ctx.helper.round(d.qty, precision.value);
+                    nd.tp = this.ctx.helper.mul(nd.unit_price, nd.qty, tpDecimal);
+                } else if(d.unit_price !== undefined) {
+                    nd.tp = this.ctx.helper.mul(nd.unit_price, od.qty, tpDecimal);
+                }
+                if (d.memo !== undefined) nd.memo = d.memo;
+                uDatas.push(nd);
+            }
+            if (uDatas.length > 0) {
+                await this.db.updateRows(this.tableName, uDatas);
+                return uDatas;
+            } else {
+                return [];
+            }
+        }
+
+        async updateDatas(data) {
+            const result = {add: [], del: [], update: []};
+            try {
+                if (data.add) {
+                    result.add = await this._addDatas(data.add);
+                }
+                if (data.update) {
+                    result.update = await this._updateDatas(data.update);
+                }
+                if (data.del) {
+                    result.del = await this._delDatas(data.del);
+                }
+                return result;
+            } catch (err) {
+                throw err;
+            }
+        }
+
+        async updateHistory(stage, transaction) {
+            const datas = await this.getStageData(stage);
+            if (datas.length === 0) return;
+
+            const updateDatas = [];
+            const times = this.ctx.stage.curTimes, order = this.ctx.stage.curOrder;
+            for (const d of datas) {
+                const history = d.shistory && d.shistory !== '' ? JSON.parse(d.shistory) : [];
+                const his = history.find(function (x) {
+                    return x.stimes && x.stimes === times
+                        && x.sorder && x.sorder === order;
+                });
+                if (his) {
+                    his.qty = d.qty;
+                    his.tp = d.tp;
+                } else {
+                    const nHis = { stimes: this.ctx.stage.curTimes, sorder: this.ctx.stage.curOrder, qty: d.qty, tp: d.tp };
+                    history.push(nHis);
+                }
+                updateDatas.push({ id: d.id, shistory: JSON.stringify(history) });
+            }
+            await transaction.updateRows(this.tableName, updateDatas);
+        }
+
+        async updateHistory4CheckAgain(stage, transaction) {
+            const datas = await this.getStageData(stage);
+            if (datas.length === 0) return;
+
+            const updateDatas = [];
+            const times = this.ctx.stage.curTimes, order = this.ctx.stage.curOrder + 1;
+            for (const d of datas) {
+                const history = d.shistory && d.shistory !== '' ? JSON.parse(d.shistory) : [];
+                const his = history.find(function (x) {
+                    return x.stimes && x.stimes === times
+                        && x.sorder && x.sorder === order;
+                });
+                if (his) {
+                    his.qty = d.qty;
+                    his.tp = d.tp;
+                } else {
+                    const nHis = { stimes: times, sorder: order, qty: d.qty, tp: d.tp };
+                    history.push(nHis);
+                }
+                updateDatas.push({ id: d.id, shistory: JSON.stringify(history) });
+            }
+            await transaction.updateRows(this.tableName, updateDatas);
+        }
+
+        async addInitialStageData(stage, preStage, transaction) {
+            if (!stage || !preStage) {
+                throw '标段数据有误';
+            }
+            const preDatas = await this.getAllDataByCondition({
+                columns: ['tid', 'uuid', 'add_uid', 'add_sid', 'add_time', 'name', 'unit', 'unit_price', 'qty', 'order', 'memo', 'pre_used'],
+                where: { sid: preStage.id }
+            });
+            if (preDatas.length > 0) {
+                for (const pd of preDatas) {
+                    pd.pre_used = pd.pre_used || !this.ctx.helper.checkZero(pd.qty);
+                    delete pd.qty;
+                    pd.sid = stage.id;
+                    pd.sorder = stage.order;
+                }
+                const result = await transaction.insert(this.tableName, preDatas);
+                return result.affectedRows === preDatas.length;
+            } else {
+                return true;
+            }
+
+        }
+
+        async deleteStageTimesData(sid, times, transaction) {
+            const datas = await this.getAllDataByCondition({where: { sid: sid }});
+            if (datas.length === 0) return;
+
+            const updateDatas = [];
+            for (const d of datas) {
+                const history = d.shistory && d.shistory !== '' ? JSON.parse(d.shistory) : [];
+                const his = history.filter(function (x) {
+                    return x.stimes && x.stimes < times;
+                });
+                his.sort(function (x, y) {
+                    return (x.stimes * 1000 + x.sorder) - (y.stimes * 1000 + y.sorder);
+                });
+                updateDatas.push({
+                    id: d.id,
+                    shistory: JSON.stringify(his),
+                    qty: his.length > 0 ? his[his.length - 1].qty : null,
+                    tp: his.length > 0 ? his[his.length - 1].tp : null,
+                });
+            }
+            await transaction.updateRows(this.tableName, updateDatas);
+        }
+    }
+
+    return StageTempLand;
+};

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

@@ -128,9 +128,17 @@
                         </div>
                     </div>
                     <div class="col-12">
-                        <div class="mb-2 col-6 p-0 search-group mt-2">
-                            <input class="form-control form-control-sm" id="code-input" placeholder="输入 项目节编号、名称、计量单元 检索">
-                            <a href="javascript:void(0);" style="display: none" data-btn="code" class="text-danger remove-btn" title="移除关键词"><i class="fa fa-times-circle "></i></a>
+                        <div class="row mb-2 mt-2 mx-0 p-0">
+                            <div class="col-6 pl-0 p-0 search-group mt-2">
+                                <input class="form-control form-control-sm" id="code-input" placeholder="输入 项目节编号、名称、计量单元 检索">
+                                <a href="javascript:void(0);" style="display: none" data-btn="code" class="text-danger remove-btn" title="移除关键词"><i class="fa fa-times-circle "></i></a>
+                            </div>
+                            <div class="ml-auto mt-2">
+                                <div class="custom-control custom-checkbox mt-1">
+                                    <input type="checkbox" id="code-select-all" class="custom-control-input">
+                                    <label class="custom-control-label" for="code-select-all">全选</label>
+                                </div>
+                            </div>
                         </div>
                         <div style="overflow-y:auto" class="sjs-biangeng-height">
                             <table class="table table-striped table-bordered table-hover table-sm fixed_headers2">

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

@@ -71,13 +71,8 @@
 <script>
     const materialType = JSON.parse('<%- materialType %>');
     const materialBillsData = JSON.parse(unescape('<%- escape(JSON.stringify(materialBillsData)) %>'));
-    let materialListData = JSON.parse(unescape('<%- escape(JSON.stringify(materialListData)) %>'));
-    const notJoinList = JSON.parse('<%- JSON.stringify(materialNotJoinListData) %>');
-    const ledger = JSON.parse(unescape('<%- escape(JSON.stringify(ledger)) %>'));
-    const curLedgerData = JSON.parse(unescape('<%- escape(JSON.stringify(curLedgerData)) %>'));
-    const pos = JSON.parse(unescape('<%- escape(JSON.stringify(pos)) %>'));
-    const curPosData = JSON.parse(unescape('<%- escape(JSON.stringify(curPosData)) %>'));
     const readOnly = <%- material.readOnly %>;
     const stage_order = <%- material.order %>;
     const materialID = <%- material.id %>;
+    let materialListData, notJoinList, ledger, curLedgerData, pos, curPosData, gclGatherData;
 </script>

+ 11 - 3
app/view/setting/fun.ejs

@@ -14,8 +14,12 @@
                             <label>超计控制</label>
                             <div>
                                 <div class="form-check form-check-inline">
-                                    <input class="form-check-input" type="checkbox" name="ban_over" <% if (funRela.banOver) { %>checked<% } %> onchange="updateSetting();">
-                                    <label class="form-check-label" for="inlineCheckbox6">超计时限制上报审批/审批通过</label>
+                                    <input class="form-check-input" type="checkbox" id="ban_over" name="ban_over" <% if (funRela.banOver) { %>checked<% } %> onchange="updateSetting();">
+                                    <label class="form-check-label" for="ban_over">超计时限制上报审批/审批通过</label>
+                                </div>
+                                <div class="form-check form-check-inline">
+                                    <input class="form-check-input" type="checkbox" id="hint_over" name="hint_over" <% if (funRela.hintOver) { %>checked<% } %> onchange="updateSetting();">
+                                    <label class="form-check-label" for="hint_over">超计时标红显示</label>
                                 </div>
                             </div>
                         </div>
@@ -46,6 +50,10 @@
         autoFlashHeight();
     });
     const updateSetting = function () {
-        postData('/setting/fun/update', {imType: parseInt($('[name=im_type]:checked').val()), banOver: $('[name=ban_over]')[0].checked});
+        postData('/setting/fun/update', {
+            imType: parseInt($('[name=im_type]:checked').val()),
+            banOver: $('[name=ban_over]')[0].checked,
+            hintOver: $('#hint_over')[0].checked,
+        });
     }
 </script>

+ 1 - 1
app/view/setting/user.ejs

@@ -3,7 +3,7 @@
     <div class="panel-title">
         <div class="title-main">
             <h2>账号管理
-                <% if (projectData.max_user > user_total) { %>
+                <% 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>
                 <% } else { %>
                 <a href="#add-unpass" data-toggle="modal" data-target="#add-unpass" class="btn btn-primary btn-sm pull-right">添加账号(受限)</a>

+ 1 - 1
app/view/setting/user_permission.ejs

@@ -3,7 +3,7 @@
     <div class="panel-title">
         <div class="title-main">
             <h2>账号管理
-                <% if (projectData.max_user > user_total) { %>
+                <% 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>
                 <% } else { %>
                 <a href="#add-unpass" data-toggle="modal" data-target="#add-unpass" class="btn btn-primary btn-sm pull-right">添加账号(受限)</a>

+ 4 - 0
app/view/stage/detail_modal.ejs

@@ -29,6 +29,10 @@
                     <input type="text" class="form-control form-control-sm" value="<%- ctx.stage.im_pre ? ctx.stage.im_pre : '' %>" id="im-pre" maxlength="10">
                 </div>
                 <div class="form-group">
+                    <label>中间计量表号起始编号</label>
+                    <input type="Number" class="form-control form-control-sm" value="<%- ctx.stage.im_start_num ? ctx.stage.im_start_num : '' %>" id="im-start-num" maxlength="10">
+                </div>
+                <div class="form-group">
                     <label> </label>
                     <a  href="#choose2" data-toggle="modal" data-target="#choose2">高级设置</a>
                 </div>

+ 1 - 0
app/view/stage/gather.ejs

@@ -103,4 +103,5 @@
     }
     const chapter = JSON.parse('<%- JSON.stringify(ctx.tender.info.chapter) %>');
     const thousandth = <%- ctx.tender.info.display.thousandth %>;
+    const hintOver = <%- hintOver %>;
 </script>

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 9 - 134
app/view/stage/index.ejs


+ 47 - 6
app/view/stage/modal.ejs

@@ -264,12 +264,23 @@
                     </div>
                 </div>
                 <div class="form-group">
-                    <label>中间计量表号前缀</label>
-                    <input type="text" class="form-control form-control-sm" value="<%- ctx.stage.im_pre ? ctx.stage.im_pre : '' %>" id="im-pre" maxlength="20">
+                    <label>中间计量表号</label>
+                    <div class="input-group input-group-sm mb-2">
+                        <div class="input-group-prepend">
+                            <span class="input-group-text" style="width:70px; height:30px">前缀</span>
+                        </div>
+                        <input type="text" class="form-control" id="im-pre" maxlength="20" style="height: 30px">
+                    </div>
+                    <div class="input-group input-group-sm mb-2">
+                        <div class="input-group-prepend">
+                            <span class="input-group-text" style="width:70px; height:30px">起始编号</span>
+                        </div>
+                        <input type="Number" class="form-control" id="im-start-num" maxlength="10" style="height: 30px">
+                    </div>
                 </div>
                 <div class="form-group">
                     <label> </label>
-                    <a  href="#choose2" data-toggle="modal" data-target="#choose2">高级设置</a>
+                    <a href="#choose2" data-toggle="modal" data-target="#choose2">高级设置</a>
                 </div>
             </div>
             <div class="modal-footer">
@@ -545,9 +556,9 @@
             <div class="modal-header">
                 <h5 class="modal-title">收方单附件</h5>
             </div>
-            <div class="modal-body modal-height-300" style="overflow: auto">
-                <table class="table table-bordered">
-                    <tr><th>文件名</th><th>上传时间</th><th>下载</th></tr>
+            <div class="modal-body modal-height-300" style="overflow: auto;overflow-x:hidden;">
+                <table class="table table-sm table-bordered table-hover" style="word-break:break-all; table-layout: fixed">
+                    <tr><th>文件名</th><th width="120">上传时间</th><th width="70">下载</th></tr>
                     <tbody id="shoufang-flie-list">
 
                     </tbody>
@@ -580,6 +591,36 @@
         </div>
     </div>
 </div>
+<!--中间计量 附件-->
+<div class="modal fade" id="im-file" 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">
+                <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>
+                    <input type="file" class="" id="upload-im-file" multiple>
+                    <% } %>
+                </div>
+                <div class="modal-height-500" style="overflow:auto;">
+                    <table class="table table-sm table-bordered" style="word-break:break-all; table-layout: fixed">
+                        <thead>
+                        <tr><th width="240">文件名</th><th>上传人</th><th>上传时间</th><th width="40">操作</th></tr>
+                        </thead>
+                        <tbody id="im-attList">
+                        </tbody>
+                    </table>
+                </div>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-secondary btn-sm" data-dismiss="modal">关闭</button>
+            </div>
+        </div>
+    </div>
+</div>
 <% include ./audit_modal.ejs %>
 <% include ../shares/merge_peg_modal.ejs %>
 <% include ../shares/check_modal2.ejs %>

+ 42 - 0
app/view/stage_extra/safe_prod.ejs

@@ -0,0 +1,42 @@
+<% include ./sub_menu.ejs %>
+<div class="panel-content">
+    <div class="panel-title">
+        <div class="title-main d-flex">
+            <% include ./sub_mini_menu.ejs %>
+            <div>
+                安全生产
+            </div>
+            <% if (ctx.app.config.is_debug) { %>
+            <div class="d-inline-block ml-3">
+                <a id="exportExcel" class="btn btn-primary btn-sm" href="javascript: void(0)">导出安全生产Excel</a>
+            </div>
+            <% } %>
+            <div class="ml-auto"></div>
+        </div>
+    </div>
+    <div class="content-wrap">
+        <div class="c-header p-0"></div>
+        <div class="w-100 sub-content row">
+            <div class="c-body col-9">
+                <div class="sjs-height-1" id="safe-prod-spread" z-index="1">
+                </div>
+                <div z-index="0" style="display: none;">
+                    <input class="datepicker-here form-control form-control-sm" data-date-format="yyyy-MM-DD" data-language="zh" type="text" autocomplete="off" id="dp-input">
+                </div>
+            </div>
+            <div class="c-body col-3">
+                <table class="table table-bordered" style="width: 99%">
+                    <tr><th class="text-center" width="66.6%">名称</th><th class="text-center">金额</th></tr>
+                    <tbody id="sum">
+                    </tbody>
+                </table>
+            </div>
+        </div>
+    </div>
+</div>
+<script>
+    const stageId = <%- ctx.stage.id %>;
+    const stageUserId = <%- ctx.stage.user_id %>;
+    const readOnly = <%- ctx.stage.readOnly %>;
+    const thousandth = <%- ctx.tender.info.display.thousandth %>;
+</script>

+ 2 - 0
app/view/stage_extra/sub_menu_list.ejs

@@ -1,4 +1,6 @@
 <nav-menu title="返回" url="/tender/<%= ctx.tender.id %>/measure/stage/<%= ctx.stage.order %>" tclass="text-primary" ml="1" icon="fa-chevron-left"></nav-menu>
 <nav-menu title="甲供材料" url="/tender/<%= ctx.tender.id %>/measure/stage/<%= ctx.stage.order + '/extra/jgcl'%>" ml="3" active="<%= ctx.url.indexOf('extra/jgcl') %>"></nav-menu>
 <nav-menu title="奖罚金" url="/tender/<%= ctx.tender.id %>/measure/stage/<%= ctx.stage.order + '/extra/bonus'%>" ml="3" active="<%= ctx.url.indexOf('extra/bonus') %>"></nav-menu>
+<nav-menu title="安全生产" url="/tender/<%= ctx.tender.id %>/measure/stage/<%= ctx.stage.order + '/extra/safeProd'%>" ml="3" active="<%= ctx.url.indexOf('extra/safeProd') %>"></nav-menu>
+<nav-menu title="临时占地" url="/tender/<%= ctx.tender.id %>/measure/stage/<%= ctx.stage.order + '/extra/tempLand'%>" ml="3" active="<%= ctx.url.indexOf('extra/tempLand') %>"></nav-menu>
 <nav-menu title="其他" url="/tender/<%= ctx.tender.id %>/measure/stage/<%= ctx.stage.order + '/extra/other'%>" ml="3" active="<%= ctx.url.indexOf('extra/other') %>"></nav-menu>

+ 42 - 0
app/view/stage_extra/temp_land.ejs

@@ -0,0 +1,42 @@
+<% include ./sub_menu.ejs %>
+<div class="panel-content">
+    <div class="panel-title">
+        <div class="title-main d-flex">
+            <% include ./sub_mini_menu.ejs %>
+            <div>
+                临时占地
+            </div>
+            <% if (ctx.app.config.is_debug) { %>
+            <div class="d-inline-block ml-3">
+                <a id="exportExcel" class="btn btn-primary btn-sm" href="javascript: void(0)">导出临时占地Excel</a>
+            </div>
+            <% } %>
+            <div class="ml-auto"></div>
+        </div>
+    </div>
+    <div class="content-wrap">
+        <div class="c-header p-0"></div>
+        <div class="w-100 sub-content row">
+            <div class="c-body col-9">
+                <div class="sjs-height-1" id="temp-land-spread" z-index="1">
+                </div>
+                <div z-index="0" style="display: none;">
+                    <input class="datepicker-here form-control form-control-sm" data-date-format="yyyy-MM-DD" data-language="zh" type="text" autocomplete="off" id="dp-input">
+                </div>
+            </div>
+            <div class="c-body col-3">
+                <table class="table table-bordered" style="width: 99%">
+                    <tr><th class="text-center" width="66.6%">名称</th><th class="text-center">金额</th></tr>
+                    <tbody id="sum">
+                    </tbody>
+                </table>
+            </div>
+        </div>
+    </div>
+</div>
+<script>
+    const stageId = <%- ctx.stage.id %>;
+    const stageUserId = <%- ctx.stage.user_id %>;
+    const readOnly = <%- ctx.stage.readOnly %>;
+    const thousandth = <%- ctx.tender.info.display.thousandth %>;
+</script>

+ 4 - 4
app/view/stage_rela/detail.ejs

@@ -123,8 +123,8 @@
                             <div class="zhongjian-msg" style="height:400px;overflow-y: auto">
                                 <table class="table table-sm table-bordered" id="detail-show">
                                     <tbody>
-                                    <tr><th width="120" id="type-title-contract">本期合同计量数量</th><td id="show-contract-jl"></td></tr>
-                                    <tr><th id="type-title-qc">本期变更计量数量</th><td id="show-qc-jl"></td></tr>
+                                    <tr><th width="120" name="type-title-contract">本期合同计量数量</th><td id="show-contract-jl"></td></tr>
+                                    <tr><th name="type-title-qc">本期变更计量数量</th><td id="show-qc-jl"></td></tr>
                                     <tr><th>变更令号</th><td id="show-bgl-code"></td></tr>
                                     <tr><th>变更图号</th><td id="show-bgl-drawing-code"></td></tr>
                                     <tr><th>部位</th><td id="show-bw-name"></td></tr>
@@ -143,13 +143,13 @@
                                     <!--编辑状态-->
                                     <div class="input-group input-group-sm mb-2">
                                         <div class="input-group-prepend">
-                                            <span class="input-group-text" id="type-title-contract">本期合同计量数量</span>
+                                            <span class="input-group-text" name="type-title-contract">本期合同计量数量</span>
                                         </div>
                                         <input type="text" class="form-control" value="" id="contract-jl" readonly="">
                                     </div>
                                     <div class="input-group input-group-sm mb-2">
                                         <div class="input-group-prepend">
-                                            <span class="input-group-text" id="type-title-qc">本期变更计量数量</span>
+                                            <span class="input-group-text" name="type-title-qc">本期变更计量数量</span>
                                         </div>
                                         <input type="text" class="form-control" value="" id="qc-jl" readonly="">
                                     </div>

+ 8 - 0
app/view/tender/detail_modal.ejs

@@ -1794,6 +1794,12 @@
                         <label class="custom-control-label" for="sr_show">关联台账</label>
                     </div>
                 </div>
+                <div class="form-group">
+                    <div class="custom-control custom-checkbox mb-2">
+                        <input type="checkbox" class="custom-control-input" id="hint_over" checked="">
+                        <label class="custom-control-label" for="hint_over">超计时标红显示</label>
+                    </div>
+                </div>
             </div>
             <div class="modal-footer">
                 <button type="button" class="btn btn-sm btn-secondary" data-dismiss="modal">取消</button>
@@ -1835,6 +1841,7 @@
     const loadFunRelaProperty = function () {
         $('#sl_ignoreParent')[0].checked = property.fun_rela.sum_load.ignoreParent;
         $('#sr_show')[0].checked = property.fun_rela.stage_rela.show;
+        $('#hint_over')[0].checked = property.fun_rela.hintOver;
     };
 
     $('#bd-set-12').on('show.bs.modal', function () {
@@ -1843,6 +1850,7 @@
     function post12 () {
         const prop = {
             fun_rela: {
+                hintOver: $('#hint_over')[0].checked,
                 sum_load: {
                     ignoreParent: $('#sl_ignoreParent')[0].checked,
                 },

+ 1 - 1
app/view/wap/shoufangupload.ejs

@@ -35,7 +35,7 @@
                 <td>第 <%- order %> 期</td>
             </tr>
             <tr>
-                <th>台帐清单/计量单元</th>
+                <th>清单/计量单元</th>
                 <td><%- name %></td>
             </tr>
             </tbody>

+ 80 - 13
builder_report_index_define.js

@@ -74,11 +74,11 @@ const stage_jgcl = {
         { name: 'uuid', field: 'uuid', type: dataType.str },
         { name: '名称', field: 'name', type: dataType.str },
         { name: '单位', field: 'unit', type: dataType.str },
-        { name: '单价', field: 'unit_price', type: dataType.currency, tag: { type: 'up' } },
-        { name: '本期到场-数量', field: 'arrive_qty', type: dataType.currency, tag: { type: 'qty', unitKey: 4 } },
-        { name: '本期到场-金额', field: 'arrive_tp', type: dataType.currency, tag: { type: 'tp' } },
-        { name: '本期扣回-数量', field: 'deduct_qty', type: dataType.currency, tag: { type: 'qty', unitKey: 4 } },
-        { name: '本期扣回-金额', field: 'deduct_tp', type: dataType.currency, tag: { type: 'tp' } },
+        { name: '单价', field: 'unit_price', type: dataType.currency },
+        { name: '本期到场-数量', field: 'arrive_qty', type: dataType.currency },
+        { name: '本期到场-金额', field: 'arrive_tp', type: dataType.currency },
+        { name: '本期扣回-数量', field: 'deduct_qty', type: dataType.currency },
+        { name: '本期扣回-金额', field: 'deduct_tp', type: dataType.currency },
         { name: '材料来源', field: 'source', type: dataType.str },
         { name: '单据号', field: 'bills_code', type: dataType.str },
         { name: '检验单编号', field: 'check_code', type: dataType.str },
@@ -88,11 +88,15 @@ const stage_jgcl = {
         { name: '期历史记录', field: 'shistory', type: dataType.str },
         { name: '排序', field: 'order', type: dataType.int },
         { name: '往期是否已用', field: 'pre_used', type: dataType.int },
-        { name: '截止上期到场-数量', field: 'pre_arrive_qty', type: dataType.currency, tag: { type: 'qty', unitKey: 4 } },
-        { name: '截止上期到场-金额', field: 'pre_arrive_tp', type: dataType.currency, tag: { type: 'tp' } },
-        { name: '截止上期扣回-数量', field: 'pre_deduct_qty', type: dataType.currency, tag: { type: 'qty', unitKey: 4 } },
-        { name: '截止上期扣回-金额', field: 'pre_deduct_tp', type: dataType.currency, tag: { type: 'tp' } },
+        { name: '截止上期到场-数量', field: 'pre_arrive_qty', type: dataType.currency },
+        { name: '截止上期到场-金额', field: 'pre_arrive_tp', type: dataType.currency },
+        { name: '截止上期扣回-数量', field: 'pre_deduct_qty', type: dataType.currency },
+        { name: '截止上期扣回-金额', field: 'pre_deduct_tp', type: dataType.currency },
         { name: '备注', field: 'memo', type: dataType.str },
+        { name: '截止本期到场-数量', field: 'end_arrive_qty', type: dataType.currency },
+        { name: '截止本期到场-金额', field: 'end_arrive_tp', type: dataType.currency },
+        { name: '截止本期扣回-数量', field: 'end_deduct_qty', type: dataType.currency },
+        { name: '截止本期扣回-金额', field: 'end_deduct_tp', type: dataType.currency },
     ],
 };
 const stage_bonus = {
@@ -136,15 +140,78 @@ const stage_other = {
         { name: '新增期id', field: 'add_sid', type: dataType.int },
         { name: '创建时间', field: 'add_time', type: dataType.time },
         { name: '名称', field: 'name', type: dataType.str },
-        { name: '金额', field: 'total_price', type: dataType.currency, tag: { type: 'tp' } },
-        { name: '本期金额', field: 'tp', type: dataType.currency, tag: { type: 'tp' } },
+        { name: '金额', field: 'total_price', type: dataType.currency },
+        { name: '本期金额', field: 'tp', type: dataType.currency },
         { name: '时间', field: 'real_time', type: dataType.time },
         { name: '备注', field: 'memo', type: dataType.str },
         { name: '期历史记录', field: 'shistory', type: dataType.str },
         { name: '排序', field: 'order', type: dataType.int },
         { name: '往期是否已用', field: 'pre_used', type: dataType.int },
-        { name: '截止上期-金额', field: 'pre_tp', type: dataType.currency, tag: { type: 'tp' } },
+        { name: '截止上期-金额', field: 'pre_tp', type: dataType.currency },
         { name: '类型', field: 'o_type', type: dataType.str },
+        { name: '截止本期-金额', field: 'end_tp', type: dataType.currency},
+    ],
+};
+const stage_safe_prod = {
+    name: '期-安全生产(mem_stage_safe_prod)',
+    remark: '',
+    id: 69,
+    key: 'mem_stage_safe_prod',
+    prefix: '期-安全生产',
+    cols: [
+        { name: 'id', field: 'id', type: dataType.int },
+        { name: 'uuid', field: 'uuid', type: dataType.str },
+        { name: '所属标段id', field: 'tid', type: dataType.int },
+        { name: '所属期id', field: 'sid', type: dataType.int },
+        { name: '所属第几期', field: 'sorder', type: dataType.int },
+        { name: '新增人id', field: 'add_uid', type: dataType.int },
+        { name: '新增期id', field: 'add_sid', type: dataType.int },
+        { name: '创建时间', field: 'add_time', type: dataType.time },
+        { name: '名称', field: 'name', type: dataType.str },
+        { name: '单位', field: 'unit', type: dataType.str },
+        { name: '单价', field: 'unit_price', type: dataType.currency },
+        { name: '数量', field: 'quantity', type: dataType.currency },
+        { name: '金额', field: 'total_price', type: dataType.currency },
+        { name: '本期数量', field: 'qty', type: dataType.currency },
+        { name: '本期金额', field: 'tp', type: dataType.currency },
+        { name: '备注', field: 'memo', type: dataType.str },
+        { name: '期历史记录', field: 'shistory', type: dataType.str },
+        { name: '排序', field: 'order', type: dataType.int },
+        { name: '往期是否已用', field: 'pre_used', type: dataType.int },
+        { name: '截止上期-数量', field: 'pre_qty', type: dataType.currency },
+        { name: '截止上期-金额', field: 'pre_tp', type: dataType.currency },
+        { name: '截止本期-数量', field: 'end_qty', type: dataType.currency },
+        { name: '截止本期-金额', field: 'end_tp', type: dataType.currency },
+    ],
+};
+const stage_temp_land = {
+    name: '期-临时占地(mem_stage_temp_land)',
+    remark: '',
+    id: 70,
+    key: 'mem_stage_temp_land',
+    prefix: '期-临时占地',
+    cols: [
+        { name: 'id', field: 'id', type: dataType.int },
+        { name: 'uuid', field: 'uuid', type: dataType.str },
+        { name: '所属标段id', field: 'tid', type: dataType.int },
+        { name: '所属期id', field: 'sid', type: dataType.int },
+        { name: '所属第几期', field: 'sorder', type: dataType.int },
+        { name: '新增人id', field: 'add_uid', type: dataType.int },
+        { name: '新增期id', field: 'add_sid', type: dataType.int },
+        { name: '创建时间', field: 'add_time', type: dataType.time },
+        { name: '名称', field: 'name', type: dataType.str },
+        { name: '单位', field: 'unit', type: dataType.str },
+        { name: '单价', field: 'unit_price', type: dataType.currency},
+        { name: '本期数量', field: 'qty', type: dataType.currency },
+        { name: '本期金额', field: 'tp', type: dataType.currency },
+        { name: '备注', field: 'memo', type: dataType.str },
+        { name: '期历史记录', field: 'shistory', type: dataType.str },
+        { name: '排序', field: 'order', type: dataType.int },
+        { name: '往期是否已用', field: 'pre_used', type: dataType.int },
+        { name: '截止上期-数量', field: 'pre_qty', type: dataType.currency },
+        { name: '截止上期-金额', field: 'pre_tp', type: dataType.currency },
+        { name: '截止本期-数量', field: 'end_qty', type: dataType.currency },
+        { name: '截止本期-金额', field: 'end_tp', type: dataType.currency },
     ],
 };
 // 变更令
@@ -2067,7 +2134,7 @@ const defines = [
     union_data,
     month_progress,
     stage_bills, stage_bills_compare,
-    stage_jgcl, stage_bonus, stage_other,
+    stage_jgcl, stage_bonus, stage_other, stage_safe_prod, stage_temp_land,
     change, change_bills,
     stage_pos, stage_pos_compare,
     stage_pay,

+ 1 - 1
config/config.default.js

@@ -147,7 +147,7 @@ module.exports = appInfo => {
     config.gzip = {
         threshold: 2048,
         // 下载的url要用正则忽略
-        ignore: /(\w*)(\/download\/file)|(\/profile\/qrCode)|(\/download\/compresse-file)|(\/compresse\/file)(\w*)/ig,
+        ignore: /(\w*)(\/download\/file)|(\/profile\/qrCode)|(\/download\/compresse-file)|(\/compresse\/file)|(\/im-file\/download)(\w*)/ig,
     };
 
     config.customLogger = {

+ 40 - 0
config/web.js

@@ -503,6 +503,46 @@ const JsFiles = {
                 ],
                 mergeFile: 'se_other',
             },
+            safeProd: {
+                files: [
+                    '/public/js/spreadjs/sheets/v11/gc.spread.sheets.all.11.2.2.min.js',
+                    '/public/js/spreadjs/sheets/v11/interop/gc.spread.excelio.11.2.2.min.js',
+                    '/public/js/decimal.min.js',
+                    '/public/js/file-saver/FileSaver.js',
+                    '/public/js/shares/export_excel.js',
+                    '/public/js/component/menu.js',
+                ],
+                mergeFiles: [
+                    '/public/js/sub_menu.js',
+                    '/public/js/div_resizer.js',
+                    '/public/js/spreadjs_rela/spreadjs_zh.js',
+                    '/public/js/shares/sjs_setting.js',
+                    '/public/js/zh_calc.js',
+                    '/public/js/path_tree.js',
+                    '/public/js/se_safe_prod.js',
+                ],
+                mergeFile: 'se_safe_prod',
+            },
+            tempLand: {
+                files: [
+                    '/public/js/spreadjs/sheets/v11/gc.spread.sheets.all.11.2.2.min.js',
+                    '/public/js/spreadjs/sheets/v11/interop/gc.spread.excelio.11.2.2.min.js',
+                    '/public/js/decimal.min.js',
+                    '/public/js/file-saver/FileSaver.js',
+                    '/public/js/shares/export_excel.js',
+                    '/public/js/component/menu.js',
+                ],
+                mergeFiles: [
+                    '/public/js/sub_menu.js',
+                    '/public/js/div_resizer.js',
+                    '/public/js/spreadjs_rela/spreadjs_zh.js',
+                    '/public/js/shares/sjs_setting.js',
+                    '/public/js/zh_calc.js',
+                    '/public/js/path_tree.js',
+                    '/public/js/se_temp_land.js',
+                ],
+                mergeFile: 'se_temp_land',
+            },
         },
         stageRela: {
             info: {

+ 50 - 144
sql/update.sql

@@ -1,167 +1,68 @@
-ALTER TABLE `zh_tender`
-ADD COLUMN `has_rela`  tinyint(1) NOT NULL DEFAULT 0 COMMENT '是否有关联台账' AFTER `had_map`;
 
-CREATE TABLE `zh_stage_rela` (
-  `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT 'id',
-  `tid` int(11) unsigned NOT NULL COMMENT '标段id',
-  `sid` int(11) unsigned NOT NULL COMMENT '期id',
-  `sorder` tinyint(4) unsigned NOT NULL COMMENT '期序号',
-  `rela_tid` int(11) unsigned NOT NULL COMMENT '关联标段id',
-  `rela_tname` varchar(255) CHARACTER SET utf8 NOT NULL COMMENT '关联标段-名称',
-  `rela_sid` int(11) unsigned NOT NULL COMMENT '关联期id',
-  `rela_sorder` tinyint(4) unsigned NOT NULL COMMENT '关联期序号',
-  `add_time` datetime NOT NULL COMMENT '添加时间',
-  `update_time` datetime NOT NULL COMMENT '更新时间',
-  `total_price` decimal(28,6) DEFAULT NULL,
-  `cache_tp` text CHARACTER SET utf8 COMMENT '缓存金额(当前标段单价计算)',
-  `cache_org_tp` text CHARACTER SET utf8 COMMENT '缓存金额(原关联标段单价计算)',
-  PRIMARY KEY (`id`)
-) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='计量期-关联台账(含缓存数据)';
+ALTER TABLE `zh_stage`
+ADD COLUMN `im_start_num`  int(11) NOT NULL DEFAULT 1 AFTER `im_gather_node`;
 
-CREATE TABLE `zh_stage_rela_bills` (
-  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+-- 安全生产
+CREATE TABLE `zh_stage_safe_prod` (
+  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
   `tid` int(11) unsigned NOT NULL COMMENT '标段id',
   `sid` int(11) unsigned NOT NULL COMMENT '期id',
   `sorder` tinyint(4) unsigned NOT NULL COMMENT '期序号',
-  `rela_tid` int(11) unsigned NOT NULL COMMENT '关联标段id',
-  `rela_sid` int(11) unsigned NOT NULL COMMENT '关联期id',
-  `rela_sorder` tinyint(4) unsigned NOT NULL COMMENT '关联期序号',
-  `lid` varchar(36) CHARACTER SET ascii NOT NULL COMMENT '台账id',
-  `contract_qty` decimal(24,8) DEFAULT NULL COMMENT '合同计量-数量',
-  `contract_tp` decimal(24,8) DEFAULT NULL COMMENT '合同计量-金额',
-  `contract_expr` varchar(255) CHARACTER SET utf8 DEFAULT NULL COMMENT '合同计量-公式',
-  `qc_qty` decimal(24,8) DEFAULT NULL COMMENT '数量变更-数量',
-  `qc_tp` decimal(24,8) DEFAULT NULL COMMENT '数量变更-金额',
-  `postil` varchar(1000) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '本期批注',
+  `uuid` varchar(36) CHARACTER SET ascii NOT NULL COMMENT '安全生产标识id',
+  `add_sid` int(11) unsigned NOT NULL COMMENT '新增期id',
+  `add_uid` int(11) NOT NULL COMMENT '新增人id',
+  `add_time` datetime NOT NULL COMMENT '新增时间',
+  `order` int(11) unsigned NOT NULL COMMENT '排序',
+  `name` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '名称',
+  `unit` varchar(20) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '单位',
   `unit_price` decimal(24,8) DEFAULT NULL COMMENT '单价',
+  `quantity` decimal(24,8) DEFAULT NULL COMMENT '计划-数量',
+  `total_price` decimal(24,8) DEFAULT NULL COMMENT '计划-金额',
+  `qty` decimal(24,8) DEFAULT NULL COMMENT '本期-数量',
+  `tp` decimal(24,8) DEFAULT NULL COMMENT '本期-金额',
+  `shistory` text CHARACTER SET utf8 COMMENT '本期历史数据',
+  `pre_used` tinyint(4) DEFAULT '0' COMMENT '往期是否使用',
+  `memo` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '备注',
   PRIMARY KEY (`id`)
-) ENGINE=InnoDB AUTO_INCREMENT=81 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
-
-CREATE TABLE `zh_stage_rela_pos` (
-  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
-  `tid` int(11) unsigned NOT NULL COMMENT '标段id',
-  `sid` int(11) unsigned NOT NULL COMMENT '期id',
-  `sorder` tinyint(4) unsigned NOT NULL COMMENT '期序号',
-  `rela_tid` int(11) unsigned NOT NULL COMMENT '关联标段id',
-  `rela_sid` int(11) unsigned NOT NULL COMMENT '关联期id',
-  `rela_sorder` tinyint(4) unsigned NOT NULL COMMENT '关联期序号',
-  `lid` varchar(36) CHARACTER SET ascii NOT NULL COMMENT '台账id',
-  `pid` varchar(36) CHARACTER SET ascii NOT NULL COMMENT '计量单元id',
-  `contract_qty` decimal(24,8) DEFAULT NULL COMMENT '合同计量-数量',
-  `contract_expr` varchar(255) CHARACTER SET utf8 DEFAULT NULL COMMENT '合同计量-公式',
-  `qc_qty` decimal(24,8) DEFAULT NULL COMMENT '数量变更-数量',
-  `postil` varchar(1000) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '本期批注',
-  PRIMARY KEY (`id`)
-) ENGINE=InnoDB AUTO_INCREMENT=81 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
+) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
 
-CREATE TABLE `zh_stage_rela_bills_final` (
-  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+-- 临时占地
+CREATE TABLE `zh_stage_temp_land` (
+  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
   `tid` int(11) unsigned NOT NULL COMMENT '标段id',
   `sid` int(11) unsigned NOT NULL COMMENT '期id',
   `sorder` tinyint(4) unsigned NOT NULL COMMENT '期序号',
-  `rela_tid` int(11) unsigned NOT NULL COMMENT '关联标段id',
-  `rela_sid` int(11) unsigned NOT NULL COMMENT '关联期id',
-  `rela_sorder` tinyint(4) unsigned NOT NULL COMMENT '关联期序号',
-  `lid` varchar(36) CHARACTER SET ascii NOT NULL COMMENT '台账id',
-  `contract_qty` decimal(24,8) DEFAULT NULL COMMENT '合同计量-数量',
-  `contract_tp` decimal(24,8) DEFAULT NULL COMMENT '合同计量-金额',
-  `qc_qty` decimal(24,8) DEFAULT NULL COMMENT '数量变更-数量',
-  `qc_tp` decimal(24,8) DEFAULT NULL COMMENT '数量变更-金额',
+  `uuid` varchar(36) CHARACTER SET ascii NOT NULL COMMENT '安全生产标识id',
+  `add_sid` int(11) unsigned NOT NULL COMMENT '新增期id',
+  `add_uid` int(11) NOT NULL COMMENT '新增人id',
+  `add_time` datetime NOT NULL COMMENT '新增时间',
+  `order` int(11) unsigned NOT NULL COMMENT '排序',
+  `name` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '名称',
+  `unit` varchar(20) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '单位',
+  `unit_price` decimal(24,8) DEFAULT NULL COMMENT '单价',
+  `qty` decimal(24,8) DEFAULT NULL COMMENT '本期-数量',
+  `tp` decimal(24,8) DEFAULT NULL COMMENT '本期-金额',
+  `shistory` text CHARACTER SET utf8 COMMENT '本期历史数据',
+  `pre_used` tinyint(4) DEFAULT '0' COMMENT '往期是否使用',
+  `memo` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '备注',
   PRIMARY KEY (`id`)
-) ENGINE=InnoDB AUTO_INCREMENT=81 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
+) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
 
-CREATE TABLE `zh_stage_rela_pos_final` (
-  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+CREATE TABLE `zh_stage_detail_attachment` (
+  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
   `tid` int(11) unsigned NOT NULL COMMENT '标段id',
-  `sid` int(11) unsigned NOT NULL COMMENT 'id',
+  `sid` int(11) unsigned NOT NULL COMMENT '标段id',
   `sorder` tinyint(4) unsigned NOT NULL COMMENT '期序号',
-  `rela_tid` int(11) unsigned NOT NULL COMMENT '关联标段id',
-  `rela_sid` int(11) unsigned NOT NULL COMMENT '关联期id',
-  `rela_sorder` tinyint(4) unsigned NOT NULL COMMENT '关联期序号',
-  `lid` varchar(36) CHARACTER SET ascii NOT NULL COMMENT '台账id',
-  `pid` varchar(36) CHARACTER SET ascii NOT NULL COMMENT '计量单元id',
-  `contract_qty` decimal(24,8) DEFAULT NULL COMMENT '合同计量-数量',
-  `qc_qty` decimal(24,8) DEFAULT NULL COMMENT '数量变更-数量',
+  `uuid` varchar(50) CHARACTER SET ascii NOT NULL COMMENT '中间计量附件标识uuid',
+  `lid` varchar(50) CHARACTER SET ascii NOT NULL COMMENT '台账id(标识)',
+  `pid` varchar(50) CHARACTER SET ascii NOT NULL COMMENT '计量单元id(标识)',
+  `code` varchar(50) CHARACTER SET utf8 DEFAULT NULL COMMENT '编号(标识)',
+  `name` varchar(255) CHARACTER SET utf8 DEFAULT NULL COMMENT '名称(标识)',
+  `unit` varchar(20) CHARACTER SET utf8 DEFAULT NULL COMMENT '单位(标识)',
+  `unit_price` decimal(24,8) DEFAULT NULL COMMENT '单价(标识)',
+  `pos_name` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '计量单元名称(标识)',
+  `im_type` tinyint(1) NOT NULL COMMENT '中间计量类型(标识)',
+  `attachment` text CHARACTER SET utf8 COMMENT '附件',
   PRIMARY KEY (`id`)
-) ENGINE=InnoDB AUTO_INCREMENT=81 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
-
-ALTER TABLE `zh_rpt_archive`
-CHANGE COLUMN `content` `content` JSON NULL DEFAULT NULL ;
-
-CREATE TABLE `zh_s2b_z_proj` (
-  `id` int(11) NOT NULL AUTO_INCREMENT,
-  `pid` int(11) NOT NULL COMMENT '项目id',
-  `xmName` varchar(255) COLLATE utf8_unicode_ci NOT NULL COMMENT '项目发改全称',
-  `xmCode` varchar(255) COLLATE utf8_unicode_ci NOT NULL COMMENT '项目发改赋码',
-  `opCode` varchar(255) COLLATE utf8_unicode_ci NOT NULL COMMENT '来源编码',
-  `base_data` text COLLATE utf8_unicode_ci COMMENT '用来存定制接口所需参数',
-  PRIMARY KEY (`id`),
-  KEY `pid` (`pid`)
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
 
-
---
---
-
-CREATE TABLE `zh_s2b_z_tender` (
-  `id` int(11) NOT NULL AUTO_INCREMENT,
-  `tid` int(11) NOT NULL COMMENT '标段id',
-  `base_data` text COLLATE utf8_unicode_ci COMMENT 'json,一些需要的数据',
-  PRIMARY KEY (`id`),
-  KEY `tid` (`tid`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='浙江项目标段表';
-
-ALTER TABLE `zh_project` ADD `customType` VARCHAR(255) NOT NULL DEFAULT '' COMMENT '定制项目通知类型参数' AFTER `secret`;
-
-ALTER TABLE `calculation`.`zh_tender_info`
-ADD COLUMN `fun_rela` varchar(255) CHARACTER SET utf8 NULL COMMENT '功能设置' AFTER `ledger_check`;
-
-ALTER TABLE `zh_project` ADD `data_collect` TINYINT(1) NOT NULL DEFAULT '0' COMMENT '决策大屏是否显示及对应大屏编号' AFTER `sjs_rela`;
-
-CREATE TABLE `zh_datacollect_audit` (
-  `id` int(11) NOT NULL AUTO_INCREMENT,
-  `pid` int(11) NOT NULL COMMENT '项目id',
-  `groupid` int(11) DEFAULT NULL COMMENT '用户组id',
-  `uid` int(11) DEFAULT NULL COMMENT '用户id',
-  `create_time` datetime NOT NULL COMMENT '添加时间',
-   PRIMARY KEY (`id`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='决策大屏用户查看权限表';
-
-ALTER TABLE `zh_material_bills` ADD `origin` VARCHAR(255) NULL DEFAULT NULL COMMENT '来源地' AFTER `pre_tp`;
-ALTER TABLE `zh_material_bills_history` ADD `origin` VARCHAR(255) NULL DEFAULT NULL COMMENT '来源地' AFTER `pre_tp`;
-
-ALTER TABLE `zh_material_bills` ADD `order` INT NULL DEFAULT NULL COMMENT '排序' AFTER `mid`;
-UPDATE `zh_material_bills` SET `order`=`id`
-
-CREATE TABLE `zh_stage_shoufang`  (
-  `id` int(11) NOT NULL AUTO_INCREMENT,
-  `tid` int(11) NOT NULL COMMENT '标段id',
-  `order` tinyint(4) NOT NULL COMMENT '期数',
-  `sid` int(11) NOT NULL COMMENT '期id',
-  `lid` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL COMMENT '台账uuid',
-  `pid` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL COMMENT '计量单元uuid',
-  `qrcode` VARCHAR(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL COMMENT '二维码存放地址',
-  `create_time` datetime NOT NULL COMMENT '创建时间',
-  PRIMARY KEY (`id`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT = '收方单数据';
-
-CREATE TABLE `zh_stage_shoufang_attachment`  (
-  `id` int(11) NOT NULL AUTO_INCREMENT,
-  `tid` int(11) NOT NULL COMMENT '标段id',
-  `sid` int(11) NOT NULL COMMENT '期id',
-  `sfid` int(11) NOT NULL COMMENT '收方单id',
-  `filename` varchar(1000) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL COMMENT '文件名称',
-  `fileext` varchar(10) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL COMMENT '文件后缀',
-  `filesize` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL COMMENT '文件大小',
-  `filepath` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL COMMENT '文件存储路径',
-  `in_time` datetime NOT NULL COMMENT '创建时间',
-  PRIMARY KEY (`id`),
-  INDEX `sfid`(`sfid`),
-  INDEX `sid`(`sid`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT = '收方单附件表';
-

+ 278 - 0
sql/update20211015.sql

@@ -0,0 +1,278 @@
+ALTER TABLE `zh_tender`
+ADD COLUMN `has_rela`  tinyint(1) NOT NULL DEFAULT 0 COMMENT '是否有关联台账' AFTER `had_map`;
+
+CREATE TABLE `zh_stage_rela` (
+  `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT 'id',
+  `tid` int(11) unsigned NOT NULL COMMENT '标段id',
+  `sid` int(11) unsigned NOT NULL COMMENT '期id',
+  `sorder` tinyint(4) unsigned NOT NULL COMMENT '期序号',
+  `rela_tid` int(11) unsigned NOT NULL COMMENT '关联标段id',
+  `rela_tname` varchar(255) CHARACTER SET utf8 NOT NULL COMMENT '关联标段-名称',
+  `rela_sid` int(11) unsigned NOT NULL COMMENT '关联期id',
+  `rela_sorder` tinyint(4) unsigned NOT NULL COMMENT '关联期序号',
+  `add_time` datetime NOT NULL COMMENT '添加时间',
+  `update_time` datetime NOT NULL COMMENT '更新时间',
+  `total_price` decimal(28,6) DEFAULT NULL,
+  `cache_tp` text CHARACTER SET utf8 COMMENT '缓存金额(当前标段单价计算)',
+  `cache_org_tp` text CHARACTER SET utf8 COMMENT '缓存金额(原关联标段单价计算)',
+  PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='计量期-关联台账(含缓存数据)';
+
+CREATE TABLE `zh_stage_rela_bills` (
+  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+  `tid` int(11) unsigned NOT NULL COMMENT '标段id',
+  `sid` int(11) unsigned NOT NULL COMMENT '期id',
+  `sorder` tinyint(4) unsigned NOT NULL COMMENT '期序号',
+  `rela_tid` int(11) unsigned NOT NULL COMMENT '关联标段id',
+  `rela_sid` int(11) unsigned NOT NULL COMMENT '关联期id',
+  `rela_sorder` tinyint(4) unsigned NOT NULL COMMENT '关联期序号',
+  `lid` varchar(36) CHARACTER SET ascii NOT NULL COMMENT '台账id',
+  `contract_qty` decimal(24,8) DEFAULT NULL COMMENT '合同计量-数量',
+  `contract_tp` decimal(24,8) DEFAULT NULL COMMENT '合同计量-金额',
+  `contract_expr` varchar(255) CHARACTER SET utf8 DEFAULT NULL COMMENT '合同计量-公式',
+  `qc_qty` decimal(24,8) DEFAULT NULL COMMENT '数量变更-数量',
+  `qc_tp` decimal(24,8) DEFAULT NULL COMMENT '数量变更-金额',
+  `postil` varchar(1000) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '本期批注',
+  `unit_price` decimal(24,8) DEFAULT NULL COMMENT '单价',
+  PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
+
+CREATE TABLE `zh_stage_rela_pos` (
+  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+  `tid` int(11) unsigned NOT NULL COMMENT '标段id',
+  `sid` int(11) unsigned NOT NULL COMMENT '期id',
+  `sorder` tinyint(4) unsigned NOT NULL COMMENT '期序号',
+  `rela_tid` int(11) unsigned NOT NULL COMMENT '关联标段id',
+  `rela_sid` int(11) unsigned NOT NULL COMMENT '关联期id',
+  `rela_sorder` tinyint(4) unsigned NOT NULL COMMENT '关联期序号',
+  `lid` varchar(36) CHARACTER SET ascii NOT NULL COMMENT '台账id',
+  `pid` varchar(36) CHARACTER SET ascii NOT NULL COMMENT '计量单元id',
+  `contract_qty` decimal(24,8) DEFAULT NULL COMMENT '合同计量-数量',
+  `contract_expr` varchar(255) CHARACTER SET utf8 DEFAULT NULL COMMENT '合同计量-公式',
+  `qc_qty` decimal(24,8) DEFAULT NULL COMMENT '数量变更-数量',
+  `postil` varchar(1000) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '本期批注',
+  PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
+
+CREATE TABLE `zh_stage_rela_bills_final` (
+  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+  `tid` int(11) unsigned NOT NULL COMMENT '标段id',
+  `sid` int(11) unsigned NOT NULL COMMENT '期id',
+  `sorder` tinyint(4) unsigned NOT NULL COMMENT '期序号',
+  `rela_tid` int(11) unsigned NOT NULL COMMENT '关联标段id',
+  `rela_sid` int(11) unsigned NOT NULL COMMENT '关联期id',
+  `rela_sorder` tinyint(4) unsigned NOT NULL COMMENT '关联期序号',
+  `lid` varchar(36) CHARACTER SET ascii NOT NULL COMMENT '台账id',
+  `contract_qty` decimal(24,8) DEFAULT NULL COMMENT '合同计量-数量',
+  `contract_tp` decimal(24,8) DEFAULT NULL COMMENT '合同计量-金额',
+  `qc_qty` decimal(24,8) DEFAULT NULL COMMENT '数量变更-数量',
+  `qc_tp` decimal(24,8) DEFAULT NULL COMMENT '数量变更-金额',
+  PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
+
+CREATE TABLE `zh_stage_rela_pos_final` (
+  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+  `tid` int(11) unsigned NOT NULL COMMENT '标段id',
+  `sid` int(11) unsigned NOT NULL COMMENT '期id',
+  `sorder` tinyint(4) unsigned NOT NULL COMMENT '期序号',
+  `rela_tid` int(11) unsigned NOT NULL COMMENT '关联标段id',
+  `rela_sid` int(11) unsigned NOT NULL COMMENT '关联期id',
+  `rela_sorder` tinyint(4) unsigned NOT NULL COMMENT '关联期序号',
+  `lid` varchar(36) CHARACTER SET ascii NOT NULL COMMENT '台账id',
+  `pid` varchar(36) CHARACTER SET ascii NOT NULL COMMENT '计量单元id',
+  `contract_qty` decimal(24,8) DEFAULT NULL COMMENT '合同计量-数量',
+  `qc_qty` decimal(24,8) DEFAULT NULL COMMENT '数量变更-数量',
+  PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
+
+CREATE TABLE `zh_stage_rela_im` (
+  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+  `tid` int(11) NOT NULL,
+  `sid` int(11) NOT NULL,
+  `sorder` tinyint(4) NOT NULL,
+  `rela_tid` int(11) NOT NULL,
+  `rela_sid` int(11) NOT NULL,
+  `rela_sorder` tinyint(4) NOT NULL,
+  `lid` varchar(50) CHARACTER SET ascii NOT NULL,
+  `pid` varchar(50) CHARACTER SET ascii NOT NULL,
+  `im_id` int(11) NOT NULL,
+  `code` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL,
+  `name` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
+  `unit` varchar(20) COLLATE utf8_unicode_ci DEFAULT NULL,
+  `unit_price` decimal(24,8) DEFAULT NULL,
+  `peg` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
+  `drawing_code` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL,
+  `bw` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
+  `xm` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
+  `position` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
+  `jldy` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
+  `dwgc` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
+  `fbgc` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
+  `fxgc` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
+  `doc_code` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
+  `im_code` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
+  `calc_memo` text COLLATE utf8_unicode_ci,
+  `calc_img_remark` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
+  `calc_img` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
+  `bgl_code` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
+  `bgl_drawing_code` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
+  `jl` decimal(24,8) DEFAULT NULL,
+  `contract_jl` decimal(24,8) DEFAULT NULL,
+  `qc_jl` decimal(24,8) DEFAULT NULL,
+  `pre_jl` decimal(24,8) DEFAULT NULL,
+  `pre_contract_jl` decimal(24,8) DEFAULT NULL,
+  `pre_qc_jl` decimal(24,8) DEFAULT NULL,
+  `end_jl` decimal(24,8) DEFAULT NULL,
+  `end_contract_jl` decimal(24,8) DEFAULT NULL,
+  `end_qc_jl` decimal(24,8) DEFAULT NULL,
+  `tp` decimal(24,8) DEFAULT NULL,
+  `contract_tp` decimal(24,8) DEFAULT NULL,
+  `qc_tp` decimal(24,8) DEFAULT NULL,
+  `pre_tp` decimal(24,8) DEFAULT NULL,
+  `pre_contract_tp` decimal(24,8) DEFAULT NULL,
+  `pre_qc_tp` decimal(24,8) DEFAULT NULL,
+  `end_tp` decimal(24,8) DEFAULT NULL,
+  `end_contract_tp` decimal(24,8) DEFAULT NULL,
+  `end_qc_tp` decimal(24,8) DEFAULT NULL,
+  `quantity` decimal(24,8) DEFAULT NULL,
+  `total_price` decimal(24,8) DEFAULT NULL,
+  PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='关联台账,缓存,中间计量';
+
+CREATE TABLE `zh_stage_rela_im_bills` (
+  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+  `tid` int(11) NOT NULL,
+  `sid` int(11) NOT NULL,
+  `sorder` tinyint(4) NOT NULL,
+  `rela_tid` int(11) NOT NULL,
+  `rela_sid` int(11) NOT NULL,
+  `rela_sorder` tinyint(4) NOT NULL,
+  `bid` varchar(50) CHARACTER SET ascii DEFAULT NULL,
+  `im_id` int(11) NOT NULL,
+  `b_code` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL,
+  `name` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
+  `unit` varchar(20) COLLATE utf8_unicode_ci DEFAULT NULL,
+  `unit_price` decimal(24,8) DEFAULT NULL,
+  `im_code` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
+  `jl` decimal(24,8) DEFAULT NULL,
+  `contract_jl` decimal(24,8) DEFAULT NULL,
+  `qc_jl` decimal(24,8) DEFAULT NULL,
+  `pre_jl` decimal(24,8) DEFAULT NULL,
+  `pre_contract_jl` decimal(24,8) DEFAULT NULL,
+  `pre_qc_jl` decimal(24,8) DEFAULT NULL,
+  `end_jl` decimal(24,8) DEFAULT NULL,
+  `end_contract_jl` decimal(24,8) DEFAULT NULL,
+  `end_qc_jl` decimal(24,8) DEFAULT NULL,
+  `tp` decimal(24,8) DEFAULT NULL,
+  `contract_tp` decimal(24,8) DEFAULT NULL,
+  `qc_tp` decimal(24,8) DEFAULT NULL,
+  `pre_tp` decimal(24,8) DEFAULT NULL,
+  `pre_contract_tp` decimal(24,8) DEFAULT NULL,
+  `pre_qc_tp` decimal(24,8) DEFAULT NULL,
+  `end_tp` decimal(24,8) DEFAULT NULL,
+  `end_contract_tp` decimal(24,8) DEFAULT NULL,
+  `end_qc_tp` decimal(24,8) DEFAULT NULL,
+  `quantity` decimal(24,8) DEFAULT NULL,
+  `total_price` decimal(24,8) DEFAULT NULL,
+  PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='关联台账,缓存,中间计量-清单';
+
+ALTER TABLE `zh_rpt_archive`
+CHANGE COLUMN `content` `content` JSON NULL DEFAULT NULL ;
+
+CREATE TABLE `zh_s2b_z_proj` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `pid` int(11) NOT NULL COMMENT '项目id',
+  `xmName` varchar(255) COLLATE utf8_unicode_ci NOT NULL COMMENT '项目发改全称',
+  `xmCode` varchar(255) COLLATE utf8_unicode_ci NOT NULL COMMENT '项目发改赋码',
+  `opCode` varchar(255) COLLATE utf8_unicode_ci NOT NULL COMMENT '来源编码',
+  `base_data` text COLLATE utf8_unicode_ci COMMENT '用来存定制接口所需参数',
+  PRIMARY KEY (`id`),
+  KEY `pid` (`pid`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
+
+-- --------------------------------------------------------
+
+--
+-- 表的结构 `zh_s2b_z_tender`
+--
+
+CREATE TABLE `zh_s2b_z_tender` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `tid` int(11) NOT NULL COMMENT '标段id',
+  `base_data` text COLLATE utf8_unicode_ci COMMENT 'json,一些需要的数据',
+  PRIMARY KEY (`id`),
+  KEY `tid` (`tid`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='浙江项目标段表';
+
+ALTER TABLE `zh_project` ADD `customType` VARCHAR(255) NOT NULL DEFAULT '' COMMENT '定制项目通知类型参数' AFTER `secret`;
+
+ALTER TABLE `calculation`.`zh_tender_info`
+ADD COLUMN `fun_rela` varchar(255) CHARACTER SET utf8 NULL COMMENT '功能设置' AFTER `ledger_check`;
+
+ALTER TABLE `zh_project` ADD `data_collect` TINYINT(1) NOT NULL DEFAULT '0' COMMENT '决策大屏是否显示及对应大屏编号' AFTER `sjs_rela`;
+
+CREATE TABLE `zh_datacollect_audit` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `pid` int(11) NOT NULL COMMENT '项目id',
+  `groupid` int(11) DEFAULT NULL COMMENT '用户组id',
+  `uid` int(11) DEFAULT NULL COMMENT '用户id',
+  `create_time` datetime NOT NULL COMMENT '添加时间',
+   PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='决策大屏用户查看权限表';
+
+ALTER TABLE `zh_material_bills` ADD `origin` VARCHAR(255) NULL DEFAULT NULL COMMENT '来源地' AFTER `pre_tp`;
+ALTER TABLE `zh_material_bills_history` ADD `origin` VARCHAR(255) NULL DEFAULT NULL COMMENT '来源地' AFTER `pre_tp`;
+
+ALTER TABLE `zh_material_bills` ADD `order` INT NULL DEFAULT NULL COMMENT '排序' AFTER `mid`;
+-- 按id排序赋值
+UPDATE `zh_material_bills` SET `order`=`id`
+
+-- 收方单表
+CREATE TABLE `zh_stage_shoufang`  (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `tid` int(11) NOT NULL COMMENT '标段id',
+  `order` tinyint(4) NOT NULL COMMENT '期数',
+  `sid` int(11) NOT NULL COMMENT '期id',
+  `lid` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL COMMENT '台账uuid',
+  `pid` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL COMMENT '计量单元uuid',
+  `qrcode` VARCHAR(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL COMMENT '二维码存放地址',
+  `extra_upload` TINYINT(1) NOT NULL DEFAULT '0' COMMENT '是否为审核通过后再次生成的,0为否',
+  `create_time` datetime NOT NULL COMMENT '创建时间',
+  PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT = '收方单数据';
+
+-- 收方单附件表
+CREATE TABLE `zh_stage_shoufang_attachment`  (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `tid` int(11) NOT NULL COMMENT '标段id',
+  `sid` int(11) NOT NULL COMMENT '期id',
+  `sfid` int(11) NOT NULL COMMENT '收方单id',
+  `filename` varchar(1000) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL COMMENT '文件名称',
+  `fileext` varchar(10) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL COMMENT '文件后缀',
+  `filesize` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL COMMENT '文件大小',
+  `filepath` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL COMMENT '文件存储路径',
+  `extra_upload` TINYINT(1) NOT NULL DEFAULT '0' COMMENT '是否为审核通过后再次上传的,0为否',
+  `in_time` datetime NOT NULL COMMENT '创建时间',
+  PRIMARY KEY (`id`),
+  INDEX `sfid`(`sfid`),
+  INDEX `sid`(`sid`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT = '收方单附件表';
+
+ALTER TABLE `zh_stage_detail`
+ADD COLUMN `im_type`  tinyint(1) UNSIGNED NULL COMMENT '中间计量模式' AFTER `calc_img_remark`;
+
+ALTER TABLE `zh_project`
+ADD COLUMN `map_json` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL COMMENT '地图信息及默认坐标' AFTER `data_collect`;
+
+CREATE TABLE `zh_s2b_z_log`  (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `pid` int(11) NOT NULL COMMENT '项目id',
+  `tid` int(11) NOT NULL COMMENT '标段id',
+  `sid` int(11) NOT NULL COMMENT '期id',
+  `url` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL COMMENT '请求url',
+  `post_data` text CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL COMMENT '请求数据',
+  `result_data` text CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL COMMENT '推送结果',
+  `post_time` datetime NULL DEFAULT NULL COMMENT '请求时间',
+  PRIMARY KEY (`id`)
+) ENGINE = InnoDB AUTO_INCREMENT = 25 CHARACTER SET = utf8 COLLATE = utf8_unicode_ci COMMENT = '浙江项目日志表';

+ 43 - 0
test/app/temp_test.test.js

@@ -0,0 +1,43 @@
+'use strict';
+
+/**
+ *
+ *
+ * @author Mai
+ * @date
+ * @version
+ */
+
+const { app, assert } = require('egg-mock/bootstrap');
+const mockData = {};
+const tenderId = 3992;
+const stageOrder = 1;
+const postData = {
+    account: 'fuqingqing',
+    project: 'P0505',
+    project_password: '123456',
+};
+
+describe('temp_test.js', () => {
+    // 准备测试数据
+    before(function* () {
+        const ctx = app.mockContext();
+        // 模拟登录session
+        ctx.session = {};
+        const loginResult = yield ctx.service.projectAccount.accountLogin(postData, 2);
+        assert(loginResult);
+        mockData.session = ctx.session;
+    });
+    it('test temp', function* () {
+        const ctx = app.mockContext(mockData);
+        //  大数据材差 第一期
+        const stage = yield ctx.service.stage.getDataByCondition({ tid: tenderId, order: stageOrder });
+        console.time('oldQuery');
+        const oldResult = yield ctx.service.stageBills.getLastestStageData(tenderId, stage.id);
+        console.timeEnd('oldQuery');
+        console.time('newQuery');
+        const newResult = yield ctx.service.stageBills.getLastestStageData2(tenderId, stage.id);
+        console.timeEnd('newQuery');
+        assert(oldResult.length === newResult.length);
+    });
+});