Prechádzať zdrojové kódy

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

TonyKang 4 rokov pred
rodič
commit
a877cb22f6
42 zmenil súbory, kde vykonal 1676 pridanie a 526 odobranie
  1. 2 2
      Dockerfile
  2. 1 1
      app/base/base_bills_service.js
  3. 6 0
      app/const/schedule.js
  4. 151 12
      app/controller/schedule_controller.js
  5. 17 0
      app/controller/tender_controller.js
  6. 43 0
      app/middleware/schedule_check.js
  7. 12 0
      app/middleware/tender_check.js
  8. 1 0
      app/public/js/change_information_set.js
  9. 7 0
      app/public/js/global.js
  10. 49 23
      app/public/js/ledger.js
  11. 53 20
      app/public/js/ledger_audit.js
  12. 2 0
      app/public/js/material.js
  13. 2 0
      app/public/js/material_exponent.js
  14. 54 21
      app/public/js/path_tree.js
  15. 41 19
      app/public/js/revise.js
  16. 26 19
      app/public/js/revise_history.js
  17. 46 26
      app/public/js/schedule_ledger.js
  18. 133 77
      app/public/js/schedule_plan.js
  19. 242 107
      app/public/js/schedule_stage_gcl.js
  20. 34 10
      app/public/js/schedule_stage_tp.js
  21. 18 1
      app/public/js/spreadjs_rela/spreadjs_zh.js
  22. 157 95
      app/public/js/stage.js
  23. 52 22
      app/public/js/stage_compare.js
  24. 79 21
      app/public/js/stage_im.js
  25. 1 1
      app/public/js/tender.js
  26. 13 13
      app/public/js/xlsx-populate/xlsx-populate.js
  27. 10 3
      app/public/report/js/rpt_custom.js
  28. 10 6
      app/router.js
  29. 54 0
      app/service/schedule_audit.js
  30. 4 3
      app/service/schedule_ledger.js
  31. 91 1
      app/service/schedule_ledger_month.js
  32. 14 2
      app/service/schedule_stage.js
  33. 0 1
      app/service/stage_bills.js
  34. 8 8
      app/view/revise/info.ejs
  35. 1 1
      app/view/schedule/ledger.ejs
  36. 1 1
      app/view/schedule/plan.ejs
  37. 8 8
      app/view/schedule/stage_gcl.ejs
  38. 2 2
      app/view/schedule/stage_tp.ejs
  39. 3 0
      app/view/tender/detail.ejs
  40. 224 0
      app/view/tender/detail_modal.ejs
  41. 2 0
      app/view/tender/tender_sub_menu.ejs
  42. 2 0
      app/view/tender/tender_sub_mini_menu.ejs

+ 2 - 2
Dockerfile

@@ -1,8 +1,8 @@
 FROM node:12
 
-WORKDIR /home
+COPY . /app/calc
 
-COPY . .
+WORKDIR /app/calc
 
 RUN npm install --registery=https://registery.npm.taobao.org
 

+ 1 - 1
app/base/base_bills_service.js

