Ver código fonte

Merge branch 'master' of http://192.168.1.41:3000/maixinrong/Calculation

TonyKang 5 anos atrás
pai
commit
3e794d1d6e

+ 3 - 0
README.zh-CN.md

@@ -10,6 +10,9 @@
 
 如需进一步了解,参见 [egg 文档][egg]。
 
+### 开发要求
+- 所有文件的写入,均应写入到ctx.app.config.filePath下。其中:临时存储文件(例如导入签约清单,上传文件接受后,将数据导入到数据库后,该文件不会再次使用),应写入cache文件夹,该文件夹会定期清理;其他必需文件,可自行规划存储
+
 ### 本地开发
 - 首先导入sql
 - 开启redis

+ 1 - 1
app/controller/profile_controller.js

@@ -65,7 +65,7 @@ module.exports = app => {
                 const baseRule = ctx.service.projectAccount.rule('profileBase');
                 ctx.helper.validate(baseRule);
 
-                const result = await ctx.service.projectAccount.save(ctx.request.body, sessionUser.accountId);
+                const result = await ctx.service.projectAccount.saveInfo(ctx.request.body, sessionUser.accountId);
                 if (!result) {
                     throw '保存信息失败';
                 }

+ 10 - 18
app/controller/report_controller.js

@@ -282,8 +282,6 @@ async function getReportData(ctx, params, filters) {
     const rst = {};
     const runnableRst = [];
     const runnableKey = []; // 这个配合runnableRst用,未来考虑并行查询优化
-    const mem_tz_runnableRst = []; // 内存表,中间计量台账清单数据,因有主从关系,但却又只算一次,所以上面的并行机制无法满足需求,所以得走另外的分支处理
-    const mem_tz_runnableKey = []; // 配合内存表用
     // console.log('params');
     // console.log(params);
     for (const filter of filters) {
@@ -317,18 +315,6 @@ async function getReportData(ctx, params, filters) {
                     runnableRst.push(ctx.service.stagePay.getAuditorStageData(params.stage_id, params.stage_times, params.stage_order));
                     runnableKey.push('stage_pay');
                     break;
-                case 'mem_stage_im_tz':
-                    if (mem_tz_runnableRst.length === 0) {
-                        mem_tz_runnableRst.push(ctx.service.reportMemory.getStageImTzNoReturn(params.tender_id, params.stage_id));
-                    }
-                    mem_tz_runnableKey.push('mem_stage_im_tz');
-                    break;
-                case 'mem_stage_im_tz_bills':
-                    if (mem_tz_runnableRst.length === 0) {
-                        mem_tz_runnableRst.push(ctx.service.reportMemory.getStageImTzNoReturn(params.tender_id, params.stage_id));
-                    }
-                    mem_tz_runnableKey.push('mem_stage_im_tz_bills');
-                    break;
                 case 'mem_stage_im_zl':
                     runnableRst.push(ctx.service.reportMemory.getStageImZlData(params.tender_id, params.stage_id));
                     runnableKey.push('mem_stage_im_zl');
@@ -348,10 +334,16 @@ async function getReportData(ctx, params, filters) {
         // if (rst[runnableKey[idx]] instanceof Array) console.log('is Array')
         // else console.log('is not Array');
     }
-    if (mem_tz_runnableRst.length > 0) {
-        await mem_tz_runnableRst[0];
-        for (const key of mem_tz_runnableKey) {
-            rst[key] = ctx.service.reportMemory.getStageImTzDataDirectlyByKey(key);
+    for (const filter of filters) {
+        switch (filter) {
+            case 'mem_stage_im_tz':
+                rst[filter] = await ctx.service.reportMemory.getStageImTzData(params.tender_id, params.stage_id);
+                break;
+            case 'mem_stage_im_tz_bills':
+                rst[filter] = await ctx.service.reportMemory.getStageImTzBillsData(params.tender_id, params.stage_id);
+                break;
+            default:
+                break;
         }
     }
     return rst;

+ 6 - 0
app/controller/stage_controller.js

@@ -351,6 +351,7 @@ module.exports = app => {
                         responseData.data = await ctx.service.stageBills.updateStageBillsCalcType(data.bills.calcType);                    }
                 }
                 await ctx.service.stage.updateCheckCalcFlag(ctx.stage.id, true);
+                await ctx.service.stage.updateCacheTime(ctx.stage.id);
                 ctx.body = responseData;
             } catch (err) {
                 this.log(err);
@@ -404,6 +405,7 @@ module.exports = app => {
                         ctx.stage.id, data.target.bills.id, '-1');
                 }
                 await ctx.service.stage.updateCheckCalcFlag(ctx.stage.id, true);
+                await ctx.service.stage.updateCacheTime(ctx.stage.id);
                 ctx.body = {err: 0, msg: '', data: result};
             } catch(err) {
                 this.log(err);
@@ -458,6 +460,7 @@ module.exports = app => {
 
                 const data = JSON.parse(ctx.request.body.data);
                 await ctx.service.stage.buildDetailData(ctx.tender.id, ctx.stage.order, data);
+                await ctx.service.stage.updateCacheTime(ctx.stage.id);
 
                 ctx.body = {err: 0, msg: '', data: null};
             } catch (err) {
@@ -522,6 +525,7 @@ module.exports = app => {
 
                 const data = JSON.parse(ctx.request.body.data);
                 await ctx.service.stage.update(data, { id: ctx.stage.id });
+                await ctx.service.stage.updateCacheTime(ctx.stage.id);
 
                 ctx.body = {err: 0, msg: '', data: this.ctx.stage};
             } catch (err) {
@@ -546,6 +550,7 @@ module.exports = app => {
                 } else {
                     responseData.data = await ctx.service.stageDetail.saveDetailData(data);
                 }
+                await ctx.service.stage.updateCacheTime(ctx.stage.id);
                 ctx.body = responseData;
             } catch (err) {
                 this.log(err);
@@ -603,6 +608,7 @@ module.exports = app => {
                 }
                 await this.ctx.service.stageDetail.saveDetailData(data);
                 const imData = await ctx.service.stageDetail.getLastestImStageData(this.ctx.tender.id, this.ctx.stage.id, data.lid, data.uuid);
+                await ctx.service.stage.updateCacheTime(ctx.stage.id);
                 const responseData = {err: 0, msg: '', data: imData};
                 ctx.body = responseData;
             } catch (err) {

+ 4 - 4
app/controller/tender_controller.js

@@ -73,9 +73,9 @@ module.exports = app => {
             }
         }
 
-        async _list(view, renderData, modal = '') {
+        async _list(view, renderData, modal = '', list_status = '') {
             try {
-                renderData.tenderList = await this.ctx.service.tender.getList('', renderData.userPermission);
+                renderData.tenderList = await this.ctx.service.tender.getList(list_status, renderData.userPermission);
 
                 for (const t of renderData.tenderList) {
                     if (t.ledger_status === auditConst.ledger.status.checked) {
@@ -145,9 +145,9 @@ module.exports = app => {
             if (userPermission !== null && userPermission.tender !== undefined && userPermission.tender.indexOf('1') !== -1) {
                 const renderData = {
                     accountInfo,
-                    userPermission
+                    userPermission,
                 };
-                await this._list('tender/manage.ejs', renderData, 'tender/manage_modal.ejs')
+                await this._list('tender/manage.ejs', renderData, 'tender/manage_modal.ejs', 'manage');
                 // 获取用户新建标段权利
                 // const accountInfo = await this.ctx.service.projectAccount.getDataById(this.ctx.session.sessionUser.accountId);
                 // const userPermission = accountInfo !== undefined && accountInfo.permission !== '' ? JSON.parse(accountInfo.permission) : null;

+ 0 - 1
app/extend/helper.js

@@ -504,7 +504,6 @@ module.exports = {
      * @returns {Promise<void>}
      */
     async saveBufferFile(buffer, fileName) {
-        console.log(fileName);
         // 检查文件夹是否存在,不存在则直接创建文件夹
         const pathName = path.dirname(fileName);
         if (!fs.existsSync(pathName)) {

+ 45 - 0
app/lib/stage_im.js

@@ -29,16 +29,28 @@ class StageIm {
             calcFields: ['deal_tp', 'total_price', 'contract_tp', 'qc_tp', 'gather_tp'],
             calc: function (node) {
                 if (node.children && node.children.length === 0) {
+                    node.pre_gather_qty = self.ctx.helper.add(node.pre_contract_qty, node.pre_qc_qty);
                     node.gather_qty = self.ctx.helper.add(node.contract_qty, node.qc_qty);
+                    node.end_contract_qty = self.ctx.helper.add(node.pre_contract_qty, node.contract_qty);
+                    node.end_qc_qty = self.ctx.helper.add(node.pre_qc_qty, node.qc_qty);
+                    node.end_gather_qty = self.ctx.helper.add(node.pre_gather_qty, node.gather_qty);
                 }
+                node.pre_gather_tp = self.ctx.helper.add(node.pre_contract_tp, node.pre_qc_tp);
                 node.gather_tp = self.ctx.helper.add(node.contract_tp, node.qc_tp);
+                node.end_contract_tp = self.ctx.helper.add(node.pre_contract_tp, node.contract_tp);
+                node.end_qc_tp = self.ctx.helper.add(node.pre_qc_tp, node.qc_tp);
+                node.end_gather_tp = self.ctx.helper.add(node.pre_gather_tp, node.gather_tp);
             }
         });
         this.pos = new Ledger.pos({
             id: 'id', ledgerId: 'lid',
             updateFields: ['contract_qty', 'qc_qty', 'postil'],
             calc: function (p) {
+                p.pre_gather_qty = self.ctx.helper.add(p.pre_contract_qty, p.pre_qc_qty);
                 p.gather_qty = self.ctx.helper.add(p.contract_qty, p.qc_qty);
+                p.end_contract_qty = self.ctx.helper.add(p.pre_contract_qty, p.contract_qty);
+                p.end_qc_qty = self.ctx.helper.add(p.pre_qc_qty, p.qc_qty);
+                p.end_gather_qty = self.ctx.helper.add(p.pre_gather_qty, p.gather_qty);
             }
         });
         // relaData
@@ -67,6 +79,11 @@ class StageIm {
                 {data: curStage, fields: ['contract_qty', 'contract_tp', 'qc_qty', 'qc_tp'], prefix: '', relaId: 'lid'}
             ]);
         }
+        const preStage = this.ctx.stage.order > 1 ? await this.ctx.service.stageBillsFinal.getFinalData(this.ctx.tender, this.ctx.stage.order - 1) : [];
+
+        this.ctx.helper.assignRelaData(billsData, [
+            {data: preStage, fields: ['contract_qty', 'contract_tp', 'qc_qty', 'qc_tp'], prefix: 'pre_', relaId: 'lid'}
+        ]);
         this.billsTree.loadDatas(billsData);
         this.billsTree.calculateAll();
 
@@ -83,6 +100,10 @@ class StageIm {
                 {data: curPosStage, fields: ['contract_qty', 'qc_qty'], prefix: '', relaId: 'pid'}
             ]);
         }
+        const prePosStage = this.ctx.stage.order > 1 ? await this.ctx.service.stagePosFinal.getFinalData(this.ctx.tender, this.ctx.stage.order - 1) : [];
+        this.ctx.helper.assignRelaData(posData, [
+            {data: prePosStage, fields: ['contract_qty', 'qc_qty'], prefix: 'pre_', relaId: 'pid'}
+        ]);
         this.pos.loadDatas(posData);
         this.pos.calculateAll();
     }
@@ -303,6 +324,14 @@ class StageIm {
             b.jl = this.ctx.helper.add(b.jl, p.gather_qty);
             b.contract_jl = this.ctx.helper.add(b.contract_jl, p.contract_qty);
             b.qc_jl = this.ctx.helper.add(b.qc_jl, p.qc_qty);
+
+            b.pre_jl = this.ctx.helper.add(b.pre_jl, p.pre_gather_qty);
+            b.pre_contract_jl = this.ctx.helper.add(b.pre_contract_jl, p.pre_contract_qty);
+            b.pre_qc_jl = this.ctx.helper.add(b.pre_qc_jl, p.pre_qc_qty);
+
+            b.end_jl = this.ctx.helper.add(b.end_jl, p.end_gather_qty);
+            b.end_contract_jl = this.ctx.helper.add(b.end_contract_jl, p.end_contract_qty);
+            b.end_qc_jl = this.ctx.helper.add(b.end_qc_jl, p.end_qc_qty);
             this._generateTzPosData(p, b);
         }
     }
@@ -350,6 +379,8 @@ class StageIm {
                 id: this.ImData.length + 1,
                 lid: node.id, pid: '', code: node.code,
                 jl: node.gather_tp, contract_jl: node.contract_tp, qc_jl: node.qc_tp,
+                pre_jl: node.pre_gather_tp, pre_contract_jl: node.pre_contract_tp, pre_qc_jl: node.pre_qc_tp,
+                end_jl: node.end_gather_tp, end_contract_jl: node.end_contract_tp, end_qc_jl: node.end_qc_tp,
                 peg: peg ? this._getPegStr(peg.name) : '', drawing_code: this._getDrawingCode(node),
             };
             if (this.ctx.stage.im_gather && node.check) {
@@ -454,6 +485,8 @@ class StageIm {
                     id: this.ImData.length + 1,
                     lid: node.id, pid: '', code: p.b_code, name: p.name, unit: p.unit, unit_price: p.unit_price,
                     jl: 0, contract_jl: 0, qc_jl: 0,
+                    pre_jl: 0, pre_contract_jl: 0, pre_qc_jl: 0,
+                    end_jl: 0, end_contract_jl: 0, end_qc_jl: 0,
                     peg: peg ? this._getPegStr(peg.name) : ''
                 };
                 if (this.ctx.stage.im_gather && node.check) {
@@ -478,6 +511,14 @@ class StageIm {
             im.jl = this.ctx.helper.add(im.jl, p.gather_qty);
             im.contract_jl = this.ctx.helper.add(im.contract_jl, p.contract_qty);
             im.qc_jl = this.ctx.helper.add(im.qc_jl, p.qc_qty);
+
+            im.pre_jl = this.ctx.helper.add(im.pre_jl, p.pre_gather_qty);
+            im.pre_contract_jl = this.ctx.helper.add(im.pre_contract_jl, p.pre_contract_qty);
+            im.pre_qc_jl = this.ctx.helper.add(im.pre_qc_jl, p.pre_qc_qty);
+
+            im.end_jl = this.ctx.helper.add(im.end_jl, p.end_gather_qty);
+            im.end_contract_jl = this.ctx.helper.add(im.end_contract_jl, p.end_contract_qty);
+            im.end_qc_jl = this.ctx.helper.add(im.end_qc_jl, p.end_qc_qty);
         }
     }
 
@@ -496,6 +537,8 @@ class StageIm {
                         id: this.ImData.length + 1,
                         lid: node.id, code: p.b_code, name: p.name, unit: p.unit, unit_price: p.unit_price, pid: pp.id,
                         jl: pp.gather_qty, contract_jl: pp.contract_qty, qc_jl: pp.qc_qty,
+                        pre_jl: pp.pre_gather_qty, pre_contract_jl: pp.pre_contract_qty, pre_qc_jl: pp.pre_qc_qty,
+                        end_jl: pp.end_gather_qty, end_contract_jl: pp.end_contract_qty, end_qc_jl: pp.end_qc_qty,
                         bw: bw,
                         peg: this._checkPeg(pp.name) ? this._getPegStr(pp.name) : (peg ? this._getPegStr(peg.name) : ''),
                         xm: pp.name,
@@ -520,6 +563,8 @@ class StageIm {
                     id: this.ImData.length + 1,
                     lid: node.id, code: p.b_code, name: p.name, unit: p.unit, unit_price: p.unit_price, pid: '',
                     jl: p.gather_qty, contract_jl: p.contract_qty, qc_jl: p.qc_qty,
+                    pre_jl: p.pre_gather_qty, pre_contract_jl: p.pre_contract_qty, pre_qc_jl: p.pre_qc_qty,
+                    end_jl: p.end_gather_qty, end_contract_jl: p.end_contract_qty, end_qc_jl: p.end_qc_qty,
                     bw: bw,
                     peg: peg ? this._getPegStr(peg.name) : '',
                     xm: node.name,

+ 1 - 0
app/public/js/category.js

@@ -113,6 +113,7 @@ function bindCategoryValueControl() {
         const value = _.find(editCate.value, function (v) {
             return v.id == vid;
         });
+        $(this).remove();
         if (value.delete) { return; }
 
         if (value.relaTenders.length > 0) {

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

@@ -15,8 +15,7 @@ function generateChapterHtml(data) {
             if (['1000', '1100', '1200', '1300'].indexOf(d.code) >= 0) {
                 if (checkZero(d.total_price) && checkZero(d.deal_bills_tp) &&
                     checkZero(d.contract_tp) && checkZero(d.qc_tp) && checkZero(d.gather_tp) &&
-                    checkZero(d.end_contract_tp) && checkZero(d.end_qc_tp) && checkZero(d.end_gather_tp) &&
-                    checkZero(d.end_final_tp)) {
+                    checkZero(d.end_contract_tp) && checkZero(d.end_qc_tp) && checkZero(d.end_gather_tp)){
                     continue;
                 }
             }
@@ -35,7 +34,7 @@ function generateChapterHtml(data) {
             html.push('<td class="text-right">', d.end_contract_tp ? d.end_contract_tp : '', '</td>');
             html.push('<td class="text-right">', d.end_qc_tp ? d.end_qc_tp : '', '</td>');
             html.push('<td class="text-right">', d.end_gather_tp ? d.end_gather_tp : '', '</td>');
-            html.push('<td class="text-right">', d.end_final_tp ? d.end_final_tp : '', '</td>');
+            html.push('<td class="text-right">', d.end_final_ratio ? d.end_final_ratio : '', '</td>');
             html.push('</tr>');
         }
     }
@@ -142,6 +141,7 @@ $(document).ready(function () {
         c.end_qc_tp = ZhCalc.add(c.qc_tp, c.pre_qc_tp);
         c.end_gather_tp = ZhCalc.add(c.end_contract_tp, c.end_qc_tp);
         c.end_final_tp = ZhCalc.add(c.end_qc_tp, c.total_price);
+        c.end_final_ratio = ZhCalc.mul(ZhCalc.div(c.end_gather_tp, c.end_final_tp), 100, 2);
     }
     generateChapterHtml(chapterData);
 

+ 19 - 5
app/public/js/tender_list.js

@@ -41,6 +41,7 @@ const levelTreeSetting = {
     callback: {
         beforeDrop: beforeDropNode,
         onDrop: onDropNode,
+
     }
 };
 const levelNodes =[];
@@ -54,12 +55,24 @@ function createTree() {
     $.fn.zTree.init($("#treeLevel"), levelTreeSetting, levelNodes);
 }
 function beforeDropNode(treeId, treeNodes, targetNode, moveType, isCopy) {
-    if (targetNode.lid !== 1) {
+    if (targetNode !== null && targetNode.lid !== 1) {
         const parent = targetNode.getParentNode();
         if (parent && parent.lid === 1) {
             return false;
         }
     }
+    for (var i=0,l=treeNodes.length; i<l; i++) {
+        if (treeNodes[i].drag === false) {
+            return false;
+        }
+        if (!targetNode && treeNodes[i].dropRoot === false) {
+            return false;
+        }
+        if(treeNodes[i].isParent === true && targetNode.lid !== 1){
+            return false;
+        }
+    }
+    return true;
 }
 function onDropNode(event, treeId, treeNodes, targetNode, moveType) {
     const zTree = $.fn.zTree.getZTreeObj(treeId);
@@ -78,9 +91,9 @@ function onDropNode(event, treeId, treeNodes, targetNode, moveType) {
     }
     resetFixNode(1);
     resetFixNode(2);
-    if (targetNode.lid === 1 && treeNodes[0].children && treeNodes[0].children.length !== 0) {
+    if (targetNode !== null && targetNode.lid === 1 && treeNodes[0].children && treeNodes[0].children.length !== 0) {
         moveChildren(treeNodes[0].children, zTree.getNodeByParam('lid', 1));
-    } else if (targetNode.lid !== 1) {
+    } else if (targetNode !== null && targetNode.lid !== 1) {
         if (targetNode.children.length >= 2) {
             for (const c of targetNode.children) {
                 if (c.lid !== treeNodes[0].lid) {
@@ -120,13 +133,14 @@ function sortCategory() {
 function initCategoryLevelNode() {
     levelNodes.splice(0, levelNodes.length);
     levelNodes.push(
-        { lid:1, lpId:0, name:"可用类别", open:true, isParent: true},
-        { lid:2, lpId:0, name:"已用类别", open:true, isParent: true}
+        { lid:1, lpId:0, name:"可用类别", open:true, isParent: true, drag: false},
+        { lid:2, lpId:0, name:"已用类别", open:true, isParent: true, drag: false}
     );
     for (const c of category) {
         const cate = JSON.parse(JSON.stringify(c));
         cate.lid = levelNodes.length + 1;
         cate.open = true;
+        cate.dropRoot = false;
         if (!cate.level) {
             cate.lpId = 1;
             levelNodes.push(cate);

+ 17 - 5
app/public/js/tender_list_info.js

@@ -54,12 +54,23 @@ function createTree() {
     $.fn.zTree.init($("#treeLevel"), levelTreeSetting, levelNodes);
 }
 function beforeDropNode(treeId, treeNodes, targetNode, moveType, isCopy) {
-    if (targetNode.lid !== 1) {
+    if (targetNode !== null && targetNode.lid !== 1) {
         const parent = targetNode.getParentNode();
         if (parent && parent.lid === 1) {
             return false;
         }
     }
+    for (var i=0,l=treeNodes.length; i<l; i++) {
+        if (treeNodes[i].drag === false) {
+            return false;
+        }
+        if (!targetNode && treeNodes[i].dropRoot === false) {
+            return false;
+        }
+        if(treeNodes[i].isParent === true && targetNode.lid !== 1){
+            return false;
+        }
+    }
 }
 function onDropNode(event, treeId, treeNodes, targetNode, moveType) {
     const zTree = $.fn.zTree.getZTreeObj(treeId);
@@ -78,9 +89,9 @@ function onDropNode(event, treeId, treeNodes, targetNode, moveType) {
     }
     resetFixNode(1);
     resetFixNode(2);
-    if (targetNode.lid === 1 && treeNodes[0].children && treeNodes[0].children.length !== 0) {
+    if (targetNode !== null && targetNode.lid === 1 && treeNodes[0].children && treeNodes[0].children.length !== 0) {
         moveChildren(treeNodes[0].children, zTree.getNodeByParam('lid', 1));
-    } else if (targetNode.lid !== 1) {
+    } else if (targetNode !== null && targetNode.lid !== 1) {
         if (targetNode.children.length >= 2) {
             for (const c of targetNode.children) {
                 if (c.lid !== treeNodes[0].lid) {
@@ -120,13 +131,14 @@ function sortCategory() {
 function initCategoryLevelNode() {
     levelNodes.splice(0, levelNodes.length);
     levelNodes.push(
-        { lid:1, lpId:0, name:"可用类别", open:true, isParent: true},
-        { lid:2, lpId:0, name:"已用类别", open:true, isParent: true}
+        { lid:1, lpId:0, name:"可用类别", open:true, isParent: true, drag: false},
+        { lid:2, lpId:0, name:"已用类别", open:true, isParent: true, drag: false}
     );
     for (const c of category) {
         const cate = JSON.parse(JSON.stringify(c));
         cate.lid = levelNodes.length + 1;
         cate.open = true;
+        cate.dropRoot = false;
         if (!cate.level) {
             cate.lpId = 1;
             levelNodes.push(cate);

+ 18 - 5
app/public/js/tender_list_manage.js

@@ -54,12 +54,24 @@ function createTree() {
     $.fn.zTree.init($("#treeLevel"), levelTreeSetting, levelNodes);
 }
 function beforeDropNode(treeId, treeNodes, targetNode, moveType, isCopy) {
-    if (targetNode.lid !== 1) {
+    if (targetNode !== null && targetNode.lid !== 1) {
         const parent = targetNode.getParentNode();
         if (parent && parent.lid === 1) {
             return false;
         }
     }
+    for (var i=0,l=treeNodes.length; i<l; i++) {
+        if (treeNodes[i].drag === false) {
+            return false;
+        }
+        if (!targetNode && treeNodes[i].dropRoot === false) {
+            return false;
+        }
+        if(treeNodes[i].isParent === true && targetNode.lid !== 1){
+            return false;
+        }
+    }
+    return true;
 }
 function onDropNode(event, treeId, treeNodes, targetNode, moveType) {
     const zTree = $.fn.zTree.getZTreeObj(treeId);
@@ -78,9 +90,9 @@ function onDropNode(event, treeId, treeNodes, targetNode, moveType) {
     }
     resetFixNode(1);
     resetFixNode(2);
-    if (targetNode.lid === 1 && treeNodes[0].children && treeNodes[0].children.length !== 0) {
+    if (targetNode !== null && targetNode.lid === 1 && treeNodes[0].children && treeNodes[0].children.length !== 0) {
         moveChildren(treeNodes[0].children, zTree.getNodeByParam('lid', 1));
-    } else if (targetNode.lid !== 1) {
+    } else if (targetNode !== null && targetNode.lid !== 1) {
         if (targetNode.children.length >= 2) {
             for (const c of targetNode.children) {
                 if (c.lid !== treeNodes[0].lid) {
@@ -120,13 +132,14 @@ function sortCategory() {
 function initCategoryLevelNode() {
     levelNodes.splice(0, levelNodes.length);
     levelNodes.push(
-        { lid:1, lpId:0, name:"可用类别", open:true, isParent: true},
-        { lid:2, lpId:0, name:"已用类别", open:true, isParent: true}
+        { lid:1, lpId:0, name:"可用类别", open:true, isParent: true, drag: false},
+        { lid:2, lpId:0, name:"已用类别", open:true, isParent: true, drag: false}
     );
     for (const c of category) {
         const cate = JSON.parse(JSON.stringify(c));
         cate.lid = levelNodes.length + 1;
         cate.open = true;
+        cate.dropRoot = false;
         if (!cate.level) {
             cate.lpId = 1;
             levelNodes.push(cate);

+ 18 - 5
app/public/js/tender_list_progress.js

@@ -54,12 +54,24 @@ function createTree() {
     $.fn.zTree.init($("#treeLevel"), levelTreeSetting, levelNodes);
 }
 function beforeDropNode(treeId, treeNodes, targetNode, moveType, isCopy) {
-    if (targetNode.lid !== 1) {
+    if (targetNode !== null && targetNode.lid !== 1) {
         const parent = targetNode.getParentNode();
         if (parent && parent.lid === 1) {
             return false;
         }
     }
+    for (var i=0,l=treeNodes.length; i<l; i++) {
+        if (treeNodes[i].drag === false) {
+            return false;
+        }
+        if (!targetNode && treeNodes[i].dropRoot === false) {
+            return false;
+        }
+        if(treeNodes[i].isParent === true && targetNode.lid !== 1){
+            return false;
+        }
+    }
+    return true;
 }
 function onDropNode(event, treeId, treeNodes, targetNode, moveType) {
     const zTree = $.fn.zTree.getZTreeObj(treeId);
@@ -78,9 +90,9 @@ function onDropNode(event, treeId, treeNodes, targetNode, moveType) {
     }
     resetFixNode(1);
     resetFixNode(2);
-    if (targetNode.lid === 1 && treeNodes[0].children && treeNodes[0].children.length !== 0) {
+    if (targetNode !== null && targetNode.lid === 1 && treeNodes[0].children && treeNodes[0].children.length !== 0) {
         moveChildren(treeNodes[0].children, zTree.getNodeByParam('lid', 1));
-    } else if (targetNode.lid !== 1) {
+    } else if (targetNode !== null && targetNode.lid !== 1) {
         if (targetNode.children.length >= 2) {
             for (const c of targetNode.children) {
                 if (c.lid !== treeNodes[0].lid) {
@@ -120,13 +132,14 @@ function sortCategory() {
 function initCategoryLevelNode() {
     levelNodes.splice(0, levelNodes.length);
     levelNodes.push(
-        { lid:1, lpId:0, name:"可用类别", open:true, isParent: true},
-        { lid:2, lpId:0, name:"已用类别", open:true, isParent: true}
+        { lid:1, lpId:0, name:"可用类别", open:true, isParent: true, drag: false},
+        { lid:2, lpId:0, name:"已用类别", open:true, isParent: true, drag: false}
     );
     for (const c of category) {
         const cate = JSON.parse(JSON.stringify(c));
         cate.lid = levelNodes.length + 1;
         cate.open = true;
+        cate.dropRoot = false;
         if (!cate.level) {
             cate.lpId = 1;
             levelNodes.push(cate);

+ 16 - 0
app/service/project_account.js

@@ -352,6 +352,22 @@ module.exports = app => {
         }
 
         /**
+         * 修改账号资料
+         *
+         * @param {Object} data - post过来的数据
+         * @return {Boolean} - 返回修改结果
+         */
+        async saveInfo(data, id) {
+            if (data._csrf !== undefined) {
+                delete data._csrf;
+            }
+            data.id = parseInt(id);
+            const operate = await this.db.update(this.tableName, data);
+            return operate.affectedRows > 0;
+        }
+
+
+        /**
          * 修改密码
          *
          * @param {Number} accountId - 账号id

+ 125 - 49
app/service/report_memory.js

@@ -8,13 +8,18 @@
  * @version
  */
 
-const Service = require('egg').Service;
 const StageIm = require('../lib/stage_im');
 const imType = require('../const/tender').imType;
 const audit = require('../const/audit');
+// const path = require('path');
+// const fs = require('fs');
+
+const stageImTz = 'mem_stage_im_tz';
+const stageImTzBills = 'mem_stage_im_tz_bills';
+const stageImZl = 'mem_stage_im_zl';
 
 module.exports = app => {
-    class ReportMemory extends Service {
+    class ReportMemory extends app.BaseService {
 
         /**
          * 构造函数
@@ -24,69 +29,117 @@ module.exports = app => {
          */
         constructor(ctx) {
             super(ctx);
-            this.db = this.app.mysql;
-            this.cache = this.app.redis;
-            this._ = this.app._;
+            this.tableName = 'report_memory';
             // 需要缓存的数据
             this.stageImData = null;
         }
 
         async _checkTender(tid) {
+            if (this.ctx.tender) return;
             const tender = await this.ctx.service.tender.getTender(tid);
             tender.info = await this.ctx.service.tenderInfo.getTenderInfo(tid);
             this.ctx.tender = tender;
         }
 
         async _checkStage(sid) {
-            const status = audit.stage.status;
-            const stage = await this.ctx.service.stage.getDataById(sid);
-            stage.auditors = await this.ctx.service.stageAudit.getAuditors(stage.id, stage.times);
-            stage.curAuditor = await this.ctx.service.stageAudit.getCurAuditor(stage.id, stage.times);
-
-            const accountId = this.ctx.session.sessionUser.accountId, auditorIds = this._.map(stage.auditors, 'aid'), shareIds = [];
-            if (accountId === stage.user_id) { // 原报
-                if (stage.curAuditor) {
-                    stage.readOnly = stage.curAuditor.aid !== accountId;
-                } else {
-                    stage.readOnly = stage.status !== status.uncheck && stage.status !== status.checkNo;
-                }
-                stage.curTimes = stage.times;
-                if (stage.status === status.uncheck || stage.status === status.checkNo) {
-                    stage.curOrder = 0;
-                } else if (stage.status === status.checked) {
-                    stage.curOrder = this._.max(this._.map(stage.auditors, 'order'));
-                } else {
-                    stage.curOrder = stage.curAuditor.aid === accountId ? stage.curAuditor.order : stage.curAuditor.order - 1;
-                }
-            } else if (auditorIds.indexOf(accountId) !== -1) { // 审批人
-                if (stage.status === status.uncheck) {
-                    throw '您无权查看该数据';
-                }
-                stage.curTimes = stage.status === status.checkNo ? stage.times - 1 : stage.times;
-                if (stage.status === status.checked) {
-                    stage.curOrder = this._.max(this._.map(stage.auditors, 'order'));
-                } else if (stage.status === status.checkNo) {
-                    const audit = await this.service.stageAudit.getDataByCondition({
-                        sid: stage.id, times: stage.times - 1, status: status.checkNo
-                    });
-                    stage.curOrder = audit.order;
-                } else {
-                    stage.curOrder = accountId === stage.curAuditor.aid ? stage.curAuditor.order : stage.curAuditor.order - 1;
-                }
-            } else if (shareIds.indexOf(accountId) !== -1) { // 分享人
-                if (stage.status === status.uncheck) {
-                    throw '您无权查看该数据';
+            if (!this.ctx.stage) {
+                const status = audit.stage.status;
+                const stage = await this.ctx.service.stage.getDataById(sid);
+                stage.auditors = await this.ctx.service.stageAudit.getAuditors(stage.id, stage.times);
+                stage.curAuditor = await this.ctx.service.stageAudit.getCurAuditor(stage.id, stage.times);
+
+                const accountId = this.ctx.session.sessionUser.accountId, auditorIds = this._.map(stage.auditors, 'aid'), shareIds = [];
+                if (accountId === stage.user_id) { // 原报
+                    if (stage.curAuditor) {
+                        stage.readOnly = stage.curAuditor.aid !== accountId;
+                    } else {
+                        stage.readOnly = stage.status !== status.uncheck && stage.status !== status.checkNo;
+                    }
+                    stage.curTimes = stage.times;
+                    if (stage.status === status.uncheck || stage.status === status.checkNo) {
+                        stage.curOrder = 0;
+                    } else if (stage.status === status.checked) {
+                        stage.curOrder = this._.max(this._.map(stage.auditors, 'order'));
+                    } else {
+                        stage.curOrder = stage.curAuditor.aid === accountId ? stage.curAuditor.order : stage.curAuditor.order - 1;
+                    }
+                } else if (auditorIds.indexOf(accountId) !== -1) { // 审批人
+                    if (stage.status === status.uncheck) {
+                        throw '您无权查看该数据';
+                    }
+                    stage.curTimes = stage.status === status.checkNo ? stage.times - 1 : stage.times;
+                    if (stage.status === status.checked) {
+                        stage.curOrder = this._.max(this._.map(stage.auditors, 'order'));
+                    } else if (stage.status === status.checkNo) {
+                        const audit = await this.service.stageAudit.getDataByCondition({
+                            sid: stage.id, times: stage.times - 1, status: status.checkNo
+                        });
+                        stage.curOrder = audit.order;
+                    } else {
+                        stage.curOrder = accountId === stage.curAuditor.aid ? stage.curAuditor.order : stage.curAuditor.order - 1;
+                    }
+                } else if (shareIds.indexOf(accountId) !== -1) { // 分享人
+                    if (stage.status === status.uncheck) {
+                        throw '您无权查看该数据';
+                    }
+                    stage.curTimes = stage.status === status.checkNo ? stage.times - 1 : stage.times;
+                    stage.curOrder = stage.status === status.checked ? this._.max(this._.map(stage.auditors, 'order')) : stage.curAuditor.order - 1;
                 }
-                stage.curTimes = stage.status === status.checkNo ? stage.times - 1 : stage.times;
-                stage.curOrder = stage.status === status.checked ? this._.max(this._.map(stage.auditors, 'order')) : stage.curAuditor.order - 1;
+
+                this.ctx.stage = stage;
+                this.ctx.stage.cacheTime = this.ctx.stage.readOnly ? (this.ctx.stage.cache_time_r).getTime(): (this.ctx.stage.cache_time_l).getTime();
+            }
+        }
+
+        // build-time: 162-384ms, redis-cache: 0-41ms, mysql + IO: 116-146ms
+        // 一定程度上算是大Value缓存,数据多了以后:
+        // 1. 达到redis内存阈值时,数据会swap到磁盘,此时将消耗IO时间
+        // 2. redis单独服务器
+        // 3. redis集群
+        async _getReportMemoryCache(name, tid, sid, time) {
+            // redis
+            const cacheKey = name + '-t' + tid + (sid ? '-s' + sid : '') + (time ? '-' + time : '');
+            const data = await this.cache.get(cacheKey);
+            if (data) {
+                return eval(data);
+            } else {
+                return null;
             }
 
-            this.ctx.stage = stage;
+            // mysql + IO
+            // const rm = await this.getDataByCondition({
+            //     tid: tid, sid: sid, name: name, time: time
+            // });
+            // if (rm && rm.file) {
+            //     const file = path.join(this.ctx.app.config.filePath, 'report', 'cache', rm.file);
+            //     if (fs.existsSync(file)) {
+            //         const data = await fs.readFileSync(file, 'utf8');
+            //         return eval(data);
+            //     } else {
+            //         return null;
+            //     }
+            // }
+        }
+
+        async _setReportMemoryCache(name, tid, sid, time, data) {
+            // redis
+            const cacheKey = name + '-t' + tid + (sid ? '-s' + sid : '') + (time ? '-' + time : '');
+            this.cache.set(cacheKey, JSON.stringify(data), 'EX', this.ctx.app.config.cacheTime);
+
+            // mysql + IO
+            // const file = path.join('report', 'cache', 'rm' + (new Date()).getTime() + '.json');
+            // await this.ctx.helper.saveBufferFile(JSON.stringify(data), path.join(this.ctx.app.config.filePath, file));
+            // const rm = await this.getDataByCondition({
+            //     tid: tid, sid: sid, name: name, time: time
+            // });
+            // if (rm) {
+            //     await this.db.update(this.tableName, {id: rm.id, file: file});
+            // } else {
+            //     await this.db.insert(this.tableName, {tid: tid, sid: sid, name: name, time: time, file: file});
+            // }
         }
 
         async _generateStageIm(tid, sid, isTz = true) {
-            await this._checkTender(tid);
-            await this._checkStage(sid);
             if (isTz && this.ctx.stage.im_type !== imType.tz.value) {
                 throw '您查看的报表跟设置不符,请查看“总量控制”的报表';
             } else if (!isTz && this.ctx.stage.im_type === imType.tz.value) {
@@ -97,6 +150,10 @@ module.exports = app => {
             this.stageImData.main = stageIm.ImData;
             if (isTz) {
                 this.stageImData.bills = stageIm.ImBillsData;
+                await this._setReportMemoryCache(stageImTz, tid, sid, this.ctx.stage.cacheTime, this.stageImData.main);
+                await this._setReportMemoryCache(stageImTzBills, tid, sid, this.ctx.stage.cacheTime, this.stageImData.bills);
+            } else {
+                await this._setReportMemoryCache(stageImZl, tid, sid, this.ctx.stage.cacheTime, this.stageImData.main);
             }
         }
 
@@ -125,6 +182,15 @@ module.exports = app => {
         }
 
         async getStageImTzData(tid, sid) {
+            await this._checkTender(tid);
+            await this._checkStage(sid);
+            const cache = await this._getReportMemoryCache('mem_stage_im_tz', tid, sid, this.ctx.stage.cacheTime);
+            if (cache) {
+                //console.log('cache');
+                return cache;
+            }
+
+            //console.log('build');
             if (!this.stageImData) {
                 this.stageImData = {};
                 try {
@@ -141,6 +207,11 @@ module.exports = app => {
         }
 
         async getStageImTzBillsData(tid, sid) {
+            await this._checkTender(tid);
+            await this._checkStage(sid);
+            const cache = await this._getReportMemoryCache('mem_stage_im_tz_bills', tid, sid, this.ctx.stage.cacheTime);
+            if (cache) return cache;
+
             if (!this.stageImData) {
                 this.stageImData = {};
                 try {
@@ -150,13 +221,18 @@ module.exports = app => {
                         this.ctx.logger.error(err);
                     }
                     this.stageImData.main = err.statck ? '数据错误' : err;
-                    this.stageImData.bills = this.stageImTz.main;
+                    this.stageImData.bills = this.stageImData.main;
                 }
             }
             return this.stageImData.bills;
         }
 
         async getStageImZlData(tid, sid) {
+            await this._checkTender(tid);
+            await this._checkStage(sid);
+            const cache = await this._getReportMemoryCache('mem_stage_im_zl', tid, sid, this.ctx.stage.cacheTime);
+            if (cache) return cache;
+
             this.stageImData = {};
             try {
                 await this._generateStageIm(tid, sid, false);

+ 5 - 0
app/service/stage.js

@@ -297,6 +297,11 @@ module.exports = app => {
             return result.affectedRows === 1;
         }
 
+        async updateCacheTime(sid) {
+            const result = await this.db.update(this.tableName, {id: sid, cache_time_l: new Date()});
+            return result.affectedRows === 1;
+        }
+
         /**
          * 删除计量期
          *

+ 7 - 0
app/service/stage_audit.js

@@ -202,6 +202,7 @@ module.exports = app => {
                     contract_tp: tpData.contract_tp,
                     qc_tp: tpData.qc_tp,
                     yf_tp: yfPay.tp,
+                    cache_time_r: this.ctx.stage.cache_time_l,
                 });
 
                 // 添加短信通知-需要审批提醒功能
@@ -239,6 +240,7 @@ module.exports = app => {
 
             const transaction = await this.db.beginTransaction();
             try {
+                console.log(checkData.opinion);
                 await transaction.update(this.tableName, {id: audit.id, status: checkData.checkType, opinion: checkData.opinion, end_time: time});
                 // 计算并合同支付最终数据
                 const yfPay = await this.ctx.service.stagePay.calcAllStagePays(this.ctx.stage, transaction);
@@ -254,6 +256,7 @@ module.exports = app => {
                         contract_tp: tpData.contract_tp,
                         qc_tp: tpData.qc_tp,
                         yf_tp: yfPay.tp,
+                        cache_time_r: this.ctx.stage.cache_time_l,
                     });
 
                     // 添加短信通知-需要审批提醒功能
@@ -282,6 +285,7 @@ module.exports = app => {
                         contract_tp: tpData.contract_tp,
                         qc_tp: tpData.qc_tp,
                         yf_tp: yfPay.tp,
+                        cache_time_r: this.ctx.stage.cache_time_l,
                     });
 
                     // 添加短信通知-审批通过提醒功能
@@ -347,6 +351,7 @@ module.exports = app => {
                     contract_tp: tpData.contract_tp,
                     qc_tp: tpData.qc_tp,
                     times: times + 1,
+                    cache_time_r: this.ctx.stage.cache_time_l,
                 });
                 // 拷贝新一次审核流程列表
                 await transaction.insert(this.tableName, auditors);
@@ -435,6 +440,7 @@ module.exports = app => {
                 // 同步 期信息
                 await transaction.update(this.ctx.service.stage.tableName, {
                     id: stageId, status: checkData.checkType,
+                    cache_time_r: this.ctx.stage.cache_time_l,
                 });
 
                 // 添加短信通知-需要审批提醒功能
@@ -644,6 +650,7 @@ module.exports = app => {
                 // 同步 期信息
                 await transaction.update(this.ctx.service.stage.tableName, {
                     id: stageId, status: auditConst.status.checking,
+                    cache_time_r: this.ctx.stage.cache_time_l,
                 });
 
                 // 添加短信通知-需要审批提醒功能

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

@@ -203,7 +203,7 @@
                         <div style="height:400px;overflow-y:hidden">
                             <table class="table table-striped table-bordered table-hover table-sm fixed_headers2">
                                 <thead>
-                                <tr><th>项目节编号</th><th>名称</th><th>部位明细</th><th>部位数量</th><th>选择</th></tr>
+                                <tr><th>项目节编号</th><th>名称</th><th>计量单元</th><th>数量</th><th>选择</th></tr>
                                 </thead>
                                 <tbody id="code-list" data-index="">
                                 </tbody>

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

@@ -662,7 +662,6 @@
         };
         const tenderId = window.location.pathname.split('/')[2];
         postData('/tender/' + tenderId + '/save', prop, function (data) {
-            setReadOnly('#v-pills-1', true);
             property.deal_info = data.deal_info;
             property.construction_unit = data.construction_unit;
             property.tech_param = data.tech_param;

+ 1 - 1
app/view/tender/manage.ejs

@@ -12,4 +12,4 @@
     const categoryType = JSON.parse('<%- JSON.stringify(settingConst.cType) %>');
     const category = JSON.parse('<%- JSON.stringify(categoryData) %>');
     const auditConst = JSON.parse('<%- JSON.stringify(auditConst) %>');
-</script>
+</script>

+ 1 - 1
config/config.default.js

@@ -71,7 +71,7 @@ module.exports = appInfo => {
     };
 
     // 缓存时间
-    config.cacheTime = 1800;
+    config.cacheTime = 3600 * 24 * 31; // 31天 计量一期的时间,估计为1月
 
     // 安全性配置
     config.security = {

+ 7 - 0
test/app/service/report_memory.test.js

@@ -21,6 +21,11 @@ describe('test/app/service/report_memory.test.js', () => {
             project: 'T201711273363',
             project_password: 'mai654321',
         };
+        // const postData = {
+        //     account: 'chente',
+        //     project: 'T201711273363',
+        //     project_password: '123456',
+        // };
         ctx.session = {};
         const loginResult = yield ctx.service.projectAccount.accountLogin(postData, 2);
         assert(loginResult);
@@ -35,9 +40,11 @@ describe('test/app/service/report_memory.test.js', () => {
         const mainData = yield ctx.service.reportMemory.getStageImTzData(12, stage.id);
         const billsData = yield ctx.service.reportMemory.getStageImTzBillsData(12, stage.id);
         if (mainData instanceof Array) {
+            console.log('mem_stage_im_tz.length:' + mainData.length);
             yield ctx.helper.saveBufferFile(JSON.stringify(mainData,"","\t"), ctx.app.baseDir + '/mem_stage_im_tz.json');
         }
         if (billsData instanceof Array) {
+            console.log('mem_stage_im_tz_bills.length:' + billsData.length);
             yield ctx.helper.saveBufferFile(JSON.stringify(billsData,"","\t"), ctx.app.baseDir + '/mem_stage_im_tz_bills.json');
         }
     });