@@ -594,7 +594,7 @@ class BaseBillsSerivce extends TreeService {
                     unit_price: this.ctx.helper.round(d.unit_price, this.ctx.tender.info.decimal.up),
                     source: d.source,
                     remark: d.remark,
-                    drawing_code: d.drawaing_code,
+                    drawing_code: d.drawing_code,
                     memo: d.memo,
                     node_type: d.node_type,
                     sgfh_expr: d.sgfh_expr,

+ 6 - 0
app/const/schedule.js

@@ -4,7 +4,13 @@ const plan_mode = {
     tp: 1,
     gcl: 2,
 };
+const permission = {
+    no: 0,
+    show: 1,
+    edit: 2,
+}
 
 module.exports = {
     plan_mode,
+    permission,
 };

+ 151 - 12
app/controller/schedule_controller.js

@@ -37,6 +37,9 @@ module.exports = app => {
             if (await this._getLastReviseStatus(ctx)) {
                 throw '台账修订中,请勿修改提交进度数据';
             }
+            if (ctx.tender.schedule_permission !== scheduleConst.permission.edit) {
+                throw '权限不足,无法修改进度数据';
+            }
         }
 
         async index(ctx) {
@@ -67,6 +70,7 @@ module.exports = app => {
                     planMonth: await this._getLastPlanMonth(ctx),
                     scheduleLedgerList: await this._getSelectedLedgerList(ctx),
                     preUrl: '/tender/' + ctx.tender.id,
+                    scPermission: scheduleConst.permission,
                     revising: await this._getLastReviseStatus(ctx),
                 };
                 await this.layout('schedule/index.ejs', renderData, 'schedule/modal.ejs');
@@ -102,6 +106,7 @@ module.exports = app => {
                 scheduleLedgerList,
                 hadDataLidList,
                 scheduleStage,
+                scPermission: scheduleConst.permission,
                 jsFiles: this.app.jsFiles.common.concat(this.app.jsFiles.schedule.ledger),
                 revising: await this._getLastReviseStatus(ctx),
             };
@@ -120,6 +125,7 @@ module.exports = app => {
                 planMonth: await this._getLastPlanMonth(ctx),
                 measureType,
                 mode: scheduleConst.plan_mode,
+                scPermission: scheduleConst.permission,
                 scheduleLedgerList: await this._getSelectedLedgerList(ctx),
                 jsFiles: this.app.jsFiles.common.concat(this.app.jsFiles.schedule.plan),
                 revising: await this._getLastReviseStatus(ctx),
@@ -146,6 +152,7 @@ module.exports = app => {
                 endSlmList,
                 yearSlmList,
                 curYearStageData,
+                scPermission: scheduleConst.permission,
                 scheduleLedgerList: await this._getSelectedLedgerList(ctx),
                 revising: await this._getLastReviseStatus(ctx),
                 jsFiles: this.app.jsFiles.common.concat(this.app.jsFiles.schedule.stageTp),
@@ -157,16 +164,19 @@ module.exports = app => {
             const tender = ctx.tender;
             const schedule = await ctx.service.schedule.getDataByCondition({ tid: tender.id });
             const scheduleMonth = await ctx.service.scheduleMonth.getAllDataByCondition({ where: { tid: tender.id }, orders: [['yearmonth', 'asc']] });
-            const scheduleStage = await ctx.service.scheduleStage.getAllDataByCondition({ where: { tid: tender.id }, orders: [['order', 'desc']] });
-            const curScheduleStage = scheduleStage.length > 0 ? _.maxBy(scheduleStage, 'order') : null;
+            // const scheduleStage = await ctx.service.scheduleStage.getAllDataByCondition({ where: { tid: tender.id }, orders: [['order', 'desc']] });
+            const gclScheduleMonth = _.orderBy(_.filter(scheduleMonth, { stage_gcl_used: 1 }), ['yearmonth'], ['desc']);
+            const curScheduleMonth = scheduleMonth.length > 0 ? _.maxBy(gclScheduleMonth, 'yearmonth') : null;
             const renderData = {
                 tender: tender.data,
                 tenderInfo: tender.info,
                 schedule,
                 scheduleMonth,
                 measureType,
-                scheduleStage,
-                curScheduleStage,
+                // scheduleStage,
+                curScheduleMonth,
+                gclScheduleMonth,
+                scPermission: scheduleConst.permission,
                 scheduleLedgerList: await this._getSelectedLedgerList(ctx),
                 revising: await this._getLastReviseStatus(ctx),
                 jsFiles: this.app.jsFiles.common.concat(this.app.jsFiles.schedule.stageGcl),
@@ -195,6 +205,25 @@ module.exports = app => {
                 ctx.body = { err: 1, msg: err.toString(), data: [] };
             }
         }
+        /**
+         * 获取工程量模式下台账数据(Ajax)
+         *
+         * @param ctx
+         * @return {Promise<void>}
+         */
+        async loadGclLedgerData(ctx) {
+            try {
+                const ledgerData = await ctx.service.ledger.getData(ctx.tender.id);
+                const postData = { ledgerData };
+                const { slmList, nextSlmList, endSlmList, yearSlmList } = await this._getGclAndPlanData(ctx);
+                _.assignIn(postData, { slmList, nextSlmList, endSlmList, yearSlmList });
+                ctx.body = { err: 0, msg: '', data: postData };
+            } catch (err) {
+                this.log(err);
+                ctx.body = { err: 1, msg: err.toString(), data: [] };
+            }
+        }
+
 
         /**
          * 获取所有期下台账数据(Ajax)
@@ -203,7 +232,7 @@ module.exports = app => {
          * @return {Promise<void>}
          */
         async _getAllStageLedgerData(ctx) {
-            const ledgerData = await ctx.service.ledger.getData(ctx.tender.id);
+            let ledgerData = await ctx.service.ledger.getData(ctx.tender.id);
             const stageList = await ctx.service.scheduleStage.getAllDataByCondition({ where: { tid: ctx.tender.id }, orders: [['order', 'desc']] });
             if (stageList.length > 0) {
                 const dgnData = await ctx.service.stageBillsDgn.getDgnData(ctx.tender.id);
@@ -230,12 +259,13 @@ module.exports = app => {
                         // { data: preStageData, fields: ['contract_qty', 'contract_tp', 'qc_qty', 'qc_tp', 'used'], prefix: s.order + '_pre_', relaId: 'lid' },
                     ]);
                 }
+                ledgerData = await this._addFinalStageData(ctx, ledgerData);
             }
             return ledgerData;
         }
 
         async _getStageLedgerData(ctx, stageOrder) {
-            const ledgerData = await ctx.service.ledger.getData(ctx.tender.id);
+            let ledgerData = await ctx.service.ledger.getData(ctx.tender.id);
             const dgnData = await ctx.service.stageBillsDgn.getDgnData(ctx.tender.id);
             for (const d of dgnData) {
                 const l = ctx.app._.find(ledgerData, { id: d.id });
@@ -258,6 +288,36 @@ module.exports = app => {
                 { data: curStageData, fields: ['contract_qty', 'contract_expr', 'contract_tp', 'qc_qty', 'qc_tp', 'postil'], prefix: '', relaId: 'lid' },
                 { data: preStageData, fields: ['contract_qty', 'contract_tp', 'qc_qty', 'qc_tp', 'used'], prefix: 'pre_', relaId: 'lid' },
             ]);
+            // 获取进度最新期的数据
+            ledgerData = await this._addFinalStageData(ctx, ledgerData);
+            return ledgerData;
+        }
+
+        /**
+         * 添加最新一期的进度期下期数据到台账数据中(Ajax)
+         *
+         * @param ctx
+         * @return {Promise<void>}
+         */
+        async _addFinalStageData(ctx, ledgerData) {
+            const scheduleStage = await ctx.service.scheduleStage.getAllDataByCondition({ where: { tid: ctx.tender.id }, orders: [['order', 'desc']] });
+            if (scheduleStage && scheduleStage.length > 0) {
+                let prefinalStageData;
+                const finalStageInfo = await ctx.service.stage.getDataByCondition({
+                    tid: ctx.tender.id,
+                    order: scheduleStage[0].order,
+                });
+                const finalStageData = await ctx.service.stageBills.getLastestStageData(ctx.tender.id, finalStageInfo.id);
+                if (finalStageInfo.order > 1) {
+                    prefinalStageData = await ctx.service.stageBillsFinal.getFinalData(ctx.tender.data, finalStageInfo.order - 1);
+                } else {
+                    prefinalStageData = [];
+                }
+                this.ctx.helper.assignRelaData(ledgerData, [
+                    { data: finalStageData, fields: ['contract_qty', 'contract_expr', 'contract_tp', 'qc_qty', 'qc_tp', 'postil'], prefix: 'final_', relaId: 'lid' },
+                    { data: prefinalStageData, fields: ['contract_qty', 'contract_tp', 'qc_qty', 'qc_tp', 'used'], prefix: 'pre_final_', relaId: 'lid' },
+                ]);
+            }
             return ledgerData;
         }
 
@@ -271,8 +331,8 @@ module.exports = app => {
             const tender = ctx.tender;
             const scheduleMonth = await ctx.service.scheduleMonth.getAllDataByCondition({ where: { tid: tender.id }, orders: [['yearmonth', 'asc']] });
             const stageOrderList = await ctx.service.stage.getAllDataByCondition({ columns: ['id', 's_time', 'order'], where: { tid: tender.id } });
-            const scheduleStage = await ctx.service.scheduleStage.getAllDataByCondition({ where: { tid: tender.id }, orders: [['order', 'desc']] });
-            let curScheduleStage = scheduleStage.length > 0 ? _.maxBy(scheduleStage, 'order') : null;
+            const scheduleStage = await ctx.service.scheduleStage.getAllDataByCondition({ where: { tid: tender.id }, orders: [['yearmonth', 'desc']] });
+            let curScheduleStage = scheduleStage.length > 0 ? _.maxBy(scheduleStage, 'yearmonth') : null;
             if (ctx.params.order && scheduleStage.length > 0) {
                 curScheduleStage = _.find(scheduleStage, { order: parseInt(ctx.params.order) });
             }
@@ -302,16 +362,55 @@ module.exports = app => {
                 const stageList = _.filter(stageOrderList, function(item) {
                     return item.s_time.indexOf(curStage.s_time.split('-')[0]) !== -1;
                 });
-                const newSS = _.sortBy(scheduleStage, 'yearmonth');
-                const stageIdList = _.map(_.filter(stageList, function(item) {
-                    return _.find(newSS, { order: item.order });
-                }), 'id');
+                // const newSS = _.sortBy(scheduleStage, 'yearmonth');
+                // const stageIdList = _.map(_.filter(stageList, function(item) {
+                //     return _.find(newSS, { order: item.order });
+                // }), 'id');
+                const stageIdList = _.map(stageList, 'id');
                 curYearStageData = await ctx.service.stageBills.getStagesData(tender.id, stageIdList.join(','));
             }
             return { slmList, nextSlmList, endSlmList, yearSlmList, curYearStageData, scheduleMonth, stageOrderList, scheduleStage, curScheduleStage };
         }
 
         /**
+         * 获取工程量模式汇总下台账的计划数据(Ajax)
+         *
+         * @param ctx
+         * @return {Promise<void>}
+         */
+        async _getGclAndPlanData(ctx) {
+            const tender = ctx.tender;
+            const scheduleMonth = await ctx.service.scheduleMonth.getAllDataByCondition({ where: { tid: tender.id }, orders: [['yearmonth', 'asc']] });
+            let curScheduleMonth = scheduleMonth.length > 0 ? _.maxBy(scheduleMonth, 'order') : null;
+            const gclScheduleMonth = _.filter(scheduleMonth, { stage_gcl_used: 1 });
+            if (ctx.params.order && gclScheduleMonth.length > 0) {
+                curScheduleMonth = _.find(gclScheduleMonth, { id: parseInt(ctx.params.order) });
+            }
+            let slmList = [];
+            let nextSlmList = [];
+            let endSlmList = [];
+            let yearSlmList = [];
+            if (curScheduleMonth) {
+                const newSM = _.sortBy(scheduleMonth, 'yearmonth');
+                const nowScheduleMonth = _.findIndex(newSM, { yearmonth: curScheduleMonth.yearmonth });
+                slmList = await ctx.service.scheduleLedgerMonth.getAllDataByCondition({ where: { tid: tender.id, yearmonth: curScheduleMonth.yearmonth } });
+                const nextScheduleMonth = nowScheduleMonth >= 0 && nowScheduleMonth + 1 <= newSM.length - 1 ? newSM[nowScheduleMonth + 1] : null;
+                if (nextScheduleMonth) nextSlmList = await ctx.service.scheduleLedgerMonth.getAllDataByCondition({ where: { tid: tender.id, yearmonth: nextScheduleMonth.yearmonth } });
+                if (nowScheduleMonth === 0) {
+                    endSlmList = slmList;
+                } else if (nowScheduleMonth > 0) {
+                    const endYearmonthCollection = _.map(_.take(newSM, nowScheduleMonth + 1), 'yearmonth');
+                    endSlmList = await ctx.service.scheduleLedgerMonth.getConllectionList(tender.id, endYearmonthCollection);
+                }
+                const yearConllection = _.map(_.filter(newSM, function(item) {
+                    return item.yearmonth.indexOf(curScheduleMonth.yearmonth.split('-')[0]) !== -1;
+                }), 'yearmonth');
+                yearSlmList = await ctx.service.scheduleLedgerMonth.getConllectionList(tender.id, yearConllection);
+            }
+            return { slmList, nextSlmList, endSlmList, yearSlmList, scheduleMonth, curScheduleMonth };
+        }
+
+        /**
          * 获取台账数据(Ajax)
          *
          * @param ctx
@@ -381,6 +480,9 @@ module.exports = app => {
                     case 'ledger_edit':
                         responseData.data = await ctx.service.scheduleLedgerMonth.save(data.postData);
                         break;
+                    case 'ledger_paste':
+                        responseData.data = await ctx.service.scheduleLedgerMonth.saveDatas(data.postData);
+                        break;
                     default: throw '参数有误';
                 }
                 ctx.body = responseData;
@@ -452,6 +554,9 @@ module.exports = app => {
                     case 'ledger_edit':
                         responseData.data = await ctx.service.scheduleLedgerMonth.saveSj(data.postData);
                         break;
+                    case 'ledger_paste':
+                        responseData.data = await ctx.service.scheduleLedgerMonth.saveSjDatas(data.postData);
+                        break;
                     default: throw '参数有误';
                 }
                 ctx.body = responseData;
@@ -460,6 +565,40 @@ module.exports = app => {
                 ctx.body = { err: 1, msg: err.toString(), data: null };
             }
         }
+
+        async saveAudit(ctx) {
+            try {
+                const data = JSON.parse(ctx.request.body.data);
+                if (!data) {
+                    throw '提交数据错误';
+                }
+                // 判断修改权限
+                if (ctx.session.sessionUser.is_admin === 0) {
+                    throw '你没有权限修改形象进度';
+                }
+                let info = '';
+                switch (data.type) {
+                    case 'add':
+                        const result = await ctx.service.scheduleAudit.addAudit(data);
+                        if (!result) {
+                            throw '添加审批人失败';
+                        }
+                        info = result;
+                        break;
+                    case 'del':
+                        await ctx.service.scheduleAudit.removeAudit(data);
+                        break;
+                    case 'edit':
+                        await ctx.service.scheduleAudit.editAudit(data);
+                        break;
+                    default:break;
+                }
+                ctx.body = { err: 0, msg: '', data: info };
+            } catch (err) {
+                this.log(err);
+                ctx.body = this.ajaxErrorBody(err, '保存审批流程设置失败');
+            }
+        }
     }
 
     return ScheduleController;

+ 17 - 0
app/controller/tender_controller.js

@@ -20,6 +20,7 @@ const measureType = require('../const/tender').measureType;
 const billsPosConvert = require('../lib/bills_pos_convert');
 const path = require('path');
 const sendToWormhole = require('stream-wormhole');
+const scheduleConst = require('../const/schedule');
 
 module.exports = app => {
 
@@ -452,6 +453,22 @@ module.exports = app => {
                     audit: auditConst,
                     jsFiles: this.app.jsFiles.common.concat(this.app.jsFiles.tender.tenderInfo),
                 };
+                if (ctx.session.sessionProject.page_show !== null && parseInt(ctx.session.sessionProject.page_show.xxjd) === 1 && ctx.session.sessionUser.is_admin) {
+                    // 形象进度内容
+                    // 获取所有项目参与者
+                    const accountList = await ctx.service.projectAccount.getAllDataByCondition({
+                        where: { project_id: ctx.session.sessionProject.id, enable: 1 },
+                        columns: ['id', 'name', 'company', 'role', 'enable', 'is_admin', 'account_group', 'mobile'],
+                    });
+                    const accountGroupList = accountGroup.map((item, idx) => {
+                        const groupList = accountList.filter(item => item.account_group === idx);
+                        return { groupName: item, groupList };
+                    });
+                    renderData.scheduleAuditList = await ctx.service.scheduleAudit.getAllDataByCondition({ where: { tid: tender.id } });
+                    renderData.accountList = accountList;
+                    renderData.accountGroup = accountGroupList;
+                    renderData.scPermission = scheduleConst.permission;
+                }
                 await this.layout('tender/detail.ejs', renderData, 'tender/detail_modal.ejs');
             } catch (error) {
                 this.log(error);

+ 43 - 0
app/middleware/schedule_check.js

@@ -0,0 +1,43 @@
+'use strict';
+
+/**
+ *
+ *
+ * @author EllisRan
+ * @date
+ * @version
+ */
+const scPermission = require('../const/schedule').permission;
+module.exports = options => {
+    /**
+     * 形象进度校验 中间件
+     *
+     * @param {function} next - 中间件继续执行的方法
+     * @return {void}
+     */
+    return function* scheduleCheck(next) {
+        try {
+            if (this.tender.schedule_permission === scPermission.no) {
+                throw '您无权查看该内容';
+            }
+            yield next;
+        } catch (err) {
+            // 输出错误到日志
+            if (err.stack) {
+                this.logger.error(err);
+            } else {
+                this.getLogger('fail').info(JSON.stringify({
+                    error: err,
+                    project: this.session.sessionProject,
+                    user: this.session.sessionUser,
+                    body: this.session.body,
+                }));
+            }
+            if (this.helper.isWap(this.request)) {
+                this.redirect('/wap/list');
+            } else {
+                err === '您无权查看该内容' ? this.redirect(this.request.headers.referer) : this.redirect('/list');
+            }
+        }
+    };
+};

+ 12 - 0
app/middleware/tender_check.js

@@ -10,6 +10,7 @@
 
 const auditConst = require('../const/audit').ledger;
 const messageType = require('../const/message_type');
+const scPermission = require('../const/schedule').permission;
 
 module.exports = options => {
     /**
@@ -74,6 +75,17 @@ module.exports = options => {
             tender.ledgerUsers = tender.ledger_status === auditConst.status.uncheck ? [tender.data.user_id] : [tender.data.user_id, ...auditorsId];
             this.tender = tender;
             this.session.sessionProject.page_show = yield this.service.project.getPageshow(this.session.sessionProject.id);
+            // 形象进度权限获取
+            let schedule_permission = scPermission.no;
+            if (this.session.sessionUser.accountId === tender.data.user_id) {
+                schedule_permission = scPermission.edit;
+            } else {
+                const scheduleUser = yield this.service.scheduleAudit.getDataByCondition({ tid: tender.id, audit_id: this.session.sessionUser.accountId });
+                if (scheduleUser) {
+                    schedule_permission = scheduleUser.permission;
+                }
+            }
+            tender.schedule_permission = schedule_permission;
             yield next;
         } catch (err) {
             // 输出错误到日志

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

@@ -256,6 +256,7 @@ $(document).ready(() => {
                     select.spamount = ZhCalc.round(select.camount, findDecimal(select.unit)) || 0;
                 }
                 console.log(select);
+                delete select.waitingLoading;
 
                 // 更新至服务器
                 postData(window.location.pathname + '/save', { type:'update', updateData: select }, function (result) {

+ 7 - 0
app/public/js/global.js

@@ -103,6 +103,13 @@ $(function(){
     });
 });
 
+function checkShowLast (count) {
+    if (count > 12500) {
+        $('a[name=showLevel][tag=last]').hide();
+    } else {
+        $('a[name=showLevel][tag=last]').show();
+    }
+}
 
 /**
  * 提示框

+ 49 - 23
app/public/js/ledger.js

@@ -59,6 +59,7 @@ $(document).ready(function() {
     autoFlashHeight();
     // 初始化台账
     const ledgerSpread = SpreadJsObj.createNewSpread($('#ledger-spread')[0]);
+    removeLocalCache('bills-fold');
     const treeSetting = {
         id: 'ledger_id',
         pid: 'ledger_pid',
@@ -68,8 +69,11 @@ $(document).ready(function() {
         keys: ['id', 'tender_id', 'ledger_id'],
         preUrl: '/tender/' + getTenderId() + '/ledger',
         //treeCacheKey: 'ledger_bills_fold' + '_' + getTenderId(),
-        markFoldKey: 'bills-fold',
-        markFoldSubKey: window.location.pathname.split('/')[2],
+        autoExpand: 3,
+        // markFoldKey: 'bills-fold',
+        // markFoldSubKey: window.location.pathname.split('/')[2],
+        markExpandKey: 'bills-expand',
+        markExpandSubKey: window.location.pathname.split('/')[2],
     };
     if (checkTzMeasureType()) {
         treeSetting.calcFields = ['sgfh_tp', 'sjcl_tp', 'qtcl_tp', 'total_price'];
@@ -1410,8 +1414,8 @@ $(document).ready(function() {
                 return !readOnly;
             }
         };
-        billsContextMenuOptions.items.sprBlock = '----';
     }
+    billsContextMenuOptions.items.sprBlock = '----';
     if (!readOnly) {
         billsContextMenuOptions.items.sortChildren = {
             name: '顺序重排子项编号',
@@ -1542,6 +1546,7 @@ $(document).ready(function() {
                             SpreadJsObj.loadSheetData(ledgerSpread.getActiveSheet(), 'tree', ledgerTree);
                             pos.loadDatas(result.pos);
                             posOperationObj.loadCurPosData();
+                            checkShowLast(result.bills.length);
                         }, null);
                     },
                     // callback: function (file, select) {
@@ -1617,6 +1622,21 @@ $(document).ready(function() {
             return !node;
         }
     };
+    billsContextMenuOptions.items.sprTag = '----';
+    billsContextMenuOptions.items.showLast = {
+        name: '显示至最底层',
+        callback: function (key, opt, menu, e) {
+            const node = SpreadJsObj.getSelectObject(ledgerSpread.getActiveSheet());
+            setTimeout(() => {
+                showWaitingView();
+                ledgerTree.expandByCustom(x => {
+                    return x.expanded ||(x.id === node.id) || (x.full_path.indexOf(node.ledger_id + '-') >= 0);
+                });
+                SpreadJsObj.refreshTreeRowVisible(ledgerSpread.getActiveSheet());
+                closeWaitingView();
+            }, 100);
+        },
+    };
 
     $.contextMenu(billsContextMenuOptions);
 
@@ -2179,6 +2199,8 @@ $(document).ready(function() {
         SpreadJsObj.loadSheetData(ledgerSpread.getActiveSheet(), 'tree', ledgerTree);
         SpreadJsObj.loadTopAndSelect(ledgerSpread.getActiveSheet(), ckBillsSpread);
 
+        checkShowLast(data.bills.length);
+
         pos.loadDatas(data.pos);
         posOperationObj.loadCurPosData();
         SpreadJsObj.resetTopAndSelect(posSpread.getActiveSheet());
@@ -3126,7 +3148,7 @@ $(document).ready(function() {
                 }
             }
         }, 400);
-    })
+    });
 
     // 添加审批流程按钮逻辑
     $('.book-list').on('click', 'dt', function () {
@@ -3145,7 +3167,7 @@ $(document).ready(function() {
             })
         }
         return false
-    })
+    });
 
     // 添加到审批流程中
     $('dl').on('click', 'dd', function () {
@@ -3232,24 +3254,28 @@ $(document).ready(function() {
             const tag = $(this).attr('tag');
             const tree = sheet.zh_tree;
             if (!tree) return;
-            switch (tag) {
-                case "1":
-                case "2":
-                case "3":
-                case "4":
-                case "5":
-                    tree.expandByLevel(parseInt(tag));
-                    SpreadJsObj.refreshTreeRowVisible(sheet);
-                    break;
-                case "last":
-                    tree.expandByCustom(() => { return true; });
-                    SpreadJsObj.refreshTreeRowVisible(sheet);
-                    break;
-                case "leafXmj":
-                    tree.expandToLeafXmj();
-                    SpreadJsObj.refreshTreeRowVisible(sheet);
-                    break;
-            }
+            setTimeout(() => {
+                showWaitingView();
+                switch (tag) {
+                    case "1":
+                    case "2":
+                    case "3":
+                    case "4":
+                    case "5":
+                        tree.expandByLevel(parseInt(tag));
+                        SpreadJsObj.refreshTreeRowVisible(sheet);
+                        break;
+                    case "last":
+                        tree.expandByCustom(() => { return true; });
+                        SpreadJsObj.refreshTreeRowVisible(sheet);
+                        break;
+                    case "leafXmj":
+                        tree.expandToLeafXmj();
+                        SpreadJsObj.refreshTreeRowVisible(sheet);
+                        break;
+                }
+                closeWaitingView();
+            }, 100);
         });
     })('a[name=showLevel]', ledgerSpread.getActiveSheet());
 

+ 53 - 20
app/public/js/ledger_audit.js

@@ -28,8 +28,11 @@ $(document).ready(() => {
         rootId: -1,
         keys: ['id', 'tender_id', 'ledger_id'],
         preUrl: '/ledger',
-        markFoldKey: 'bills-fold',
-        markFoldSubKey: window.location.pathname.split('/')[2],
+        autoExpand: 3,
+        // markFoldKey: 'bills-fold',
+        // markFoldSubKey: window.location.pathname.split('/')[2],
+        markExpandKey: 'bills-expand',
+        markExpandSubKey: window.location.pathname.split('/')[2],
     };
     if (checkTzMeasureType()) {
         treeSetting.calcFields = ['sgfh_tp', 'sjcl_tp', 'qtcl_tp', 'total_price'];
@@ -92,12 +95,38 @@ $(document).ready(() => {
         SpreadJsObj.initSheet(posSpread.getActiveSheet(), posSpreadSetting);
     }
 
+    $.contextMenu({
+        selector: '#ledger-spread',
+        build: function ($trigger, e) {
+            const target = SpreadJsObj.safeRightClickSelection($trigger, e, ledgerSpread);
+            return target.hitTestType === spreadNS.SheetArea.viewport || target.hitTestType === spreadNS.SheetArea.rowHeader;
+        },
+        items: {
+            showLast: {
+                name: '显示至最底层',
+                callback: function (key, opt, menu, e) {
+                    const node = SpreadJsObj.getSelectObject(ledgerSpread.getActiveSheet());
+                    setTimeout(() => {
+                        showWaitingView();
+                        ledgerTree.expandByCustom(x => {
+                            return x.expanded || (x.id === node.id) || (x.full_path.indexOf(node.ledger_id + '-') >= 0);
+                        });
+                        SpreadJsObj.refreshTreeRowVisible(ledgerSpread.getActiveSheet());
+                        closeWaitingView();
+                    }, 100);
+                },
+            },
+        }
+    });
+
     postData('/tender/' + getTenderId() + '/ledger/load', null, function (data) {
         ledgerTree.loadDatas(data.bills);
         treeCalc.calculateAll(ledgerTree);
         SpreadJsObj.loadSheetData(ledgerSpread.getActiveSheet(), 'tree', ledgerTree);
         SpreadJsObj.loadTopAndSelect(ledgerSpread.getActiveSheet(), ckBillsSpread);
 
+        checkShowLast(data.bills.length);
+
         pos.loadDatas(data.pos);
         loadCurPosData();
         SpreadJsObj.resetTopAndSelect(posSpread.getActiveSheet());
@@ -238,24 +267,28 @@ $(document).ready(() => {
             if (!sheet.zh_tree) return;
             const tag = $(this).attr('tag');
             const tree = sheet.zh_tree;
-            switch (tag) {
-                case "1":
-                case "2":
-                case "3":
-                case "4":
-                case "5":
-                    tree.expandByLevel(parseInt(tag));
-                    SpreadJsObj.refreshTreeRowVisible(sheet);
-                    break;
-                case "last":
-                    tree.expandByCustom(() => { return true; });
-                    SpreadJsObj.refreshTreeRowVisible(sheet);
-                    break;
-                case "leafXmj":
-                    tree.expandToLeafXmj();
-                    SpreadJsObj.refreshTreeRowVisible(sheet);
-                    break;
-            }
+            setTimeout(() => {
+                showWaitingView();
+                switch (tag) {
+                    case "1":
+                    case "2":
+                    case "3":
+                    case "4":
+                    case "5":
+                        tree.expandByLevel(parseInt(tag));
+                        SpreadJsObj.refreshTreeRowVisible(sheet);
+                        break;
+                    case "last":
+                        tree.expandByCustom(() => { return true; });
+                        SpreadJsObj.refreshTreeRowVisible(sheet);
+                        break;
+                    case "leafXmj":
+                        tree.expandToLeafXmj();
+                        SpreadJsObj.refreshTreeRowVisible(sheet);
+                        break;
+                }
+                closeWaitingView();
+            }, 100);
         });
     })('a[name=showLevel]', ledgerSpread.getActiveSheet());
 });

+ 2 - 0
app/public/js/material.js

@@ -358,6 +358,7 @@ $(document).ready(() => {
                 select.msg_spread = materialCol.getValue.msg_spread(select);
                 select.m_spread = materialCol.getValue.m_spread(select);
                 select.m_tp = materialCol.getValue.m_tp(select);
+                delete select.waitingLoading;
 
                 // console.log(select);
 
@@ -389,6 +390,7 @@ $(document).ready(() => {
                         info.sheet.endEdit(true);
                     }
                     select.is_summary = info.sheet.getValue(info.row, info.col) ? 1 : 0;
+                    delete select.waitingLoading;
                     // 更新至服务器
                     postData(window.location.pathname + '/save', { type:'update', updateData: select }, function (result) {
                         m_tp = result.m_tp;

+ 2 - 0
app/public/js/material_exponent.js

@@ -187,6 +187,7 @@ $(document).ready(() => {
                 }
                 select[col.field] = validText;
                 select.calc_num = materialExponentCol.getValue.calc_num(select);
+                delete select.waitingLoading;
                 // console.log(select);
 
                 // 更新至服务器
@@ -220,6 +221,7 @@ $(document).ready(() => {
                         info.sheet.endEdit(true);
                     }
                     select.is_summary = info.sheet.getValue(info.row, info.col) ? 1 : 0;
+                    delete select.waitingLoading;
                     // 更新至服务器
                     postData(window.location.pathname + '/save', { type:'update', updateData: select }, function (result) {
                         ex_tp = result.ex_tp;

+ 54 - 21
app/public/js/path_tree.js

@@ -275,11 +275,18 @@ const createNewPathTree = function (type, setting) {
             // 树设置
             this.setting = setting;
 
-            if (this.setting.markFoldKey) {
+            this.hasMark = false;
+            if (this.setting.markExpandKey) {
+                const markStr = getLocalCache(this.setting.markExpandKey);
+                const markData = markStr ? markStr.split('|') : ['', ''];
+                this.hasMark = markData[0] === this.setting.markExpandSubKey;
+                this.markExpand = this.hasMark && markData[1] ? _.map(markData[1].split(','), _.toInteger) : [];
+
+            } else if (this.setting.markFoldKey) {
                  const markStr = getLocalCache(this.setting.markFoldKey);
                  const markData = markStr ? markStr.split('|') : ['', ''];
-                 this.markFold = markData[0] === this.setting.markFoldSubKey && markData[1]
-                     ? _.map(markData[1].split(','), _.toInteger) : [];
+                 this.hasMark = markData[0] === this.setting.markFoldSubKey;
+                 this.markFold = this.hasMark && markData[1] ? _.map(markData[1].split(','), _.toInteger) : [];
             }
             // if (this.setting.treeCacheKey) {
             //     localforage.getItem(this.setting.treeCacheKey).then(function (v) {
@@ -355,10 +362,25 @@ const createNewPathTree = function (type, setting) {
                 return a.order - b.order;
             });
             this.sortTreeNode(true);
-            if (this.setting.autoExpand >= 0) this.expandByLevel(this.setting.autoExpand);
-            if (this.setting.markFoldKey) this.expandByCustom(function (node) {
-                return self.markFold.indexOf(node[self.setting.id]) === -1;
-            });
+            if (this.hasMark) {
+                if (this.setting.markExpandKey) {
+                    this.expandByCustom(node => {
+                        return self.markExpand.indexOf(node[self.setting.id]) >= 0;
+                    });
+                } else if (this.setting.markFoldKey) {
+                    this.expandByCustom(function (node) {
+                        return self.markFold.indexOf(node[self.setting.id]) === -1;
+                    });
+                }
+            } else {
+                if (this.setting.autoExpand >= 0) {
+                    this.expandByLevel(this.setting.autoExpand);
+                    for (const node of this.nodes) {
+                        this._markExpandFold(node);
+                    }
+                    this._saveMarkExpandFold();
+                }
+            }
         }
 
         getItemsByIndex(index) {
@@ -526,18 +548,29 @@ const createNewPathTree = function (type, setting) {
             return node[this.setting.id];
         };
 
-        _markFold(node) {
-            if (!this.setting.markFoldKey || !this.setting.markFoldSubKey) return;
-            // if (!this.setting.treeCacheKey) return;
-
-            if (!node.expanded) {
-                if (this.markFold.indexOf(node[this.setting.id]) === -1) this.markFold.push(node[this.setting.id]);
-            } else {
-                if (this.markFold.indexOf(node[this.setting.id]) >= 0) this.markFold.splice(this.markFold.indexOf(node[this.setting.id]), 1);
+        _markExpandFold(node) {
+            if (this.setting.markExpandKey && this.setting.markExpandSubKey) {
+                if (node.expanded) {
+                    if (this.markExpand.indexOf(node[this.setting.id]) === -1)
+                        this.markExpand.push(node[this.setting.id]);
+                } else {
+                    if (this.markExpand.indexOf(node[this.setting.id]) >= 0)
+                        this.markExpand.splice(this.markExpand.indexOf(node[this.setting.id]), 1);
+                }
+            } else if (this.setting.markFoldKey && this.setting.markFoldSubKey) {
+                if (!node.expanded) {
+                    if (this.markFold.indexOf(node[this.setting.id]) === -1)
+                        this.markFold.push(node[this.setting.id]);
+                } else {
+                    if (this.markFold.indexOf(node[this.setting.id]) >= 0)
+                        this.markFold.splice(this.markFold.indexOf(node[this.setting.id]), 1);
+                }
             }
         }
-        _saveMarkFold() {
-            if (this.setting.markFoldKey && this.setting.markFoldSubKey) {
+        _saveMarkExpandFold() {
+            if (this.setting.markExpandKey && this.setting.markExpandSubKey) {
+                setLocalCache(this.setting.markExpandKey, this.setting.markExpandSubKey + '|' + this.markExpand.join(','));
+            } else if (this.setting.markFoldKey && this.setting.markFoldSubKey) {
                 setLocalCache(this.setting.markFoldKey, this.setting.markFoldSubKey + '|' + this.markFold.join(','));
             }
             // if (this.setting.treeCacheKey) {
@@ -570,9 +603,9 @@ const createNewPathTree = function (type, setting) {
          */
         setExpanded(node, expanded) {
             node.expanded = expanded;
-            this._markFold(node);
+            this._markExpandFold(node);
             this._refreshChildrenVisible(node);
-            this._saveMarkFold();
+            this._saveMarkExpandFold();
         };
         /**
          * 递归 设置节点展开状态
@@ -586,7 +619,7 @@ const createNewPathTree = function (type, setting) {
                 const expanded = checkFun(node);
                 if (node.expanded !== expanded) {
                     node.expanded = expanded;
-                    this._markFold(node);
+                    this._markExpandFold(node);
                 }
                 node.visible = parent ? (parent.expanded && parent.visible) : true;
                 this._recursiveExpand(node.children, node, checkFun);
@@ -598,7 +631,7 @@ const createNewPathTree = function (type, setting) {
          */
         expandByCustom(checkFun) {
             this._recursiveExpand(this.children, null, checkFun);
-            this._saveMarkFold();
+            this._saveMarkExpandFold();
         }
         /**
          * 展开到第几层

+ 41 - 19
app/public/js/revise.js

@@ -103,6 +103,7 @@ $(document).ready(() => {
         rootId: -1,
         keys: ['id', 'tender_id', 'ledger_id'],
         calcFields: ['sgfh_tp', 'sjcl_tp', 'qtcl_tp', 'total_price'],
+        autoExpand: 3,
     };
     if (!isTz) {
         treeSetting.calcFields.push('deal_tp');
@@ -1257,6 +1258,21 @@ $(document).ready(() => {
             }
         };
     }
+    billsContextMenuOptions.items.sprTag = '----';
+    billsContextMenuOptions.items.showLast = {
+        name: '显示至最底层',
+        callback: function (key, opt, menu, e) {
+            const node = SpreadJsObj.getSelectObject(billsSheet);
+            setTimeout(() => {
+                showWaitingView();
+                billsTree.expandByCustom(x => {
+                    return x.expanded || (x.id === node.id) || (x.full_path.indexOf(node.ledger_id + '-') >= 0);
+                });
+                SpreadJsObj.refreshTreeRowVisible(billsSheet);
+                closeWaitingView();
+            }, 100);
+        },
+    };
     $.contextMenu(billsContextMenuOptions);
 
     // 计量单元 相关方法&绑定spreadjs事件
@@ -1798,7 +1814,9 @@ $(document).ready(() => {
         billsTree.loadDatas(result.bills);
         treeCalc.calculateAll(billsTree);
         SpreadJsObj.loadSheetData(billsSheet, SpreadJsObj.DataType.Tree, billsTree);
-        SpreadJsObj.loadTopAndSelect(billsSheet, ckBillsSpread);        
+        SpreadJsObj.loadTopAndSelect(billsSheet, ckBillsSpread);
+
+        checkShowLast(result.bills.length);
 
         pos.loadDatas(result.pos);
         posSpreadObj.loadCurPosData();
@@ -2543,24 +2561,28 @@ $(document).ready(() => {
             if (!sheet.zh_tree) return;
             const tag = $(this).attr('tag');
             const tree = sheet.zh_tree;
-            switch (tag) {
-                case "1":
-                case "2":
-                case "3":
-                case "4":
-                case "5":
-                    tree.expandByLevel(parseInt(tag));
-                    SpreadJsObj.refreshTreeRowVisible(sheet);
-                    break;
-                case "last":
-                    tree.expandByCustom(() => { return true; });
-                    SpreadJsObj.refreshTreeRowVisible(sheet);
-                    break;
-                case "leafXmj":
-                    tree.expandToLeafXmj();
-                    SpreadJsObj.refreshTreeRowVisible(sheet);
-                    break;
-            }
+            setTimeout(() => {
+                showWaitingView();
+                switch (tag) {
+                    case "1":
+                    case "2":
+                    case "3":
+                    case "4":
+                    case "5":
+                        tree.expandByLevel(parseInt(tag));
+                        SpreadJsObj.refreshTreeRowVisible(sheet);
+                        break;
+                    case "last":
+                        tree.expandByCustom(() => { return true; });
+                        SpreadJsObj.refreshTreeRowVisible(sheet);
+                        break;
+                    case "leafXmj":
+                        tree.expandToLeafXmj();
+                        SpreadJsObj.refreshTreeRowVisible(sheet);
+                        break;
+                }
+                closeWaitingView();
+            }, 100);
         });
     })('a[name=showLevel]', billsSheet);
 

+ 26 - 19
app/public/js/revise_history.js

@@ -53,6 +53,7 @@ $(document).ready(() => {
         rootId: -1,
         keys: ['id', 'tender_id', 'ledger_id'],
         calcFields: ['sgfh_tp', 'sjcl_tp', 'qtcl_tp', 'total_price'],
+        autoExpand: 3,
     };
     if (!isTz) {
         treeSetting.calcFields.push('deal_tp');
@@ -158,7 +159,9 @@ $(document).ready(() => {
         billsTree.loadDatas(result.bills);
         treeCalc.calculateAll(billsTree);
         SpreadJsObj.loadSheetData(billsSheet, SpreadJsObj.DataType.Tree, billsTree);
-        SpreadJsObj.loadTopAndSelect(billsSheet, ckBillsSpread);        
+        SpreadJsObj.loadTopAndSelect(billsSheet, ckBillsSpread);
+
+        checkShowLast(result.bills.length);
 
         pos.loadDatas(result.pos);
         posSpreadObj.loadCurPosData();
@@ -271,24 +274,28 @@ $(document).ready(() => {
             if (!sheet.zh_tree) return;
             const tag = $(this).attr('tag');
             const tree = sheet.zh_tree;
-            switch (tag) {
-                case "1":
-                case "2":
-                case "3":
-                case "4":
-                case "5":
-                    tree.expandByLevel(parseInt(tag));
-                    SpreadJsObj.refreshTreeRowVisible(sheet);
-                    break;
-                case "last":
-                    tree.expandByCustom(() => { return true; });
-                    SpreadJsObj.refreshTreeRowVisible(sheet);
-                    break;
-                case "leafXmj":
-                    tree.expandToLeafXmj();
-                    SpreadJsObj.refreshTreeRowVisible(sheet);
-                    break;
-            }
+            setTimeout(() => {
+                showWaitingView();
+                switch (tag) {
+                    case "1":
+                    case "2":
+                    case "3":
+                    case "4":
+                    case "5":
+                        tree.expandByLevel(parseInt(tag));
+                        SpreadJsObj.refreshTreeRowVisible(sheet);
+                        break;
+                    case "last":
+                        tree.expandByCustom(() => { return true; });
+                        SpreadJsObj.refreshTreeRowVisible(sheet);
+                        break;
+                    case "leafXmj":
+                        tree.expandToLeafXmj();
+                        SpreadJsObj.refreshTreeRowVisible(sheet);
+                        break;
+                }
+                closeWaitingView();
+            }, 100);
         });
     })('a[name=showLevel]', billsSheet);
 

+ 46 - 26
app/public/js/schedule_ledger.js

@@ -43,7 +43,7 @@ $(function () {
         font: '12px 微软雅黑',
         // readOnly: true,
         localCache: {
-            key: 'ledger-bills',
+            key: 'ledger-schedule',
             colWidth: true,
         }
     };
@@ -64,9 +64,13 @@ $(function () {
 
     postData(window.location.pathname + '/load', {}, function (data) {
         const showList = ['is_select', 'can_select', 'ledger_id', 'ledger_pid', 'order', 'level', 'tender_id', 'full_path',
-            'code', 'name', 'unit', 'dgn_qty1', 'dgn_qty2', 'dgn_price', 'quantity', 'total_price'];
-        const calcList = ['total_price'];
-        const fileds = [];
+            'code', 'name', 'unit', 'dgn_qty1', 'dgn_qty2', 'dgn_price', 'quantity', 'total_price',
+            'final_contract_tp', 'final_qc_tp', 'pre_final_contract_tp', 'pre_final_qc_tp', 'pre_final_gather_tp', 'final_gather_tp', 'end_final_gather_tp'
+        ];
+        const calcList = ['total_price',
+            'final_contract_tp', 'final_qc_tp', 'pre_final_contract_tp', 'pre_final_qc_tp', 'pre_final_gather_tp', 'final_gather_tp', 'end_final_gather_tp'
+        ];
+        const fileds = ['final_contract_tp', 'final_qc_tp'];
         if (scheduleStage.length > 0) {
             for (const ss of scheduleStage) {
                 // const pushTp = [ss.order + '_gather_tp', ss.order + '_contract_tp', ss.order + '_qc_tp'];
@@ -96,6 +100,9 @@ $(function () {
                         node[ss.order + '_gather_tp'] = ZhCalc.add(node[ss.order + '_contract_tp'], node[ss.order + '_qc_tp']);
                     }
                 }
+                node.pre_final_gather_tp = ZhCalc.add(node.pre_final_contract_tp, node.pre_final_qc_tp);
+                node.final_gather_tp = ZhCalc.add(node.final_contract_tp, node.final_qc_tp);
+                node.end_final_gather_tp = ZhCalc.add(node.pre_final_gather_tp, node.final_gather_tp);
             }
         });
         slh = data.slh;
@@ -222,7 +229,9 @@ $(function () {
         const [under_ledger, parent_ledger] = getPAndULedger(select_ledger);
         // 重新计算每一期计量进度金额并提交
         const stageTpList = calcStageTp(under_ledger);
-        postData(window.location.pathname + '/save',  { select_ledger: _.map(select_ledger, 'ledger_id'), under_ledger, parent_ledger, stageTpList, type: 'sz' } , function (result) {
+        const stageSjTp = calcStageSjTp(under_ledger);
+        console.log(under_ledger, stageSjTp);
+        postData(window.location.pathname + '/save',  { select_ledger: _.map(select_ledger, 'ledger_id'), under_ledger, parent_ledger, stageTpList, stageSjTp, type: 'sz' } , function (result) {
             toastr.success('设置成功');
             setTimeout(function () {
                 window.location.reload();
@@ -230,31 +239,33 @@ $(function () {
         });
     });
     $('#update-ledger').click(function () {
-        const addLedgerData = _.map(_.differenceBy(ledgerTree.nodes, slh, 'ledger_id'), 'ledger_id');
+        // const addLedgerData = _.map(_.differenceBy(ledgerTree.nodes, slh, 'ledger_id'), 'ledger_id');
         const select_ledger = _.filter(ledgerTree.nodes, { 'is_select': 1 });
         // 找出addLedgerData父节点并一起加入到select_ledger中
-        if (addLedgerData.length > 0) {
-            for (const a of addLedgerData) {
-                const ainfo = _.find(ledgerTree.nodes, { 'ledger_id': a });
-                select_ledger.push(ainfo);
-                findandpush(ainfo.ledger_pid);
-            }
-        }
-        function findandpush(ledger) {
-            const parent = _.find(ledgerTree.nodes, { ledger_id: ledger });
-            if (parent) {
-                const hadinfo = _.find(select_ledger, { ledger_id: parent.ledger_id });
-                if (!hadinfo) {
-                    select_ledger.push(parent);
-                    findandpush(parent);
-                }
-            }
-        }
+        // if (addLedgerData.length > 0) {
+        //     for (const a of addLedgerData) {
+        //         const ainfo = _.find(ledgerTree.nodes, { 'ledger_id': a });
+        //         select_ledger.push(ainfo);
+        //         findandpush(ainfo.ledger_pid);
+        //     }
+        // }
+        // function findandpush(ledger) {
+        //     const parent = _.find(ledgerTree.nodes, { ledger_id: ledger });
+        //     if (parent) {
+        //         const hadinfo = _.find(select_ledger, { ledger_id: parent.ledger_id });
+        //         if (!hadinfo) {
+        //             select_ledger.push(parent);
+        //             findandpush(parent);
+        //         }
+        //     }
+        // }
         $(this).attr('disabled', true);
         const [under_ledger, parent_ledger] = getPAndULedger(select_ledger);
         // 重新计算每一期计量进度金额并提交
         const stageTpList = calcStageTp(under_ledger);
-        postData(window.location.pathname + '/save',  { select_ledger: _.map(select_ledger, 'ledger_id'), under_ledger, parent_ledger, stageTpList, type: 'xz' } , function (result) {
+        const stageSjTp = calcStageSjTp(under_ledger);
+        console.log(stageSjTp);
+        postData(window.location.pathname + '/save',  { select_ledger: _.map(select_ledger, 'ledger_id'), under_ledger, parent_ledger, stageTpList, stageSjTp, type: 'xz' } , function (result) {
             toastr.success('进度台账数据修正成功');
             setTimeout(function () {
                 window.location.reload();
@@ -268,7 +279,7 @@ $(function () {
         for (const sl of select_ledger) {
             const hadChild = _.find(select_ledger, { ledger_pid: sl.ledger_id });
             if (!hadChild) {
-                const pushArray = { ledger_id: sl.ledger_id, ledger_pid: sl.ledger_pid, gcl: sl.dgn_qty1, tp: sl.total_price };
+                const pushArray = { ledger_id: sl.ledger_id, ledger_pid: sl.ledger_pid, gcl: sl.dgn_qty1, tp: sl.total_price, end_final_gather_tp: sl.end_final_gather_tp };
                 if (scheduleStage.length > 0) {
                     for (const ss of scheduleStage) {
                         pushArray[ss.order + '_gather_tp'] = sl[ss.order + '_gather_tp'];
@@ -357,6 +368,15 @@ function calcStageTp(list) {
     }
     return false;
 }
+
+function calcStageSjTp(list) {
+    let stageSjTp = 0;
+    for (const l of list) {
+        stageSjTp = ZhCalc.add(stageSjTp, l.end_final_gather_tp);
+    }
+    return stageSjTp;
+}
+
 function addIsSelect(datas) {
     // const newDatas = [];
     for (const d of datas) {
@@ -440,7 +460,7 @@ function updateOtherSiblingsSelect(tree, pid, is_select, select_msg) {
                         }
                     }
                     if(p) {
-                        if(d.can_select) {
+                        if(d.can_select && is_select) {
                             d.is_select = is_select;
                         }
                         selects.push(d);

+ 133 - 77
app/public/js/schedule_plan.js

@@ -24,6 +24,11 @@ $(function () {
         fullPath: 'full_path',
         calcFun: function (node) {
             node.dgn_price = ZhCalc.round(ZhCalc.div(node.total_price, node.dgn_qty1), 2);
+            if (node.children && node.children.length > 0) {
+                for (const sm of scheduleMonth) {
+                    node[sm.yearmonth+'_gcl'] = ZhCalc.round(ZhCalc.div(node[sm.yearmonth+'_tp'], node.dgn_price), 3);
+                }
+            }
         }
         //treeCacheKey: 'ledger_bills_fold' + '_' + getTenderId(),
         // markFoldKey: 'bills-fold',
@@ -49,7 +54,7 @@ $(function () {
         font: '12px 微软雅黑',
         // readOnly: true,
         localCache: {
-            key: 'ledger-bills',
+            key: 'ledger-plan',
             colWidth: true,
         }
     };
@@ -96,6 +101,8 @@ $(function () {
     SpreadJsObj.initSpreadSettingEvents(ledgerSpreadSetting, ledgerCol);
     SpreadJsObj.initSheet(ledgerSpread.getActiveSheet(), ledgerSpreadSetting);
     SpreadJsObj.selChangedRefreshBackColor(ledgerSpread.getActiveSheet());
+    ledgerSpread.getActiveSheet().frozenColumnCount(6);
+    ledgerSpread.getActiveSheet().options.frozenlineColor = '#93b5e4';
 
     postData('/tender/' + getTenderId() + '/schedule/ledger/load', {}, function (data) {
         // let treeData = [];
@@ -130,7 +137,6 @@ $(function () {
         const newLedgerList = setMonthToLedger(data.bills, data.slm);
         baseLedgerTree.loadDatas(newLedgerList);
         treeCalc.calculateAll(baseLedgerTree);
-        console.log(baseLedgerTree);
         for (const d of baseLedgerTree.nodes) {
             if (!d.b_code) {
                 const one = _.find(selectedLedgerList, function (item) {
@@ -141,7 +147,6 @@ $(function () {
                 }
             }
         }
-        // console.log(ledgerTree);
         ledgerTree.sortTreeNode(true);
         treeCalc.calculateAll(ledgerTree);
         console.log(ledgerTree);
@@ -246,7 +251,6 @@ $(function () {
                     plan_gcl,
                     plan_tp,
                 };
-                console.log(updateData);
                 postData(window.location.pathname + '/save', {type: 'ledger_edit', postData: updateData}, function (result) {
                     if (mode === 'tp') {
                         select[yearmonth + '_gcl'] = plan_gcl;
@@ -265,82 +269,129 @@ $(function () {
             }
         },
         deletePress: function (sheet) {
-            return;
+            if (!sheet.zh_setting) return;
+            if (sheet.zh_setting && sheet.zh_tree) {
+                const sel = sheet.getSelections()[0], datas = [], filterNodes = [];
+                if (!sel) return;
+                for (let iRow = sel.row; iRow < sel.row + sel.rowCount; iRow++) {
+                    let bDel = false;
+                    const node = sheet.zh_tree.nodes[iRow];
+                    for (let iCol = sel.col; iCol < sel.col + sel.colCount; iCol++) {
+                        const col = sheet.zh_setting.cols[iCol];
+                        const orgValue = node[col.field];
+                        if (!orgValue) {
+                            continue;
+                        }
+                        const yearmonth = col.field.split('_')[0];
+                        const modes = col.field.split('_')[1];
+                        if(col.readOnly === true || !node.is_leaf || mode[modes] !== schedule.mode) {
+                            continue;
+                        }
+                        const updateData = {
+                            lid: node.ledger_id,
+                            yearmonth,
+                            plan_gcl: null,
+                            plan_tp: null,
+                        };
+                        datas.push(updateData);
+                        node[yearmonth+'_gcl'] = null;
+                        node[yearmonth+'_tp'] = null;
+                        bDel = true;
+                    }
+                    if (bDel) filterNodes.push(node);
+                }
+                if (datas.length > 0) {
+                    postData(window.location.pathname + '/save', {type: 'ledger_paste', postData: datas}, function (result) {
+                        for (const uul of filterNodes) {
+                            const nodes = treeCalc.calculateParent(sheet.zh_tree, uul);
+                            const refreshNode = ledgerTree.loadPostData({update: nodes});
+                            ledgerSpreadObj.refreshTree(sheet, refreshNode);
+                        }
+                        SpreadJsObj.reLoadSheetData(sheet);
+                    }, function () {
+                        SpreadJsObj.reLoadSheetData(sheet);
+                    });
+                }
+            }
         },
         clipboardPasted(e, info) {
             const hint = {
                 cellError: {type: 'error', msg: '粘贴内容超出了表格范围'},
                 numberExpr: {type: 'error', msg: '不能粘贴其它非数字类型字符'},
             };
-            const range = info.cellRange;
-            if (range.rowCount > 1 || range.colCount > 1) {
-                toastMessageUniq(hint.cellError);
-                SpreadJsObj.reLoadSheetHeader(ledgerSpread.getActiveSheet());
-                SpreadJsObj.reLoadSheetData(ledgerSpread.getActiveSheet());
-                return;
-            }
-            const select = SpreadJsObj.getSelectObject(info.sheet);
-            const col = info.sheet.zh_setting.cols[range.col];
-            let validText = is_numeric(info.pasteData.text) ? parseFloat(info.pasteData.text) : (info.pasteData.text ? trimInvalidChar(info.pasteData.text) : null);
-            const orgValue = select[col.field];
-            if (orgValue == validText || ((!orgValue || orgValue === '') && (validText === ''))) {
-                SpreadJsObj.reLoadRowData(info.sheet, info.row);
-                return;
-            }
-            if (isNaN(validText)) {
-                toastr.error('不能粘贴其它非数字类型字符');
-                SpreadJsObj.reLoadRowData(info.sheet, info.cellRange.row, info.cellRange.rowCount);
-                return;
-            }
-            const yearmonth = col.field.split('_')[0];
-            const mode = col.field.split('_')[1];
-            let plan_gcl = 0;
-            let plan_tp = 0;
-            // 判断输入位数,提示
-            if (mode === 'tp') {
-                const reg = new RegExp('^([-]?)\\d+$');
-                if (validText !== null && (!reg.test(validText))) {
-                    // toastr.error('粘贴的金额小数位数不能大于0位');
-                    // SpreadJsObj.reLoadRowData(info.sheet, info.cellRange.row, info.cellRange.rowCount);
-                    // return;
-                    validText = ZhCalc.round(validText, 0);
+            const tree = info.sheet.zh_tree;
+            if (!tree) { return; }
+
+            const sortData = info.sheet.zh_tree.nodes;
+            const datas = [], filterNodes = [];
+
+            for (let iRow = 0; iRow < info.cellRange.rowCount; iRow ++) {
+                let bPaste = false;
+                const curRow = info.cellRange.row + iRow;
+                const node = sortData[curRow];
+                if (node) {
+                    for (let iCol = 0; iCol < info.cellRange.colCount; iCol++) {
+                        const curCol = info.cellRange.col + iCol;
+                        const colSetting = info.sheet.zh_setting.cols[curCol];
+                        let validText = is_numeric(info.sheet.getText(curRow, curCol)) ? parseFloat(info.sheet.getText(curRow, curCol)) : (info.sheet.getText(curRow, curCol) ? trimInvalidChar(info.sheet.getText(curRow, curCol)) : null);
+                        const orgValue = node[colSetting.field];
+                        if (orgValue == validText || ((!orgValue || orgValue === '') && (validText === ''))) {
+                            continue;
+                        }
+                        if (isNaN(validText)) {
+                            toastMessageUniq(hint.numberExpr);
+                            continue;
+                        }
+                        const yearmonth = colSetting.field.split('_')[0];
+                        const mode = colSetting.field.split('_')[1];
+                        let plan_gcl = 0;
+                        let plan_tp = 0;
+                        // 判断输入位数,提示
+                        if (mode === 'tp') {
+                            const reg = new RegExp('^([-]?)\\d+$');
+                            if (validText !== null && (!reg.test(validText))) {
+                                validText = ZhCalc.round(validText, 0);
+                            }
+                            plan_gcl = node.dgn_price && node.dgn_price !== 0 ? ZhCalc.round(ZhCalc.div(validText, node.dgn_price), 3) : 0;
+                            plan_tp = validText;
+                        } else {
+                            const reg = new RegExp('^([-]?)\\d+$');
+                            if (validText !== null && (!reg.test(validText))) {
+                                validText = ZhCalc.round(validText, 3);
+                            }
+                            plan_gcl = validText;
+                            plan_tp = node.dgn_price && node.dgn_price !== 0 ? ZhCalc.round(ZhCalc.mul(validText, node.dgn_price), 0) : 0;
+                        }
+                        const updateData = {
+                            lid: node.ledger_id,
+                            yearmonth,
+                            plan_gcl,
+                            plan_tp,
+                        };
+                        datas.push(updateData);
+                        node[yearmonth+'_gcl'] = plan_gcl;
+                        node[yearmonth+'_tp'] = plan_tp;
+                        bPaste = true;
+                    }
+                    if (bPaste) {
+                        filterNodes.push(node);
+                    }
                 }
-                plan_gcl = select.dgn_price && select.dgn_price !== 0 ? ZhCalc.round(ZhCalc.div(validText, select.dgn_price), 3) : 0;
-                plan_tp = validText;
+            }
+            if (datas.length > 0) {
+                postData(window.location.pathname + '/save', {type: 'ledger_paste', postData: datas}, function (result) {
+                    for (const uul of filterNodes) {
+                        const nodes = treeCalc.calculateParent(info.sheet.zh_tree, uul);
+                        const refreshNode = ledgerTree.loadPostData({update: nodes});
+                        ledgerSpreadObj.refreshTree(info.sheet, refreshNode);
+                    }
+                    SpreadJsObj.reLoadSheetData(info.sheet);
+                }, function () {
+                    SpreadJsObj.reLoadRowData(info.sheet, info.cellRange.row, info.cellRange.rowCount);
+                });
             } else {
-                // const reg = new RegExp('^([-]?)\\d+(\\.\\d{0,3})?$');
-                const reg = new RegExp('^([-]?)\\d+$');
-                if (validText !== null && (!reg.test(validText))) {
-                    // toastr.error('粘贴的工程量小数位数不能大于3位');
-                    // SpreadJsObj.reLoadRowData(info.sheet, info.cellRange.row, info.cellRange.rowCount);
-                    // return;
-                    validText = ZhCalc.round(validText, 3);
-                }
-                plan_gcl = validText;
-                plan_tp = select.dgn_price && select.dgn_price !== 0 ? ZhCalc.round(ZhCalc.mul(validText, select.dgn_price), 0) : 0;
+                SpreadJsObj.reLoadRowData(info.sheet, info.cellRange.row, info.cellRange.rowCount);
             }
-            select[col.field] = validText;
-            const updateData = {
-                lid: select.ledger_id,
-                yearmonth,
-                plan_gcl,
-                plan_tp,
-            };
-            postData(window.location.pathname + '/save', {type: 'ledger_edit', postData: updateData}, function (result) {
-                if (mode === 'tp') {
-                    select[yearmonth + '_gcl'] = plan_gcl;
-                } else {
-                    select[yearmonth + '_tp'] = plan_tp;
-                }
-                const nodes = treeCalc.calculateParent(info.sheet.zh_tree, select);
-                const refreshNode = ledgerTree.loadPostData({update: nodes});
-                ledgerSpreadObj.refreshTree(info.sheet, refreshNode);
-            },function () {
-                select[col.field] = orgValue;
-                const nodes = treeCalc.calculateParent(info.sheet.zh_tree, select);
-                const refreshNode = ledgerTree.loadPostData({update: nodes});
-                ledgerSpreadObj.refreshTree(info.sheet, refreshNode);
-            })
         },
     };
 
@@ -352,11 +403,14 @@ $(function () {
     $('.mode-select').on('click', function () {
         const _self = $(this);
         const this_mode = _self.data('mode');
-        // 重新计算所有月份数据
+        // 重新计算所有未使用的月份数据
         const under_ledger = _.filter(ledgerTree.nodes, { is_leaf: true});
         const update_nodes = [];
+        const newMonthList = _.map(_.filter(scheduleMonth, function (item) {
+            return item.stage_gcl_used === 0 && item.stage_tp_used === 0;
+        }), 'yearmonth');
         for (const ul of under_ledger) {
-            for (const m of monthList) {
+            for (const m of newMonthList) {
                 if (ul[m+'_tp'] || ul[m+'_gcl']) {
                     update_nodes.push(ul);
                     break;
@@ -367,14 +421,14 @@ $(function () {
         if(update_nodes.length > 0) {
             for (const un of update_nodes) {
                 if (this_mode === mode.gcl) {
-                    for (const m of monthList) {
+                    for (const m of newMonthList) {
                         if(un[m+'_tp']) {
                             un[m+'_tp'] = un.dgn_price && un.dgn_price !== 0 ? ZhCalc.round(ZhCalc.mul(un[m+'_gcl'], un.dgn_price), 0) : 0;
                             update_under_ledger.push({lid: un.ledger_id, yearmonth: m, plan_tp: un[m+'_tp'], plan_gcl: un[m+'_gcl']});
                         }
                     }
                 } else {
-                    for (const m of monthList) {
+                    for (const m of newMonthList) {
                         if(un[m+'_gcl']) {
                             un[m+'_gcl'] = un.dgn_price && un.dgn_price !== 0 ? ZhCalc.round(ZhCalc.div(un[m+'_tp'], un.dgn_price), 3) : 0;
                             update_under_ledger.push({lid: un.ledger_id, yearmonth: m, plan_tp: un[m+'_tp'], plan_gcl: un[m+'_gcl']});
@@ -383,10 +437,12 @@ $(function () {
                 }
             }
         }
-        console.log(update_under_ledger);
+
         postData(window.location.pathname + '/save', {type: 'mode', postData: {mode: this_mode, update_under_ledger}}, function (result) {
             _self.addClass('disabled').attr('disabled', true);
+            _self.text('当前');
             _self.parents('.col-6').siblings('.col-6').find('button').removeClass('disabled').removeAttr('disabled');
+            _self.parents('.col-6').siblings('.col-6').find('button').text('选择');
             $('#mode-tips').show();
             $('#mode-cancel').show();
             $('#mode').modal('hide');
@@ -441,7 +497,7 @@ $(function () {
             const hadmonth = [];
             for (const m of addMonthList) {
                 const one = _.find(scheduleMonth, { yearmonth: m });
-                console.log(one, m);
+
                 if (one) {
                     hadmonth.push(m);
                 }

+ 242 - 107
app/public/js/schedule_stage_gcl.js

@@ -24,6 +24,14 @@ $(function () {
         fullPath: 'full_path',
         calcFun: function (node) {
             node.dgn_price = ZhCalc.round(ZhCalc.div(node.total_price, node.dgn_qty1), 2);
+            if (node.children && node.children.length > 0) {
+                for (const sm of scheduleMonth) {
+                    if (sm.stage_gcl_used === 1) {
+                        node[sm.yearmonth + '_plan_gcl'] = ZhCalc.round(ZhCalc.div(node[sm.yearmonth + '_plan_tp'], node.dgn_price), 3);
+                        node[sm.yearmonth + '_sj_gcl'] = ZhCalc.round(ZhCalc.div(node[sm.yearmonth + '_sj_tp'], node.dgn_price), 3);
+                    }
+                }
+            }
         }
         //treeCacheKey: 'ledger_bills_fold' + '_' + getTenderId(),
         // markFoldKey: 'bills-fold',
@@ -49,7 +57,7 @@ $(function () {
         font: '12px 微软雅黑',
         // readOnly: true,
         localCache: {
-            key: 'ledger-bills',
+            key: 'ledger-gcl',
             colWidth: true,
         }
     };
@@ -92,6 +100,8 @@ $(function () {
     SpreadJsObj.initSpreadSettingEvents(ledgerSpreadSetting, ledgerCol);
     SpreadJsObj.initSheet(ledgerSpread.getActiveSheet(), ledgerSpreadSetting);
     SpreadJsObj.selChangedRefreshBackColor(ledgerSpread.getActiveSheet());
+    ledgerSpread.getActiveSheet().frozenColumnCount(6);
+    ledgerSpread.getActiveSheet().options.frozenlineColor = '#93b5e4';
 
     postData('/tender/' + getTenderId() + '/schedule/ledger/load', {}, function (data) {
         // let treeData = [];
@@ -198,6 +208,28 @@ $(function () {
                 }
             });
         },
+        reCalcHuizong: function(row, yearmonth, orgValue, validText) {
+            // 下方汇总sjs实时更新计量数据
+            // 先判断当前修改月份与汇总sjs月份是否一致或更大,获取修改的对应台账行
+            const huizongSelect = SpreadJsObj.getRowObject(huizongSpread.getActiveSheet(), row);
+            if (yearmonth.split('-')[0] === curScheduleMonth.yearmonth.split('-')[0]) {
+                console.log(huizongSelect, row);
+                huizongSelect.year_sj_gcl = huizongSelect.year_sj_gcl ? ZhCalc.add(ZhCalc.sub(huizongSelect.year_sj_gcl, orgValue), validText) : validText;
+                huizongSelect.year_sj_tp = huizongSelect.dgn_price && huizongSelect.dgn_price !== 0 ? ZhCalc.round(ZhCalc.mul(huizongSelect.year_sj_gcl, huizongSelect.dgn_price), 0) : 0;
+            }
+            if (yearmonth === curScheduleMonth.yearmonth) {
+                // 判断是否同年同月,是则修改本年,截至本月,本月计量数据,否则只修改截至本月计量数据
+                huizongSelect.sj_gcl = validText;
+                huizongSelect.sj_tp = huizongSelect.dgn_price && huizongSelect.dgn_price !== 0 ? ZhCalc.round(ZhCalc.mul(huizongSelect.sj_gcl, huizongSelect.dgn_price), 0) : 0;
+            }
+            if (yearmonth <= curScheduleMonth.yearmonth) {
+                huizongSelect.end_sj_gcl = huizongSelect.end_sj_gcl ? ZhCalc.add(ZhCalc.sub(huizongSelect.end_sj_gcl, orgValue), validText) : validText;
+                huizongSelect.end_sj_tp = huizongSelect.dgn_price && huizongSelect.dgn_price !== 0 ? ZhCalc.round(ZhCalc.mul(huizongSelect.end_sj_gcl, huizongSelect.dgn_price), 0) : 0;
+            }
+            const nodes = treeCalc.calculateParent(huizongSpread.getActiveSheet().zh_tree, huizongSelect);
+            const refreshNode = huizongTree.loadPostData({update: nodes});
+            huizongObj.refreshTree(huizongSpread.getActiveSheet(), refreshNode);
+        },
         editEnded: function (e, info) {
             if (info.sheet.zh_setting) {
                 const select = SpreadJsObj.getSelectObject(info.sheet);
@@ -237,6 +269,7 @@ $(function () {
                     const nodes = treeCalc.calculateParent(info.sheet.zh_tree, select);
                     const refreshNode = ledgerTree.loadPostData({update: nodes});
                     ledgerSpreadObj.refreshTree(info.sheet, refreshNode);
+                    ledgerSpreadObj.reCalcHuizong(info.row, yearmonth, orgValue, validText);
                 },function () {
                     select[col.field] = orgValue;
                     const nodes = treeCalc.calculateParent(info.sheet.zh_tree, select);
@@ -246,63 +279,123 @@ $(function () {
             }
         },
         deletePress: function (sheet) {
-            return;
+            if (!sheet.zh_setting) return;
+            if (sheet.zh_setting && sheet.zh_tree) {
+                const sel = sheet.getSelections()[0], datas = [], filterNodes = [], huizongNodes = [];
+                if (!sel) return;
+                for (let iRow = sel.row; iRow < sel.row + sel.rowCount; iRow++) {
+                    let bDel = false;
+                    const node = sheet.zh_tree.nodes[iRow];
+                    for (let iCol = sel.col; iCol < sel.col + sel.colCount; iCol++) {
+                        const col = sheet.zh_setting.cols[iCol];
+                        const orgValue = node[col.field];
+                        if (!orgValue) {
+                            continue;
+                        }
+                        const yearmonth = col.field.split('_')[0];
+                        if(col.readOnly === true || !node.is_leaf) {
+                            continue;
+                        }
+                        const updateData = {
+                            lid: node.ledger_id,
+                            yearmonth,
+                            sj_gcl: null,
+                            sj_tp: null,
+                        };
+                        datas.push(updateData);
+                        huizongNodes.push({row: iRow, yearmonth, orgValue, nowValue: null});
+                        node[yearmonth+'_sj_gcl'] = null;
+                        node[yearmonth+'_sj_tp'] = null;
+                        bDel = true;
+                    }
+                    if (bDel) filterNodes.push(node);
+                }
+                if (datas.length > 0) {
+                    postData(window.location.pathname + '/save', {type: 'ledger_paste', postData: datas}, function (result) {
+                        for (const uul of filterNodes) {
+                            const nodes = treeCalc.calculateParent(sheet.zh_tree, uul);
+                            const refreshNode = ledgerTree.loadPostData({update: nodes});
+                            ledgerSpreadObj.refreshTree(sheet, refreshNode);
+                        }
+                        for (const hz of huizongNodes) {
+                            ledgerSpreadObj.reCalcHuizong(hz.row, hz.yearmonth, hz.orgValue, hz.nowValue);
+                        }
+                        SpreadJsObj.reLoadSheetData(sheet);
+                    }, function () {
+                        SpreadJsObj.reLoadSheetData(sheet);
+                    });
+                }
+            }
         },
         clipboardPasted(e, info) {
             const hint = {
                 cellError: {type: 'error', msg: '粘贴内容超出了表格范围'},
                 numberExpr: {type: 'error', msg: '不能粘贴其它非数字类型字符'},
             };
-            const range = info.cellRange;
-            if (range.rowCount > 1 || range.colCount > 1) {
-                toastMessageUniq(hint.cellError);
-                SpreadJsObj.reLoadSheetHeader(ledgerSpread.getActiveSheet());
-                SpreadJsObj.reLoadSheetData(ledgerSpread.getActiveSheet());
-                return;
-            }
-            const select = SpreadJsObj.getSelectObject(info.sheet);
-            const col = info.sheet.zh_setting.cols[range.col];
-            let validText = is_numeric(info.pasteData.text) ? parseFloat(info.pasteData.text) : (info.pasteData.text ? trimInvalidChar(info.pasteData.text) : null);
-            const orgValue = select[col.field];
-            if (orgValue == validText || ((!orgValue || orgValue === '') && (validText === ''))) {
-                SpreadJsObj.reLoadRowData(info.sheet, info.row);
-                return;
+            const tree = info.sheet.zh_tree;
+            if (!tree) { return; }
+            const sortData = info.sheet.zh_tree.nodes;
+            const datas = [], filterNodes = [], huizongNodes = [];
+
+            for (let iRow = 0; iRow < info.cellRange.rowCount; iRow ++) {
+                let bPaste = false;
+                const curRow = info.cellRange.row + iRow;
+                const node = sortData[curRow];
+                if (node) {
+                    for (let iCol = 0; iCol < info.cellRange.colCount; iCol++) {
+                        const curCol = info.cellRange.col + iCol;
+                        const colSetting = info.sheet.zh_setting.cols[curCol];
+                        let validText = is_numeric(info.sheet.getText(curRow, curCol)) ? parseFloat(info.sheet.getText(curRow, curCol)) : (info.sheet.getText(curRow, curCol) ? trimInvalidChar(info.sheet.getText(curRow, curCol)) : null);
+                        const orgValue = node[colSetting.field];
+                        if (orgValue == validText || ((!orgValue || orgValue === '') && (validText === ''))) {
+                            continue;
+                        }
+                        if (isNaN(validText)) {
+                            toastMessageUniq(hint.numberExpr);
+                            continue;
+                        }
+                        const yearmonth = colSetting.field.split('_')[0];
+                        // 判断输入位数,提示
+                        const reg = new RegExp('^([-]?)\\d+$');
+                        if (validText !== null && (!reg.test(validText))) {
+                            validText = ZhCalc.round(validText, 3);
+                        }
+                        const sj_gcl = validText;
+                        const sj_tp = node.dgn_price && node.dgn_price !== 0 ? ZhCalc.round(ZhCalc.mul(validText, node.dgn_price), 0) : 0;
+                        const updateData = {
+                            lid: node.ledger_id,
+                            yearmonth,
+                            sj_gcl,
+                            sj_tp,
+                        };
+                        datas.push(updateData);
+                        huizongNodes.push({row: info.cellRange.row + iRow, yearmonth, orgValue, nowValue: validText});
+                        node[yearmonth+'_sj_gcl'] = sj_gcl;
+                        node[yearmonth+'_sj_tp'] = sj_tp;
+                        bPaste = true;
+                    }
+                    if (bPaste) {
+                        filterNodes.push(node);
+                    }
+                }
             }
-            if (isNaN(validText)) {
-                toastr.error('不能粘贴其它非数字类型字符');
+            if (datas.length > 0) {
+                postData(window.location.pathname + '/save', {type: 'ledger_paste', postData: datas}, function (result) {
+                    for (const uul of filterNodes) {
+                        const nodes = treeCalc.calculateParent(info.sheet.zh_tree, uul);
+                        const refreshNode = ledgerTree.loadPostData({update: nodes});
+                        ledgerSpreadObj.refreshTree(info.sheet, refreshNode);
+                    }
+                    for (const hz of huizongNodes) {
+                        ledgerSpreadObj.reCalcHuizong(hz.row, hz.yearmonth, hz.orgValue, hz.nowValue);
+                    }
+                    SpreadJsObj.reLoadSheetData(info.sheet);
+                }, function () {
+                    SpreadJsObj.reLoadRowData(info.sheet, info.cellRange.row, info.cellRange.rowCount);
+                });
+            } else {
                 SpreadJsObj.reLoadRowData(info.sheet, info.cellRange.row, info.cellRange.rowCount);
-                return;
             }
-            const yearmonth = col.field.split('_')[0];
-            // 判断输入位数,提示
-            const reg = new RegExp('^([-]?)\\d+$');
-            if (validText !== null && (!reg.test(validText))) {
-                // toastr.error('粘贴的工程量小数位数不能大于3位');
-                // SpreadJsObj.reLoadRowData(info.sheet, info.cellRange.row, info.cellRange.rowCount);
-                // return;
-                validText = ZhCalc.round(validText, 3);
-            }
-            const sj_gcl = validText;
-            const sj_tp = select.dgn_price && select.dgn_price !== 0 ? ZhCalc.round(ZhCalc.mul(validText, select.dgn_price), 0) : 0;
-            select[col.field] = validText;
-            const updateData = {
-                lid: select.ledger_id,
-                yearmonth,
-                sj_gcl,
-                sj_tp,
-            };
-            console.log(updateData);
-            postData(window.location.pathname + '/save', {type: 'ledger_edit', postData: updateData}, function (result) {
-                select[yearmonth + '_sj_tp'] = sj_tp;
-                const nodes = treeCalc.calculateParent(info.sheet.zh_tree, select);
-                const refreshNode = ledgerTree.loadPostData({update: nodes});
-                ledgerSpreadObj.refreshTree(info.sheet, refreshNode);
-            },function () {
-                select[col.field] = orgValue;
-                const nodes = treeCalc.calculateParent(info.sheet.zh_tree, select);
-                const refreshNode = ledgerTree.loadPostData({update: nodes});
-                ledgerSpreadObj.refreshTree(info.sheet, refreshNode);
-            })
         },
     };
 
@@ -322,8 +415,17 @@ $(function () {
         fullPath: 'full_path',
         calcFun: function (node) {
             node.dgn_price = ZhCalc.round(ZhCalc.div(node.total_price, node.dgn_qty1), 2);
+            if (node.children && node.children.length > 0) {
+                node.end_plan_gcl = ZhCalc.round(ZhCalc.div(node.end_plan_tp, node.dgn_price), 3);
+                node.year_plan_gcl = ZhCalc.round(ZhCalc.div(node.year_plan_tp, node.dgn_price), 3);
+                node.plan_gcl = ZhCalc.round(ZhCalc.div(node.plan_tp, node.dgn_price), 3);
+                node.next_plan_gcl = ZhCalc.round(ZhCalc.div(node.next_plan_tp, node.dgn_price), 3);
+                node.end_sj_gcl = ZhCalc.round(ZhCalc.div(node.end_sj_tp, node.dgn_price), 3);
+                node.year_sj_gcl = ZhCalc.round(ZhCalc.div(node.year_sj_tp, node.dgn_price), 3);
+                node.sj_gcl = ZhCalc.round(ZhCalc.div(node.sj_tp, node.dgn_price), 3);
+            }
         },
-        calcFields: ['plan_tp', 'next_plan_tp', 'end_plan_tp', 'year_plan_tp', 'total_price', 'end_gather_tp', 'year_gather_tp', 'gather_tp'],
+        calcFields: ['plan_tp', 'next_plan_tp', 'end_plan_tp', 'year_plan_tp', 'total_price', 'end_sj_tp', 'year_sj_tp', 'sj_tp'],
     };
 
     const huizong_static_cols = [
@@ -335,14 +437,16 @@ $(function () {
         {title: '|金额(元)', colSpan: '|1', rowSpan: '|1', field: 'total_price', hAlign: 2, width: 70, type: 'Number', readOnly: true},
         {title: '自开工至本月计划完成|工程量', colSpan: '2|1', rowSpan: '1|1', field: 'end_plan_gcl', hAlign: 2, width: 70, type: 'Number', readOnly: true},
         {title: '|金额(元)', colSpan: '|1', rowSpan: '|1', field: 'end_plan_tp', hAlign: 2, width: 70, type: 'Number', readOnly: true},
-        {title: '截止本月完成计量|工程量', colSpan: '2|1', rowSpan: '1|1', field: 'end_gather_qty', hAlign: 2, width: 70, type: 'Number', readOnly: true},
-        {title: '|金额(元)', colSpan: '|1', rowSpan: '|1', field: 'end_gather_tp', hAlign: 2, width: 70, type: 'Number', readOnly: true},
+        {title: '截止本月完成计量|工程量', colSpan: '2|1', rowSpan: '1|1', field: 'end_sj_gcl', hAlign: 2, width: 70, type: 'Number', readOnly: true},
+        {title: '|金额(元)', colSpan: '|1', rowSpan: '|1', field: 'end_sj_tp', hAlign: 2, width: 70, type: 'Number', readOnly: true},
         {title: '本年计划完成|工程量', colSpan: '2|1', rowSpan: '1|1', field: 'year_plan_gcl', hAlign: 2, width: 70, type: 'Number', readOnly: true},
         {title: '|金额(元)', colSpan: '|1', rowSpan: '|1', field: 'year_plan_tp', hAlign: 2, width: 70, type: 'Number', readOnly: true},
-        {title: '本年累计完成|工程量', colSpan: '2|1', rowSpan: '1|1', field: 'year_contract_qty', hAlign: 2, width: 70, type: 'Number', readOnly: true},
-        {title: '|金额(元)', colSpan: '|1', rowSpan: '|1', field: 'year_gather_tp', hAlign: 2, width: 70, type: 'Number', readOnly: true},
+        {title: '本年累计完成|工程量', colSpan: '2|1', rowSpan: '1|1', field: 'year_sj_gcl', hAlign: 2, width: 70, type: 'Number', readOnly: true},
+        {title: '|金额(元)', colSpan: '|1', rowSpan: '|1', field: 'year_sj_tp', hAlign: 2, width: 70, type: 'Number', readOnly: true},
         {title: '本月计划完成|工程量', colSpan: '2|1', rowSpan: '1|1', field: 'plan_gcl', hAlign: 2, width: 70, type: 'Number', readOnly: true},
         {title: '|金额(元)', colSpan: '|1', rowSpan: '|1', field: 'plan_tp', hAlign: 2, width: 70, type: 'Number', readOnly: true},
+        {title: '本月计量完成|工程量', colSpan: '2|1', rowSpan: '1|1', field: 'sj_gcl', hAlign: 2, width: 70, type: 'Number', readOnly: true},
+        {title: '|金额(元)', colSpan: '|1', rowSpan: '|1', field: 'sj_tp', hAlign: 2, width: 70, type: 'Number', readOnly: true},
         {title: '下月计划|工程量', colSpan: '2|1', rowSpan: '1|1', field: 'next_plan_gcl', hAlign: 2, width: 70, type: 'Number', readOnly: true},
         {title: '|金额(元)', colSpan: '|1', rowSpan: '|1', field: 'next_plan_tp', hAlign: 2, width: 70, type: 'Number', readOnly: true},
     ];
@@ -356,7 +460,7 @@ $(function () {
         font: '12px 微软雅黑',
         // readOnly: true,
         localCache: {
-            key: 'ledger-bills',
+            key: 'ledger-huizong',
             colWidth: true,
         }
     };
@@ -366,18 +470,68 @@ $(function () {
     if (thousandth) sjsSettingObj.setTpThousandthFormat(huizongSpreadSetting);
     SpreadJsObj.initSheet(huizongSpread.getActiveSheet(), huizongSpreadSetting);
     SpreadJsObj.selChangedRefreshBackColor(huizongSpread.getActiveSheet());
+    huizongSpread.getActiveSheet().frozenColumnCount(6);
+    huizongSpread.getActiveSheet().options.frozenlineColor = '#93b5e4';
+
+    let huizongTree = '';
 
     const huizongObj = {
+        refreshTree: function (sheet, data) {
+            SpreadJsObj.massOperationSheet(sheet, function () {
+                const tree = sheet.zh_tree;
+                // 处理删除
+                if (data.delete) {
+                    data.delete.sort(function (x, y) {
+                        return y.deleteIndex - x.deleteIndex;
+                    });
+                    for (const d of data.delete) {
+                        sheet.deleteRows(d.deleteIndex, 1);
+                    }
+                }
+                // 处理新增
+                if (data.create) {
+                    const newNodes = data.create;
+                    if (newNodes) {
+                        newNodes.sort(function (a, b) {
+                            return a.index - b.index;
+                        });
+
+                        for (const node of newNodes) {
+                            sheet.addRows(node.index, 1);
+                            SpreadJsObj.reLoadRowData(sheet, tree.nodes.indexOf(node), 1);
+                        }
+                    }
+                }
+                // 处理更新
+                if (data.update) {
+                    const rows = [];
+                    for (const u of data.update) {
+                        rows.push(tree.nodes.indexOf(u));
+                    }
+                    SpreadJsObj.reLoadRowsData(sheet, rows);
+                }
+                // 处理展开
+                if (data.expand) {
+                    const expanded = [];
+                    for (const e of data.expand) {
+                        if (expanded.indexOf(e) === -1) {
+                            const posterity = tree.getPosterity(e);
+                            for (const p of posterity) {
+                                sheet.setRowVisible(tree.nodes.indexOf(p), p.visible);
+                                expanded.push(p);
+                            }
+                        }
+                    }
+                }
+            });
+        },
         setSjs: function (order) {
-            postData('/tender/' + getTenderId() + '/schedule/stage/' + order + '/load', {filter: 'gcl' }, function (data) {
-                const calcList = ['year_gather_tp',
-                    'contract_tp', 'qc_qty', 'qc_tp', 'total_price', 'contract_tp', 'qc_tp', 'gather_tp',
-                    'pre_contract_tp', 'pre_qc_tp', 'pre_gather_tp', 'end_contract_tp', 'end_qc_tp', 'end_gather_tp', 'end_correct_tp'];
+            postData('/tender/' + getTenderId() + '/schedule/stage/gcl/' + order + '/load', {}, function (data) {
+                const calcList = ['total_price'];
                 const showList = ['plan_gcl', 'plan_tp', 'next_plan_gcl', 'next_plan_tp', 'end_plan_gcl', 'end_plan_tp', 'year_plan_gcl', 'year_plan_tp',
-                    'year_contract_qty', 'year_gather_tp',
-                    'contract_qty', 'end_gather_qty', 'contract_tp', 'qc_qty', 'qc_tp', 'ledger_id', 'ledger_pid', 'order', 'level', 'tender_id', 'full_path',
-                    'code', 'name', 'unit', 'dgn_qty1', 'dgn_qty2', 'dgn_price', 'quantity', 'total_price', 'contract_tp', 'qc_tp', 'gather_tp',
-                    'pre_contract_tp', 'pre_qc_tp', 'pre_gather_tp', 'end_contract_tp', 'end_qc_tp', 'end_gather_tp', 'end_correct_tp'];
+                    'sj_gcl', 'sj_tp', 'year_sj_gcl', 'year_sj_tp', 'end_sj_gcl', 'end_sj_tp',
+                    'ledger_id', 'ledger_pid', 'order', 'level', 'tender_id', 'full_path',
+                    'code', 'name', 'unit', 'dgn_qty1', 'dgn_qty2', 'dgn_price', 'quantity', 'total_price'];
                 const baseLedgerTreeSetting = {
                     id: 'ledger_id',
                     pid: 'ledger_pid',
@@ -387,38 +541,16 @@ $(function () {
                     fullPath: 'full_path',
                     calcFields: calcList,
                 };
-                baseLedgerTreeSetting.updateFields = ['year_contract_qty', 'year_gather_tp', 'contract_qty', 'end_gather_qty', 'contract_tp', 'qc_qty', 'qc_tp', 'postil', 'used', 'contract_expr'];
+                // baseLedgerTreeSetting.updateFields = ['year_sj_qty', 'year_gather_tp', 'contract_qty', 'end_gather_qty', 'contract_tp', 'qc_qty', 'qc_tp', 'postil', 'used', 'contract_expr'];
                 baseLedgerTreeSetting.calcFun = function (node) {
-                    if (!node.children || node.children.length === 0) {
-                        node.pre_gather_qty = ZhCalc.add(node.pre_contract_qty, node.pre_qc_qty);
-                        node.gather_qty = ZhCalc.add(node.contract_qty, node.qc_qty);
-                        node.end_contract_qty = ZhCalc.add(node.pre_contract_qty, node.contract_qty);
-                        node.end_qc_qty = ZhCalc.add(node.pre_qc_qty, node.qc_qty);
-                        node.end_gather_qty = ZhCalc.add(node.pre_gather_qty, node.gather_qty);
-                    }
-                    node.pre_gather_tp = ZhCalc.add(node.pre_contract_tp, node.pre_qc_tp);
-                    node.gather_tp = ZhCalc.add(node.contract_tp, node.qc_tp);
-                    node.end_contract_tp = ZhCalc.add(node.pre_contract_tp, node.contract_tp);
-                    node.end_qc_tp = ZhCalc.add(node.pre_qc_tp, node.qc_tp);
-                    node.end_gather_tp = ZhCalc.add(node.pre_gather_tp, node.gather_tp);
-                    node.end_final_tp = ZhCalc.add(node.end_qc_tp, node.total_price);
-                    if (!node.children || node.children.length === 0) {
-                        if (node.end_contract_qty) {
-                            node.end_correct_tp = ZhCalc.add(node.end_qc_tp, ZhCalc.mul(node.end_contract_qty, node.unit_price, tenderInfo.decimal.tp));
-                        } else {
-                            node.end_correct_tp = node.end_gather_tp;
-                        }
-                    }
-                    node.end_gather_percent = ZhCalc.mul(ZhCalc.div(node.end_gather_tp, node.end_final_tp), 100, 2);
-                    node.end_correct_percent = ZhCalc.mul(ZhCalc.div(node.end_correct_tp, node.end_final_tp), 100, 2);
-                    node.final_dgn_price = ZhCalc.round(ZhCalc.div(node.end_gather_tp, ZhCalc.add(node.deal_dgn_qty1, node.c_dgn_qty1)), tenderInfo.decimal.up);
                     node.dgn_price = ZhCalc.round(ZhCalc.div(node.total_price, node.dgn_qty1), 2);
                 };
                 const baseLedgerTree = createNewPathTree('base', baseLedgerTreeSetting);
-                const newLedgerList = setTpMonthToLedger(data.ledgerData, data.slmList, data.nextSlmList, data.endSlmList, data.yearSlmList, data.curYearStageData);
+                console.log(data.slmList);
+                const newLedgerList = setGclMonthToLedger(data.ledgerData, data.slmList, data.nextSlmList, data.endSlmList, data.yearSlmList);
                 baseLedgerTree.loadDatas(newLedgerList);
                 treeCalc.calculateAll(baseLedgerTree);
-                const huizongTree = createNewPathTree('filter', huizongTreeSetting);
+                huizongTree = createNewPathTree('filter', huizongTreeSetting);
                 for (const d of baseLedgerTree.nodes) {
                     if (!d.b_code) {
                         const one = _.find(selectedLedgerList, function (item) {
@@ -433,26 +565,29 @@ $(function () {
                 treeCalc.calculateAll(huizongTree);
                 console.log(huizongTree);
                 SpreadJsObj.loadSheetData(huizongSpread.getActiveSheet(), SpreadJsObj.DataType.Tree, huizongTree);
+                // huizongSpread.refresh();
             }, null, true);
         },
         dropDownHtml: function (order) {
             let html = '';
-            for (const ss of scheduleStage) {
-                if (ss.order !== order) {
-                    html += '<a class="dropdown-item change-tp" data-order="'+ ss.order +'" href="javascript:void(0);">'+ ss.yearmonth.split('-')[0] + '年' + parseInt(ss.yearmonth.split('-')[1]) +'月(第'+ ss.order +'期)</a>';
+            for (const ss of gclScheduleMonth) {
+                if (ss.id !== order) {
+                    html += '<a class="dropdown-item change-tp" data-order="'+ ss.id +'" href="javascript:void(0);">'+ ss.yearmonth.split('-')[0] + '年' + parseInt(ss.yearmonth.split('-')[1]) +'月</a>';
                 } else {
-                    $('#stageDropdownMenuButton').text(ss.yearmonth.split('-')[0] + '年' + parseInt(ss.yearmonth.split('-')[1]) +'月(第'+ ss.order +'期)');
+                    $('#stageDropdownMenuButton').text(ss.yearmonth.split('-')[0] + '年' + parseInt(ss.yearmonth.split('-')[1]) +'月');
                 }
             }
             $('#stageDropdownMenu').html(html);
         }
     };
 
-    if (curScheduleStage && curScheduleStage.order) {
-        let order = parseInt(localStorage.getItem('tender_' + getTenderId() + '_schedule_tp_sjs') ? localStorage.getItem('tender_' + getTenderId() + '_schedule_tp_sjs') : curScheduleStage.order);
-        const ssinfo = _.find(scheduleStage, { order });
+    if (curScheduleMonth && curScheduleMonth.id) {
+        let order = parseInt(localStorage.getItem('tender_' + getTenderId() + '_schedule_tp_sjs') ? localStorage.getItem('tender_' + getTenderId() + '_schedule_tp_sjs') : curScheduleMonth.id);
+        const ssinfo = _.find(gclScheduleMonth, { id: order });
         if (!ssinfo) {
-            order = curScheduleStage.order;
+            order = curScheduleMonth.id;
+        } else {
+            curScheduleMonth = ssinfo;
         }
         huizongObj.dropDownHtml(order);
         huizongObj.setSjs(order);
@@ -464,6 +599,7 @@ $(function () {
         huizongObj.dropDownHtml(order);
         huizongObj.setSjs(order);
         localStorage.setItem('tender_' + getTenderId() + '_schedule_tp_sjs', order);
+        curScheduleMonth = _.find(gclScheduleMonth, { id: order });
     });
 
     // 月份添加
@@ -475,6 +611,7 @@ $(function () {
         }
         const _self = $(this);
         postData(window.location.pathname + '/save', {type: 'add_stage', postData: { id: id }}, function (result) {
+            localStorage.setItem('tender_' + getTenderId() + '_schedule_tp_sjs', id);
             _self.addClass('disabled').attr('disabled', true);
             toastr.success('创建成功');
             setTimeout(function () {
@@ -509,6 +646,7 @@ $(function () {
         }
         const _self = $(this);
         postData(window.location.pathname + '/save', {type: 'del_stage', postData: selectedMonth}, function (result) {
+            localStorage.removeItem('tender_' + getTenderId() + '_schedule_tp_sjs');
             _self.addClass('disabled').attr('disabled', true);
             toastr.success('删除成功');
             setTimeout(function () {
@@ -575,13 +713,15 @@ function setMonthToLedger(ledgerList, slm) {
     }
     return ledgerList;
 }
-function setTpMonthToLedger(ledgerList, slm, nextSlm, endSlm, yearSlm, yearLedgerData) {
+function setGclMonthToLedger(ledgerList, slm, nextSlm, endSlm, yearSlm) {
     if (slm.length > 0) {
         for(const s of slm) {
             const index = _.findIndex(ledgerList, { 'ledger_id': s.lid });
             if (index && index !== -1) {
                 ledgerList[index].plan_tp = s.plan_tp;
                 ledgerList[index].plan_gcl = s.plan_gcl;
+                ledgerList[index].sj_tp = s.sj_tp;
+                ledgerList[index].sj_gcl = s.sj_gcl;
             }
         }
     }
@@ -600,6 +740,8 @@ function setTpMonthToLedger(ledgerList, slm, nextSlm, endSlm, yearSlm, yearLedge
             if (index && index !== -1) {
                 ledgerList[index].end_plan_tp = es.plan_tp;
                 ledgerList[index].end_plan_gcl = es.plan_gcl;
+                ledgerList[index].end_sj_tp = es.sj_tp;
+                ledgerList[index].end_sj_gcl = es.sj_gcl;
             }
         }
     }
@@ -609,15 +751,8 @@ function setTpMonthToLedger(ledgerList, slm, nextSlm, endSlm, yearSlm, yearLedge
             if (index && index !== -1) {
                 ledgerList[index].year_plan_tp = ys.plan_tp;
                 ledgerList[index].year_plan_gcl = ys.plan_gcl;
-            }
-        }
-    }
-    if (yearLedgerData.length > 0) {
-        for (const yl of yearLedgerData) {
-            const index = _.findIndex(ledgerList, {'id': yl.lid});
-            if (index && index !== -1) {
-                ledgerList[index].year_contract_qty = ZhCalc.add(yl.contract_qty, yl.qc_qty);
-                ledgerList[index].year_gather_tp = ZhCalc.add(yl.contract_tp, yl.qc_tp);
+                ledgerList[index].year_sj_tp = ys.sj_tp;
+                ledgerList[index].year_sj_gcl = ys.sj_gcl;
             }
         }
     }

+ 34 - 10
app/public/js/schedule_stage_tp.js

@@ -24,8 +24,17 @@ $(function () {
         fullPath: 'full_path',
         calcFun: function (node) {
             node.dgn_price = ZhCalc.round(ZhCalc.div(node.total_price, node.dgn_qty1), 2);
+            if (node.children && node.children.length > 0) {
+                node.end_plan_gcl = ZhCalc.round(ZhCalc.div(node.end_plan_tp, node.dgn_price), 3);
+                node.year_plan_gcl = ZhCalc.round(ZhCalc.div(node.year_plan_tp, node.dgn_price), 3);
+                node.plan_gcl = ZhCalc.round(ZhCalc.div(node.plan_tp, node.dgn_price), 3);
+                node.next_plan_gcl = ZhCalc.round(ZhCalc.div(node.next_plan_tp, node.dgn_price), 3);
+            }
+            node.end_gather_qty = ZhCalc.round(ZhCalc.div(node.end_gather_tp, node.dgn_price), 3);
+            node.year_contract_qty = ZhCalc.round(ZhCalc.div(node.year_gather_tp, node.dgn_price), 3);
+            node.contract_qty = ZhCalc.round(ZhCalc.div(node.gather_tp, node.dgn_price), 3);
         },
-        calcFields: ['plan_tp', 'next_plan_tp', 'end_plan_tp', 'year_plan_tp', 'total_price', 'end_gather_tp', 'year_gather_tp', 'gather_tp'],
+        calcFields: ['plan_tp', 'next_plan_tp', 'end_plan_tp', 'year_plan_tp', 'total_price', 'end_gather_tp', 'year_gather_tp', 'gather_tp', 'end_final_gather_tp'],
     };
     const ledgerTree = createNewPathTree('filter', treeSetting);
 
@@ -61,7 +70,7 @@ $(function () {
         font: '12px 微软雅黑',
         // readOnly: true,
         localCache: {
-            key: 'ledger-bills',
+            key: 'ledger-tp',
             colWidth: true,
         }
     };
@@ -71,17 +80,21 @@ $(function () {
     if (thousandth) sjsSettingObj.setTpThousandthFormat(ledgerSpreadSetting);
     SpreadJsObj.initSheet(ledgerSpread.getActiveSheet(), ledgerSpreadSetting);
     SpreadJsObj.selChangedRefreshBackColor(ledgerSpread.getActiveSheet());
+    ledgerSpread.getActiveSheet().frozenColumnCount(6);
+    ledgerSpread.getActiveSheet().options.frozenlineColor = '#93b5e4';
 
     if (curScheduleStage && curScheduleStage.order) {
         postData('/tender/' + getTenderId() + '/schedule/stage/' + curScheduleStage.order + '/load', {}, function (data) {
             const calcList = ['year_gather_tp',
                 'contract_tp', 'qc_qty', 'qc_tp', 'total_price', 'contract_tp', 'qc_tp', 'gather_tp',
-                'pre_contract_tp', 'pre_qc_tp', 'pre_gather_tp', 'end_contract_tp', 'end_qc_tp', 'end_gather_tp', 'end_correct_tp'];
+                'pre_contract_tp', 'pre_qc_tp', 'pre_gather_tp', 'end_contract_tp', 'end_qc_tp', 'end_gather_tp', 'end_correct_tp',
+                'final_contract_tp', 'final_qc_tp', 'pre_final_contract_tp', 'pre_final_qc_tp', 'pre_final_gather_tp', 'final_gather_tp', 'end_final_gather_tp'];
             const showList = ['plan_gcl', 'plan_tp', 'next_plan_gcl', 'next_plan_tp', 'end_plan_gcl', 'end_plan_tp', 'year_plan_gcl', 'year_plan_tp',
                 'year_contract_qty', 'year_gather_tp',
                 'contract_qty', 'end_gather_qty', 'contract_tp', 'qc_qty', 'qc_tp', 'ledger_id', 'ledger_pid', 'order', 'level', 'tender_id', 'full_path',
                 'code', 'name', 'unit', 'dgn_qty1', 'dgn_qty2', 'dgn_price', 'quantity', 'total_price', 'contract_tp', 'qc_tp', 'gather_tp',
-                'pre_contract_tp', 'pre_qc_tp', 'pre_gather_tp', 'end_contract_tp', 'end_qc_tp', 'end_gather_tp', 'end_correct_tp'];
+                'pre_contract_tp', 'pre_qc_tp', 'pre_gather_tp', 'end_contract_tp', 'end_qc_tp', 'end_gather_tp', 'end_correct_tp',
+                'final_contract_tp', 'final_qc_tp', 'pre_final_contract_tp', 'pre_final_qc_tp', 'pre_final_gather_tp', 'final_gather_tp', 'end_final_gather_tp'];
             const baseLedgerTreeSetting = {
                 id: 'ledger_id',
                 pid: 'ledger_pid',
@@ -91,7 +104,7 @@ $(function () {
                 fullPath: 'full_path',
                 calcFields: calcList,
             };
-            baseLedgerTreeSetting.updateFields = ['year_contract_qty', 'year_gather_tp', 'contract_qty', 'end_gather_qty', 'contract_tp', 'qc_qty', 'qc_tp', 'postil', 'used', 'contract_expr'];
+            baseLedgerTreeSetting.updateFields = ['year_contract_qty', 'year_gather_tp', 'contract_qty', 'end_gather_qty', 'contract_tp', 'qc_qty', 'qc_tp', 'final_contract_tp', 'final_qc_tp', 'postil', 'used', 'contract_expr'];
             baseLedgerTreeSetting.calcFun = function (node) {
                 if (!node.children || node.children.length === 0) {
                     node.pre_gather_qty = ZhCalc.add(node.pre_contract_qty, node.pre_qc_qty);
@@ -117,12 +130,15 @@ $(function () {
                 node.end_correct_percent = ZhCalc.mul(ZhCalc.div(node.end_correct_tp, node.end_final_tp), 100, 2);
                 node.final_dgn_price = ZhCalc.round(ZhCalc.div(node.end_gather_tp, ZhCalc.add(node.deal_dgn_qty1, node.c_dgn_qty1)), tenderInfo.decimal.up);
                 node.dgn_price = ZhCalc.round(ZhCalc.div(node.total_price, node.dgn_qty1), 2);
+
+                node.pre_final_gather_tp = ZhCalc.add(node.pre_final_contract_tp, node.pre_final_qc_tp);
+                node.final_gather_tp = ZhCalc.add(node.final_contract_tp, node.final_qc_tp);
+                node.end_final_gather_tp = ZhCalc.add(node.pre_final_gather_tp, node.final_gather_tp);
             };
             const baseLedgerTree = createNewPathTree('base', baseLedgerTreeSetting);
             const newLedgerList = setMonthToLedger(data.ledgerData, slmList, nextSlmList, endSlmList, yearSlmList, curYearStageData);
             baseLedgerTree.loadDatas(newLedgerList);
             treeCalc.calculateAll(baseLedgerTree);
-            console.log(baseLedgerTree);
             for (const d of baseLedgerTree.nodes) {
                 if (!d.b_code) {
                     const one = _.find(selectedLedgerList, function (item) {
@@ -136,6 +152,7 @@ $(function () {
             ledgerTree.sortTreeNode(true);
             treeCalc.calculateAll(ledgerTree);
             calcGatherTpAndUpdate(ledgerTree.nodes);
+            console.log(ledgerTree);
             SpreadJsObj.loadSheetData(ledgerSpread.getActiveSheet(), SpreadJsObj.DataType.Tree, ledgerTree);
         }, null, true);
     }
@@ -212,15 +229,20 @@ $(function () {
 function calcGatherTpAndUpdate(tree) {
     if (tree.length > 0) {
         let total_tp = 0;
+        let final_total_tp = 0;
         for (const t of tree) {
             if (t.is_leaf && t.gather_tp) {
                 total_tp = ZhCalc.add(total_tp, t.gather_tp);
             }
+            if (t.is_leaf && t.end_final_gather_tp) {
+                final_total_tp = ZhCalc.add(final_total_tp, t.end_final_gather_tp);
+            }
         }
         console.log(curScheduleStage.tp, total_tp);
-        if (curScheduleStage.tp !== total_tp) {
+        console.log(schedule.stage_sj_tp, final_total_tp);
+        if (curScheduleStage.tp !== total_tp || schedule.stage_sj_tp !== final_total_tp) {
             // 更新计量总金额并更新金额模式总金额
-            postData('/tender/' + getTenderId() + '/schedule/stage/save', { type: 'update_tp', postData: { tp: total_tp, order: curScheduleStage.order } }, function (data) {
+            postData('/tender/' + getTenderId() + '/schedule/stage/save', { type: 'update_tp', postData: { tp: total_tp, stage_sj_tp: final_total_tp, order: curScheduleStage.order } }, function (data) {
                 console.log('总金额已更新');
             })
         }
@@ -267,8 +289,10 @@ function setMonthToLedger(ledgerList, slm, nextSlm, endSlm, yearSlm, yearLedgerD
     }
     if (yearLedgerData.length > 0) {
         for (const yl of yearLedgerData) {
-            const index = _.findIndex(ledgerList, {'id': yl.lid});
-            if (index && index !== -1) {
+            const index = _.findIndex(ledgerList, function (item) {
+                return item.id === yl.lid;
+            });
+            if (index !== undefined && index !== -1) {
                 ledgerList[index].year_contract_qty = ZhCalc.add(yl.contract_qty, yl.qc_qty);
                 ledgerList[index].year_gather_tp = ZhCalc.add(yl.contract_tp, yl.qc_tp);
             }

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

@@ -506,6 +506,10 @@ const SpreadJsObj = {
     _loadRowData: function (sheet, data, row) {
         // 单元格重新写入数据
         if (!data) { return }
+        if (sheet.zh_dataType === SpreadJsObj.DataType.Tree && !data.visible && data.waitingLoading === undefined) {
+            data.waitingLoading = true;
+            return;
+        }
         sheet.zh_setting.cols.forEach(function (col, j) {
             const cell = sheet.getCell(row, j);
             if (col.getValue && Object.prototype.toString.apply(col.getValue) === "[object Function]") {
@@ -550,6 +554,7 @@ const SpreadJsObj = {
             cell.backColor(SpreadJsObj._getBackColor(sheet, data, row, col));
 
             cell.setBorder(sheet.borderLine, {all: true});
+            data.waitingLoading = false;
         });
     },
     _addActivePaintEvents: function (sheet, cellType) {
@@ -1067,8 +1072,14 @@ const SpreadJsObj = {
         for (const iRow in sortData) {
             const node = sortData[iRow];
             if (node.visible !== undefined && node.visible !== null) {
+                if (node.visible && node.waitingLoading) {
+                    SpreadJsObj._loadRowData(sheet, node, parseInt(iRow));
+                }
                 sheet.setRowVisible(iRow, node.visible);
             } else {
+                if (node.waitingLoading) {
+                    this._loadRowData(sheet, node, parseInt(iRow));
+                }
                 sheet.setRowVisible(iRow, true);
             }
         }
@@ -1392,7 +1403,13 @@ const SpreadJsObj = {
                         SpreadJsObj.massOperationSheet(hitinfo.sheet, function () {
                             const posterity = tree.getPosterity(node);
                             for (const child of posterity) {
-                                hitinfo.sheet.setRowVisible(tree.nodes.indexOf(child), child.visible, hitinfo.sheetArea);
+                                const cIndex = tree.getNodeIndex(child);
+                                if (child.visible && child.waitingLoading) {
+                                    SpreadJsObj._loadRowData(hitinfo.sheet, child, cIndex);
+                                }
+                                //if (child.visible !== hitinfo.sheet.getRowVisible(cIndex, hitinfo.sheetArea)) {
+                                    hitinfo.sheet.setRowVisible(cIndex, child.visible, hitinfo.sheetArea);
+                                //}
                             }
                         });
                         hitinfo.sheet.repaint();

+ 157 - 95
app/public/js/stage.js

@@ -172,6 +172,13 @@ function getDaglText(data) {
     return def ? def.name : '';
 }
 
+function getHintMsg () {
+    return {
+        invalidExpr: {type: 'warning', msg: '粘贴了非法表达式,已过滤'},
+        disableChange: {type: 'error', msg: '不可修改此数据'},
+    }
+}
+
 
 $(document).ready(() => {
     const exportExcelSetting = {
@@ -238,6 +245,7 @@ $(document).ready(() => {
     // 界面布局
     autoFlashHeight();
     // 初始化 台账树结构 数据结构
+    removeLocalCache('bills-fold');
     const stageTreeSetting = {
         id: 'ledger_id',
         pid: 'ledger_pid',
@@ -246,8 +254,11 @@ $(document).ready(() => {
         rootId: -1,
         keys: ['id', 'tender_id', 'ledger_id'],
         stageId: 'id',
-        markFoldKey: 'bills-fold',
-        markFoldSubKey: window.location.pathname.split('/')[2],
+        autoExpand: 3,
+        // markFoldKey: 'bills-fold',
+        // markFoldSubKey: window.location.pathname.split('/')[2],
+        markExpandKey: 'stage-bills-expand',
+        markExpandSubKey: window.location.pathname.split('/')[2],
     };
     // 台账树结构计算相关设置
     stageTreeSetting.updateFields = ['contract_qty', 'contract_tp', 'qc_qty', 'qc_tp', 'postil', 'used', 'contract_expr'];
@@ -467,9 +478,9 @@ $(document).ready(() => {
                     stageTreeSpreadObj.refreshTreeNodes(slSpread.getActiveSheet(), nodes);
                     stagePosSpreadObj.loadCurPosData();
                     if (detail) {
-                        detail.loadStageChangeUpdateData(result);
+                        detail.loadStageChangeUpdateData(result, nodes);
                     } else {
-                        stageIm.loadUpdateChangeData(result)
+                        stageIm.loadUpdateChangeData(result, nodes)
                     }
                     self.obj.modal('hide');
                 });
@@ -916,9 +927,9 @@ $(document).ready(() => {
                     const nodes = stageTree.loadPostStageData(data);
                     stageTreeSpreadObj.refreshTreeNodes(slSpread.getActiveSheet(), nodes);
                     if (detail) {
-                        detail.loadStageLedgerUpdateData(data);
+                        detail.loadStageLedgerUpdateData(data, nodes);
                     } else {
-                        stageIm.loadUpdateLedgerData(data);
+                        stageIm.loadUpdateLedgerData(data, nodes);
                     }
                 }, function () {
                     SpreadJsObj.reLoadRowData(info.sheet, info.row);
@@ -963,9 +974,18 @@ $(document).ready(() => {
                             if (sheet.zh_setting.dgnUpFields.indexOf(colSetting.field) !== -1) {
                                 if (node.b_code && node.b_code !== '') continue;
                             } else if (colSetting.field !== 'postil') {
-                                if (node.children && node.children.length > 0) { continue; }
+                                if (colSetting.field === 'contract_qty') {
+                                    if (node.is_tp) continue;
+                                } else if (colSetting.field === 'contract_tp') {
+                                    if (!node.is_tp) continue;
+                                }
+                                if (node.children && node.children.length > 0) {
+                                    continue;
+                                }
                                 const nodePos = stagePos.getLedgerPos(node.id);
-                                if (nodePos && nodePos.length > 0) { continue; }
+                                if (nodePos && nodePos.length > 0) {
+                                    continue;
+                                }
                             }
 
                             if (sheet.zh_setting.dgnUpFields.indexOf(colSetting.field) !== -1) {
@@ -999,9 +1019,9 @@ $(document).ready(() => {
                         const nodes = stageTree.loadPostStageData(result);
                         stageTreeSpreadObj.refreshTreeNodes(slSpread.getActiveSheet(), nodes);
                         if (detail) {
-                            detail.loadStageLedgerUpdateData(result);
+                            detail.loadStageLedgerUpdateData(result, nodes);
                         } else {
-                            stageIm.loadUpdateLedgerData(result);
+                            stageIm.loadUpdateLedgerData(result, nodes);
                         }
                         stageTreeSpreadObj.loadExprToInput(sheet);
                     });
@@ -1025,10 +1045,10 @@ $(document).ready(() => {
             }
         },
         clipboardPasted(e, info) {
+            const pasteHint = getHintMsg();
             if (info.sheet.zh_setting && info.sheet.zh_tree) {
                 const sheet = info.sheet, setting = info.sheet.zh_setting;
                 const filterNodes = [], datas = [], dgnDatas = [], mainDatas = [];
-                let bHint = false;
 
                 for (let iRow = 0; iRow < info.cellRange.rowCount; iRow++) {
                     const curRow = iRow + info.cellRange.row;
@@ -1064,10 +1084,7 @@ $(document).ready(() => {
                                     dgnData[col.field] = math.evaluate(transExpr(text));
                                     filterDgn = false;
                                 } catch(err) {
-                                    if (!bHint) {
-                                        toastr.warning('粘贴了非法表达式,已过滤');
-                                        bHint = true;
-                                    }
+                                    toastMessageUniq(pasteHint.invalidExpr);
                                 }
                             }
                         } else if (col.field === 'memo') {
@@ -1075,24 +1092,26 @@ $(document).ready(() => {
                             filterMain = false;
                         } else {
                             if (col.type === 'Number') {
-                                const num = _.toNumber(text);
-                                if (_.isFinite(num)) {
+                                const exprInfo = getExprInfo(col.field);
+                                if (text) {
+                                    const num = _.toNumber(text);
                                     data[col.field] = num;
-                                    filter = false;
-                                } else {
-                                    try {
-                                        data[col.field] = math.evaluate(transExpr(text));
-                                        const exprInfo = getExprInfo(col.field);
-                                        if (exprInfo) {
-                                            data[exprInfo.expr] = text;
-                                        }
+                                    if (exprInfo) data[exprInfo.expr] = '';
+                                    if (_.isFinite(num)) {
                                         filter = false;
-                                    } catch(err) {
-                                        if (!bHint) {
-                                            toastr.warning('粘贴了非法表达式,已过滤');
-                                            bHint = true;
+                                    } else {
+                                        try {
+                                            data[col.field] = math.evaluate(transExpr(text));
+                                            if (exprInfo) data[exprInfo.expr] = text;
+                                            filter = false;
+                                        } catch(err) {
+                                            toastMessageUniq(pasteHint.invalidExpr);
                                         }
                                     }
+                                } else {
+                                    data[col.field] = null;
+                                    if (exprInfo) data[exprInfo.expr] = '';
+                                    filter = false;
                                 }
                             } else {
                                 data[col.field] = text;
@@ -1116,9 +1135,9 @@ $(document).ready(() => {
                         const nodes = stageTree.loadPostStageData(data);
                         stageTreeSpreadObj.refreshTreeNodes(slSpread.getActiveSheet(), nodes.concat(filterNodes));
                         if (detail) {
-                            detail.loadStageLedgerUpdateData(data);
+                            detail.loadStageLedgerUpdateData(data, nodes);
                         } else {
-                            stageIm.loadUpdateLedgerData(data);
+                            stageIm.loadUpdateLedgerData(data, nodes);
                         }
                         stageTreeSpreadObj.loadExprToInput(sheet);
                     }, function () {
@@ -1168,9 +1187,9 @@ $(document).ready(() => {
                 stageTreeSpreadObj.refreshTreeNodes(slSpread.getActiveSheet(), nodes);
                 stagePosSpreadObj.loadCurPosData();
                 if (detail) {
-                    detail.loadStagePosUpdateData(result);
+                    detail.loadStagePosUpdateData(result, nodes);
                 } else {
-                    stageIm.loadUpdatePosData(result);
+                    stageIm.loadUpdatePosData(result, nodes);
                 }
                 toastr.success('已计量' + data.updateData.length + '条');
             }, function () {
@@ -1224,9 +1243,9 @@ $(document).ready(() => {
                         const nodes = stageTree.loadPostStageData(result);
                         stageTreeSpreadObj.refreshTreeNodes(slSpread.getActiveSheet(), nodes);
                         if (detail) {
-                            detail.loadStageLedgerUpdateData(result);
+                            detail.loadStageLedgerUpdateData(result, nodes);
                         } else {
-                            stageIm.loadUpdateLedgerData(result);
+                            stageIm.loadUpdateLedgerData(result, nodes);
                         }
                     });
                 };
@@ -1290,9 +1309,9 @@ $(document).ready(() => {
                 stageTreeSpreadObj.refreshTreeNodes(slSpread.getActiveSheet(), nodes);
                 stagePosSpreadObj.loadCurPosData();
                 if (detail) {
-                    detail.loadStagePosUpdateData(result);
+                    detail.loadStagePosUpdateData(result, nodes);
                 } else {
-                    stageIm.loadUpdatePosData(result);
+                    stageIm.loadUpdatePosData(result, nodes);
                 }
                 $('#calc-by-ratio').modal('hide');
             }, function () {
@@ -1393,6 +1412,11 @@ $(document).ready(() => {
             postData(window.location.pathname + '/update', {bills: updateData}, function (result) {
                 const nodes = stageTree.loadPostStageData(result);
                 stageTreeSpreadObj.refreshTreeNodes(slSpread.getActiveSheet(), nodes);
+                if (detail) {
+                    detail.loadStagePosUpdateData(result, nodes);
+                } else {
+                    stageIm.loadUpdatePosData(result, nodes);
+                }
             });
         });
     }
@@ -1449,6 +1473,21 @@ $(document).ready(() => {
                     return !node;
                 }
             },
+            showSpr: '----',
+            showLast: {
+                name: '显示至最底层',
+                callback: function (key, opt, menu, e) {
+                    const node = SpreadJsObj.getSelectObject(slSpread.getActiveSheet());
+                    setTimeout(() => {
+                        showWaitingView();
+                        stageTree.expandByCustom(x => {
+                            return x.expanded || (x.id === node.id) || (x.full_path.indexOf(node.ledger_id + '-') >= 0);
+                        });
+                        SpreadJsObj.refreshTreeRowVisible(slSpread.getActiveSheet());
+                        closeWaitingView();
+                    }, 100);
+                },
+            },
             exportSpr: '----',
             exportSelectNodeXlsx: {
                 name: '导出选中节点至Excel',
@@ -1604,9 +1643,9 @@ $(document).ready(() => {
                     stageTreeSpreadObj.refreshTreeNodes(slSpread.getActiveSheet(), refreshData);
                     stagePosSpreadObj.loadCurPosData();
                     if (detail) {
-                        detail.loadStagePosUpdateData(result);
+                        detail.loadStagePosUpdateData(result, refreshData);
                     } else {
-                        stageIm.loadUpdatePosData(result);
+                        stageIm.loadUpdatePosData(result, refreshData);
                     }
                 }, function () {
                     stagePosSpreadObj.loadCurPosData();
@@ -1687,22 +1726,29 @@ $(document).ready(() => {
 
                             const newValue = trimInvalidChar(info.sheet.getText(curRow, curCol));
                             if (colSetting.type === 'Number') {
-                                const num = _.toNumber(newValue);
-                                if (num) {
-                                    newData[colSetting.field] = num;
-                                } else {
-                                    try {
-                                        newData[colSetting.field] = math.evaluate(transExpr(newValue));
-                                        const exprInfo = getExprInfo(colSetting.field);
+                                const exprInfo = getExprInfo(colSetting.field);
+                                if (newValue) {
+                                    const num = _.toNumber(newValue);
+                                    if (num) {
+                                        newData[colSetting.field] = num;
                                         if (exprInfo) {
-                                            newData[exprInfo.expr] = newValue;
+                                            newData[exprInfo.expr] = '';
+                                        }
+                                    } else {
+                                        try {
+                                            newData[colSetting.field] = math.evaluate(transExpr(newValue));
+                                            if (exprInfo) {
+                                                newData[exprInfo.expr] = newValue;
+                                            }
+                                        } catch(err) {
+                                            toastr.error('输入的表达式非法');
+                                            stagePosSpreadObj.loadCurPosData();
+                                            return;
                                         }
-                                    } catch(err) {
-                                        toastr.error('输入的表达式非法');
-                                        stagePosSpreadObj.loadCurPosData();
-                                        return;
-
                                     }
+                                } else {
+                                    newData[colSetting.field] = null;
+                                    if (exprInfo) newData[exprInfo.expr] = '';
                                 }
                             } else {
                                 newData[colSetting.field] = newValue;
@@ -1722,16 +1768,22 @@ $(document).ready(() => {
                                 const colSetting = info.sheet.zh_setting.cols[curCol];
                                 const newValue = trimInvalidChar(info.sheet.getText(curRow, curCol));
                                 if (colSetting.type === 'Number') {
+                                    const exprInfo = getExprInfo(colSetting.field);
+                                    if (!newValue) {
+                                        newData[colSetting.field] = newValue;
+                                        if (exprInfo) newData[exprInfo.expr] = '';
+                                        continue;
+                                    }
+
                                     const num = _.toNumber(newValue);
                                     if (num) {
                                         newData[colSetting.field] = num;
+                                        if (exprInfo) newData[exprInfo.expr] = '';
                                     } else {
                                         try {
                                             newData[colSetting.field] = math.evaluate(transExpr(newValue));
                                             const exprInfo = getExprInfo(colSetting.field);
-                                            if (exprInfo) {
-                                                newData[exprInfo.expr] = newValue;
-                                            }
+                                            if (exprInfo) newData[exprInfo.expr] = newValue;
                                         } catch(err) {
                                             toastr.error('输入的表达式非法');
                                             stagePosSpreadObj.loadCurPosData();
@@ -1755,9 +1807,9 @@ $(document).ready(() => {
                     stageTreeSpreadObj.refreshTreeNodes(slSpread.getActiveSheet(), nodes);
                     stagePosSpreadObj.loadCurPosData();
                     if (detail) {
-                        detail.loadStagePosUpdateData(result);
+                        detail.loadStagePosUpdateData(result, nodes);
                     } else {
-                        stageIm.loadUpdatePosData(result);
+                        stageIm.loadUpdatePosData(result, nodes);
                     }
                 }, function () {
                     stagePosSpreadObj.loadCurPosData();
@@ -1811,9 +1863,9 @@ $(document).ready(() => {
                         const nodes = stageTree.loadPostStageData(result.ledger);
                         stageTreeSpreadObj.refreshTreeNodes(slSpread.getActiveSheet(), nodes);
                         if (detail) {
-                            detail.loadStagePosUpdateData(result);
+                            detail.loadStagePosUpdateData(result, nodes);
                         } else {
-                            stageIm.loadUpdatePosData(result);
+                            stageIm.loadUpdatePosData(result, nodes);
                         }
                         // todo 只加载改变项
                         stagePosSpreadObj.loadCurPosData();
@@ -1839,9 +1891,9 @@ $(document).ready(() => {
                         stageTreeSpreadObj.refreshTreeNodes(slSpread.getActiveSheet(), refreshData);
                         stagePosSpreadObj.loadCurPosData();
                         if (detail) {
-                            detail.loadStagePosUpdateData(result);
+                            detail.loadStagePosUpdateData(result, refreshData);
                         } else {
-                            stageIm.loadUpdatePosData(result);
+                            stageIm.loadUpdatePosData(result, refreshData);
                         }
                     });
                 }
@@ -1886,6 +1938,7 @@ $(document).ready(() => {
         stageTree.loadDatas(result.ledgerData);
         // stageTree.loadCurStageData(curStageData);
         // stageTree.loadPreStageData(preStageData);
+        checkShowLast(result.ledgerData.length);
         treeCalc.calculateAll(stageTree);
         // 加载解锁相关
         if (result.cooperation) {
@@ -1961,6 +2014,11 @@ $(document).ready(() => {
                 }
                 const refreshData = stageTree.loadPostStageData(result.ledger);
                 stageTreeSpreadObj.refreshTreeNodes(slSpread.getActiveSheet(), refreshData);
+                if (detail) {
+                    detail.loadStagePosUpdateData(result, refreshData);
+                } else {
+                    stageIm.loadUpdatePosData(result, refreshData);
+                }
             });
         });
     }
@@ -3180,18 +3238,18 @@ $(document).ready(() => {
             this.reLoadDetailData();
             this.loadLocateInfo();
         }
-        loadStageLedgerUpdateData(data) {
-            const imData = stageIm.loadUpdateLedgerData(data);
+        loadStageLedgerUpdateData(data, nodes) {
+            const imData = stageIm.loadUpdateLedgerData(data, nodes);
             SpreadJsObj.loadSheetData(this.sheet, SpreadJsObj.DataType.Data, imData);
             this.reLoadDetailData();
         }
-        loadStagePosUpdateData(data) {
-            const imData = stageIm.loadUpdatePosData(data);
+        loadStagePosUpdateData(data, nodes) {
+            const imData = stageIm.loadUpdatePosData(data, nodes);
             SpreadJsObj.loadSheetData(this.sheet, SpreadJsObj.DataType.Data, imData);
             this.reLoadDetailData();
         }
-        loadStageChangeUpdateData(data) {
-            const imData = stageIm.loadUpdateChangeData(data);
+        loadStageChangeUpdateData(data, nodes) {
+            const imData = stageIm.loadUpdateChangeData(data, nodes);
             SpreadJsObj.loadSheetData(this.sheet, SpreadJsObj.DataType.Data, imData);
             this.reLoadDetailData();
         }
@@ -3882,35 +3940,39 @@ $(document).ready(() => {
             if (!sheet.zh_tree) return;
             const tag = $(this).attr('tag');
             const tree = sheet.zh_tree;
-            switch (tag) {
-                case "1":
-                case "2":
-                case "3":
-                case "4":
-                case "5":
-                    tree.expandByLevel(parseInt(tag));
-                    SpreadJsObj.refreshTreeRowVisible(sheet);
-                    break;
-                case "last":
-                    tree.expandByCustom(() => { return true; });
-                    SpreadJsObj.refreshTreeRowVisible(sheet);
-                    break;
-                case "leafXmj":
-                    tree.expandToLeafXmj();
-                    SpreadJsObj.refreshTreeRowVisible(sheet);
-                    break;
-                case "curMeasure":
-                    tree.expandByCustom(function (node) {
-                        for (const field of ['contract_tp', 'qc_tp', 'gather_tp']) {
-                            if (node[field]) {
-                                return true;
+            setTimeout(() => {
+                showWaitingView();
+                switch (tag) {
+                    case "1":
+                    case "2":
+                    case "3":
+                    case "4":
+                    case "5":
+                        tree.expandByLevel(parseInt(tag));
+                        SpreadJsObj.refreshTreeRowVisible(sheet);
+                        break;
+                    case "last":
+                        tree.expandByCustom(() => { return true; });
+                        SpreadJsObj.refreshTreeRowVisible(sheet);
+                        break;
+                    case "leafXmj":
+                        tree.expandToLeafXmj();
+                        SpreadJsObj.refreshTreeRowVisible(sheet);
+                        break;
+                    case "curMeasure":
+                        tree.expandByCustom(function (node) {
+                            for (const field of ['contract_tp', 'qc_tp', 'gather_tp']) {
+                                if (node[field]) {
+                                    return true;
+                                }
                             }
-                        }
-                        return false;
-                    });
-                    SpreadJsObj.refreshTreeRowVisible(sheet);
-                    break;
-            }
+                            return false;
+                        });
+                        SpreadJsObj.refreshTreeRowVisible(sheet);
+                        break;
+                }
+                closeWaitingView();
+            }, 100);
         });
     })('a[name=showLevel]', slSpread.getActiveSheet());
 

+ 52 - 22
app/public/js/stage_compare.js

@@ -134,6 +134,7 @@ $(document).ready(function () {
         masterId: 'id',
         minorId: 'lid',
         calcFields: [],
+        autoExpand: 3,
     };
     const scTree = createNewPathTree('master', scTreeSetting);
     // 加载 部位 数据
@@ -145,6 +146,30 @@ $(document).ready(function () {
     };
     const scPos = new MasterPosData(scPosSetting);
 
+    $.contextMenu({
+        selector: '#ledger-spread',
+        build: function ($trigger, e) {
+            const target = SpreadJsObj.safeRightClickSelection($trigger, e, ledgerSpread);
+            return target.hitTestType === spreadNS.SheetArea.viewport || target.hitTestType === spreadNS.SheetArea.rowHeader;
+        },
+        items: {
+            showLast: {
+                name: '显示至最底层',
+                callback: function (key, opt, menu, e) {
+                    const node = SpreadJsObj.getSelectObject(ledgerSpread.getActiveSheet());
+                    setTimeout(() => {
+                        showWaitingView();
+                        scTree.expandByCustom(x => {
+                            return x.expanded || (x.id === node.id) || (x.full_path.indexOf(node.ledger_id + '-') >= 0);
+                        });
+                        SpreadJsObj.refreshTreeRowVisible(ledgerSpread.getActiveSheet());
+                        closeWaitingView();
+                    }, 100);
+                },
+            },
+        }
+    });
+
     postData(window.location.pathname + '/load', {main: true, roles: scRoles}, function (result) {
         for (const r of scRoles) {
             if (r && r != 0) {
@@ -153,6 +178,7 @@ $(document).ready(function () {
             }
         }
         scTree.loadDatas(result.main.ledger);
+        checkShowLast(result.main.ledger.length);
         scPos.loadDatas(result.main.pos);
         for (const aData of result.roles) {
             calculateStageLedgerData(aData.bills);
@@ -252,28 +278,32 @@ $(document).ready(function () {
             if (!sheet.zh_tree) return;
             const tag = $(this).attr('tag');
             const tree = sheet.zh_tree;
-            switch (tag) {
-                case "1":
-                case "2":
-                case "3":
-                case "4":
-                case "5":
-                    tree.expandByLevel(parseInt(tag));
-                    SpreadJsObj.refreshTreeRowVisible(sheet);
-                    break;
-                case "last":
-                    tree.expandByCustom(() => { return true; });
-                    SpreadJsObj.refreshTreeRowVisible(sheet);
-                    break;
-                case "leafXmj":
-                    tree.expandToLeafXmj();
-                    SpreadJsObj.refreshTreeRowVisible(sheet);
-                    break;
-                case "curMeasure":
-                    tree.expandByCalcFields();
-                    SpreadJsObj.refreshTreeRowVisible(sheet);
-                    break;
-            }
+            setTimeout(() => {
+                showWaitingView();
+                switch (tag) {
+                    case "1":
+                    case "2":
+                    case "3":
+                    case "4":
+                    case "5":
+                        tree.expandByLevel(parseInt(tag));
+                        SpreadJsObj.refreshTreeRowVisible(sheet);
+                        break;
+                    case "last":
+                        tree.expandByCustom(() => { return true; });
+                        SpreadJsObj.refreshTreeRowVisible(sheet);
+                        break;
+                    case "leafXmj":
+                        tree.expandToLeafXmj();
+                        SpreadJsObj.refreshTreeRowVisible(sheet);
+                        break;
+                    case "curMeasure":
+                        tree.expandByCalcFields();
+                        SpreadJsObj.refreshTreeRowVisible(sheet);
+                        break;
+                }
+                closeWaitingView();
+            }, 100);
         });
     })('a[name=showLevel]', ledgerSpread.getActiveSheet());
 

+ 79 - 21
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;
+    let stage, imType, decimal, details, changes, ImData, pre, orgImData;
     const gsTreeSetting = {
         id: 'ledger_id',
         pid: 'ledger_pid',
@@ -837,25 +837,20 @@ const stageIm = (function () {
         }
     }
 
-    /**
-     * 生成 中间计量表 全部数据
-     * @returns {Array}
-     */
-    function buildImData () {
-        // 初始化
-        ImData = [];
-        pre = (stage.im_pre && stage.im_pre !== '') ? stage.im_pre + splitChar : '';
-        if (stage.im_gather) {
-            initCheck();
-        }
-        // 生成数据
-        recursiveBuildImData(gsTree.children);
+    function _sortImData() {
         if (stage.im_type !== imType.tz.value) {
             ImData.sort(function (x, y) {
                 const iCode = compareCode(x.code, y.code);
                 return iCode === 0 ? x.lIndex - y.lIndex : iCode;
             });
+        } else {
+            ImData.sort(function (x, y) {
+                return x.lIndex - y.lIndex;
+            });
         }
+    }
+
+    function _buildInfoByCache() {
         for (const [i, im] of ImData.entries()) {
             if (im.source.length > 1) {
                 im.source.sort(function (x, y) {
@@ -864,9 +859,72 @@ const stageIm = (function () {
             }
             getCalcMemo(im);
             getChangeInfo(im);
-            im.im_code = pre + getNumberFormat(stage.order, 2) + splitChar + getNumberFormat(i + 1, 3);
             checkCustomDetail(im);
         }
+    }
+
+    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);
+        }
+    }
+
+    /**
+     * 生成 中间计量表 全部数据
+     * @returns {Array}
+     */
+    function buildImData () {
+        // 初始化
+        ImData = [];
+        if (stage.im_gather) initCheck();
+        // 生成数据
+        recursiveBuildImData(gsTree.children);
+        _buildInfoByCache();
+        _sortImData();
+        _buildImCode();
+        return ImData;
+    }
+
+    /**
+     * 递归 生成中间计量表
+     * @param {Array} nodes
+     */
+    function recursiveReBuildImData (nodes, refreshNodeIds) {
+        if (!nodes || nodes.length === 0) { return; }
+        for (const node of nodes) {
+            if (refreshNodeIds.indexOf(node.id) < 0) continue;
+            if (gsTree.isLeafXmj(node) || ((stage.im_type !== imType.bw.value && stage.im_type !== imType.bb.value) && stage.im_gather && node.check)) {
+                if (stage.im_type === imType.tz.value) {
+                    generateTzImData(node);
+                } else if (stage.im_type === imType.zl.value) {
+                    generateZlImData(node);
+                } else if (stage.im_type === imType.bw.value) {
+                    generateBwImData(node);
+                } else if (stage.im_type === imType.bb.value) {
+                    generateBwBillsImData(node);
+                }
+            } else {
+                recursiveReBuildImData(node.children, refreshNodeIds);
+            }
+        }
+    }
+
+    function refreshImData(refreshNodes) {
+        // 初始化
+        const refreshIds = refreshNodes.map(x => {return x.id});
+        orgImData = ImData ? ImData.filter(x => {
+            return refreshIds.indexOf(x.lid) < 0
+        }) : [];
+        ImData = [];
+        if (stage.im_gather) initCheck();
+        // 生成数据
+        recursiveReBuildImData(gsTree.children, refreshIds);
+        _buildInfoByCache();
+        ImData = ImData.concat(orgImData);
+        _sortImData();
+        _buildImCode();
+        orgImData = [];
         return ImData;
     }
 
@@ -897,7 +955,7 @@ const stageIm = (function () {
         }
     }
 
-    function loadUpdateLedgerData(data) {
+    function loadUpdateLedgerData(data, refreshNodes) {
         gsTree.loadPostStageData(data);
         if (data.change) {
             _.remove(changes, data.change.target);
@@ -905,9 +963,9 @@ const stageIm = (function () {
                 changes.push(c);
             }
         }
-        return buildImData();
+        return refreshImData(refreshNodes);
     }
-    function loadUpdatePosData(data) {
+    function loadUpdatePosData(data, refreshNodes) {
         if (data.pos) {
             if (data.pos.pos && data.pos.pos.length > 0 && typeof data.pos.pos[0] === 'string') {
                 gsPos.removeDatas(data.pos.pos);
@@ -919,9 +977,9 @@ const stageIm = (function () {
             }
         }
         gsTree.loadPostStageData(data.ledger);
-        return buildImData();
+        return refreshImData(refreshNodes);
     }
-    function loadUpdateChangeData(data) {
+    function loadUpdateChangeData(data, refreshNodes) {
         if (data.pos) {
             gsPos.loadCurStageData(data.pos.curStageData);
         }
@@ -932,7 +990,7 @@ const stageIm = (function () {
                 changes.push(c);
             }
         }
-        return buildImData();
+        return refreshImData(refreshNodes);
     }
 
     function getParentCheckNode(node) {

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

@@ -713,4 +713,4 @@ $(document).ready(function() {
             $('#edit-6').parent().show();
         });
     });
-});
+});

+ 13 - 13
app/public/js/xlsx-populate/xlsx-populate.js

@@ -7356,7 +7356,7 @@ var Workbook = function () {
 
         /**
          * Add a new sheet to the workbook.
-         * 
+         *
          * **WARN:** this function has limits:  if you clone a sheet with some images or other things link outside the Sheet object, these things in the cloned sheet will be locked when you open in MS Excel app.
          * @param {Sheet} from - The sheet to be cloned.
          * @param {string} name - The name of the new sheet. Must be unique, less than 31 characters, and may not contain the following characters: \ / * [ ] : ?
@@ -19910,7 +19910,7 @@ $export.P = 8;   // proto
 $export.B = 16;  // bind
 $export.W = 32;  // wrap
 $export.U = 64;  // safe
-$export.R = 128; // real proto method for `library` 
+$export.R = 128; // real proto method for `library`
 module.exports = $export;
 },{"./_core":86,"./_ctx":87,"./_global":92,"./_hide":93}],91:[function(require,module,exports){
 module.exports = function(exec){
@@ -31243,8 +31243,8 @@ exports.prepareContent = function(name, inputData, isBinary, isOptimizedBinarySt
 
     // if inputData is already a promise, this flatten it.
     var promise = external.Promise.resolve(inputData).then(function(data) {
-        
-        
+
+
         var isBlob = support.blob && (data instanceof Blob || ['[object File]', '[object Blob]'].indexOf(Object.prototype.toString.call(data)) !== -1);
 
         if (isBlob && typeof FileReader !== "undefined") {
@@ -33773,7 +33773,7 @@ function race(iterable) {
 
     /*------------------------------------------------------------------------*/
 
-    /**
+    t/**
      * Creates a `lodash` object which wraps `value` to enable implicit method
      * chain sequences. Methods that operate on and return arrays, collections,
      * and functions can be chained together. Methods that retrieve a single value
@@ -62607,13 +62607,13 @@ Script.prototype.runInContext = function (context) {
     if (!(context instanceof Context)) {
         throw new TypeError("needs a 'context' argument.");
     }
-    
+
     var iframe = document.createElement('iframe');
     if (!iframe.style) iframe.style = {};
     iframe.style.display = 'none';
-    
+
     document.body.appendChild(iframe);
-    
+
     var win = iframe.contentWindow;
     var wEval = win.eval, wExecScript = win.execScript;
 
@@ -62622,7 +62622,7 @@ Script.prototype.runInContext = function (context) {
         wExecScript.call(win, 'null');
         wEval = win.eval;
     }
-    
+
     forEach(Object_keys(context), function (key) {
         win[key] = context[key];
     });
@@ -62631,11 +62631,11 @@ Script.prototype.runInContext = function (context) {
             win[key] = context[key];
         }
     });
-    
+
     var winKeys = Object_keys(win);
 
     var res = wEval.call(win, this.code);
-    
+
     forEach(Object_keys(win), function (key) {
         // Avoid copying circular objects like `top` and `window` by only
         // updating existing context properties or new properties in the `win`
@@ -62650,9 +62650,9 @@ Script.prototype.runInContext = function (context) {
             defineProp(context, key, win[key]);
         }
     });
-    
+
     document.body.removeChild(iframe);
-    
+
     return res;
 };
 

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

@@ -432,9 +432,16 @@ const rptCustomObj = (function () {
                 return;
             }
         }
-        if (data[sGatherSelect].tenders.length <= specCol.length) {
-            hintObj.html('请至少选择1个普通汇总项目').show();
-            return;
+        if (gsObj.setting.onlySpec) {
+            if (data[sGatherSelect].tenders.length > specCol.length) {
+                hintObj.html('请勿选择普通汇总项目').show();
+                return;
+            }
+        } else {
+            if (data[sGatherSelect].tenders.length <= specCol.length) {
+                hintObj.html('请至少选择1个普通汇总项目').show();
+                return;
+            }
         }
         if (gsObj.setting.type === 'month') {
             data[sGatherSelect].month = $('#gather-month').val();

+ 10 - 6
app/router.js

@@ -25,6 +25,8 @@ module.exports = app => {
     const advanceCheck = app.middlewares.advanceCheck();
     // 变更令中间件
     const changeCheck = app.middlewares.changeCheck();
+    // 形象进度中间件
+    const scheduleCheck = app.middlewares.scheduleCheck();
     // 登入登出相关
     app.get('/login', 'loginController.index');
     app.get('/login/port', api2otherCheck, 'loginController.port');
@@ -438,18 +440,20 @@ module.exports = app => {
     app.get('/MP_verify_t3MkWAMqplVxPjmr.txt', 'wechatController.oauthTxt');
 
     // 形象进度
-    app.get('/tender/:id/schedule', sessionAuth, tenderCheck, uncheckTenderCheck, 'scheduleController.index');
-    app.get('/tender/:id/schedule/ledger', sessionAuth, tenderCheck, uncheckTenderCheck, 'scheduleController.ledger');
+    app.get('/tender/:id/schedule', sessionAuth, tenderCheck, uncheckTenderCheck, scheduleCheck, 'scheduleController.index');
+    app.get('/tender/:id/schedule/ledger', sessionAuth, tenderCheck, uncheckTenderCheck, scheduleCheck, 'scheduleController.ledger');
     app.post('/tender/:id/schedule/ledger/load', sessionAuth, tenderCheck, uncheckTenderCheck, 'scheduleController.loadLedgerData');
     app.post('/tender/:id/schedule/ledger/save', sessionAuth, tenderCheck, uncheckTenderCheck, 'scheduleController.saveLedger');
-    app.get('/tender/:id/schedule/plan', sessionAuth, tenderCheck, uncheckTenderCheck, 'scheduleController.plan');
+    app.get('/tender/:id/schedule/plan', sessionAuth, tenderCheck, uncheckTenderCheck, scheduleCheck, 'scheduleController.plan');
     app.post('/tender/:id/schedule/plan/save', sessionAuth, tenderCheck, uncheckTenderCheck, 'scheduleController.savePlan');
-    app.get('/tender/:id/schedule/stage', sessionAuth, tenderCheck, uncheckTenderCheck, 'scheduleController.stageTp');
-    app.get('/tender/:id/schedule/stage/order/:order', sessionAuth, tenderCheck, uncheckTenderCheck, 'scheduleController.stageTp');
+    app.get('/tender/:id/schedule/stage', sessionAuth, tenderCheck, uncheckTenderCheck, scheduleCheck, 'scheduleController.stageTp');
+    app.get('/tender/:id/schedule/stage/order/:order', sessionAuth, tenderCheck, uncheckTenderCheck, scheduleCheck, 'scheduleController.stageTp');
     app.post('/tender/:id/schedule/stage/save', sessionAuth, tenderCheck, uncheckTenderCheck, 'scheduleController.saveStageTp');
-    app.get('/tender/:id/schedule/stage/gcl', sessionAuth, tenderCheck, uncheckTenderCheck, 'scheduleController.stageGcl');
+    app.get('/tender/:id/schedule/stage/gcl', sessionAuth, tenderCheck, uncheckTenderCheck, scheduleCheck, 'scheduleController.stageGcl');
     app.post('/tender/:id/schedule/stage/gcl/save', sessionAuth, tenderCheck, uncheckTenderCheck, 'scheduleController.saveStageGcl');
     app.post('/tender/:id/schedule/stage/:order/load', sessionAuth, tenderCheck, uncheckTenderCheck, 'scheduleController.loadTpLedgerData');
+    app.post('/tender/:id/schedule/stage/gcl/:order/load', sessionAuth, tenderCheck, uncheckTenderCheck, 'scheduleController.loadGclLedgerData');
+    app.post('/tender/:id/schedule/audit/save', sessionAuth, tenderCheck, uncheckTenderCheck, 'scheduleController.saveAudit');
 
     // 书签
     app.post('/tender/:id/ledger/tag', sessionAuth, tenderCheck, uncheckTenderCheck, 'tenderController.billsTag');

+ 54 - 0
app/service/schedule_audit.js

@@ -0,0 +1,54 @@
+'use strict';
+const scheduleConst = require('../const/schedule');
+module.exports = app => {
+    class ScheduleAudit extends app.BaseService {
+        constructor(ctx) {
+            super(ctx);
+            this.tableName = 'schedule_audit';
+        }
+
+        async addAudit(data) {
+            const transaction = await this.db.beginTransaction();
+            try {
+                const insertData = {
+                    tid: this.ctx.tender.id,
+                    audit_id: data.audit_id,
+                    in_time: new Date(),
+                };
+                const result = await transaction.insert(this.tableName, insertData);
+                await transaction.commit();
+                return await this.getDataById(result.insertId);
+            } catch (err) {
+                await transaction.rollback();
+                throw err;
+            }
+        }
+
+        async removeAudit(data) {
+            const transaction = await this.db.beginTransaction();
+            try {
+                await transaction.delete(this.tableName, { id: data.id });
+                await transaction.commit();
+                return true;
+            } catch (err) {
+                await transaction.rollback();
+                throw err;
+            }
+        }
+        async editAudit(data) {
+            const transaction = await this.db.beginTransaction();
+            try {
+                if (!this._.includes(this._.values(scheduleConst.permission), data.permission)) {
+                    throw '修改失败';
+                }
+                await transaction.update(this.tableName, { id: data.id, permission: data.permission });
+                await transaction.commit();
+                return true;
+            } catch (err) {
+                await transaction.rollback();
+                throw err;
+            }
+        }
+    }
+    return ScheduleAudit;
+};

+ 4 - 3
app/service/schedule_ledger.js

@@ -93,8 +93,8 @@ module.exports = app => {
                                             const new_sj_gcl = l.sj_tp ? this.ctx.helper.round(this.ctx.helper.div(l.sj_tp, dgn_price), 3) : null;
                                             slmUpdateOption.push({ id: l.id, plan_gcl: new_plan_gcl, sj_gcl: new_sj_gcl });
                                         } else if (mode === scheduleConst.plan_mode.gcl) {
-                                            const new_plan_tp = l.plan_gcl ? this.ctx.helper.round(this.ctx.helper.div(l.plan_gcl, dgn_price), 0) : null;
-                                            const new_sj_tp = l.sj_gcl ? this.ctx.helper.round(this.ctx.helper.div(l.sj_gcl, dgn_price), 0) : null;
+                                            const new_plan_tp = l.plan_gcl ? this.ctx.helper.round(this.ctx.helper.mul(l.plan_gcl, dgn_price), 0) : null;
+                                            const new_sj_tp = l.sj_gcl ? this.ctx.helper.round(this.ctx.helper.mul(l.sj_gcl, dgn_price), 0) : null;
                                             slmUpdateOption.push({ id: l.id, plan_tp: new_plan_tp, sj_tp: new_sj_tp });
                                         }
                                     }
@@ -121,8 +121,9 @@ module.exports = app => {
                 // 判断是否已存在计量进度,并更新计量统计数据,再更新总的统计数据
                 if (datas.stageTpList) {
                     await transaction.updateRows(this.ctx.service.scheduleStage.tableName, datas.stageTpList);
-                    await this.ctx.service.scheduleStage.calcStageSjTp(transaction, this.ctx.tender.id);
+                    // await this.ctx.service.scheduleStage.calcStageSjTp(transaction, this.ctx.tender.id);
                 }
+                await this.ctx.service.scheduleStage.updateStageSjTp(transaction, this.ctx.tender.id, datas.stageSjTp);
                 await transaction.commit();
                 return true;
             } catch (err) {

+ 91 - 1
app/service/schedule_ledger_month.js

@@ -44,6 +44,51 @@ module.exports = app => {
             }
         }
 
+        async saveDatas(datas) {
+            // 判断是添加,删除,还是修改
+            const transaction = await this.db.beginTransaction();
+            try {
+                const ymArray = [];
+                for (const data of datas) {
+                    const info = await this.getDataByCondition({ tid: this.ctx.tender.id, lid: data.lid, yearmonth: data.yearmonth });
+                    if (!this._.includes(ymArray, data.yearmonth)) {
+                        ymArray.push(data.yearmonth);
+                    }
+                    if (info) {
+                        if (data.plan_gcl === null && data.plan_tp === null && info.sj_gcl === null && info.sj_tp === null) {
+                            await transaction.delete(this.tableName, { id: info.id });
+                        } else {
+                            const updateData = {
+                                id: info.id,
+                                plan_gcl: data.plan_gcl,
+                                plan_tp: data.plan_tp,
+                            };
+                            await transaction.update(this.tableName, updateData);
+                        }
+                    } else {
+                        const insertData = {
+                            tid: this.ctx.tender.id,
+                            lid: data.lid,
+                            yearmonth: data.yearmonth,
+                            plan_gcl: data.plan_gcl,
+                            plan_tp: data.plan_tp,
+                        };
+                        await transaction.insert(this.tableName, insertData);
+                    }
+                }
+                // 重新计算本月、总 计划金额和计划工程量
+                for (const ym of ymArray) {
+                    await this.calcMonthPlan(transaction, this.ctx.tender.id, ym);
+                }
+                await this.ctx.service.scheduleMonth.calcPlan(transaction, this.ctx.tender.id);
+                await transaction.commit();
+                return true;
+            } catch (err) {
+                await transaction.rollback();
+                throw err;
+            }
+        }
+
         async saveSj(data) {
             // 判断是添加,删除,还是修改
             const transaction = await this.db.beginTransaction();
@@ -81,6 +126,51 @@ module.exports = app => {
             }
         }
 
+        async saveSjDatas(datas) {
+            // 判断是添加,删除,还是修改
+            const transaction = await this.db.beginTransaction();
+            try {
+                const ymArray = [];
+                for (const data of datas) {
+                    const info = await this.getDataByCondition({ tid: this.ctx.tender.id, lid: data.lid, yearmonth: data.yearmonth });
+                    if (!this._.includes(ymArray, data.yearmonth)) {
+                        ymArray.push(data.yearmonth);
+                    }
+                    if (info) {
+                        if (data.sj_gcl === null && data.sj_tp === null && info.plan_gcl === null && info.plan_tp === null) {
+                            await transaction.delete(this.tableName, { id: info.id });
+                        } else {
+                            const updateData = {
+                                id: info.id,
+                                sj_gcl: data.sj_gcl,
+                                sj_tp: data.sj_tp,
+                            };
+                            await transaction.update(this.tableName, updateData);
+                        }
+                    } else {
+                        const insertData = {
+                            tid: this.ctx.tender.id,
+                            lid: data.lid,
+                            yearmonth: data.yearmonth,
+                            sj_gcl: data.sj_gcl,
+                            sj_tp: data.sj_tp,
+                        };
+                        await transaction.insert(this.tableName, insertData);
+                    }
+                }
+                // 重新计算本月、总 计划金额和计划工程量
+                for (const ym of ymArray) {
+                    await this.calcMonthSj(transaction, this.ctx.tender.id, ym);
+                }
+                await this.ctx.service.scheduleMonth.calcSj(transaction, this.ctx.tender.id);
+                await transaction.commit();
+                return true;
+            } catch (err) {
+                await transaction.rollback();
+                throw err;
+            }
+        }
+
         async calcMonthSj(transaction, tid, yearmonth) {
             const sql = 'SELECT SUM(`sj_gcl`) as total_sj_gcl, SUM(`sj_tp`) as total_sj_tp FROM ?? WHERE tid = ? and yearmonth = ?';
             const sqlParam = [this.tableName, tid, yearmonth];
@@ -116,7 +206,7 @@ module.exports = app => {
         }
 
         async getConllectionList(tid, yearmonthArray) {
-            const sql = 'SELECT tid, lid, SUM(`plan_gcl`) as plan_gcl, SUM(`plan_tp`) as plan_tp' +
+            const sql = 'SELECT tid, lid, SUM(`plan_gcl`) as plan_gcl, SUM(`plan_tp`) as plan_tp, SUM(`sj_gcl`) as sj_gcl, SUM(`sj_tp`) as sj_tp' +
                 ' FROM ?? WHERE `tid` = ? AND `yearmonth` in (?) GROUP BY `lid`';
             const sqlParam = [this.tableName, tid, yearmonthArray];
             const result = await this.db.query(sql, sqlParam);

+ 14 - 2
app/service/schedule_stage.js

@@ -96,8 +96,7 @@ module.exports = app => {
                     },
                 };
                 await transaction.update(this.tableName, updateData, option);
-                // 重算计量总金额
-                await this.calcStageSjTp(transaction, this.ctx.tender.id);
+                await this.updateStageSjTp(transaction, this.ctx.tender.id, data.stage_sj_tp);
                 await transaction.commit();
                 return true;
             } catch (err) {
@@ -120,6 +119,19 @@ module.exports = app => {
             };
             return await transaction.update(this.ctx.service.schedule.tableName, updateData, option);
         }
+
+        async updateStageSjTp(transaction, tid, sj_tp) {
+            // 更新计量总金额
+            const stageData = {
+                stage_sj_tp: sj_tp,
+            };
+            const stageOption = {
+                where: {
+                    tid,
+                },
+            };
+            await transaction.update(this.ctx.service.schedule.tableName, stageData, stageOption);
+        }
     }
     return ScheduleStage;
 };

+ 0 - 1
app/service/stage_bills.js

@@ -251,7 +251,6 @@ module.exports = app => {
                         await this._insertStageBillsData(transaction, d, stageBills, ledgerBills);
                     } else {
                         d.id = stageBills.id;
-                        console.log(d);
                         this._calcStageBillsData(d, stageBills, ledgerBills);
                         await transaction.update(this.tableName, d);
                     }

+ 8 - 8
app/view/revise/info.ejs

@@ -12,13 +12,13 @@
                             <i class="fa fa-list-ol"></i> 显示层级
                         </button>
                         <div class="dropdown-menu" aria-labelledby="dropdownMenuButton">
-                            <a class="dropdown-item" name="showLevel" tag="1" href="javascirpt: void(0);">第一层</a>
-                            <a class="dropdown-item" name="showLevel" tag="2" href="javascirpt: void(0);">第二层</a>
-                            <a class="dropdown-item" name="showLevel" tag="3" href="javascirpt: void(0);">第三层</a>
-                            <a class="dropdown-item" name="showLevel" tag="4" href="javascirpt: void(0);">第四层</a>
-                            <a class="dropdown-item" name="showLevel" tag="5" href="javascirpt: void(0);">第五层</a>
-                            <a class="dropdown-item" name="showLevel" tag="last" href="javascirpt: void(0);">最底层</a>
-                            <a class="dropdown-item" name="showLevel" tag="leafXmj" href="javascirpt: void(0);">只显示项目节</a>
+                            <a class="dropdown-item" name="showLevel" tag="1" href="javascript: void(0);">第一层</a>
+                            <a class="dropdown-item" name="showLevel" tag="2" href="javascript: void(0);">第二层</a>
+                            <a class="dropdown-item" name="showLevel" tag="3" href="javascript: void(0);">第三层</a>
+                            <a class="dropdown-item" name="showLevel" tag="4" href="javascript: void(0);">第四层</a>
+                            <a class="dropdown-item" name="showLevel" tag="5" href="javascript: void(0);">第五层</a>
+                            <a class="dropdown-item" name="showLevel" tag="last" href="javascript: void(0);">最底层</a>
+                            <a class="dropdown-item" name="showLevel" tag="leafXmj" href="javascript: void(0);">只显示项目节</a>
                         </div>
                     </div>
                 </div>
@@ -118,7 +118,7 @@
                     <div id="xd-content" class="tab-pane">
                         <% if ((revise.status === audit.status.uncheck || revise.status === audit.status.checkNo) && !readOnly) { %>
                         <div class="sjs-bar">
-                            <div class="d-flex"><a href="javascirpt: void(0);" class="btn btn-sm btn-outline-success mb-1 ml-auto" id="save">保存</a></div>
+                            <div class="d-flex"><a href="javascript: void(0);" class="btn btn-sm btn-outline-success mb-1 ml-auto" id="save">保存</a></div>
                         </div>
                         <% } %>
                         <div class="sjs-sh" style="overflow:auto">

+ 1 - 1
app/view/schedule/ledger.ejs

@@ -29,7 +29,7 @@
                     </a>
                 </div>
             </div>
-            <% if (tender.user_id === ctx.session.sessionUser.accountId && !revising) { %>
+            <% if (ctx.tender.schedule_permission === scPermission.edit && !revising) { %>
             <div class="ml-auto">
                 <button type="button" id="ledger_submit" class="btn btn-primary btn-sm pull-right">确认提交</button>
             </div>

+ 1 - 1
app/view/schedule/plan.ejs

@@ -6,7 +6,7 @@
             <h2>
                 <% if (planMonth) { %>计划至 <%- planMonth.split('-')[0] %>年<%- parseInt(planMonth.split('-')[1]) %>月 <% } %>
             </h2>
-            <% if (!revising) { %>
+            <% if (!revising && ctx.tender.schedule_permission === scPermission.edit) { %>
             <div class="ml-auto">
                 <a href="#mode" data-toggle="modal" data-target="#mode" class="btn btn-sm btn-outline-primary">计算方式</a>
                 <a href="#edit-plan" data-toggle="modal" data-target="#edit-plan" class="btn btn-sm btn-outline-primary">管理计划</a>

+ 8 - 8
app/view/schedule/stage_gcl.ejs

@@ -15,7 +15,7 @@
                     </div>
                 </div>
             </div>
-            <% if (!revising) { %>
+            <% if (!revising && ctx.tender.schedule_permission === scPermission.edit) { %>
             <div class="ml-auto">
                 <a href="#edit-stage" data-toggle="modal" data-target="#edit-stage" class="btn btn-sm btn-outline-primary">管理计量</a>
                 <a href="#add" class="btn btn-sm btn-primary" data-toggle="modal" data-target="#add">创建实际计量</a>
@@ -35,16 +35,16 @@
                             <a class="nav-link active" data-toggle="tab" href="#huizong" role="tab">汇总</a>
                         </li>
                         <li class="nav-item">
-                            <% if (scheduleStage.length > 0) { %>
+                            <% if (gclScheduleMonth.length > 0) { %>
                                 <div class="d-inline-block ml-2">
                                     <div class="dropdown">
                                         <button class="btn btn-sm btn-light dropdown-toggle text-primary" type="button" id="stageDropdownMenuButton" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
-                                            <%- curScheduleStage.yearmonth.split('-')[0] %>年<%- parseInt(curScheduleStage.yearmonth.split('-')[1]) %>月(第<%- curScheduleStage.order %>期)
+                                            <%- curScheduleMonth.yearmonth.split('-')[0] %>年<%- parseInt(curScheduleMonth.yearmonth.split('-')[1]) %>月
                                         </button>
                                         <div class="dropdown-menu" id="stageDropdownMenu" aria-labelledby="stageDropdownMenuButton" x-placement="bottom-start" style="position: absolute; transform: translate3d(0px, 26px, 0px); top: 0px; left: 0px; will-change: transform;">
-                                            <% for (const s of scheduleStage) { %>
-                                                <% if (s.id !== curScheduleStage.id) { %>
-                                                    <a class="dropdown-item change-tp" data-order="<%- s.order %>" href="javascript:void(0);"><%- s.yearmonth.split('-')[0] %>年<%- parseInt(s.yearmonth.split('-')[1]) %>月(第<%- s.order %>期)</a>
+                                            <% for (const s of gclScheduleMonth) { %>
+                                                <% if (s.id !== curScheduleMonth.id) { %>
+                                                    <a class="dropdown-item change-tp" data-order="<%- s.id %>" href="javascript:void(0);"><%- s.yearmonth.split('-')[0] %>年<%- parseInt(s.yearmonth.split('-')[1]) %>月</a>
                                                 <% } %>
                                             <% } %>
                                         </div>
@@ -67,7 +67,7 @@
     const measureType = JSON.parse('<%- JSON.stringify(measureType) %>');
     const schedule = JSON.parse('<%- JSON.stringify(schedule) %>');
     const scheduleMonth = JSON.parse('<%- JSON.stringify(scheduleMonth) %>');
+    const gclScheduleMonth = JSON.parse('<%- JSON.stringify(gclScheduleMonth) %>');
     const monthList = _.map(scheduleMonth, 'yearmonth');
-    const scheduleStage = JSON.parse('<%- JSON.stringify(scheduleStage) %>');
-    const curScheduleStage = JSON.parse('<%- JSON.stringify(curScheduleStage) %>');
+    let curScheduleMonth = gclScheduleMonth.length > 0 ? JSON.parse('<%- JSON.stringify(curScheduleMonth) %>') : null;
 </script>

+ 2 - 2
app/view/schedule/stage_tp.ejs

@@ -29,14 +29,14 @@
                         </div>
                     </div>
                 </div>
-                <% if (!revising) { %>
+                <% if (!revising && ctx.tender.schedule_permission === scPermission.edit) { %>
                 <div class="d-inline-flex">
                     <a href="#delete" data-toggle="modal" data-target="#delete" class="btn btn-sm btn-outline-danger">删除本期进度</a>
                 </div>
                 <% } %>
                 <% } %>
             </div>
-            <% if (!revising) { %>
+            <% if (!revising && ctx.tender.schedule_permission === scPermission.edit) { %>
             <div class="ml-auto">
                 <a href="" class="btn btn-sm btn-primary" data-toggle="modal" data-target="#add">创建新计量进度</a>
                 <% if (scheduleStage.length > 0) { %>

+ 3 - 0
app/view/tender/detail.ejs

@@ -101,6 +101,9 @@
                                 <a href="#bd-set-6" data-toggle="modal" data-target="#bd-set-6" class="btn btn-sm btn-outline-primary">章节设置</a>
                                 <a href="#bd-set-7" data-toggle="modal" data-target="#bd-set-7" class="btn btn-sm btn-outline-primary">付款账号</a>
                                 <i class="mx-2">|</i>
+                                <% if (ctx.session.sessionProject.page_show !== null && parseInt(ctx.session.sessionProject.page_show.xxjd) === 1 && ctx.session.sessionUser.is_admin) { %>
+                                <a href="#xxjd-set" data-toggle="modal" data-target="#xxjd-set" class="btn btn-sm btn-outline-primary">形象进度</a>
+                                <% } %>
                                 <a href="javascript: void(0);" class="btn btn-sm btn-outline-primary" id="copyBtn">拷贝设置</a>
                                 <% if (ctx.session.sessionUser.is_admin) { %>
                                 <a href="/tender/<%- tender.id %>/shenpi" class="btn btn-sm btn-outline-primary">审批流程</a>

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

@@ -1505,3 +1505,227 @@
         });
     }
 </script>
+
+<% if (ctx.session.sessionProject.page_show !== null && parseInt(ctx.session.sessionProject.page_show.xxjd) === 1 && ctx.session.sessionUser.is_admin) { %>
+<!--标段设置-形象进度-->
+<div class="modal fade" id="xxjd-set" 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="alert alert-warning">设置可使用「形象进度」用户</div>
+                <div class="dropdown">
+                    <button class="btn btn-outline-primary btn-sm dropdown-toggle" type="button" id="xxjd_dropdownMenuButton" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
+                        添加用户
+                    </button>
+                    <div class="dropdown-menu" id="xxjd_dropdownMenu" aria-labelledby="xxjd_dropdownMenuButton" style="width:220px">
+                        <div class="mb-2 p-2"><input class="form-control form-control-sm gr-search"
+                                                     placeholder="姓名/手机 检索" autocomplete="off"></div>
+                        <dl class="list-unstyled book-list">
+                            <% accountGroup.forEach((group, idx) => { %>
+                                <dt><a href="javascript: void(0);" class="acc-btn" data-groupid="<%- idx %>" data-type="hide"><i class="fa fa-plus-square"></i></a> <%- group.groupName %></dt>
+                                <div class="dd-content" data-toggleid="<%- idx %>">
+                                    <% group.groupList.forEach(item => { %>
+                                        <% if (item.id !== ctx.tender.data.user_id) { %>
+                                            <dd class="border-bottom p-2 mb-0 " data-id="<%- item.id %>" >
+                                                <p class="mb-0 d-flex"><span class="text-primary"><%- item.name %></span><span
+                                                            class="ml-auto"><%- item.mobile %></span></p>
+                                                <span class="text-muted"><%- item.role %></span>
+                                            </dd>
+                                        <% } %>
+                                    <% });%>
+                                </div>
+                            <% }) %>
+                        </dl>
+                    </div>
+                </div>
+                <div class="mt-1" style="max-height: 300px;overflow: auto">
+                    <table class="table table-bordered">
+                        <tr><th>用户</th><th>查看</th><th>修改</th><th>移除</th></tr>
+                        <tbody id="schedule-users">
+                        <% for (const sa of scheduleAuditList) { %>
+                        <% const audit = ctx.helper._.find(accountList, { id : sa.audit_id }); %>
+                        <% if (audit) { %>
+                        <tr data-id="<%- audit.id %>"><td><p class="mb-0 d-flex"><b class="col-5 pl-0"><%- audit.name %></b> <span class="text-muted"><%- audit.mobile %></span><p class="text-muted mb-0"><%- audit.role %></p></td><td>
+                                <div class="custom-control custom-checkbox mb-2">
+                                    <input type="checkbox" data-zhi="<%- scPermission.show %>" data-id="<%- sa.id %>" id="<%- sa.id %>_customRadio41" name="customCheckbox" class="custom-control-input" <% if (sa.permission !== scPermission.no) { %>checked<% } %>>
+                                    <label class="custom-control-label" for="<%- sa.id %>_customRadio41"></label>
+                                </div>
+                            </td><td>
+                                <div class="custom-control custom-checkbox mb-2">
+                                    <input type="checkbox" data-zhi="<%- scPermission.edit %>" data-id="<%- sa.id %>" id="<%- sa.id %>_customRadio42" name="customCheckbox" class="custom-control-input" <% if (sa.permission === scPermission.edit) { %>checked<% } %>>
+                                    <label class="custom-control-label" for="<%- sa.id %>_customRadio42"></label>
+                                </div>
+                            </td>
+                            <td><a href="javascript:void(0);" class="text-danger remove-schedule-user" data-id="<%- sa.id %>">移除</a></td>
+                        </tr>
+                        <% } %>
+                        <% } %>
+                        </tbody>
+                    </table>
+                </div>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-sm btn-secondary" data-dismiss="modal">关闭</button>
+            </div>
+        </div>
+    </div>
+</div>
+<script>
+    $(function () {
+        const accountGroup = JSON.parse(unescape('<%- escape(JSON.stringify(accountGroup)) %>'));
+        const accountList = JSON.parse(unescape('<%- escape(JSON.stringify(accountList)) %>'));
+        const cur_uid = parseInt('<%- ctx.tender.data.user_id %>');
+        const cur_tenderid = parseInt('<%- ctx.tender.id %>');
+        const scPermission = JSON.parse(unescape('<%- escape(JSON.stringify(scPermission)) %>'));
+        // 形象进度
+        let timer = null;
+        let oldSearchVal = null;
+        $('body').on('input propertychange', '.gr-search', function(e) {
+            oldSearchVal = e.target.value;
+            timer && clearTimeout(timer);
+            timer = setTimeout(() => {
+                const newVal = $(this).val();
+                let html = '';
+                if (newVal && newVal === oldSearchVal) {
+                    accountList.filter(item => item && item.id !== cur_uid && (item.name.indexOf(newVal) !== -1 || (item.mobile && item.mobile.indexOf(newVal) !== -1))).forEach(item => {
+                        html += `<dd class="border-bottom p-2 mb-0 " data-id="${item.id}" >
+                        <p class="mb-0 d-flex"><span class="text-primary">${item.name}</span><span
+                                class="ml-auto">${item.mobile || ''}</span></p>
+                        <span class="text-muted">${item.role || ''}</span>
+                    </dd>`
+                    });
+                    $('#xxjd_dropdownMenu .book-list').empty();
+                    $('#xxjd_dropdownMenu .book-list').append(html);
+                } else {
+                    if (!$('#xxjd_dropdownMenu .acc-btn').length) {
+                        accountGroup.forEach((group, idx) => {
+                            if (!group) return;
+                            html += `<dt><a href="javascript: void(0);" class="acc-btn" data-groupid="${idx}" data-type="hide"><i class="fa fa-plus-square"></i>
+                        </a> ${group.groupName}</dt>
+                        <div class="dd-content" data-toggleid="${idx}">`;
+                            group.groupList.forEach(item => {
+                                if (item.id !== cur_uid) {
+                                    html += `<dd class="border-bottom p-2 mb-0 " data-id="${item.id}" >
+                                    <p class="mb-0 d-flex"><span class="text-primary">${item.name}</span><span
+                                            class="ml-auto">${item.mobile || ''}</span></p>
+                                    <span class="text-muted">${item.role || ''}</span>
+                                </dd>`;
+                                }
+                            });
+                            html += '</div>';
+                        });
+                        $('#xxjd_dropdownMenu .book-list').empty();
+                        $('#xxjd_dropdownMenu .book-list').append(html);
+                    }
+                }
+            }, 400);
+        });
+
+        // 添加审批流程按钮逻辑
+        $('body').on('click', '.book-list dt', function () {
+            const idx = $(this).find('.acc-btn').attr('data-groupid');
+            const type = $(this).find('.acc-btn').attr('data-type');
+            if (type === 'hide') {
+                $(this).parent().find(`div[data-toggleid="${idx}"]`).show(() => {
+                    $(this).children().find('i').removeClass('fa-plus-square').addClass('fa-minus-square-o');
+                    $(this).find('.acc-btn').attr('data-type', 'show');
+
+                })
+            } else {
+                $(this).parent().find(`div[data-toggleid="${idx}"]`).hide(() => {
+                    $(this).children().find('i').removeClass('fa-minus-square-o').addClass('fa-plus-square');
+                    $(this).find('.acc-btn').attr('data-type', 'hide');
+                })
+            }
+            return false;
+        });
+
+        // 选中用户
+        $('body').on('click', 'dl dd', function () {
+            const id = parseInt($(this).data('id'));
+            if (id) {
+                const user = _.find(accountList, function (item) {
+                    return item.id === id;
+                });
+                const saIdList = [];
+                for (let i = 0; i < $('#schedule-users tr').length; i++) {
+                    saIdList.push(parseInt($('#schedule-users tr').eq(i).data('id')));
+                }
+                if (_.includes(saIdList, id)) {
+                    toastr.error('该用户已存在列表中,无需重复添加');
+                    return;
+                }
+
+                const prop = {
+                    audit_id: id,
+                    type: 'add',
+                };
+                postData('/tender/' + cur_tenderid + '/schedule/audit/save', prop, function (data) {
+                    const html = '<tr data-id="'+ user.id + '"><td><p class="mb-0 d-flex"><b class="col-5 pl-0">'+ user.name + '</b> <span class="text-muted">'+ user.mobile + '</span><p class="text-muted mb-0">'+ user.role + '</p></td><td>\n' +
+                        '                                <div class="custom-control custom-checkbox mb-2">\n' +
+                        '                                    <input type="checkbox" data-zhi="'+ scPermission.show +'" data-id="'+ data.id + '" id="'+ data.id + '_customRadio41" name="customCheckbox" class="custom-control-input" checked>\n' +
+                        '                                    <label class="custom-control-label" for="'+ data.id + '_customRadio41"></label>\n' +
+                        '                                </div>\n' +
+                        '                            </td><td>\n' +
+                        '                                <div class="custom-control custom-checkbox mb-2">\n' +
+                        '                                    <input type="checkbox" data-zhi="'+ scPermission.edit +'" data-id="'+ data.id + '" id="'+ data.id + '_customRadio42" name="customCheckbox" class="custom-control-input">\n' +
+                        '                                    <label class="custom-control-label" for="'+ data.id + '_customRadio42"></label>\n' +
+                        '                                </div>\n' +
+                        '                            </td>\n' +
+                        '                            <td><a href="javascript:void(0);" class="text-danger remove-schedule-user" data-id="'+ data.id + '">移除</a></td>\n' +
+                        '                        </tr>';
+                    $('#schedule-users').append(html);
+                });
+            }
+        });
+
+        // 移除用户
+        $('body').on('click', '.remove-schedule-user', function () {
+            const id = parseInt($(this).data('id'));
+            if (id) {
+                const prop = {
+                    id,
+                    type: 'del',
+                };
+                const _self = $(this);
+                postData('/tender/' + cur_tenderid + '/schedule/audit/save', prop, function (data) {
+                    _self.parents('tr').remove();
+                });
+            }
+        });
+
+        // 权限更改
+        $('body').on('click', '#schedule-users input[type="checkbox"]', function () {
+            let permission = scPermission.no;
+            const value = parseInt($(this).data('zhi'));
+            if ($(this).is(':checked')) {
+                if (value === scPermission.edit) {
+                    permission = scPermission.edit;
+                    $(this).parents('td').siblings().find('input').prop('checked', true);
+                } else if (value === scPermission.show) {
+                    permission = scPermission.show;
+                }
+            } else {
+                if (value === scPermission.edit) {
+                    permission = scPermission.show;
+                } else if (value === scPermission.show) {
+                    permission = scPermission.no;
+                    $(this).parents('td').siblings().find('input').prop('checked', false);
+                }
+            }
+            const id = parseInt($(this).data('id'));
+            const prop = {
+                id,
+                permission,
+                type: 'edit',
+            };
+            const _self = $(this);
+            postData('/tender/' + cur_tenderid + '/schedule/audit/save', prop, function (data) {
+            });
+        });
+    })
+</script>
+<% } %>

+ 2 - 0
app/view/tender/tender_sub_menu.ejs

@@ -43,6 +43,7 @@
                 <li <% if (ctx.url === '/tender/' + ctx.tender.id + '/measure/material') { %>class="active"<% } %>><a href="/tender/<%- ctx.tender.id %>/measure/material"><i class="fa fa-line-chart"></i> <span>材料调差</span></a></li>
             </ul>
         </div>
+        <% if (ctx.session.sessionProject.page_show !== null && parseInt(ctx.session.sessionProject.page_show.xxjd) === 1 && ctx.tender.schedule_permission !== 0) { %>
         <div class="nav-box">
             <h3><i class="fa fa-bar-chart "></i> 形象进度</h3>
             <ul class="nav-list list-unstyled sub-list">
@@ -51,6 +52,7 @@
                 <li <% if (ctx.url.indexOf('/tender/' + ctx.tender.id + '/schedule/stage') !== -1) { %>class="active"<% } %>><a href="/tender/<%- ctx.tender.id %>/schedule/stage"><span>计量进度</span></a></li>
             </ul>
         </div>
+        <% } %>
         <div class="nav-box">
             <ul class="nav-list list-unstyled">
                 <li <% if (ctx.url === '/tender/' + ctx.tender.id + '/report') { %>class="active"<% } %>><a href="/tender/<%- ctx.tender.id %>/report"><i class="fa fa-file-text-o"></i> <span>报表</span></a></li>

+ 2 - 0
app/view/tender/tender_sub_mini_menu.ejs

@@ -41,6 +41,7 @@
                 <li <% if (ctx.url === '/tender/' + ctx.tender.id + '/measure/material') { %>class="active"<% } %>><a href="/tender/<%- ctx.tender.id %>/measure/material"><i class="fa fa-line-chart"></i> <span>材料调差</span></a></li>
             </ul>
         </div>
+        <% if (ctx.session.sessionProject.page_show !== null && parseInt(ctx.session.sessionProject.page_show.xxjd) === 1 && ctx.tender.schedule_permission !== 0) { %>
         <div class="nav-box">
             <h3><i class="fa fa-bar-chart "></i> 形象进度</h3>
             <ul class="nav-list list-unstyled sub-list">
@@ -49,6 +50,7 @@
                 <li <% if (ctx.url === '/tender/' + ctx.tender.id + '/schedule/stage' || ctx.url === '/tender/' + ctx.tender.id + '/schedule/stage/gcl') { %>class="active"<% } %>><a href="/tender/<%- ctx.tender.id %>/schedule/stage"><span>计量进度</span></a></li>
             </ul>
         </div>
+        <% } %>
         <div class="nav-box">
             <ul class="nav-list list-unstyled">
                 <li <% if (ctx.url === '/tender/' + ctx.tender.id + '/report') { %>class="active"<% } %>><a href="/tender/<%- ctx.tender.id %>/report"><i class="fa fa-file-text-o"></i> <span>报表</span></a></li>