Browse Source

期计量相关,第一次提交:
1. 整体路由调整
2. 本期计量台账,部分

MaiXinRong 6 years ago
parent
commit
953caa6f5d
46 changed files with 3914 additions and 561 deletions
  1. 1 0
      app/base/base_controller.js
  2. 93 35
      app/const/spread.js
  3. 2 0
      app/controller/ledger_controller.js
  4. 107 2
      app/controller/measure_controller.js
  5. 177 86
      app/controller/stage_controller.js
  6. 1 1
      app/controller/standard_lib_controller.js
  7. 7 0
      app/middleware/session_auth.js
  8. 4 8
      app/middleware/tender_check.js
  9. 4 0
      app/middleware/url_parse.js
  10. 69 76
      app/public/css/main.css
  11. 4 4
      app/public/js/category.js
  12. 2 2
      app/public/js/datepicker/datepicker.zh.js
  13. 38 37
      app/public/js/ledger.js
  14. 1 1
      app/public/js/ledger_audit.js
  15. 1 1
      app/public/js/measure_work.js
  16. 190 77
      app/public/js/path_tree.js
  17. 29 1
      app/public/js/spreadjs_rela/spreadjs_zh.js
  18. 437 0
      app/public/js/stage.js
  19. 3 3
      app/public/js/tender.js
  20. 2 2
      app/public/js/tender_list.js
  21. 76 51
      app/router.js
  22. 3 2
      app/service/ledger.js
  23. 21 0
      app/service/pos.js
  24. 37 6
      app/service/stage.js
  25. 194 0
      app/service/stage_bills.js
  26. 285 0
      app/service/stage_pos.js
  27. 6 5
      app/service/standard_lib.js
  28. 1 0
      app/view/layout/layout.ejs
  29. 0 3
      app/view/ledger/explode.ejs
  30. 33 0
      app/view/measure/compare.ejs
  31. 22 0
      app/view/measure/compare_modal.ejs
  32. 44 0
      app/view/measure/gather.ejs
  33. 60 0
      app/view/measure/stage.ejs
  34. 162 0
      app/view/measure/stage_modal.ejs
  35. 200 0
      app/view/stage/change.ejs
  36. 39 0
      app/view/stage/compare.ejs
  37. 21 0
      app/view/stage/compare_modal.ejs
  38. 157 0
      app/view/stage/detail.ejs
  39. 58 0
      app/view/stage/detail_modal.ejs
  40. 280 125
      app/view/stage/index.ejs
  41. 459 31
      app/view/stage/modal.ejs
  42. 211 0
      app/view/stage/pay.ejs
  43. 275 0
      app/view/stage/pay_modal.ejs
  44. 1 1
      app/view/tender/tender_sub_menu.ejs
  45. 2 1
      config/config.qa.js
  46. 95 0
      config/menu.js

+ 1 - 0
app/base/base_controller.js

@@ -111,6 +111,7 @@ class BaseController extends Controller {
         if (error.stack) {
             this.ctx.logger.error(error);
         } else {
+            this.setMessage(error, messageType.ERROR);
             this.ctx.getLogger('fail').info(JSON.stringify({
                 error: error,
                 project: this.ctx.session.sessionProject,

+ 93 - 35
app/const/spread.js

@@ -10,6 +10,7 @@
 
 const tzWithoutCols = ['deal_qty', 'deal_tp'];
 const dgnCols = ['dgn_qty1', 'dgn_qty2', 'dgn_price'];
+const posTzWithoutCols = ['add_stage'];
 
 const ledgerSpread = {
     cols: [
@@ -46,58 +47,115 @@ const ledgerPosSpread = {
     defaultRowHeight: 21,
 };
 
-const measureSpread = {
+const stage = {
+    ledger: {
+        cols: [
+            {title: '项目节编号', colSpan: '1', rowSpan: '2', field: 'code', hAlign: 0, width: 150, formatter: '@', readOnly: true, cellType: 'tree'},
+            {title: '清单编号', colSpan: '1', rowSpan: '2', field: 'b_code', hAlign: 0, width: 80, formatter: '@', readOnly: true},
+            {title: '名称', colSpan: '1', rowSpan: '2', field: 'name', hAlign: 0, width: 230, formatter: '@', readOnly: true},
+            {title: '单位', colSpan: '1', rowSpan: '2', field: 'unit', hAlign: 1, width: 60, formatter: '@', readOnly: true},
+            {title: '单价', colSpan: '1', rowSpan: '2', field: 'unit_price', hAlign: 2, width: 60, readOnly: true, type: 'Number'},
+            {title: '设计数量|数量1',  colSpan: '2|1', rowSpan: '1|1', field: 'dgn_qty1', hAlign: 2, width: 60, readOnly: true, type: 'Number'},
+            {title: '|数量2',  colSpan: '|1', rowSpan: '|1', field: 'dgn_qty2', hAlign: 2, width: 60, readOnly: true, type: 'Number'},
+            {title: '经济指标',  colSpan: '1', rowSpan: '2', field: 'dgn_price', hAlign: 2, width: 60, readOnly: true, type: 'Number'},
+            {title: '签约|数量', colSpan: '2|1', rowSpan: '1|1', field: 'deal_qty', hAlign: 2, width: 60, readOnly: true, type: 'Number'},
+            {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'deal_tp', hAlign: 2, width: 60, readOnly: true, type: 'Number'},
+            {title: '施工图复核|数量', colSpan: '2|1', rowSpan: '1|1', field: 'quantity', hAlign: 2, width: 60, readOnly: true, type: 'Number'},
+            {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'total_price', hAlign: 2, width: 60, readOnly: true, type: 'Number'},
+            {title: '本期合同计量|数量', colSpan: '2|1', rowSpan: '1|1', field: 'contract_qty', hAlign: 2, width: 60, type: 'Number'},
+            {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'contract_tp', hAlign: 2, width: 60, type: 'Number'},
+            {title: '本期数量变更|数量', colSpan: '3|1', rowSpan: '1|1', field: 'qc_qty', hAlign: 2, width: 60, type: 'Number'},
+            {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'qc_tp', hAlign: 2, width: 60, type: 'Number'},
+            {title: '|变更令', colSpan: '|1', rowSpan: '|1', field: 'qc_tp_bgl', hAlign: 2, width: 60, readOnly: true, formatter: '@'},
+            {title: '本期完成计量|数量', colSpan: '2|1', rowSpan: '1|1', field: 'gather_qty', hAlign: 2, width: 60, readOnly: true, type: 'Number'},
+            {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'gather_tp', hAlign: 2, width: 60, readOnly: true, type: 'Number'},
+            {title: '截止本期合同计量|数量', colSpan: '2|1', rowSpan: '1|1', field: 'end_contract_qty', hAlign: 2, width: 60, readOnly: true, type: 'Number'},
+            {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'end_contract_tp', hAlign: 2, width: 60, readOnly: true, type: 'Number'},
+            {title: '截止本期数量变更|数量', colSpan: '2|1', rowSpan: '1|1', field: 'end_qc_qty', hAlign: 2, width: 60, readOnly: true, type: 'Number'},
+            {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'end_qc_tp', hAlign: 2, width: 60, readOnly: true, type: 'Number'},
+            {title: '截止本期完成计量|数量', colSpan: '2|1', rowSpan: '1|1', field: 'end_gather_qty', hAlign: 2, width: 60, readOnly: true, type: 'Number'},
+            {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'end_gather_tp', hAlign: 2, width: 60, readOnly: true, type: 'Number'},
+            {title: '图(册)号', colSpan: '1', rowSpan: '2', field: 'drawing_code', hAlign: 0, width: 80, formatter: '@', readOnly: true},
+            {title: '累计完成率(%)', colSpan: '1', rowSpan: '2', field: 'percent', hAlign: 0, width: 100, readOnly: true, type: 'Number'},
+            {title: '备注', colSpan: '1', rowSpan: '2', field: 'memo', hAlign: 0, width: 100, formatter: '@', readOnly: true},
+        ],
+        emptyRows: 0,
+        headRows: 2,
+        headRowHeight: [32, 32],
+        defaultRowHeight: 21,
+    },
+    pos: {
+        cols: [
+            {title: '部位名称', colSpan: '1', rowSpan: '2', field: 'name', hAlign: 0, width: 180, formatter: '@', readOnly: true},
+            {title: '复核数量', colSpan: '1', rowSpan: '2', field: 'quantity', hAlign: 2, width: 60, formatter: '@'},
+            {title: '本期|合同计量', colSpan: '4|1', rowSpan: '1|1', field: 'contract_qty', hAlign: 2, width: 60, type: 'Number'},
+            {title: '|数量变更计量', colSpan: '|1', rowSpan: '|1', field: 'qc_qty', hAlign: 2, width: 80, type: 'Number'},
+            {title: '|变更令', colSpan: '|1', rowSpan: '|1', field: 'qc_bgl', hAlign: 0, width: 60, formatter: '@', readOnly: true},
+            {title: '|完成计量', colSpan: '|1', rowSpan: '|1', field: 'gather_qty', hAlign: 2, width: 60, type: 'Number', readOnly: true},
+            {title: '截止|合同计量', colSpan: '4|1', rowSpan: '1|1', field: 'end_contract_qty', hAlign: 2, width: 60, type: 'Number', readOnly: true},
+            {title: '|数量变更计量', colSpan: '|1', rowSpan: '|1', field: 'end_qc_qty', hAlign: 2, width: 80, type: 'Number', readOnly: true},
+            {title: '|变更令', colSpan: '|1', rowSpan: '|1', field: 'end_qc_bgl', hAlign: 0, width: 60, formatter: '@', readOnly: true},
+            {title: '|完成计量', colSpan: '|1', rowSpan: '|1', field: 'end_gather_qty', hAlign: 2, width: 60, type: 'Number', readOnly: true},
+            {title: '本期批注', colSpan: '1', rowSpan: '2', field: 'b_code', hAlign: 0, width: 80, formatter: '@'},
+            {title: '图册号', colSpan: '1', rowSpan: '2', field: 'b_code', hAlign: 0, width: 80, formatter: '@', readOnly: true},
+            {title: '添加期数', colSpan: '1', rowSpan: '2', field: 'add_stage', hAlign:1, width: 80, readOnly: true},
+        ],
+        emptyRows: 3,
+        headRows: 2,
+        headRowHeight: [32, 32],
+        defaultRowHeight: 21,
+    },
+};
+
+// 计量台账 - cols需要二次计算得到
+const measure = {
+    gather: {},
+    compare: {},
+};
+// 计量台账 -- 清单汇总 -- 工程量清单汇总
+measure.gather.billsSpread = {
     cols: [
-        {title: '项目节编号', colSpan: '1', rowSpan: '2', field: 'code', hAlign: 0, width: 150, formatter: '@', readOnly: true, cellType: 'tree'},
-        {title: '清单编号', colSpan: '1', rowSpan: '2', field: 'b_code', hAlign: 0, width: 80, formatter: '@', readOnly: true},
-        {title: '名称', colSpan: '1', rowSpan: '2', field: 'name', hAlign: 0, width: 230, formatter: '@', readOnly: true},
-        {title: '单位', colSpan: '1', rowSpan: '2', field: 'unit', hAlign: 1, width: 60, formatter: '@', readOnly: true},
-        {title: '单价', colSpan: '1', rowSpan: '2', field: 'unit_price', hAlign: 2, width: 60, readOnly: true, type: 'Number'},
-        {title: '0号台账合同|数量', colSpan: '2|1', rowSpan: '1|1', field: 'quantity', hAlign: 2, width: 60, readOnly: true, type: 'Number'},
-        {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'total_price', hAlign: 2, width: 60, readOnly: true, type: 'Number'},
-        {title: '本期合同计量|数量', colSpan: '2|1', rowSpan: '1|1', field: 'deal_quantity', hAlign: 2, width: 60, type: 'Number'},
-        {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'deal_totalprice', hAlign: 2, width: 60, type: 'Number'},
-        {title: '本期数量变更|数量', colSpan: '2|1', rowSpan: '1|1', field: 'qc_quantity', hAlign: 2, width: 60, type: 'Number'},
-        {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'qc_totalprice', hAlign: 2, width: 60, type: 'Number'},
-        {title: '本期完成计量|数量', colSpan: '2|1', rowSpan: '1|1', field: 'gather_quantity', hAlign: 2, width: 60, readOnly: true, type: 'Number'},
-        {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'gather_totalprice', hAlign: 2, width: 60, readOnly: true, type: 'Number'},
-        {title: '图(册)号', colSpan: '1', rowSpan: '2', field: 'drawing_code', hAlign: 0, width: 80, formatter: '@', readOnly: true},
-        {title: '备注', colSpan: '1', rowSpan: '2', field: 'memo', hAlign: 0, width: 100, formatter: '@', readOnly: true},
+        {title: '清单编号', colSpan: '1', rowSpan: '1', field: 'b_code', hAlign: 0, width: 120, formatter: '@'},
+        {title: '名称', colSpan: '1', rowSpan: '1', field: 'name', hAlign: 0, width: 230, formatter: '@'},
+        {title: '单位', colSpan: '1', rowSpan: '1', field: 'unit', hAlign: 0, width: 60, formatter: '@'},
+        {title: '单价', colSpan: '1', rowSpan: '1', field: 'unit_price', hAlign: 2, width: 60, type: 'Number'},
+    ],
+    emptyRows: 0,
+    headRows: 1,
+    headRowHeight: [40],
+    defaultRowHeight: 21,
+};
+measure.compare.ledger = {
+    cols: [
+        {title: '项目节编号', colSpan: '1', rowSpan: '2', field: 'code', hAlign: 0, width: 150, formatter: '@', cellType: 'tree'},
+        {title: '清单编号', colSpan: '1', rowSpan: '2', field: 'b_code', hAlign: 0, width: 80, formatter: '@'},
+        {title: '名称', colSpan: '1', rowSpan: '2', field: 'name', hAlign: 0, width: 230, formatter: '@'},
+        {title: '单位', colSpan: '1', rowSpan: '2', field: 'unit', hAlign: 1, width: 50, formatter: '@'},
+        {title: '单价', colSpan: '1', rowSpan: '2', field: 'unit_price', hAlign: 2, width: 60, type: 'Number'},
     ],
     emptyRows: 3,
     headRows: 2,
     headRowHeight: [40, 40],
     defaultRowHeight: 21,
 };
-
-const measurePosSpread = {
+measure.compare.pos = {
     cols: [
-        {title: '部位名称', colSpan: '1', rowSpan: '2', field: 'name', hAlign: 0, width: 230, formatter: '@', readOnly: true},
-        {title: '复核数量', colSpan: '1', rowSpan: '2', field: 'quantity', hAlign: 0, width: 60, formatter: '@', readOnly: true},
-        {title: '本期|合同计量', colSpan: '4|1', rowSpan: '1|1', field: 'deal_quantity', hAlign: 0, width: 60, formatter: '@', readOnly: true},
-        {title: '|数量变更计量', colSpan: '|1', rowSpan: '|1', field: 'qc_quantity', hAlign: 0, width: 60, formatter: '@', readOnly: true},
-        {title: '|变更令', colSpan: '|1', rowSpan: '|1', field: 'qc_bgl', hAlign: 0, width: 60, formatter: '@', readOnly: true},
-        {title: '|完成计量', colSpan: '|1', rowSpan: '|1', field: 'gather_quantity', hAlign: 0, width: 60, formatter: '@', readOnly: true},
-        {title: '截止|合同计量', colSpan: '4|1', rowSpan: '1|1', field: 'b_code', hAlign: 0, width: 60, formatter: '@', readOnly: true},
-        {title: '|数量变更计量', colSpan: '|1', rowSpan: '|1', field: 'b_code', hAlign: 0, width: 60, formatter: '@', readOnly: true},
-        {title: '|变更令', colSpan: '|1', rowSpan: '|1', field: 'b_code', hAlign: 0, width: 60, formatter: '@', readOnly: true},
-        {title: '|完成计量', colSpan: '|1', rowSpan: '|1', field: 'b_code', hAlign: 0, width: 60, formatter: '@', readOnly: true},
-        {title: '本期批注', colSpan: '1', rowSpan: '2', field: 'b_code', hAlign: 0, width: 80, formatter: '@', readOnly: true},
-        {title: '图册号', colSpan: '1', rowSpan: '2', field: 'b_code', hAlign: 0, width: 80, formatter: '@', readOnly: true},
+        {title: '名称', colSpan: '1', rowSpan: '1', field: 'name', hAlign: 0, width: 230, formatter: '@'},
     ],
     emptyRows: 3,
-    headRows: 2,
-    headRowHeight: [40, 40],
+    headRows: 1,
+    headRowHeight: [40],
     defaultRowHeight: 21,
 };
 
 module.exports = {
     ledgerSpread,
     ledgerPosSpread,
-    measureSpread,
-    measurePosSpread,
+    stage,
     filterCols: {
         tzWithoutCols,
         dgnCols,
-    }
+        posTzWithoutCols,
+    },
+    measure,
 };

+ 2 - 0
app/controller/ledger_controller.js

@@ -162,6 +162,7 @@ module.exports = app => {
                 if (!ctx.tender.data || ctx.tender.data.user_id !== ctx.session.sessionUser.accountId || this._ledgerReadOnly()) {
                     throw '标段数据错误';
                 }
+                const data = JSON.parse(ctx.request.body.data);
 
                 switch (data.postType) {
                     case 'add':
@@ -240,6 +241,7 @@ module.exports = app => {
                 }
                 const data = JSON.parse(ctx.request.body.data);
                 responseData.data = await ctx.service.ledger.updateCalc(ctx.tender.id, data);
+                console.log(responseData.data);
             } catch (err) {
                 this.log(err);
                 responseData.err = 1;

+ 107 - 2
app/controller/measure_controller.js

@@ -12,6 +12,7 @@ const spreadConst = require('../const/spread');
 const codeRuleConst = require('../const/code_rule');
 const auditConst = require('../const/audit').flow;
 const moment = require('moment');
+const measureType = require('../const/tender').measureType;
 
 module.exports = app => {
 
@@ -30,6 +31,110 @@ module.exports = app => {
             ctx.showTitle = true;
         }
 
+        /**
+         * 期计量(Get)
+         * @param ctx
+         * @returns {Promise<void>}
+         */
+        async stage(ctx) {
+            try {
+                const renderData = {
+                    tender: ctx.tender.data,
+                    tenderMenu: this.menu.tenderMenu,
+                    preUrl: '/tender/' + ctx.tender.id,
+                    auditConst,
+                };
+                renderData.stages = await ctx.service.stage.getValidStages(ctx.tender.id);
+                for (const s of renderData.stages) {
+                    s.curAuditor = null;
+                }
+                await this.layout('measure/stage.ejs', renderData, 'measure/stage_modal.ejs');
+            } catch (err) {
+                this.log(err);
+                ctx.redirect(this.menu.menu.dashboard.url);
+            }
+        }
+
+        /**
+         * 新增期(Post)
+         * @param ctx
+         * @returns {Promise<void>}
+         */
+        async addStage(ctx) {
+            try {
+                if (ctx.session.sessionUser.accountId !== ctx.tender.data.user_id) {
+                    throw '您无权创建计量期';
+                }
+                const date = ctx.request.body.date;
+                const period = ctx.request.body.period;
+                const newStage = await ctx.service.stage.addStage(ctx.tender.id, date, period);
+                if (!newStage) {
+                    throw '新增计量期失败,请重试';
+                }
+                ctx.redirect('/tender/' + ctx.tender.id + '/measure/stage/' + newStage.order);
+            } catch (err) {
+                this.log(err);
+                ctx.redirect(ctx.request.header.referer)
+            }
+        }
+
+        async saveStage(ctx) {
+            try {
+                const data = JSON.parse(ctx.request.body.data);
+                const stage = await ctx.service.stage.getDataByCondition({
+                    tid: ctx.tender.id,
+                    order: data.order,
+                });
+                if (!stage) {
+                    throw '提交数据错误';
+                }
+                if (stage.status !== auditConst.status.uncheck) {
+                    throw '该计量期当前不可编辑';
+                }
+                if (ctx.session.sessionUser.accountId !== stage.user_id) {
+                    throw '您无权修改该数据';
+                }
+                await this.ctx.service.stage.saveStage(ctx.tender.id, data.order, data.date, data.period);
+                ctx.body = {err: 0, msg: '', data: null};
+            } catch (err) {
+                this.log(err);
+                ctx.body = {err: 1, msg: err.toString(), data: null};
+            }
+        }
+
+        /**
+         * 清单汇总
+         * @param ctx
+         * @returns {Promise<void>}
+         */
+        async gather(ctx) {
+            try {
+                const renderData = {
+                    tender: ctx.tender.data,
+                    tenderMenu: this.menu.tenderMenu,
+                    preUrl: '/tender/' + ctx.tender.id,
+                }
+                await this.layout('measure/gather.ejs', renderData);
+            } catch (err) {
+                this.log(err);
+                ctx.redirect(this.menu.menu.dashboard.url);
+            }
+        }
+
+        async compare(ctx) {
+            try {
+                const renderData = {
+                    tender: ctx.tender.data,
+                    tenderMenu: this.menu.tenderMenu,
+                    preUrl: '/tender/' + ctx.tender.id,
+                }
+                await this.layout('measure/compare.ejs', renderData, 'measure/compare_modal.ejs');
+            } catch (err) {
+                this.log(err);
+                ctx.redirect(this.menu.menu.dashboard.url);
+            }
+        }
+
         async list(ctx) {
             try {
                 const tenderList = ctx.tenderList;
@@ -70,7 +175,7 @@ module.exports = app => {
                 await this.layout('measure/wlist.ejs', renderData, 'measure/wlist_modal.ejs');
             } catch (err) {
                 this.log(err);
-                this.redirect(ctx.menuList.dashboard.url);
+                ctx.redirect(ctx.menuList.dashboard.url);
             }
         }
 
@@ -118,7 +223,7 @@ module.exports = app => {
                 await this.layout('measure/work.ejs', renderData, 'measure/work_modal.ejs');
             } catch (err) {
                 this.log(err);
-                this.redirect(ctx.menuList.dashboard.url);
+                ctx.redirect(ctx.menuList.dashboard.url);
             }
         }
 

+ 177 - 86
app/controller/stage_controller.js

@@ -9,7 +9,9 @@
  */
 
 const moment = require('moment');
-const audit = require('../const/audit');
+const auditConst = require('../const/audit');
+const spreadConst = require('../const/spread');
+const measureType = require('../const/tender').measureType;
 
 module.exports = app => {
     class StageController extends app.BaseController {
@@ -27,136 +29,225 @@ module.exports = app => {
         }
 
         /**
+         * 获取通用的renderData(用于layout, Menu, subMenu部分)
+         * @param ctx
+         * @returns {{tender, tenderMenu, auditConst}}
+         * @private
+         */
+        _getDefaultRenderData (ctx) {
+            const data = {
+                tender: ctx.tender.data,
+                tenderMenu: JSON.parse(JSON.stringify(this.menu.stageMenu)),
+                auditConst: auditConst.flow,
+                measureType,
+                preUrl: '/tender/' + ctx.tender.id + '/measure/stage/' + ctx.params.order,
+                stage: ctx.stage,
+            };
+            data.tenderMenu.back.children[0].url = '/tender/' + ctx.tender.id + '/measure/stage';
+            return data;
+        }
+
+        _stageReadOnly(stage) {
+            return stage.status === auditConst.flow.status.checked;
+        }
+
+        /**
+         * 获取SpreadSetting
+         * @private
+         */
+        _getSpreadSetting() {
+            const _ = this.app._;
+            function removeFieldCols(setting, cols) {
+                _.remove(setting.cols, function (c) {
+                    return cols.indexOf(c.field) > -1;
+                });
+            }
+            const ledger = JSON.parse(JSON.stringify(spreadConst.stage.ledger));
+            const pos = JSON.parse(JSON.stringify(spreadConst.stage.pos));
+            const tender = this.ctx.tender, stage = this.ctx.stage;
+            if (this._stageReadOnly(stage)) {
+                ledger.readOnly = true;
+                pos.readOnly = true;
+            }
+            if (tender.data.measure_type === measureType.tz.value) {
+                removeFieldCols(ledger, spreadConst.filterCols.tzWithoutCols);
+                removeFieldCols(pos, spreadConst.filterCols.posTzWithoutCols);
+            }
+            if (!tender.info.display.ledger.dgnQty) {
+                removeFieldCols(ledger, spreadConst.filterCols.dgnCols);
+            }
+            return [ledger, pos];
+        }
+
+        /**
+         * 获取期数据
+         * @param ctx
+         * @returns {Promise<void>}
+         * @private
+         */
+        async _getStage(ctx) {
+            const stageOrder = parseInt(ctx.params.order);
+            ctx.stage = await ctx.service.stage.getDataByCondition({
+                tid: ctx.tender.id,
+                order: stageOrder,
+            });
+        }
+
+        /**
          * 期计量页面 (Get)
          * @param {Object} ctx - egg全局变量
          * @returns {Promise<void>}
          */
         async index(ctx) {
             try {
-                const tenderList = await this.service.tender.getList();
-
-                const stages = await ctx.service.stage.getAllDataByCondition({
-                    where: {tid: ctx.tender.id},
-                    order: ['order'],
-                });
-                const renderData = {
-                    moment,
-                    tender: ctx.tender.data,
-                    tenderList,
-                    stages,
-                    auditConst: audit.flow,
-                };
-
+                await this._getStage(ctx);
+                const renderData = this._getDefaultRenderData(ctx);
+                [renderData.ledgerSpread, renderData.posSpread] = this._getSpreadSetting();
+                renderData.ledgerData = await ctx.service.ledger.getDataByTenderId(ctx.tender.id, -1);
+                renderData.curStageData = await ctx.service.stageBills.getAuditorStageData(ctx.tender.id, ctx.stage.id, ctx.stage.times, 0);
+                // 查询截止上期数据
+                //if (ctx.stage.order > 0) {
+                    //renderData.preStageData
+                //}
                 await this.layout('stage/index.ejs', renderData, 'stage/modal.ejs');
             } catch (err) {
-                console.log(err);
-                ctx.redirect('/dashboard');
+                this.log(err);
+                ctx.redirect('/tender/' + ctx.tender.id + '/measure/stage');
             }
         }
 
         /**
-         * 新增期 (Post)
-         *
-         * @param {Object} ctx - egg全局变量
+         * 获取期数据(截止上期 & 本期)
+         * @param ctx
          * @returns {Promise<void>}
          */
-        async add(ctx) {
+        async getStagePosData(ctx) {
             try {
-                const data = JSON.parse(ctx.request.body.data);
-                const stage = await ctx.service.stage.add(tenderId, data.name, moment(data.time).toDate());
-                console.log(stage);
-
-                ctx.body = { err: 0, msg: '', data: stage };
+                await this._getStage(ctx);
+                const condition = JSON.parse(ctx.request.body.data) || {};
+                condition.tid = ctx.tender.id;
+                const responseData = {err: 0, msg: '', data: {}};
+                responseData.data.pos = await ctx.service.pos.getPosData(condition);
+                // todo 根据当前人,或指定对象查询数据
+                responseData.data.curStageData = await ctx.service.stagePos.getAuditorStageData(ctx.tender.id,
+                    ctx.stage.id, ctx.stage.times, ctx.stage.curAuditor ? ctx.stage.curAuditor.order : 0,
+                    this.app._.map(responseData.data.pos, 'id'));
+                ctx.body = responseData;
             } catch (err) {
-                console.log(err);
-                ctx.body = { err: 1, msg: err.toString(), data: null};
+                this.log(err);
+                ctx.body = {err: 1, msg: err.toString(), data: null};
             }
         }
 
         /**
-         * 期 -- 本期计量台账 页面 (Get)
-         *
-         * @param {Object} ctx - egg全局变量
+         * 保存数据
+         * @param ctx
          * @returns {Promise<void>}
          */
-        async stageMeasure(ctx) {
+        async updateStageData(ctx) {
             try {
-                const tender = ctx.tenderData;
-                const tenderList = ctx.tenderList;
+                await this._getStage(ctx);
+                // 检查登录用户,是否可操作
+                if (ctx.session.sessionUser.accountId === ctx.stage.user_id) {
+                    if (ctx.stage.status === auditConst.flow.status.checking || ctx.stage.status === auditConst.flow.status.checked) {
+                        throw '该计量期当前您无权操作';
+                    }
+                } else {
+                    // 检查是否可修改
+                    //const curAuditor = await ctx.service.stageAudit.get
+                }
 
-                const order = parseInt(ctx.params.order);
-                const stage = await ctx.service.stage.getDataByCondition({
-                    tid: tender.id,
-                    order: order,
-                });
-                const renderData = {
-                    tender,
-                    tenderList,
-                    stage,
-                    auditConst: audit.flow,
+                const data = JSON.parse(ctx.request.body.data);
+                const responseData = { err: 0, msg: '', data: {}, };
+                if (data.pos) {
+                    responseData.data = await ctx.service.stagePos.updateStageData(data.pos);
+                } else if (data.bills) {
+                    responseData.data.bills = await ctx.service.stageBills.updateStageData(data.bills);
                 }
-                await this.layout('stage/measure.ejs', renderData, 'stage/measure_modal.ejs');
+                ctx.body = responseData;
             } catch (err) {
-                console.log(err);
-                ctx.redirect('/stage');
+                this.log(err);
+                ctx.body = {err: 1, msg: err.toString(), data: null};
             }
         }
 
         /**
-         * 期 -- 合同支付 页面 (Get)
-         *
-         * @param {Object} ctx - egg全局变量
+         * 中间计量
+         * @param ctx
          * @returns {Promise<void>}
          */
-        async stageDeal(ctx) {
+        async detail(ctx) {
             try {
-                const tender = ctx.tenderData;
-                const tenderList = ctx.tenderList;
+                await this._getStage(ctx);
+                const renderData = this._getDefaultRenderData(ctx);
+                await this.layout('stage/detail.ejs', renderData, 'stage/detail_modal.ejs');
+            } catch (err) {
+                this.log(err);
+                ctx.redirect('/tender/' + ctx.tender.id + '/measure/stage');
+            }
+        }
 
-                const order = parseInt(ctx.params.order);
-                const stage = await ctx.service.stage.getDataByCondition({
-                    tid: tender.id,
-                    order: order,
-                });
-                const renderData = {
-                    tender,
-                    tenderList,
-                    stage,
-                    auditConst: audit.flow,
-                };
-                await this.layout('stage/deal.ejs', renderData, 'stage/deal_modal.ejs');
+        /**
+         * 合同支付
+         * @param ctx
+         * @returns {Promise<void>}
+         */
+        async pay(ctx) {
+            try {
+                await this._getStage(ctx);
+                const renderData = this._getDefaultRenderData(ctx);
+                await this.layout('stage/pay.ejs', renderData, 'stage/pay_modal.ejs');
             } catch (err) {
-                console.log(err);
-                ctx.redirect('/stage');
+                this.log(err);
+                ctx.redirect('/tender/' + ctx.tender.id + '/measure/stage');
             }
         }
 
         /**
-         * 期 -- 报表 页面 (Get)
-         *
-         * @param {Object} ctx - egg全局变量
+         * 变更令
+         * @param ctx
+         * @returns {Promise<void>}
+         */
+        async change(ctx) {
+            try {
+                await this._getStage(ctx);
+                const renderData = this._getDefaultRenderData(ctx);
+                await this.layout('stage/change.ejs', renderData);
+            } catch (err) {
+                this.log(err);
+                ctx.redirect('/tender/' + ctx.tender.id + '/measure/stage');
+            }
+        }
+
+        /**
+         * 审核比较
+         * @param ctx
          * @returns {Promise<void>}
          */
-        async stageReport(ctx) {
+        async compare(ctx) {
             try {
-                const tender = ctx.tenderData;
-                const tenderList = ctx.tenderList;
+                await this._getStage(ctx);
+                const renderData = this._getDefaultRenderData(ctx);
+                await this.layout('stage/compare.ejs', renderData, 'stage/compare_modal.ejs');
+            } catch (err) {
+                this.log(err);
+                ctx.redirect('/tender/' + ctx.tender.id + '/measure/stage');
+            }
+        }
 
-                const order = parseInt(ctx.params.order);
-                const stage = await ctx.service.stage.getDataByCondition({
-                    tid: tender.id,
-                    order: order,
-                });
-                const renderData = {
-                    tender,
-                    tenderList,
-                    stage,
-                    auditConst: audit.flow,
-                };
+        /**
+         * 报表
+         * @param ctx
+         * @returns {Promise<void>}
+         */
+        async report(ctx) {
+            try {
+                await this._getStage(ctx);
+                const renderData = this._getDefaultRenderData(ctx);
                 await this.layout('stage/report.ejs', renderData, 'stage/report_modal.ejs');
             } catch (err) {
-                console.log(err);
-                ctx.redirect('/stage');
+                this.log(err);
+                ctx.redirect('/tender/' + ctx.tender.id + '/measure/stage');
             }
         }
     }

+ 1 - 1
app/controller/standard_lib_controller.js

@@ -38,7 +38,7 @@ class StandardLibController extends BaseController {
             if (isNaN(data.list_id) || data.list_id <= 0) {
                 throw '参数错误';
             }
-            const libData = await this.model.getData(data.list_id);
+            const libData = await this.model.getData(data.list_id, -1);
 
             responseData.data = libData;
         } catch (error) {

+ 7 - 0
app/middleware/session_auth.js

@@ -2,6 +2,8 @@
 
 // 加密类
 const crypto = require('crypto');
+const messageType = require('../const/message_type');
+
 module.exports = options => {
     /**
      * session判断中间件
@@ -38,6 +40,11 @@ module.exports = options => {
                     data: '',
                 };
             } else {
+                this.session.message = {
+                    type: messageType.ERROR,
+                    icon: 'exclamation-circle',
+                    message: '登录信息异常,请重新登录',
+                };
                 return this.redirect('/login?referer=' + this.url);
             }
         }

+ 4 - 8
app/middleware/tender_check.js

@@ -22,7 +22,7 @@ module.exports = options => {
     return function* tenderCheck(next) {
         try {
             // 读取标段数据
-            const tender = { id: this.params.id, };
+            const tender = { id: parseInt(this.params.id), };
             if (!tender.id) {
                 throw '当前未打开标段';
             }
@@ -42,20 +42,16 @@ module.exports = options => {
             }
             // todo 校验权限 (标段参与人、分享)
             if (tender.data.project_id !== this.session.sessionProject.id) {
-                this.session.message = {
-                    type: 'error',
-                    icon: 'exclamation-circle',
-                    message: '您无权查看该项目',
-                };
                 throw '您无权查看该项目';
             }
             this.tender = tender;
             yield next;
         } catch (err) {
             // 输出错误到日志
-            if (error.stack) {
-                this.ctx.logger.error(err);
+            if (err.stack) {
+                this.logger.error(err);
             } else {
+                this.setMessage(err, measureType.ERROR);
                 this.getLogger('fail').info(JSON.stringify({
                     error: err,
                     project: this.session.sessionProject,

+ 4 - 0
app/middleware/url_parse.js

@@ -22,6 +22,10 @@ module.exports = options => {
         this.urlInfo = urlInfo;
         // 防止为空
         this.request.headers.referer = this.request.headers.referer === undefined ? '/dashboard' : this.request.headers.referer;
+        // 清空原Message
+        if (this.session) {
+            this.session.message = null;
+        }
         yield next;
     };
 };

+ 69 - 76
app/public/css/main.css

@@ -1,12 +1,12 @@
 /*building SAAS 0.1*/
 /*bootstrap 初始化*/
 body {
-    font-size: 0.9rem;
-    overflow: hidden;
-    background: #e4e7ea
+  font-size: 0.9rem;
+  overflow: hidden;
+  background: #e4e7ea
 }
 .dropdown-menu {
-    font-size: 0.9rem
+  font-size: 0.9rem
 }
 .btn.disabled, .btn:disabled {
   color:#999
@@ -29,30 +29,30 @@ body {
 /*滚动条*/
 /* 滚动条 */
 ::-webkit-scrollbar-thumb:horizontal { /*水平滚动条的样式*/
-	width: 5px;
-	background-color: #ddd;
-	-webkit-border-radius: 6px;
+  width: 5px;
+  background-color: #ddd;
+  -webkit-border-radius: 6px;
 }
 ::-webkit-scrollbar-track-piece {
-	background-color: #fff; /*滚动条的背景颜色*/
-	-webkit-border-radius: 0; /*滚动条的圆角宽度*/
+  background-color: #fff; /*滚动条的背景颜色*/
+  -webkit-border-radius: 0; /*滚动条的圆角宽度*/
 }
 ::-webkit-scrollbar {
-	width: 10px; /*滚动条的宽度*/
-	height: 8px; /*滚动条的高度*/
+  width: 10px; /*滚动条的宽度*/
+  height: 8px; /*滚动条的高度*/
 }
 ::-webkit-scrollbar-thumb:vertical { /*垂直滚动条的样式*/
-	height: 50px;
-	background-color: #ddd;
-	-webkit-border-radius: 6px;
-	outline: 1px solid #fff;
-	outline-offset: -1px;
-	border: 1px solid #fff;
+  height: 50px;
+  background-color: #ddd;
+  -webkit-border-radius: 6px;
+  outline: 1px solid #fff;
+  outline-offset: -1px;
+  border: 1px solid #fff;
 }
 ::-webkit-scrollbar-thumb:hover { /*滚动条的hover样式*/
-	height: 50px;
-	background-color: #999;
-	-webkit-border-radius: 6px;
+  height: 50px;
+  background-color: #999;
+  -webkit-border-radius: 6px;
 }
 .sjs-height-1,.sjs-height-2,.sjs-sh-1,.sjs-sh-2,.sjs-sh-3,.sjs-sh-4,.sjs-sh-5{
   overflow: hidden;
@@ -69,46 +69,46 @@ body {
   overflow-y: auto;
 }
 .form-signin {
-    max-width: 500px;
-    margin: 150px auto;
+  max-width: 500px;
+  margin: 150px auto;
 }
 .has-danger {
-    -webkit-animation: shake 1s .2s ease both;
-    -moz-animation: shake 1s .2s ease both;
-    animation: shake 1s .2s ease both;
+  -webkit-animation: shake 1s .2s ease both;
+  -moz-animation: shake 1s .2s ease both;
+  animation: shake 1s .2s ease both;
 }
 @-webkit-keyframes shake {
-    0%, 100% {
-        -webkit-transform: translateX(0);
-    }
-    10%, 30%, 50%, 70%, 90% {
-        -webkit-transform: translateX(-10px);
-    }
-    20%, 40%, 60%, 80% {
-        -webkit-transform: translateX(10px);
-    }
+  0%, 100% {
+    -webkit-transform: translateX(0);
+  }
+  10%, 30%, 50%, 70%, 90% {
+    -webkit-transform: translateX(-10px);
+  }
+  20%, 40%, 60%, 80% {
+    -webkit-transform: translateX(10px);
+  }
 }
 @-moz-keyframes shake {
-    0%, 100% {
-        -moz-transform: translateX(0);
-    }
-    10%, 30%, 50%, 70%, 90% {
-        -moz-transform: translateX(-10px);
-    }
-    20%, 40%, 60%, 80% {
-        -moz-transform: translateX(10px);
-    }
+  0%, 100% {
+    -moz-transform: translateX(0);
+  }
+  10%, 30%, 50%, 70%, 90% {
+    -moz-transform: translateX(-10px);
+  }
+  20%, 40%, 60%, 80% {
+    -moz-transform: translateX(10px);
+  }
 }
 @keyframes shake {
-    0%, 100% {
-        transform: translateX(0);
-    }
-    10%, 30%, 50%, 70%, 90% {
-        transform: translateX(-10px);
-    }
-    20%, 40%, 60%, 80% {
-        transform: translateX(10px);
-    }
+  0%, 100% {
+    transform: translateX(0);
+  }
+  10%, 30%, 50%, 70%, 90% {
+    transform: translateX(-10px);
+  }
+  20%, 40%, 60%, 80% {
+    transform: translateX(10px);
+  }
 }
 /*2.主体框架*/
 .header {
@@ -220,9 +220,6 @@ body {
   line-height: 16px;
   font-size: 14px
 }
-.side-bar{
-  height: 31px;
-}
 .sub-content{
   margin:0;
 }
@@ -230,8 +227,8 @@ body {
   padding-right:46px
 }
 .bcontent-wrap{
-    height: 350px
-  }
+  height: 350px
+}
 @media only screen and (max-height: 768px) {
   .bcontent-wrap{
     height: 250px
@@ -239,22 +236,22 @@ body {
 }
 /*滚动*/
 .scrollbar-auto {
-    overflow-y: auto;
-    position: absolute;
-    bottom: 0;
-    left: 0;
-    top: 0;
-    right: 0;
+  overflow-y: auto;
+  position: absolute;
+  bottom: 0;
+  left: 0;
+  top: 0;
+  right: 0;
 }
 .panel-sidebar .scrollbar-auto{
-    padding-top: 20px;
-    box-sizing: border-box;
+  padding-top: 20px;
+  box-sizing: border-box;
 }
 .panel-sidebar .scrollbar-auto {
-    height: 100%;
-    width: 100%;
-    overflow-y: auto;
-    position: static;
+  height: 100%;
+  width: 100%;
+  overflow-y: auto;
+  position: static;
 }
 /*头部*/
 .header .logo {
@@ -374,9 +371,9 @@ body {
 }
 .bg-nav > li.active a span{
   display: block;
-  }
+}
 .bg-nav > li + li {
-    margin-top:0;
+  margin-top:0;
 }
 .bg-nav .sub-menu {
   list-style:none;
@@ -432,10 +429,6 @@ body {
   background:#e4e7ea;
   font-weight: 600
 }
-.nav-list li a.active{
-  background:#e4e7ea;
-  font-weight: 600
-}
 /*内容区*/
 .c-header {
   padding:0 0 5px
@@ -456,7 +449,7 @@ body {
   color:#f90000
 }
 .bg-gray {
- background-color:#bbb!important;
+  background-color:#bbb!important;
 }
 .datepickers-container {
   z-index: 9999

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

@@ -207,7 +207,7 @@ $(document).ready(() => {
             name: name.val(),
             type: parseInt(type.val()),
         };
-        postData('/api/setting/category/add', data, function (data) {
+        postData('/setting/category/add', data, function (data) {
             InitCategoryData(data);
             $('#categoryList').append(getCategoryHtml(data));
             bindCategoryControl();
@@ -246,7 +246,7 @@ $(document).ready(() => {
             id: parseInt($(this).attr('cid')),
             updateValue: valueArr,
         };
-        postData('/api/setting/category/value', data, function (data) {
+        postData('/setting/category/value', data, function (data) {
             tenders = data.tenders;
             const category = findCategory(data.category.id);
             category.value = data.category.value;
@@ -267,7 +267,7 @@ $(document).ready(() => {
             name: name.val(),
             type: parseInt(type.val()),
         }
-        postData('/api/setting/category/update', data, function (data) {
+        postData('/setting/category/update', data, function (data) {
             const category = findCategory(data.id);
             for (const c in data) {
                 category[c] = data[c];
@@ -285,7 +285,7 @@ $(document).ready(() => {
         const data = {
             id: parseInt($(this).attr('cid')),
         }
-        postData('/api/setting/category/del', data, function () {
+        postData('/setting/category/del', data, function () {
             $('tr[cid=' + data.id + ']').remove();
             $('#del-cate').modal('hide');
         }, function () {

+ 2 - 2
app/public/js/datepicker/datepicker.zh.js

@@ -2,8 +2,8 @@
     days: ['周日', '周一', '周二', '周三', '周四', '周五', '周六'],
     daysShort: ['日', '一', '二', '三', '四', '五', '六'],
     daysMin: ['日', '一', '二', '三', '四', '五', '六'],
-    months: ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'],
-    monthsShort: ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'],
+    months: ['01', '02', '03', '04', '05', '06', '07', '08', '09', '10', '11', '12'],
+    monthsShort: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'],
     today: '今天',
     clear: '清除',
     dateFormat: 'yyyy-mm-dd',

+ 38 - 37
app/public/js/ledger.js

@@ -9,6 +9,10 @@ function checkTzMeasureType () {
     return tender.measure_type === measureType.tz.value;
 }
 
+function getTenderId() {
+    return window.location.pathname.split('/')[2];
+}
+
 $(document).ready(function() {
     autoFlashHeight();
     // 初始化台账
@@ -21,7 +25,7 @@ $(document).ready(function() {
         level: 'level',
         rootId: -1,
         keys: ['id', 'tender_id', 'ledger_id'],
-        preUrl: '/ledger'
+        preUrl: '/tender/' + getTenderId() + '/ledger',
     };
     if (checkTzMeasureType()) {
         treeSetting.calcFields = ['total_price'];
@@ -37,7 +41,7 @@ $(document).ready(function() {
     };
     const ledgerTree = createNewPathTree('ledger', treeSetting);
     ledgerTree.loadDatas(ledger);
-    treeCalc.calculateAll(ledgerTree, ledgerTree.setting.calcFields, ledgerTree.setting.calcFun);
+    treeCalc.calculateAll(ledgerTree);
     SpreadJsObj.initSheet(ledgerSpread.getActiveSheet(), ledgerSpreadSetting);
     // 加载台账数据到界面
     SpreadJsObj.loadSheetData(ledgerSpread.getActiveSheet(), 'tree', ledgerTree);
@@ -161,7 +165,7 @@ $(document).ready(function() {
             if (!node) { return; }
 
             SpreadJsObj.massOperationSheet(sheet, function () {
-                tree.baseOperation(preUrl + '/ledger/base-operation', node, 'add', function (result) {
+                tree.baseOperation('/tender/' + getTenderId() + '/ledger/base-operation', node, 'add', function (result) {
                     self.refreshTree(sheet, result);
                     self.refreshOperationValid(sheet, sheet.getSelections());
                 });
@@ -183,7 +187,7 @@ $(document).ready(function() {
             if (!node) { return; }
 
             const count = ledgerTree.getPosterity(node).length;
-            tree.baseOperation(preUrl + '/ledger/base-operation', node, 'delete', function (result) {
+            tree.baseOperation('/tender/' + getTenderId() + '/ledger/base-operation', node, 'delete', function (result) {
                 sheet.deleteRows(row, count + 1);
                 for (const data of result.update) {
                     SpreadJsObj.reLoadRowData(sheet, tree.nodes.indexOf(data), tree.getPosterity(data).length + 1);
@@ -211,7 +215,7 @@ $(document).ready(function() {
             const node = tree.nodes[row];
             if (!node) { return; }
 
-            tree.baseOperation(preUrl + '/ledger/base-operation', node, 'up-move', function (result) {
+            tree.baseOperation('/tender/' + getTenderId() + '/ledger/base-operation', node, 'up-move', function (result) {
                 for (const data of result.update) {
                     SpreadJsObj.reLoadRowData(sheet, tree.nodes.indexOf(data), tree.getPosterity(data).length + 1);
                 }
@@ -237,7 +241,7 @@ $(document).ready(function() {
             const node = tree.nodes[row];
             if (!node) { return; }
 
-            tree.baseOperation(preUrl + '/ledger/base-operation', node, 'down-move', function (result) {
+            tree.baseOperation('/tender/' + getTenderId() + '/ledger/base-operation', node, 'down-move', function (result) {
                 for (const data of result.update) {
                     SpreadJsObj.reLoadRowData(sheet, tree.nodes.indexOf(data), tree.getPosterity(data).length + 1);
                 }
@@ -261,7 +265,7 @@ $(document).ready(function() {
             const node = tree.nodes[row];
             if (!node) { return; }
 
-            tree.baseOperation(preUrl + '/ledger/base-operation', node, 'up-level', function (result) {
+            tree.baseOperation('/tender/' + getTenderId() + '/ledger/base-operation', node, 'up-level', function (result) {
                 self.refreshTree(sheet, result);
                 self.refreshOperationValid(sheet, sheet.getSelections());
             });
@@ -282,7 +286,7 @@ $(document).ready(function() {
             const node = tree.nodes[row];
             if (!node) { return; }
 
-            tree.baseOperation(preUrl + '/ledger/base-operation', node, 'down-level', function (result) {
+            tree.baseOperation('/tender/' + getTenderId() + '/ledger/base-operation', node, 'down-level', function (result) {
                 self.refreshTree(sheet, result);
                 self.refreshOperationValid(sheet, sheet.getSelections());
             });
@@ -304,7 +308,7 @@ $(document).ready(function() {
                 };
                 data[col.field] = col.type === 'Number' ? parseFloat(info.editingText) : info.editingText;
 
-                info.sheet.zh_tree.update(preUrl + '/ledger/update', data, function (result) {
+                info.sheet.zh_tree.update('/tender/' + getTenderId() + '/ledger/update', data, function (result) {
                     treeOperationObj.refreshTree(info.sheet, result);
                 });
             }
@@ -316,6 +320,9 @@ $(document).ready(function() {
          */
         clipboardPasted: function (e, info) {
             if (info.sheet.zh_setting && info.sheet.zh_dataType === 'tree') {
+                const tree = sheet.zh_tree;
+                if (!tree) { return; }
+
                 const sortData = info.sheet.zh_tree.nodes;
                 const datas = [], nodes = [];
                 for (let iRow = 0; iRow < info.cellRange.rowCount; iRow ++) {
@@ -332,12 +339,8 @@ $(document).ready(function() {
                         nodes.push(node);
                     }
                 }
-                info.sheet.zh_tree.update(preUrl + '/ledger/update', datas, function (result) {
-                    const rows = [];
-                    for (const data of result) {
-                        rows.push(sortData.indexOf(data));
-                    }
-                    SpreadJsObj.reLoadRowsData(info.sheet, rows);
+                info.sheet.zh_tree.update('/tender/' + getTenderId() + '/ledger/update', datas, function (result) {
+                    treeOperationObj.refreshTree(info.sheet, result);
                 });
             }
         },
@@ -347,6 +350,9 @@ $(document).ready(function() {
          */
         deletePress: function (sheet) {
             if (sheet.zh_setting && sheet.zh_dataType === 'tree') {
+                const tree = sheet.zh_tree;
+                if (!tree) { return; }
+
                 const sortData = sheet.zh_tree.nodes;
                 const datas = [], nodes = [];
                 const sel = sheet.getSelections()[0];
@@ -362,12 +368,8 @@ $(document).ready(function() {
                         nodes.push(node);
                     }
                 }
-                sheet.zh_tree.update(preUrl + '/ledger/update-info', datas, function (result) {
-                    const rows = [];
-                    for (const data of result) {
-                        rows.push(sortData.indexOf(data));
-                    }
-                    SpreadJsObj.reLoadRowsData(sheet, rows);
+                sheet.zh_tree.update('/tender/' + getTenderId() + '/ledger/update', datas, function (result) {
+                    treeOperationObj.refreshTree(sheet, result);
                 });
             }
         },
@@ -387,7 +389,7 @@ $(document).ready(function() {
             const node = tree.nodes[row];
             if (!node) { return; }
 
-            postData(preUrl + '/ledger/paste-block', {
+            postData('/tender/' + getTenderId() + '/ledger/paste-block', {
                 id: node[tree.setting.id],
                 block: block,
             }, function (data) {
@@ -561,7 +563,7 @@ $(document).ready(function() {
     // 台账模式加载部位明细数据
     if (checkTzMeasureType()) {
         SpreadJsObj.initSheet(posSpread.getActiveSheet(), posSpreadSetting);
-        postData(preUrl + '/pos', null, function (data) {
+        postData('/tender/' + getTenderId() + '/pos', null, function (data) {
             pos.loadDatas(data);
         });
     }
@@ -588,7 +590,6 @@ $(document).ready(function() {
             if (info.sheet.zh_setting) {
                 const orgText = info.sheet.getCell(info.row, info.row).value();
                 if (orgText === info.editingText || ((!orgText || orgText === '') && (info.editingText === ''))) {
-                    console.log(3);
                     return;
                 }
                 const node = treeOperationObj.getSelectNode(ledgerSpread.getActiveSheet());
@@ -634,7 +635,7 @@ $(document).ready(function() {
                     data.updateData = {id: position.id};
                     data.updateData[col.field] = col.type === 'Number' ? parseFloat(info.editingText) : info.editingText;
                 }
-                postData(preUrl+'/pos/update', data, function (result) {
+                postData('/tender/' + getTenderId() + '/pos/update', data, function (result) {
                     pos.updateDatas(result.pos);
                     ledgerTree.loadPostData(result.ledger, function (loadResult) {
                         treeOperationObj.refreshTree(ledgerSpread.getActiveSheet(), loadResult);
@@ -670,7 +671,7 @@ $(document).ready(function() {
                         posSelects.push(node);
                     }
                 }
-                sheet.zh_tree.update(preUrl + '/pos/update', datas, function (result) {
+                sheet.zh_tree.update('/tender/' + getTenderId() + '/pos/update', datas, function (result) {
                     pos.updateDatas(result);
                     // todo 只加载改变项
                     self.loadCurPosData();
@@ -692,7 +693,7 @@ $(document).ready(function() {
             for (let iRow = 0; iRow < count; iRow++) {
                 data.updateData.push(sortData[iRow + row].id);
             }
-            postData(preUrl + '/pos/update', data, function (result) {
+            postData('/tender/' + getTenderId() + '/pos/update', data, function (result) {
                 pos.updateDatas(result.pos);
                 sheet.deleteRows(row, count);
                 ledgerTree.loadPostData(result.ledger, function (loadResult) {
@@ -761,7 +762,7 @@ $(document).ready(function() {
                         }
                     }
                 }
-                postData(preUrl + '/pos/update', data, function (result) {
+                postData('/tender/' + getTenderId() + '/pos/update', data, function (result) {
                     pos.updateDatas(result.pos);
                     ledgerTree.loadPostData(result.ledger, function (loadResult) {
                         treeOperationObj.refreshTree(ledgerSpread.getActiveSheet(), loadResult);
@@ -925,7 +926,7 @@ $(document).ready(function() {
     class stdLib {
         constructor(obj, stdType, treeSetting, spreadSetting) {
             this.obj = obj;
-            this.url = '/api/std/' + stdType;
+            this.url = '/std/' + stdType;
             this.treeSetting = treeSetting;
             treeSetting.preUrl = this.url;
             this.spreadSetting = spreadSetting;
@@ -943,7 +944,7 @@ $(document).ready(function() {
                 const mainNode = mainTree.nodes[sel.row];
                 if (!stdNode) { return; }
 
-                mainTree.postData(preUrl + '/ledger/add-by-std', mainNode, {
+                mainTree.postData('/tender/' + getTenderId() + '/ledger/add-by-std', mainNode, {
                     tender_id: mainNode.tender_id,
                     stdType: stdType,
                     stdLibId: stdNode.list_id,
@@ -953,7 +954,7 @@ $(document).ready(function() {
                     treeOperationObj.refreshOperationValid(mainSheet, mainSheet.getSelections());
                 });
             });
-            this.pathTree = createNewPathTree('active', this.treeSetting);
+            this.pathTree = createNewPathTree('base', this.treeSetting);
         }
         loadLib (listId) {
             const self = this;
@@ -967,7 +968,7 @@ $(document).ready(function() {
         constructor (obj, spreadSetting) {
             const self = this;
             this.obj = obj;
-            this.url = preUrl + '/deal';
+            this.url = '/tender/' + getTenderId() + '/deal';
             this.spreadSetting = spreadSetting;
             this.spread = SpreadJsObj.createNewSpread(this.obj);
             SpreadJsObj.initSheet(this.spread.getActiveSheet(), this.spreadSetting);
@@ -1047,7 +1048,7 @@ $(document).ready(function() {
             if (dealBills) {
                 SpreadJsObj.loadSheetData(this.dealSpread.getActiveSheet(), 'data', dealBills.data);
             } else {
-                postData(preUrl + '/deal/get-data', {}, function (data) {
+                postData('/tender/' + getTenderId() + '/deal/get-data', {}, function (data) {
                     SpreadJsObj.loadSheetData(self.dealSpread.getActiveSheet(), 'data', data);
                 });
             }
@@ -1087,7 +1088,7 @@ $(document).ready(function() {
                     insertData.batchType = (select.code && select.code !== '') ? 'child' : 'next';
                     insertData.id = select[ledgerTree.setting.id];
                     insertData.batchData = self.getBatchData();
-                    postData(preUrl+'/ledger/batch-insert', insertData, function (data) {
+                    postData('/tender/' + getTenderId() + '/ledger/batch-insert', insertData, function (data) {
                         pos.updateDatas(data.pos);
                         ledgerTree.loadPostData(data.ledger, function (result) {
                             treeOperationObj.refreshTree(sheet, result);
@@ -1185,7 +1186,7 @@ $(document).ready(function() {
         const data = {
             keyword: $('#searchName').val(),
         }
-        postData('/api/search/user', data, (data) => {
+        postData('/search/user', data, (data) => {
             const resultDiv = $('#searchResult');
             $('h5>span', resultDiv).text(data.name);
             $('#addAuditor').attr('auditorId', data.id);
@@ -1201,7 +1202,7 @@ $(document).ready(function() {
             auditorId: $('#addAuditor').attr('auditorId'),
         };
         console.log(data);
-        postData(preUrl + '/ledger/audit/add', data, (data) => {
+        postData('/tender/' + getTenderId() + '/ledger/audit/add', data, (data) => {
             const html = [];
             html.push('<li class="list-group-item" auditorId="'+ data.audit_id +'"><a href="javascript: void(0)" class="text-danger pull-right">移除</a>');
             html.push('<span>');
@@ -1219,7 +1220,7 @@ $(document).ready(function() {
         const data = {
             auditorId: parseInt(li.attr('auditorId')),
         };
-        postData(preUrl + '/ledger/audit/delete', data, (data) => {
+        postData('/tender/' + getTenderId() + '/ledger/audit/delete', data, (data) => {
             li.remove();
             for (const a of data) {
                 const aLi = $('li[auditorId=' + a.audit_id + ']');

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

@@ -41,7 +41,7 @@ $(document).ready(() => {
     // 台账模式加载部位明细数据
     if (checkTzMeasureType()) {
         SpreadJsObj.initSheet(posSpread.getActiveSheet(), posSpreadSetting);
-        postData('/api/tender/' + getTenderId() + '/pos', null, function (data) {
+        postData('/tender/' + getTenderId() + '/pos', null, function (data) {
             pos.loadDatas(data);
         });
     }

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

@@ -106,7 +106,7 @@ $(document).ready(() => {
         const data = {
             keyword: $('#searchName').val(),
         }
-        postData('/api/search/user', data, (data) => {
+        postData('/search/user', data, (data) => {
             const resultDiv = $('#searchResult');
             $('h5>span', resultDiv).text(data.name);
             $('#addAuditor').attr('auditorId', data.id);

+ 190 - 77
app/public/js/path_tree.js

@@ -20,7 +20,7 @@ class PosData {
         // 以分类id为索引的有序
         this.masterRange = {};
         // pos设置
-        this.setting = JSON.parse(JSON.stringify(setting));
+        this.setting = setting;
     }
 
     /**
@@ -111,6 +111,43 @@ class PosData {
     }
 }
 
+class StagePosData extends PosData {
+    loadStageData(datas, fieldPre, fields) {
+        datas = datas instanceof Array ? datas : [datas];
+        const loadedData = [];
+        for (const data of datas) {
+            let node = this.getPos(data.pid);
+            if (node) {
+                for (const prop of fields) {
+                    if (data[fieldPre + prop] !== undefined) {
+                        node[fieldPre + prop] = data[prop];
+                    }
+                }
+                if (this.setting.calcFun) {
+                    this.setting.calcFun(node);
+                }
+                loadedData.push(node);
+            }
+        }
+
+    }
+
+    loadPreStageData(datas) {
+        this.loadStageData(datas, 'pre_', this.setting.updateFields);
+    }
+
+    loadCurStageData(datas) {
+        this.loadStageData(datas, '', this.setting.updateFields);
+    }
+
+    calculateAll() {
+        if (!this.setting.calcFun) { return; }
+        for (const pos of this.datas) {
+            this.setting.calcFun(pos);
+        }
+    }
+}
+
 const itemsPre = 'id_';
 
 const createNewPathTree = function (type, setting) {
@@ -125,7 +162,7 @@ const createNewPathTree = function (type, setting) {
             this.items = {};
             // 以排序为索引
             this.nodes = [];
-            // 索引
+            // 根节点
             this.children = {};
             // 树设置
             this.setting = setting;
@@ -170,6 +207,9 @@ const createNewPathTree = function (type, setting) {
             }
         }
 
+        getItemsByIndex(index) {
+            return this.nodes[index];
+        }
         /**
          * 根据id获取树结构节点数据
          * @param {Number} id
@@ -354,65 +394,6 @@ const createNewPathTree = function (type, setting) {
         };
     }
 
-    class ActiveTree extends BaseTree {
-
-        /**
-         * 加载数据(动态),只加载不同部分
-         * @param {Array} datas
-         * @return {Array} 加载到树的数据
-         * @privateA
-         */
-        _loadData (datas) {
-            const loadedData = [];
-            for (const data of datas) {
-                let node = this.getItems(data[this.setting.id]);
-                if (node) {
-                    for (const prop in node) {
-                        if (data[prop] !== undefined) {
-                            node[prop] = data[prop];
-                        }
-                    }
-                    loadedData.push(node);
-                } else {
-                    const keyName = itemsPre + data[this.setting.id];
-                    const node = JSON.parse(JSON.stringify(data));
-                    this.items[keyName] = node;
-                    this.datas.push(node);
-                    node.expanded = false;
-                    node.visible = true;
-                    loadedData.push(node);
-                }
-            }
-            this.sortTreeNode();
-            for (const node of loadedData) {
-                const children = node.children;
-                if (!node.expanded && children.length > 0) {
-                    node.expanded = true;
-                    this._refreshChildrenVisible(node);
-                }
-            }
-            return loadedData;
-        };
-
-        /**
-         * 以下方法需等待响应, 通过callback刷新界面
-         */
-        /**
-         * 加载子节点
-         * @param {Object} node
-         * @param {function} callback
-         */
-        loadChildren (node, callback) {
-            const self = this;
-            const url = this.setting.preUrl ? this.setting.preUrl + '/get-children' : 'get-children';
-            console.log(url);
-            postData(url, this.getNodeKeyData(node), function (data) {
-                self._loadData(data);
-                callback();
-            });
-        };
-    }
-
     class LedgerTree extends BaseTree {
 
         /**
@@ -744,10 +725,139 @@ const createNewPathTree = function (type, setting) {
         };
     }
 
+    class StageTree extends BaseTree {
+        /**
+         * 构造函数
+         */
+        constructor (setting) {
+            super(setting);
+            // stage关联索引
+            this.stageItems = {};
+        }
+        /**
+         * 加载数据(初始化), 并给数据添加部分树结构必须数据
+         * @param datas
+         */
+        loadDatas (datas) {
+            super.loadDatas(datas);
+            // 清空旧数据
+            this.stageItems = {};
+            // 加载全部数据
+            for (const data of this.datas) {
+                const keyName = itemsPre + data[this.setting.stageId];
+                this.stageItems[keyName] = data;
+            }
+        }
+        getStageItems(id) {
+            return this.stageItems[itemsPre + id];
+        }
+
+        loadStageData(datas, fieldPre, fields) {
+            datas = datas instanceof Array ? datas : [datas];
+            const loadedData = [];
+            for (const data of datas) {
+                let node = this.getStageItems(data.lid);
+                if (node) {
+                    for (const prop of fields) {
+                        if (data[fieldPre + prop] !== undefined) {
+                            node[fieldPre + prop] = data[prop];
+                        }
+                    }
+                    loadedData.push(node);
+                }
+            }
+        }
+
+        loadPreStageData(preStageData) {
+            this.loadStageData(curStageData, 'pre_', this.setting.updateFields);
+        }
+
+        loadCurStageData(curStageData) {
+            this.loadStageData(curStageData, '', this.setting.updateFields);
+        }
+
+        /**
+         * 加载数据(动态),只加载不同部分
+         * @param {Array} datas
+         * @return {Array} 加载到树的数据
+         * @privateA
+         */
+        _updateStageData (datas) {
+            datas = datas instanceof Array ? datas : [datas];
+            const loadedData = [];
+            for (const data of datas) {
+                let node = this.getStageItems(data.lid);
+                if (node) {
+                    for (const prop of this.setting.updateFields) {
+                        if (data[prop] !== undefined) {
+                            node[prop] = data[prop];
+                        }
+                    }
+                    loadedData.push(node);
+                }
+            }
+            return loadedData;
+        };
+        /**
+         *
+         * @param parent
+         * @param node
+         * @private
+         */
+        _getNodesParents(parents, nodes) {
+            for (const node of nodes) {
+                const parent = this.getParent(node);
+                if (parent) {
+                    const paths = this.getFullPathNodes(parent.full_path);
+                    for (const p of paths) {
+                        if (parents.indexOf(p) === -1) {
+                            parents.push(p);
+                        }
+                    }
+                }
+                if (node.children && node.children.length > 0) {
+                    parents.push(node);
+                }
+            }
+        }
+
+        isLeafXmj(node) {
+            for (const child of node.children) {
+                if (child.code !== '') {
+                    return false;
+                }
+            }
+            return true;
+        }
+
+        /**
+         * 提交数据至后端,返回的前端树结构应刷新的部分
+         * StageTree仅有更新CurStage部分,不需要增删
+         *
+         * @param data - 需要更新的数据
+         * @returns {Array} - 界面需要刷新的数据
+         */
+        loadPostStageData(data) {
+            let result, parents = [];
+            if (data) {
+                result = this._updateStageData(data);
+                this._getNodesParents(parents, result);
+            }
+            result = result ? result.concat(parents) : parents;
+            result.sort((a, b) => {
+                return b.level - a.level;
+            });
+            for (const node of result) {
+                treeCalc.calculateNode(this, node);
+            }
+            return result;
+        }
+    }
+
     if (type === 'base') {
         return new BaseTree(setting);
-    } else if (type === 'active') {
-        return new ActiveTree(setting);
+    } else if (type === 'stage') {
+        return new StageTree(setting);
     } else if (type === 'ledger') {
         return new LedgerTree(setting);
     } else if (type === 'measure') {
@@ -759,10 +869,9 @@ const treeCalc = {
     getMaxLevel: function (tree) {
         return Math.max.apply(Math, tree.datas.map(function(o) {return o.level}));
     },
-    calculateNode: function (tree, node, calcFields, calcFun) {
-        const children = tree.getChildren(node);
-        if (children.length > 0) {
-            const gather = children.reduce(function (rst, x) {
+    calculateNode: function (tree, node) {
+        if (node.children && node.children.length > 0) {
+            const gather = node.children.reduce(function (rst, x) {
                 const result = {};
                 const fieldCalc = function (field) {
                     if (rst[field]) {
@@ -771,39 +880,43 @@ const treeCalc = {
                         result[field] = x[field] ? x[field] : undefined;
                     }
                 }
-                for (const cf of calcFields) {
+                for (const cf of tree.setting.calcFields) {
                     fieldCalc(cf);
                 }
                 return result;
             });
-            for (const cf of calcFields) {
+            // 汇总子项
+            for (const cf of tree.setting.calcFields) {
                 if (gather[cf]) {
                     node[cf] = gather[cf];
+                } else {
+                    node[cf] = null;
                 }
             }
         }
-        if (calcFun) {
-            calcFun(node);
+        // 自身运算
+        if (tree.setting.calcFun) {
+            tree.setting.calcFun(node);
         }
     },
-    calculateLevelNode: function (tree, level, calcFields, calcFun) {
+    calculateLevelNode: function (tree, level) {
         const nodes = tree.datas.filter((n) => { return n.level === level });
         for (const node of nodes) {
-            this.calculateNode(tree, node, calcFields, calcFun);
+            this.calculateNode(tree, node);
         }
     },
-    calculateAll: function (tree, calcFields, calcFun) {
+    calculateAll: function (tree) {
         for (let i = this.getMaxLevel(tree); i >= 0; i--) {
-            this.calculateLevelNode(tree, i, calcFields, calcFun);
+            this.calculateLevelNode(tree, i);
         }
     },
-    calculateParent: function (tree, node, calcFields, calcFun) {
+    calculateParent: function (tree, node) {
         const nodes = tree.getFullPathNodes(node.full_path);
         nodes.sort((a, b) => {
             return b.level - a.level;
         });
         for (const n of nodes) {
-            this.calculateNode(tree, n, calcFields, calcFun);
+            this.calculateNode(tree, n);
         }
         return nodes;
     }

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

@@ -8,6 +8,10 @@
 
 const spreadNS = GC.Spread.Sheets;
 const SpreadJsObj = {
+    DataType: {
+        Data: 'data',
+        Tree: 'tree',
+    },
     /**
      * 创建Spread(默认1张表,3行数据)
      * @param obj 用于创建spreadjs的Dom元素
@@ -194,7 +198,7 @@ const SpreadJsObj = {
     },
     /**
      * 整个sheet重新加载数据
-     * @param sheet
+     * @param {GC.Spread.Sheets.Worksheet} sheet
      */
     reLoadSheetData: function (sheet) {
         const self = this;
@@ -398,6 +402,11 @@ const SpreadJsObj = {
         }
         return copyData.join('\n');
     },
+    /**
+     * 树表结构,定位至指定的节点
+     * @param {GC.Spread.Sheets.Worksheet} sheet - 需要定位的sheet
+     * @param {Number} id - 定位节点的id
+     */
     locateTreeNode: function (sheet, id) {
         const tree = sheet.zh_tree;
         if (!tree) { return }
@@ -408,6 +417,25 @@ const SpreadJsObj = {
         sheet.setSelection(index, sels[0].col, 1, 1);
         sheet.showRow(index, spreadNS.VerticalPosition.center);
     },
+    /**
+     * 获取当前选行的数据对象
+     * @param {GC.Spread.Sheets.Worksheet} sheet
+     * @returns {Object}
+     */
+    getSelectObject: function (sheet) {
+        if (!sheet) {
+            return null;
+        } else if (sheet.zh_dataType) {
+            const sel = sheet.getSelections()[0];
+            if (sheet.zh_dataType === this.DataType.Tree) {
+                return sheet.zh_tree.nodes[sel.row];
+            } else if (sheet.zh_dataType === this.DataType.Data) {
+                return sheet.zh_data[sel.row];
+            } else {
+                return null;
+            }
+        }
+    },
 
     CellType: {
         /**

+ 437 - 0
app/public/js/stage.js

@@ -0,0 +1,437 @@
+'use strict';
+
+/**
+ * 期计量 - 本期计量台账页面 js
+ *
+ * @author Mai
+ * @date 2018/12/7
+ * @version
+ */
+function checkTzMeasureType () {
+    return tender.measure_type === measureType.tz.value;
+}
+
+$(document).ready(() => {
+    autoFlashHeight();
+    // 初始化 台账 spread
+    const slSpread = SpreadJsObj.createNewSpread($('#stage-ledger')[0]);
+    SpreadJsObj.initSheet(slSpread.getActiveSheet(), ledgerSpreadSetting);
+    const stageTreeSetting = {
+        id: 'ledger_id',
+        pid: 'ledger_pid',
+        order: 'order',
+        level: 'level',
+        rootId: -1,
+        keys: ['id', 'tender_id', 'ledger_id'],
+        stageId: 'id',
+    };
+    // 台账树结构计算相关设置
+    stageTreeSetting.updateFields = ['contract_qty', 'contract_tp', 'qc_qty', 'qc_tp'];
+    stageTreeSetting.calcFields = ['deal_tp', 'total_price', 'contract_tp', 'qc_tp', 'gather_tp', 'end_contract_tp', 'end_qc_tp', 'end_gather_tp'];
+    stageTreeSetting.calcFun = function (node) {
+        if (node.children && node.children.length === 0) {
+            node.end_contract_qty = _.toNumber(node.pre_contract_qty) + _.toNumber(node.contract_qty);
+            node.end_qc_qty = _.toNumber(node.pre_qc_qty) + _.toNumber(node.qc_qty);
+            node.end_gather_qty = _.toNumber(node.pre_gather_qty) + _.toNumber(node.gather_qty);
+        }
+        node.gather_tp = _.toNumber(node.contract_tp) + _.toNumber(node.qc_tp);
+        node.end_contract_tp = _.toNumber(node.pre_contract_tp) + _.toNumber(node.contract_tp);
+        node.end_qc_tp = _.toNumber(node.pre_qc_tp) + _.toNumber(node.qc_tp);
+        node.end_gather_tp = _.toNumber(node.pre_gather_tp) + _.toNumber(node.gather_tp);
+        if (checkZero(node.dgn_qty1)) {
+            node.dgn_price = _.round(node.total_price/node.dgn_qty1, 2);
+        } else {
+            node.dgn_price = null;
+        }
+    };
+    // 初始化 台账树结构
+    const stageTree = createNewPathTree('stage', stageTreeSetting);
+    stageTree.loadDatas(ledgerData);
+    stageTree.loadCurStageData(curStageData);
+    // 根据设置 计算 台账树结构
+    treeCalc.calculateAll(stageTree);
+    // 绘制界面
+    SpreadJsObj.loadSheetData(slSpread.getActiveSheet(), 'tree', stageTree);
+
+    // 初始化 部位明细
+    const stagePosSetting = {
+        id: 'id', masterId: 'lid',
+        updateFields: ['contract_qty', 'qc_qty'],
+    };
+    stagePosSetting.calcFun = function (pos) {
+        pos.gather_qty = _.toNumber(pos.contract_qty) + _.toNumber(pos.qc_qty);
+    };
+    const stagePos = new StagePosData(stagePosSetting);
+    const spSpread = SpreadJsObj.createNewSpread($('#stage-pos')[0]);
+    SpreadJsObj.initSheet(spSpread.getActiveSheet(), posSpreadSetting);
+
+    const stageTreeSpreadObj = {
+        refreshTreeNodes: function (sheet, nodes) {
+            const tree = sheet.zh_tree;
+            if (!tree) { return }
+            const rows = [];
+            for (const node of nodes) {
+                rows.push(tree.nodes.indexOf(node));
+            }
+            SpreadJsObj.reLoadRowsData(sheet, rows);
+        },
+        editEnding: function (e, info) {
+            if (info.sheet.zh_setting) {
+                const col = info.sheet.zh_setting.cols[info.col];
+                const sortData = info.sheet.zh_dataType === 'tree' ? info.sheet.zh_tree.nodes : info.sheet.zh_data;
+                const node = sortData[info.row];
+
+                if (node.children && node.children.length > 0) {
+                    toast('清单父项不可计量', 'error');
+                    info.cancel = true;
+                    return;
+                } else {
+                    const nodePos = stagePos.getMasterRange(node.id);
+                    if (nodePos && nodePos.length > 0) {
+                        toast('该清单有部位明细,请在部位明细处计量', 'error');
+                        info.cancel = true;
+                        return;
+                    }
+                }
+                const billsData = {
+                    lid: node.id
+                };
+                billsData[col.field] = col.type === 'Number' ? parseFloat(info.editingText) : info.editingText;
+
+                postData(window.location.href + '/update', { bills: billsData }, function (data) {
+                    const nodes = stageTree.loadPostStageData(data.bills);
+                    stageTreeSpreadObj.refreshTreeNodes(slSpread.getActiveSheet(), nodes);
+                });
+            }
+        },
+        selectionChanged: function (e, info) {
+            stagePosSpreadObj.loadCurPosData();
+        },
+        deletePress(sheet) {
+            if (sheet.zh_setting && sheet.zh_dataType === 'tree') {
+                const tree = sheet.zh_tree;
+                if (!tree) { return; }
+
+                const sel = sheet.getSelections()[0];
+                const validCols = [];
+                for (let iCol = sel.col; iCol < sel.col + sel.colCount; iCol++) {
+                    if (!sheet.zh_setting.cols[iCol].readOnly) {
+                        validCols.push(iCol);
+                    }
+                }
+                if (validCols.length === 0) { return; }
+
+                const sortData = sheet.zh_tree.nodes;
+                const datas = [];
+                for (let iRow = sel.row; iRow < sel.row + sel.rowCount; iRow++) {
+                    const node = sortData[iRow];
+                    if (node) {
+                        if (node.children && node.children.length > 0) { continue; }
+                        const nodePos = stagePos.getMasterRange(node.id);
+                        if (nodePos && nodePos.length > 0) { continue; }
+
+                        const data = { lid: node.id };
+                        for (const iCol of validCols) {
+                            const colSetting = sheet.zh_setting.cols[iCol];
+                            data[colSetting.field] = null;
+                        }
+                        datas.push(data);
+                    }
+                }
+                if (datas.length > 0) {
+                    postData(window.location.href + '/update', {bills: datas}, function (result) {
+                        const nodes = stageTree.loadPostStageData(result.bills);
+                        stageTreeSpreadObj.refreshTreeNodes(slSpread.getActiveSheet(), nodes);
+                    });
+                }
+            }
+        },
+        clipboardPasting(e, info) {
+            if (info.sheet.zh_setting) {
+                const sortData = info.sheet.zh_data;
+                const range = info.cellRange;
+                const validField = ['contract_qty', 'contract_tp', 'qc_qty', 'postil'];
+                for (let iCol = range.col; iCol < range.col + range.colCount; iCol++) {
+                    const col = info.sheet.zh_setting.cols[iCol];
+                    if (validField.indexOf(col.field) === -1) {
+                        toast('不可修改此数据', 'error');
+                        info.cancel = true;
+                        return;
+                    }
+                }
+            }
+        },
+        clipboardPasted(e, info) {
+            if (info.sheet.zh_setting && info.sheet.zh_tree) {
+                const sheet = info.sheet;
+                const filterNodes = [], datas = [];
+
+                console.log(info.cellRange);
+                for (let iRow = 0; iRow < info.cellRange.rowCount; iRow++) {
+                    const curRow = iRow + info.cellRange.row;
+                    const node = sheet.zh_tree.getItemsByIndex(curRow);
+                    if (node.children && node.children.length > 0) {
+                        filterNodes.push(node);
+                        continue;
+                    }
+                    const nodePos = stagePos.getMasterRange(node.id);
+                    if (nodePos && nodePos.length > 0) {
+                        filterNodes.push(node);
+                        continue;
+                    }
+
+                    const data = {lid: node.id};
+                    for (let iCol = 0; iCol < info.cellRange.colCount; iCol++) {
+                        const curCol = info.cellRange.col + iCol;
+                        const col = info.sheet.zh_setting.cols[curCol];
+                        data[col.field] = col.type === 'Number' ? _.toNumber(info.sheet.getText(curRow, curCol)) : info.sheet.getText(curRow, curCol);
+                    }
+                    datas.push(data);
+                }
+                console.log(datas);
+                if (datas.length > 0) {
+                    postData(window.location.href + '/update', { bills: datas }, function (data) {
+                        const nodes = stageTree.loadPostStageData(data.bills);
+                        stageTreeSpreadObj.refreshTreeNodes(slSpread.getActiveSheet(), nodes.concat(filterNodes));
+                    });
+                } else {
+                    stageTreeSpreadObj.refreshTreeNodes(slSpread.getActiveSheet(), filterNodes);
+                }
+            }
+        }
+    };
+    slSpread.bind(spreadNS.Events.EditEnding, stageTreeSpreadObj.editEnding);
+    slSpread.bind(spreadNS.Events.SelectionChanged, stageTreeSpreadObj.selectionChanged);
+    slSpread.bind(spreadNS.Events.ClipboardPasting, stageTreeSpreadObj.clipboardPasting);
+    slSpread.bind(spreadNS.Events.ClipboardPasted, stageTreeSpreadObj.clipboardPasted);
+    SpreadJsObj.addDeleteBind(slSpread, stageTreeSpreadObj.deletePress);
+
+    const stagePosSpreadObj = {
+        /**
+         * 加载部位明细 根据当前台账选择节点
+         */
+        loadCurPosData: function () {
+            const node = SpreadJsObj.getSelectObject(slSpread.getActiveSheet());
+            if (node) {
+                const posData = stagePos.masterRange[itemsPre + node.id] || [];
+                SpreadJsObj.loadSheetData(spSpread.getActiveSheet(), 'data', posData);
+            } else {
+                SpreadJsObj.loadSheetData(spSpread.getActiveSheet(), 'data', []);
+            }
+        },
+        editEnding: function(e, info) {
+            if (info.sheet.zh_setting) {
+                // 未改变过,则直接跳过
+                const orgText = info.sheet.getCell(info.row, info.row).value();
+                if (orgText === info.editingText || ((!orgText || orgText === '') && (info.editingText === ''))) {
+                    info.cancel = true;
+                    return;
+                }
+                // 台账模式下,不可新增
+                const pos = info.sheet.zh_data ? info.sheet.zh_data[info.row] : null;
+                if (checkTzMeasureType() && !pos) {
+                    toast('台账模式不可新增部位明细数据', 'error');
+                    info.cancel = true;
+                    return ;
+                }
+                // 不同节点下,部位明细检查输入
+                const node = SpreadJsObj.getSelectObject(slSpread.getActiveSheet());
+                if (!node) {
+                    toast('数据错误, 请刷新页面后再试', 'warning');
+                    info.cancel = true;
+                    return;
+                } else if (info.editingText !== '' && node.children && node.children > 0) {
+                    toast('父节点不可插入部位明细', 'error');
+                    info.cancel = true;
+                    return;
+                } else if (info.editingText !== '' && node.code || node.code !== '') {
+                    toast('项目节不可插入部位明细', 'error');
+                    info.cancel = true;
+                    return;
+                }
+                const col = info.sheet.zh_setting.cols[info.col];
+                const data = {};
+                if (col.field === 'name') {
+                    if (info.editingText === '' && pos) {
+                        toast('部位名称不可为空', 'error', 'exclamation-circle');
+                        info.cancel = true;
+                        return;
+                    } else if (!pos) {
+                        if (info.editingText !== '') {
+                            data.updateType = 'add';
+                            data.updateData = {name: info.editingText, lid: node.id, tid: tender.id};
+                        } else {
+                            return;
+                        }
+                    } else {
+                        data.updateType = 'update';
+                        data.updateData = {id: pos.id, name: info.editingText};
+                    }
+                } else if (!pos) {
+                    toast('新增部位请先输入名称', 'warning');
+                } else {
+                    data.updateType = 'update';
+                    data.updateData = {pid: pos.id, lid: pos.lid};
+                    data.updateData[col.field] = col.type === 'Number' ? parseFloat(info.editingText) : info.editingText;
+                }
+                postData(window.location.pathname + '/update', {pos: data}, function (result) {
+                    if (result.pos) {
+                        stagePos.updateDatas(result.pos.pos);
+                        stagePos.loadCurStageData(result.pos.curStageData);
+                    }
+                    const nodes = stageTree.loadPostStageData(result.ledger.curStageData);
+                    stageTreeSpreadObj.refreshTreeNodes(slSpread.getActiveSheet(), nodes);
+                    stagePosSpreadObj.loadCurPosData();
+                }, function () {
+                    stagePosSpreadObj.loadCurPosData();
+                });
+            }
+        },
+        clipboardPasting: function (e, info) {
+            if (info.sheet.zh_setting) {
+                const sortData = info.sheet.zh_data;
+                const range = info.cellRange;
+                const validField = ['contract_qty', 'qc_qty', 'postil'];
+                for (let iCol = range.col; iCol < range.col + range.colCount; iCol++) {
+                    const col = info.sheet.zh_setting.cols[iCol];
+                    if (validField.indexOf(col.field) === -1) {
+                        if (checkTzMeasureType()) {
+                            toast('不可修改此数据', 'error');
+                            info.cancel = true;
+                            return;
+                        } else {
+                            for (let iRow = range.row; iRow < range.row + range.rowCount; iRow) {
+                                const pos = sortData(iRow);
+                                if (pos.add_stage !== stage.id || pos.add_times !== stage.times) {
+                                    toast('不可修改此数据', 'error');
+                                    info.cancel = true;
+                                    return;
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        },
+        clipboardPasted: function (e, info) {
+            const self = this;
+            if (info.sheet.zh_setting) {
+                const data = { updateType: '', updateData: [], };
+                const sortData = info.sheet.zh_data;
+                const node = SpreadJsObj.getSelectObject(slSpread.getActiveSheet());
+                if (sortData && (info.cellRange.row >= sortData.length)) {
+                    data.updateType = 'add';
+                    if (info.cellRange.col !== 0) {
+                        toast('新增部位请先输入名称', 'warning');
+                        self.loadCurPosData();
+                        return;
+                    }
+                    for (let iRow = 0; iRow < info.cellRange.rowCount; iRow++) {
+                        const curRow = info.cellRange.row + iRow;
+                        const newData = {lid: node.id};
+                        for (let iCol = 0; iCol < info.cellRange.colCount; iCol++) {
+                            const curCol = info.cellRange.col + iCol;
+                            const colSetting = info.sheet.zh_setting.cols[curCol];
+                            newData[colSetting.field] = info.sheet.getText(curRow, curCol);
+                            if (colSetting.type === 'Number') {
+                                newData[colSetting.field] = _.toNumber(newData[colSetting.field]);
+                            }
+                        }
+                        data.updateData.push(newData);
+                    }
+                } else {
+                    data.updateType = 'update';
+                    for (let iRow = 0; iRow < info.cellRange.rowCount; iRow++) {
+                        const curRow = info.cellRange.row + iRow;
+                        const curPos = sortData[curRow];
+                        if (curPos) {
+                            const newData = {pid: curPos.id, lid: curPos.lid};
+                            for (let iCol = 0; iCol < info.cellRange.colCount; iCol++) {
+                                const curCol = info.cellRange.col + iCol;
+                                const colSetting = info.sheet.zh_setting.cols[curCol];
+                                newData[colSetting.field] = info.sheet.getText(curRow, curCol);
+                                if (colSetting.type === 'Number') {
+                                    newData[colSetting.field] = _.toNumber(newData[colSetting.field]);
+                                }
+                            }
+                            data.updateData.push(newData);
+                        }
+                    }
+                }
+                console.log(data);
+                postData(window.location.pathname + '/update', {pos: data}, function (result) {
+                    if (result.pos) {
+                        stagePos.updateDatas(result.pos.pos);
+                        stagePos.loadCurStageData(result.pos.curStageData);
+                    }
+                    const nodes = stageTree.loadPostStageData(result.ledger.curStageData);
+                    stageTreeSpreadObj.refreshTreeNodes(slSpread.getActiveSheet(), nodes);
+                    stagePosSpreadObj.loadCurPosData();
+                }, function () {
+                    stagePosSpreadObj.loadCurPosData();
+                });
+            }
+        },
+        deletePress: function (sheet) {
+            if (sheet.zh_setting && sheet.zh_data) {
+                const sortData = sheet.zh_data;
+                if (!sortData || sortData.length === 0) { return; }
+
+                const sel = sheet.getSelections()[0];
+                const validCols = [];
+                for (let iCol = sel.col; iCol < sel.col + sel.colCount; iCol++) {
+                    if (!sheet.zh_setting.cols[iCol].readOnly) {
+                        validCols.push(iCol);
+                    }
+                }
+                if (validCols.length === 0) { return; }
+
+                const datas = [], posSelects = [];
+                for (let iRow = sel.row; iRow < sel.row + sel.rowCount; iRow++) {
+                    const node = sortData[iRow];
+                    if (node) {
+                        const data = {pid: node.id, lid: node.lid};
+                        for (const iCol of validCols) {
+                            const colSetting = sheet.zh_setting.cols[iCol];
+                            if (colSetting.field === 'name') {
+                                toast('部位名称不能为空', 'error');
+                                return;
+                            }
+                            data[colSetting.field] = null;
+                        }
+                        datas.push(data);
+                        posSelects.push(node);
+                    }
+                }
+                if (datas.length > 0) {
+                    postData(window.location.pathname + '/update', {pos: {updateType: 'update', updateData: datas} }, function (result) {
+                        if (result.pos) {
+                            stagePos.updateDatas(result.pos.pos);
+                            stagePos.loadCurStageData(result.pos.curStageData);
+                        }
+                        const nodes = stageTree.loadPostStageData(result.ledger.curStageData);
+                        stageTreeSpreadObj.refreshTreeNodes(slSpread.getActiveSheet(), nodes);
+                        // todo 只加载改变项
+                        stagePosSpreadObj.loadCurPosData();
+                    });
+                }
+            }
+        },
+    };
+
+    // 加载部位明细数据 - 暂时统一加载,如有需要,切换成动态加载并缓存
+    postData(window.location.pathname + '/pos', null, function (result) {
+        stagePos.loadDatas(result.pos);
+        if (result.curStageData) {
+            stagePos.loadCurStageData(result.curStageData);
+        }
+        if (result.preStageData) {
+            stagePos.loadPreStageData(result.preStageData);
+        }
+        stagePosSpreadObj.loadCurPosData();
+    });
+    spSpread.bind(spreadNS.Events.EditEnding, stagePosSpreadObj.editEnding);
+    spSpread.bind(spreadNS.Events.ClipboardPasting, stagePosSpreadObj.clipboardPasting);
+    spSpread.bind(spreadNS.Events.ClipboardPasted, stagePosSpreadObj.clipboardPasted);
+    SpreadJsObj.addDeleteBind(spSpread, stagePosSpreadObj.deletePress);
+});

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

@@ -198,7 +198,7 @@ $(document).ready(function() {
         };
         const tenderId = window.location.pathname.split('/')[2];
         console.log(prop);
-        postData('/api/tender/' + tenderId + '/save', prop, function (data) {
+        postData('/tender/' + tenderId + '/save', prop, function (data) {
             setReadOnly('#v-pills-agi', true);
             property.deal_info = data.deal_info;
             property.construction_unit = data.construction_unit;
@@ -246,7 +246,7 @@ $(document).ready(function() {
         };
         const tenderId = window.location.pathname.split('/')[2];
         console.log(prop);
-        postData('/api/tender/' + tenderId + '/save', prop, function (data) {
+        postData('/tender/' + tenderId + '/save', prop, function (data) {
             setReadOnly('#v-pills-count', true);
             property.decimal = data.decimal;
             property.deal_param = data.deal_param;
@@ -280,7 +280,7 @@ $(document).ready(function() {
         };
         const tenderId = window.location.pathname.split('/')[2];
         console.log(prop);
-        postData('/api/tender/' + tenderId + '/save', prop, function (data) {
+        postData('/tender/' + tenderId + '/save', prop, function (data) {
             setReadOnly('#v-pills-display', true);
             property.display = data.display;
             $('#post-display').parent().hide();

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

@@ -322,7 +322,7 @@ $(document).ready(() => {
                 data.push({id: c.id, level: node.getPath().length - 1});
             }
         }
-        postData('/api/setting/category/level', data, function (rst) {
+        postData('/setting/category/level', data, function (rst) {
             for (const d of data) {
                 const c = findNode('id', d.id, category);
                 c.level = d.level;
@@ -353,7 +353,7 @@ $(document).ready(() => {
             }
             data.category.push(cate);
         }
-        postData('/api/list/add', data, function (result) {
+        postData('/list/add', data, function (result) {
             tenders.push(result);
             initTenderTree();
             $('.c-body').html(getTenderTreeHtml());

+ 76 - 51
app/router.js

@@ -35,11 +35,11 @@ module.exports = app => {
     app.get('/setting/user', sessionAuth, 'settingController.user');
     // 标段自定义类别
     app.get('/setting/category', sessionAuth, 'settingController.category');
-    app.post('/api/setting/category/add', sessionAuth, 'settingController.addCategory');
-    app.post('/api/setting/category/del', sessionAuth, 'settingController.deleteCategory');
-    app.post('/api/setting/category/update', sessionAuth, 'settingController.updateCategory');
-    app.post('/api/setting/category/value', sessionAuth, 'settingController.setCategoryValue');
-    app.post('/api/setting/category/level', sessionAuth, 'settingController.resetCategoryLevel');
+    app.post('/setting/category/add', sessionAuth, 'settingController.addCategory');
+    app.post('/setting/category/del', sessionAuth, 'settingController.deleteCategory');
+    app.post('/setting/category/update', sessionAuth, 'settingController.updateCategory');
+    app.post('/setting/category/value', sessionAuth, 'settingController.setCategoryValue');
+    app.post('/setting/category/level', sessionAuth, 'settingController.resetCategoryLevel');
 
     // 项目相关
     app.get('/project/info', sessionAuth, 'projectController.info');
@@ -59,42 +59,68 @@ module.exports = app => {
     app.get('/list/progress', sessionAuth, 'tenderController.listProgress');
     // 管理标段
     app.get('/list/manage', sessionAuth, 'tenderController.listManage');
-    app.post('/api/list/add', sessionAuth, 'tenderController.addTender');
-    app.post('/api/list/update', sessionAuth, 'tenderController.updateTender');
-    app.post('/api/list/del', sessionAuth, 'tenderController.deleteTender');
+    app.post('/list/add', sessionAuth, 'tenderController.addTender');
+    app.post('/list/update', sessionAuth, 'tenderController.updateTender');
+    app.post('/list/del', sessionAuth, 'tenderController.deleteTender');
     // 标段概况
     app.get('/tender/:id', sessionAuth, tenderCheck, 'tenderController.tenderInfo');
     app.get('/tender/:id/:type', sessionAuth, 'tenderController.tenderType');
-    app.post('/api/tender/:id/save', sessionAuth, tenderCheck, 'tenderController.saveTenderInfo');
+    app.post('/tender/:id/save', sessionAuth, tenderCheck, 'tenderController.saveTenderInfo');
     app.post('/tender/rule', sessionAuth, 'tenderController.rule');
 
 
     // 台账管理相关
     app.get('/tender/:id/ledger/explode', sessionAuth, tenderCheck, 'ledgerController.explode');
-    app.post('/api/tender/:id/ledger/get-children', sessionAuth, tenderCheck, 'ledgerController.getChildren');
-    app.post('/api/tender/:id/ledger/base-operation', sessionAuth, tenderCheck, 'ledgerController.baseOperation');
-    app.post('/api/tender/:id/ledger/update', sessionAuth, tenderCheck, 'ledgerController.update');
-    app.post('/api/tender/:id/ledger/update-info', sessionAuth, tenderCheck, 'ledgerController.updateInfo');
-    app.post('/api/tender/:id/ledger/paste-block', sessionAuth, tenderCheck, 'ledgerController.pasteBlock');
-    app.post('/api/tender/:id/ledger/add-by-std', sessionAuth, tenderCheck, 'ledgerController.addFromStandardLib');
-    app.post('/api/tender/:id/ledger/batch-insert', sessionAuth, tenderCheck, 'ledgerController.batchInsert');
-    app.post('/api/tender/:id/pos', sessionAuth, tenderCheck, 'ledgerController.pos');
-    app.post('/api/tender/:id/pos/update', sessionAuth, tenderCheck, 'ledgerController.posUpdate');
+    app.post('/tender/:id/ledger/get-children', sessionAuth, tenderCheck, 'ledgerController.getChildren');
+    app.post('/tender/:id/ledger/base-operation', sessionAuth, tenderCheck, 'ledgerController.baseOperation');
+    app.post('/tender/:id/ledger/update', sessionAuth, tenderCheck, 'ledgerController.update');
+    app.post('/tender/:id/ledger/update-info', sessionAuth, tenderCheck, 'ledgerController.updateInfo');
+    app.post('/tender/:id/ledger/paste-block', sessionAuth, tenderCheck, 'ledgerController.pasteBlock');
+    app.post('/tender/:id/ledger/add-by-std', sessionAuth, tenderCheck, 'ledgerController.addFromStandardLib');
+    app.post('/tender/:id/ledger/batch-insert', sessionAuth, tenderCheck, 'ledgerController.batchInsert');
+    app.post('/tender/:id/pos', sessionAuth, tenderCheck, 'ledgerController.pos');
+    app.post('/tender/:id/pos/update', sessionAuth, tenderCheck, 'ledgerController.posUpdate');
 
     app.get('/tender/:id/ledger/change', sessionAuth, tenderCheck, 'ledgerController.change');
     app.get('/tender/:id/ledger/index', sessionAuth, 'ledgerController.index');
 
     // 台账审批相关
     app.get('/tender/:id/ledger/audit', sessionAuth, tenderCheck, 'ledgerAuditController.index');
-    app.post('/api/tender/:id/ledger/audit/add', sessionAuth, tenderCheck, 'ledgerAuditController.add');
-    app.post('/api/tender/:id/ledger/audit/delete', sessionAuth, tenderCheck, 'ledgerAuditController.remove');
+    app.post('/tender/:id/ledger/audit/add', sessionAuth, tenderCheck, 'ledgerAuditController.add');
+    app.post('/tender/:id/ledger/audit/delete', sessionAuth, tenderCheck, 'ledgerAuditController.remove');
     app.post('/tender/:id/ledger/audit/start', sessionAuth, tenderCheck, 'ledgerAuditController.start');
     app.post('/tender/:id/ledger/audit/check', sessionAuth, tenderCheck, 'ledgerAuditController.check');
 
     // 签约清单
-    app.post('/api/tender/:id/deal/get-data', sessionAuth, tenderCheck, 'dealBillsController.getData');
-    app.post('/api/tender/:id/deal/upload-excel', sessionAuth, tenderCheck, 'dealBillsController.loadExcel');
-    app.get('/api/tender/:id/deal/download/:file', sessionAuth, tenderCheck, 'dealBillsController.download');
+    app.post('/tender/:id/deal/get-data', sessionAuth, tenderCheck, 'dealBillsController.getData');
+    app.post('/tender/:id/deal/upload-excel', sessionAuth, tenderCheck, 'dealBillsController.loadExcel');
+    app.get('/tender/:id/deal/download/:file', sessionAuth, tenderCheck, 'dealBillsController.download');
+
+    // 计量台账
+    // 期计量
+    app.get('/tender/:id/measure/stage', sessionAuth, tenderCheck, 'measureController.stage');
+    app.post('/tender/:id/measure/stage/add', sessionAuth, tenderCheck, 'measureController.addStage');
+    app.post('/tender/:id/measure/stage/save', sessionAuth, tenderCheck, 'measureController.saveStage');
+    // 计量台账 -- 清单汇总
+    app.get('/tender/:id/measure/gather', sessionAuth, tenderCheck, 'measureController.gather');
+    // 计量台账 -- 审核比较
+    app.get('/tender/:id/measure/compare', sessionAuth, tenderCheck, 'measureController.compare');
+
+    // 期计量详细
+    // 本期计量台账
+    app.get('/tender/:id/measure/stage/:order', sessionAuth, tenderCheck, 'stageController.index');
+    app.post('/tender/:id/measure/stage/:order/pos', sessionAuth, tenderCheck, 'stageController.getStagePosData');
+    app.post('/tender/:id/measure/stage/:order/update', sessionAuth, tenderCheck, 'stageController.updateStageData');
+    // 中间计量
+    app.get('/tender/:id/measure/stage/:order/detail', sessionAuth, tenderCheck, 'stageController.detail');
+    // 合同支付
+    app.get('/tender/:id/measure/stage/:order/pay', sessionAuth, tenderCheck, 'stageController.pay');
+    // 变更令
+    app.get('/tender/:id/measure/stage/:order/change', sessionAuth, tenderCheck, 'stageController.change');
+    // 审核比较
+    app.get('/tender/:id/measure/stage/:order/compare', sessionAuth, tenderCheck, 'stageController.compare');
+    // 报表
+    app.get('/tender/:id/measure/stage/:order/report', sessionAuth, tenderCheck, 'stageController.report');
 
     // 变更管理
     app.get('/tender/:id/change', sessionAuth, 'changeController.index');
@@ -116,40 +142,39 @@ module.exports = app => {
     app.post('/profile/bind', sessionAuth, 'profileController.bindMobile');
 
     // 中间计量 - 计量编制相关
-    app.get('/measure/wlist', sessionAuth, tenderSelect, 'measureController.list');
-
-    app.get('/measure/work/:mid', sessionAuth, tenderSelect, 'measureController.work');
-    app.post('/measure/newCode', sessionAuth, 'measureController.newCode');
-    app.post('/measure/add', sessionAuth, 'measureController.addMeasure');
-    app.post('/measure/delete', sessionAuth, 'measureController.deleteMeasure');
-    app.post('/measure/search', sessionAuth, 'measureController.search');
-    app.post('/measure/pos', sessionAuth, 'measureController.pos');
-    app.post('/measure/bills', sessionAuth, 'measureController.bills');
-    app.post('/measure/detail', sessionAuth, 'measureController.measureDetail');
-    app.post('/measure/billsUpdate', sessionAuth, 'measureController.billsUpdate');
+    // app.get('/measure/wlist', sessionAuth, tenderSelect, 'measureController.list');
+    //
+    // app.get('/measure/work/:mid', sessionAuth, tenderSelect, 'measureController.work');
+    // app.post('/measure/newCode', sessionAuth, 'measureController.newCode');
+    // app.post('/measure/add', sessionAuth, 'measureController.addMeasure');
+    // app.post('/measure/delete', sessionAuth, 'measureController.deleteMeasure');
+    // app.post('/measure/search', sessionAuth, 'measureController.search');
+    // app.post('/measure/pos', sessionAuth, 'measureController.pos');
+    // app.post('/measure/bills', sessionAuth, 'measureController.bills');
+    // app.post('/measure/detail', sessionAuth, 'measureController.measureDetail');
+    // app.post('/measure/billsUpdate', sessionAuth, 'measureController.billsUpdate');
 
     // 中间计量 - 计量审批相关
-    app.get('/measure/alist', sessionAuth, tenderSelect, 'measureAuditController.list');
-    app.get('/measure/audit/:mid', sessionAuth, 'measureAuditController.index');
-    app.post('/measure/audit/add', sessionAuth, 'measureAuditController.add');
-    app.post('/measure/audit/remove', sessionAuth, 'measureAuditController.remove');
-    app.post('/measure/audit/start', sessionAuth, 'measureAuditController.start');
-    app.post('/measure/audit/check', sessionAuth, 'measureAuditController.check');
+    // app.get('/measure/alist', sessionAuth, tenderSelect, 'measureAuditController.list');
+    // app.get('/measure/audit/:mid', sessionAuth, 'measureAuditController.index');
+    // app.post('/measure/audit/add', sessionAuth, 'measureAuditController.add');
+    // app.post('/measure/audit/remove', sessionAuth, 'measureAuditController.remove');
+    // app.post('/measure/audit/start', sessionAuth, 'measureAuditController.start');
+    // app.post('/measure/audit/check', sessionAuth, 'measureAuditController.check');
 
     // 期计量管理相关
-    app.get('/tender/:id/measure/stage', sessionAuth, tenderCheck, 'stageController.index');
-    app.post('/tender/:id/stage/add', sessionAuth, tenderCheck, 'stageController.add');
-    app.get('/tender/:id/stage/:order/measure', sessionAuth, tenderSelect, 'stageController.stageMeasure');
-    app.get('/tender/:id/stage/:order/deal', sessionAuth, tenderSelect, 'stageController.stageDeal');
-    app.get('/tender/:id/stage/:order/report', sessionAuth, tenderSelect, 'stageController.stageReport');
-
+    // app.get('/tender/:id/measure/stage', sessionAuth, tenderCheck, 'stageController.index');
+    // app.post('/tender/:id/stage/add', sessionAuth, tenderCheck, 'stageController.add');
+    // app.get('/tender/:id/stage/:order/measure', sessionAuth, tenderSelect, 'stageController.stageMeasure');
+    // app.get('/tender/:id/stage/:order/deal', sessionAuth, tenderSelect, 'stageController.stageDeal');
+    // app.get('/tender/:id/stage/:order/report', sessionAuth, tenderSelect, 'stageController.stageReport');
 
     //标准库相关
-    app.post('/api/std/bills/get-data', sessionAuth, 'stdBillsController.getData');
-    app.post('/api/std/bills/get-children', sessionAuth, 'stdBillsController.getChildren');
-    app.post('/api/std/chapter/get-data', sessionAuth, 'stdChapterController.getData');
-    app.post('/api/std/chapter/get-children', sessionAuth, 'stdChapterController.getChildren');
+    app.post('/std/bills/get-data', sessionAuth, 'stdBillsController.getData');
+    app.post('/std/bills/get-children', sessionAuth, 'stdBillsController.getChildren');
+    app.post('/std/chapter/get-data', sessionAuth, 'stdChapterController.getData');
+    app.post('/std/chapter/get-children', sessionAuth, 'stdChapterController.getChildren');
 
     // 查询
-    app.post('/api/search/user', sessionAuth, 'projectController.searchAccount');
+    app.post('/search/user', sessionAuth, 'projectController.searchAccount');
 };

+ 3 - 2
app/service/ledger.js

@@ -22,7 +22,7 @@ const keyFields = {
 };
 // 以下字段仅可通过树结构操作改变,不可直接通过update方式从接口提交,发现时过滤
 const readOnlyFields = ['id', 'tender_id', 'ledger_id', 'ledger_pid', 'order', 'level', 'full_path', 'is_leaf'];
-const calcFields = ['quantity', 'unit_price', 'total_price'];
+const calcFields = ['quantity', 'unit_price', 'total_price', 'deal_qty', 'deal_tp'];
 const zeroRange = 0.0000000001;
 const rootId = -1;
 const keyPre = 'tender_node_maxId:';
@@ -1240,7 +1240,7 @@ module.exports = app => {
             const result= {};
             let bChanged = false;
             for (const prop in orgData) {
-                if (newData[prop] && newData[prop] !== orgData[prop]) {
+                if (newData[prop] !== orgData[prop]) {
                     result[prop] = newData[prop];
                     bChanged = true;
                 }
@@ -1301,6 +1301,7 @@ module.exports = app => {
          * @return {Array} - 提交后的数据
          */
         async updateInfos(tenderId, datas) {
+            console.log(datas);
             if (tenderId <= 0) {
                 throw '标段不存在';
             }

+ 21 - 0
app/service/pos.js

@@ -29,6 +29,12 @@ module.exports = app => {
             });
         }
 
+        /**
+         * 保存部位明细数据
+         * @param data
+         * @param {Number} tid - 标段id
+         * @returns {Promise<{ledger: {}, pos: null}>}
+         */
         async savePosData(data, tid) {
             const transaction = await this.db.beginTransaction();
             try {
@@ -82,10 +88,25 @@ module.exports = app => {
             }
         }
 
+        /**
+         * 删除清单下部位明细数据(删除清单时调用)
+         *
+         * @param transaction - 事务
+         * @param tid - 标段id
+         * @param lid - 清单id
+         * @returns {Promise<void>}
+         */
         async deletePosData(transaction, tid, lid) {
             await transaction.delete(this.tableName, {tid: tid, lid: lid});
         }
 
+        /**
+         * 复制整块 拷贝部位明细数据
+         * @param {Number} orgLid - 拷贝的部位明细所属台账id
+         * @param {Number} newLid - 新的台账id
+         * @param transaction - 复制整块事务
+         * @returns {Promise<void>}
+         */
         async copyBillsPosData(orgLid, newLid, transaction) {
             const posData = await this.getAllDataByCondition({ where: { lid: orgLid } });
             if (posData.length > 0) {

+ 37 - 6
app/service/stage.js

@@ -24,32 +24,63 @@ module.exports = app => {
         }
 
         /**
+         * 获取标段下的全部计量期,按倒序
+         * @param tenderId
+         * @returns {Promise<void>}
+         */
+        async getValidStages(tenderId) {
+            return await this.db.select(this.tableName, {
+                where: {tid: tenderId},
+                orders: [['order', 'desc']],
+            });
+        }
+
+        /**
          *
          * @param tenderId - 标段id
-         * @param name - 期名称
-         * @param time - 计量时间
+         * @param date - 计量年月
+         * @param period - 开始-截止日期
          * @returns {Promise<void>}
          */
-        async add(tenderId, name, time) {
+        async addStage(tenderId, date, period) {
             const stages = await this.getAllDataByCondition({
                 where: {tid: tenderId},
                 order: ['order'],
             });
+            if (stages.length > 0 && stages[stages.length - 1].status !== audit.flow.status.checked) {
+                throw '上一期未审批通过,请等待上一期审批通过后,再新增数据';
+            };
             const order = stages.length + 1;
             const newStage = {
                 sid: this.uuid.v4(),
                 tid: tenderId,
                 order: order,
-                name: name,
                 in_time: new Date(),
-                s_time: time,
+                s_time: date,
+                period: period,
                 times: 1,
                 status: audit.flow.status.uncheck,
+                user_id: this.ctx.session.sessionUser.accountId,
             };
             const result = await this.db.insert(this.tableName, newStage);
-            console.log(result);
             return result.affectedRows === 1 ? newStage : null;
         }
+
+        /**
+         * 编辑计量期
+         *
+         * @param {Number} tenderId - 标段Id
+         * @param {Number} order - 第N期
+         * @param {String} date - 计量年月
+         * @param {String} period - 开始-截止时间
+         * @returns {Promise<void>}
+         */
+        async saveStage(tenderId, order, date, period) {
+            await this.db.update(this.tableName, {
+                s_time: date,
+                period: period,
+            }, { where: { tid: tenderId, order: order } });
+        }
     }
 
     return Stage;

+ 194 - 0
app/service/stage_bills.js

@@ -0,0 +1,194 @@
+'use strict';
+
+/**
+ * 期计量 - 部位明细计量
+ *
+ * @author Mai
+ * @date 2018/12/8
+ * @version
+ */
+const calcFields = ['contract_qty', 'qc_qty'];
+const auditConst = require('../const/audit');
+
+module.exports = app => {
+    class StageBills extends app.BaseService {
+        /**
+         * 构造函数
+         *
+         * @param {Object} ctx - egg全局变量
+         * @return {void}
+         */
+        constructor(ctx) {
+            super(ctx);
+            this.tableName = 'stage_bills';
+        }
+
+        /**
+         * 查询期计量最后审核人数据
+         * @param {Number} tid - 标段id
+         * @param {Number} sid - 期id
+         * @param {Number|Array} lid - 台账节点id(可以为空)
+         * @returns {Promise<*>}
+         */
+        async getLastestStageData(tid, sid, lid) {
+            const lidSql = lid ? ' And Bills.lid in (?)' : '';
+            const sql = 'SELECT * FROM ' + this.tableName + ' As Bills ' +
+                        '  INNER JOIN ( ' +
+                        '    SELECT MAX(`times`) As `times`, MAX(`order`) As `order`, `lid` From ' + this.tableName +
+                        '      GROUP BY `lid`' +
+                        '  ) As MaxFilter ' +
+                        '  ON Bills.times = MaxFilter.times And Bills.order = MaxFilter.order And Bills.lid = MaxFilter.lid' +
+                        '  WHERE Bills.tid = ? And Bills.sid = ?' + lidSql;
+            const sqlParam = [tid, sid];
+            if (!lid) {
+                return await this.db.query(sql, sqlParam);
+            } else if (lid instanceof Array) {
+                sqlParam.push(lid.join(', '));
+                return await this.db.query(sql, sqlParam);
+            } else {
+                sqlParam.push(lid);
+                return await this.db.queryOne(sql, sqlParam);
+            }
+        }
+
+        async getAuditorStageData(tid, sid, times, order, lid) {
+            const lidSql = lid ? ' And Bills.lid in (?)' : '';
+            const sql = 'SELECT * FROM ' + this.tableName + ' As Bills ' +
+                '  INNER JOIN ( ' +
+                '    SELECT MAX(`times`) As `times`, MAX(`order`) As `order`, `lid` From ' + this.tableName +
+                '      WHERE `times` <= ? AND `order` <= ?' +
+                '      GROUP BY `lid`' +
+                '  ) As MaxFilter ' +
+                '  ON Bills.times = MaxFilter.times And Bills.order = MaxFilter.order And Bills.lid = MaxFilter.lid' +
+                '  WHERE Bills.tid = ? And Bills.sid = ?' + lidSql;
+            const sqlParam = [times, order, tid, sid];
+            if (!lid) {
+                return await this.db.query(sql, sqlParam);
+            } else if (lid instanceof Array) {
+                sqlParam.push(lid.join(', '));
+                return await this.db.query(sql, sqlParam);
+            } else {
+                sqlParam.push(lid);
+                return await this.db.queryOne(sql, sqlParam);
+            }
+        }
+
+        async getStageBills(tid, sid, lid) {
+            const sql = 'SELECT Stage.*, Ledger.unit_price FROM ?? As Stage, ?? As Ledger ' +
+                        '  Where Stage.tid = ?, Stage.sid = ?, Stage.lid = ?, Stage.lid = Ledger.id ' +
+                        '  Order Stage.time DESC, Stage.order DESC ';
+            const sqlParam = [this.tableName, this.ctx.service.ledger.tableName];
+            sqlParam.push(this.db.escape(tid));
+            sqlParam.push(this.db.escape(sid));
+            sqlParam.push(this.db.escape(lid));
+            return await this.db.queryOne(sql, sqlParam);
+        }
+
+        async _insertStageBillsData(transaction, insertData, ledgerData) {
+            const d = {
+                tid: this.ctx.tender.id,
+                lid: ledgerData.id,
+                sid: this.ctx.stage.id,
+                times: this.ctx.stage.times,
+                order: this.ctx.stage.curAuditor ? this.ctx.stage.curAuditor.order : 0,
+                said: this.ctx.session.sessionUser.accountId,
+            };
+            if (insertData.contract_qty) {
+                d.contract_qty = insertData.contract_qty;
+                d.contract_tp = d.contract_qty * ledgerData.unit_price;
+            }
+            if (insertData.qc_qty) {
+                d.qc_qty = insertData.qc_qty;
+                d.qc_tp = d.qc_qty * ledgerData.unit_price;
+            }
+            if (insertData.postil) {
+                d.postil = insertData.postil;
+            }
+            await transaction.insert(this.tableName, d);
+        }
+
+        /**
+         * 前端提交数据
+         * @param {Object|Array} data - 提交的数据
+         * @returns {Promise<void>}
+         */
+        async updateStageData(data) {
+            const datas = data instanceof Array ? data : [data];
+            console.log(datas);
+            const transaction = await this.db.beginTransaction();
+            try {
+                for (const d of datas) {
+                    const stageBills = await this.getLastestStageData(this.ctx.tender.id, this.ctx.stage.id, d.lid);
+                    const ledgerBills = await this.ctx.service.ledger.getDataById(d.lid);
+                    if (d.contract_qty !== undefined) {
+                        d.contract_tp = d.contract_qty * ledgerBills.unit_price;
+                    }
+                    if (d.qc_qty !== undefined) {
+                        d.qc_tp = d.qc_qty * ledgerBills.unit_price;
+                    }
+                    console.log(d);
+                    if (!stageBills) {
+                        d.tid = this.ctx.tender.id;
+                        d.sid = this.ctx.stage.id;
+                        d.said = this.ctx.session.sessionUser.accountId;
+                        d.times = this.ctx.stage.times;
+                        d.order = this.ctx.stage.curAuditor ? this.ctx.stage.curAuditor.order : 0;
+                        await transaction.insert(this.tableName, d);
+                    } else {
+                        d.id = stageBills.id;
+                        await transaction.update(this.tableName, d);
+                    }
+                }
+                await transaction.commit();
+            } catch (err) {
+                await transaction.rollback();
+                throw err;
+            }
+            return await this.getLastestStageData(this.ctx.tender.id, this.ctx.stage.id, this._.map(datas, 'lid'));
+        }
+
+        /**
+         *
+         * @param {Number} tid - 标段id
+         * @param {Number} id - 需要计算的节点的id
+         * @param {Object} transaction - 操作所属事务,没有则创建
+         * @returns {Promise<void>}
+         */
+        async calc(tid, sid, lid, transaction) {
+            const stageBills = await this.getLastestStageData(tid, sid, lid);
+            const ledgerBills = await this.ctx.service.ledger.getDataById(lid);
+            if (!ledgerBills) {
+                throw '提交数据错误';
+            }
+            const posGather = await this.ctx.service.stagePos.getPosGather(tid, sid, lid, transaction);
+            console.log(posGather);
+            if (!posGather) { return; }
+
+            // 计算
+            if (posGather.contract_qty !== undefined) {
+                posGather.contract_tp = posGather.contract_qty * ledgerBills.unit_price;
+            }
+            if (posGather.qc_qty !== undefined) {
+                posGather.qc_tp = posGather.qc_qty * ledgerBills.unit_price;
+            }
+            if (stageBills) {
+                if (stageBills.contract_qty === posGather.contract_qty && stageBills.qc_qty === posGather.qc_qty) {
+                    return;
+                } else {
+                    const curOrder = this.ctx.stage.curAuditor ? this.ctx.stage.curAuditor.order : 0;
+                    if (stageBills.times === this.ctx.stage.times && stageBills.order === curOrder) {
+                        posGather.id = stageBills.id;
+                        await transaction.update(this.tableName, posGather);
+                    } else {
+                        await this._insertStageBillsData(transaction, posGather, ledgerBills);
+                    }
+                }
+            } else {
+                await this._insertStageBillsData(transaction, posGather, ledgerBills);
+            }
+        }
+    }
+
+    return StageBills;
+};
+

+ 285 - 0
app/service/stage_pos.js

@@ -0,0 +1,285 @@
+'use strict';
+
+/**
+ *
+ *
+ * @author Mai
+ * @date
+ * @version
+ */
+
+const measureType = require('../const/tender').measureType;
+
+module.exports = app => {
+    class StagePos extends app.BaseService {
+        /**
+         * 构造函数
+         *
+         * @param {Object} ctx - egg全局变量
+         * @return {void}
+         */
+        constructor(ctx) {
+            super(ctx);
+            this.tableName = 'stage_pos';
+        }
+
+        /**
+         * 查询期计量最后审核人数据
+         * @param {Number} tid - 标段id
+         * @param {Number} sid - 期id
+         * @param {Number|Array} pid - 部位明细id(可以为空)
+         * @returns {Promise<*>}
+         */
+        async getLastestStageData(tid, sid, pid) {
+            let pidSql = pid ? (' And Pos.pid in (' + (pid instanceof Array ? pid.join(', ') : pid) + ')') : '';
+            const sql = 'SELECT * FROM ' + this.tableName + ' As Pos ' +
+                '  INNER JOIN ( ' +
+                '    SELECT MAX(`times`) As `times`, MAX(`order`) As `order`, `pid` From ' + this.tableName +
+                '      GROUP BY `pid`' +
+                '  ) As MaxFilter ' +
+                '  ON Pos.times = MaxFilter.times And Pos.order = MaxFilter.order And Pos.pid = MaxFilter.pid' +
+                '  WHERE Pos.tid = ? And Pos.sid = ?' + pidSql;
+            const sqlParam = [tid, sid];
+            if (!pid) {
+                return await this.db.query(sql, sqlParam);
+            } else if (pid instanceof Array) {
+                return await this.db.query(sql, sqlParam);
+            } else {
+                return await this.db.queryOne(sql, sqlParam);
+            }
+        }
+        /**
+         * 查询 某期 某轮审批 某审核人数据
+         * @param {Number} tid - 标段id
+         * @param {Number} sid - 期id
+         * @param {Number} times - 期第几轮审批
+         * @param {Number} order - 审核人顺序
+         * @param {Number|Array|Null} pid - 部位明细id - 为空则查询全部
+         * @returns {Promise<*>}
+         */
+        async getAuditorStageData(tid, sid, times, order, pid) {
+            let pidSql = pid ? (' And Pos.pid in (' + (pid instanceof Array ? pid.join(', ') : pid) + ')') : '';
+            const sql = 'SELECT * FROM ' + this.tableName + ' As Pos ' +
+                '  INNER JOIN ( ' +
+                '    SELECT MAX(`times`) As `times`, MAX(`order`) As `order`, `pid` From ' + this.tableName +
+                '      WHERE `times` <= ? AND `order` <= ?' +
+                '      GROUP BY `pid`' +
+                '  ) As MaxFilter ' +
+                '  ON Pos.times = MaxFilter.times And Pos.order = MaxFilter.order And Pos.pid = MaxFilter.pid' +
+                '  WHERE Pos.tid = ? And Pos.sid = ?' + pidSql;
+            const sqlParam = [times, order, tid, sid];
+            if (!pid) {
+                return await this.db.query(sql, sqlParam);
+            } else if (pid instanceof Array) {
+                return await this.db.query(sql, sqlParam);
+            } else {
+                return await this.db.queryOne(sql, sqlParam);
+            }
+        }
+
+        /**
+         * 新增部位明细数据(仅供updateStageData调用)
+         *
+         * @param transaction - 事务
+         * @param data - 新增数据
+         * @returns {Promise<{}>}
+         * @private
+         */
+        async _addStagePosData(transaction, data) {
+            const  result = {};
+            // 在主表pos中新增数据
+            const p = JSON.parse(JSON.stringify(data.updateData));
+            p.tid = this.ctx.tender.id;
+            p.add_stage = this.ctx.stage.id;
+            p.add_times = this.ctx.stage.times;
+            p.add_user = this.ctx.session.sessionUser.accountId;
+            if (p.contract_qty) { delete p.contract_qty; }
+            if (p.qc_qty) { delete p.qc_qty; }
+            if (p.postil) { delete p.postil; }
+            const addRst = await transaction.insert(this.ctx.service.pos.tableName, data.updateData);
+            p.id = addRst.insertId;
+            result.pos = p.id;
+            // 如果存在复核数据,更新计算主表清单
+            if (p.quantity) {
+                await this.ctx.service.ledger.calc(this.ctx.tender.id, p.lid, transaction);
+                result.ledger = p.lid;
+            }
+            // 如果存在本期计算数据,更新计算清单本期计量数据
+            if (data.contract_qty || data.qc_qty || data.postil) {
+                const ps = {
+                    pid: p.id,
+                    lid: p.lid,
+                    tid: this.ctx.tender.id,
+                    sid: this.ctx.stage.id,
+                    said: this.ctx.session.sessionUser.accountId,
+                    times: this.ctx.stage.times,
+                    order: 0,
+                };
+                if (data.contract_qty) { ps.contract_qty = data.contract_qty; }
+                if (data.qc_qty) { ps.qc_qty = data.qc_qty; }
+                if (data.postil) { ps.postil = data.postil; }
+                await transaction.insert(ps);
+                await this.ctx.service.stageBills.calc(ctx.tender.id, ctx.stage.id, ps.lid, transaction);
+                result.stageUpdate = true;
+            }
+            return result;
+        }
+
+        /**
+         * 更新部位明细数据(仅供updateStageData调用)
+         *
+         * @param transaction - 事务
+         * @param data - 更新数据(允许一次性提交多条)
+         * @returns {Promise<{ledger: Array, pos: Array}>}
+         * @private
+         */
+        async _updateStagePosData(transaction, data) {
+            const result = {ledger: [], pos: [], stageUpdate: true};
+            const datas = data instanceof Array ? data : [data];
+            const orgStagePos = await this.getLastestStageData(this.ctx.tender.id, this.ctx.stage.id, this._.map(datas, 'pid'));
+            const userOrder = this.ctx.stage.curAuditor ? this.ctx.stage.curAuditor.order : 0;
+            for (const d of datas) {
+                const osp = this._.find(orgStagePos, function (p) { return p.pid === d.pid; });
+                if (osp && osp.times === this.ctx.stage.times && osp.order === userOrder) {
+                    await transaction.update(this.tableName, d, {where: {id: osp.id}});
+                } else {
+                    console.log(osp);
+                    d.tid = this.ctx.tender.id;
+                    d.sid = this.ctx.stage.id;
+                    d.said = this.ctx.session.sessionUser.accountId;
+                    d.times = this.ctx.stage.times;
+                    d.order = userOrder;
+                    await transaction.insert(this.tableName, d);
+                }
+                result.pos.push(d.pid);
+                if ((d.contract_qty === undefined || d.qc_qty === undefined) && (result.ledger.indexOf(d.lid) === -1)) {
+                    result.ledger.push(d.lid);
+                }
+            }
+            for (const lid of result.ledger) {
+                await this.ctx.service.stageBills.calc(this.ctx.tender.id, this.ctx.stage.id, lid, transaction);
+            }
+            return result;
+        }
+
+        /**
+         * 删除部位明细数据(仅供updateStageData调用)
+         *
+         * @param transaction - 事务
+         * @param data - 删除的部位明细(允许一次提醒多条,也允许跨清单(但前端操作不允许))
+         * @returns {Promise<{}>}
+         * @private
+         */
+        async _deleteStagePosData(transaction, data) {
+            const result = {};
+            const pos = await this.ctx.service.pos.getPosData({tid: this.ctx.tender.id, id: data});
+            if (pos instanceof Array) {
+                for (const p of pos) {
+                    if (p.add_stage !== this.ctx.stage.id || p.add_times !== this.ctx.stage.times || p.add_user !== this.ctx.session.sessionUser.accountId) {
+                        throw '您无权删除该数据';
+                    }
+                }
+            } else if (pos.add_stage !== this.ctx.stage.id || pos.add_times !== this.ctx.stage.times || pos.add_user !== this.ctx.session.sessionUser.accountId) {
+                throw '您无权删除该数据';
+            }
+            const ledgerIds = this._.map(pos, 'lid');
+            // 删除部位明细
+            await transaction.delete(this.ctx.service.pos.tableName, {tid: this.ctx.tender.id, id: data});
+            for (const lid of ledgerIds) {
+                await this.ctx.service.ledger.calc(tid, lid, transaction);
+            }
+            // 删除部位明细计量数据
+            await transaction.delete(this.tableName, {tid: this.ctx.tender.id, lid: data});
+            for (const lid of ledgerIds) {
+                await this.ctx.service.stageBills.calc(this.ctx.tender.id, this.ctx.stage.id, lid, transaction);
+            }
+            // 获取需要更新的数据
+            result.ledger = ledgerIds;
+            result.stageUpdate = true;
+            return result;
+        }
+
+        /**
+         * 根据前端提交数据,更新并计算
+         *
+         * @param data
+         * @returns {Promise<{ledger: {}, pos: {}}>}
+         */
+        async updateStageData(data) {
+            let refreshData;
+            const transaction = await this.db.beginTransaction();
+            try {
+                if ((data.updateType === 'add' || data.upateType === 'delete') && this.ctx.tender.measure_type === measureType.tz) {
+                    throw '台账模式下,不可在计量中新增或删除部位明细,如需操作,请进行台账修订';
+                }
+                if (data.updateType === 'add') {
+                    refreshData = await this._addStagePosData(transaction, data.updateData);
+                } else if (data.updateType === 'update') {
+                    refreshData = await this._updateStagePosData(transaction, data.updateData);
+                    console.log(refreshData);
+                } else if (data.updateType === 'delete') {
+                    if (!data.updateData || data.updateData.length === 0) {
+                        throw '提交数据错误';
+                    }
+                    refreshData = await this._deleteStagePosData(transaction, data.updateData);
+                } else {
+                    throw '提交数据错误';
+                }
+                await transaction.commit();
+            } catch (err) {
+                await transaction.rollback();
+                throw err;
+            }
+
+            try {
+                const result = {ledger: {}, pos: {}};
+                if (refreshData.ledger && refreshData.ledger.length > 0) {
+                    result.ledger.bills = await this.ctx.service.ledger.getDataByIds(refreshData.ledger);
+                    if (refreshData.stageUpdate) {
+                        result.ledger.curStageData = await await this.ctx.service.stageBills.getLastestStageData(this.ctx.tender.id, this.ctx.stage.id, refreshData.ledger);
+                    }
+                }
+                if (refreshData.pos && refreshData.pos.length > 0) {
+                    result.pos.pos = await this.ctx.service.pos.getPosData({id: refreshData.pos});
+                    if (refreshData.stageUpdate) {
+                        result.pos.curStageData = await this.getLastestStageData(this.ctx.tender.id, this.ctx.stage.id, refreshData.pos);
+                    }
+                }
+                return result;
+            } catch(err) {
+                throw '获取数据异常,请刷新页面。';
+            }
+        }
+
+        /**
+         * 统计清单下部位明细合计
+         * @param {Number} tid - 标段id
+         * @param {Number} sid - 期id
+         * @param {Number} lid - 清单节点id
+         * @param transaction - 事务(不为空则在事务中查询,反之在数据库中查询)
+         * @returns {Promise<*>}
+         */
+        async getPosGather(tid, sid, lid, transaction) {
+            const calcQtySql = 'SELECT SUM(`contract_qty`) As `contract_qty`, SUM(`qc_qty`) As `qc_qty` FROM (' +
+                '  SELECT `contract_qty`, `qc_qty` FROM ' + this.ctx.service.stagePos.tableName + ' As Pos ' +
+                '    INNER JOIN (' +
+                '      SELECT MAX(`times`) As `times`, MAX(`order`) As `order`, `pid` ' +
+                '        FROM ' + this.ctx.service.stagePos.tableName +
+                '        WHERE `tid` = ? And sid = ? And `lid` = ? ' +
+                '        GROUP BY `pid`' +
+                '    ) As MaxFilter ' +
+                '    ON Pos.times = MaxFilter.times And Pos.order = MaxFilter.order And Pos.pid = MaxFilter.pid ' +
+                '    WHERE Pos.tid = ? And Pos.sid = ? And Pos.lid = ?' +
+                '  ) As Gather';
+            const param = [tid, sid, lid];
+            const sqlParam = param.concat(param);
+            if (transaction) {
+                return await transaction.queryOne(calcQtySql, sqlParam);
+            } else {
+                return await this.db.queryOne(calcQtySql, sqlParam);
+            }
+        }
+    }
+
+    return StagePos;
+};

+ 6 - 5
app/service/standard_lib.js

@@ -39,11 +39,12 @@ class StandardLib extends BaseService {
             operate: '=',
             value: listId,
         });
-
-        this.sqlBuilder.setAndWhere('level', {
-            operate: '<=',
-            value: level,
-        });
+        if (level >= 0) {
+            this.sqlBuilder.setAndWhere('level', {
+                operate: '<=',
+                value: level,
+            });
+        }
         const [sql, sqlParam] = this.sqlBuilder.build(this.tableName);
         const list = await this.db.query(sql, sqlParam);
 

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

@@ -13,6 +13,7 @@
     <link rel="stylesheet" href="/public/css/spreadjs/sheets/gc.spread.sheets.excelsmartcost.css">
     <link rel="stylesheet" href="/public/css/jquery-contextmenu/jquery.contextMenu.min.css">
     <link rel="stylesheet" href="/public/css/ztree/zTreeStyle.css" type="text/css">
+    <link rel="stylesheet" href="/public/css/datepicker/datepicker.min.css" rel="stylesheet" type="text/css">
     <!-- JS. -->
     <script src="/public/js/jquery/jquery-3.2.1.min.js"></script>
     <script src="/public/js/jquery/jquery.validate.js"></script>

+ 0 - 3
app/view/ledger/explode.ejs

@@ -130,15 +130,12 @@
 <script type="text/javascript">
     const tender = JSON.parse('<%- JSON.stringify(tender) %>');
     const tenderInfo = JSON.parse('<%- JSON.stringify(tenderInfo) %>');
-    const preUrl = '/api<%- preUrl %>';
     const measureType = JSON.parse('<%- JSON.stringify(measureType) %>');
     let ledger = '<%- ledger %>';
     ledger = JSON.parse(ledger);
     let ledgerSpreadSetting = '<%- ledgerSpreadSetting %>';
     ledgerSpreadSetting = JSON.parse(ledgerSpreadSetting);
-    ledgerSpreadSetting.readOnly = <%- tender.user_id !== ctx.session.sessionUser.accountId || tender.ledger_status === auditConst.status.checking || tender.ledger_status === auditConst.status.checked %>;
     let posSpreadSetting = JSON.parse('<%- posSpreadSetting %>');
-    posSpreadSetting.readOnly = ledgerSpreadSetting.readOnly;
 </script>
 <script src="/public/js/spreadjs/sheets/gc.spread.sheets.all.10.0.1.min.js"></script>
 <script>

+ 33 - 0
app/view/measure/compare.ejs

@@ -0,0 +1,33 @@
+<% include ../tender/tender_sub_menu.ejs %>
+<div class="panel-content">
+    <div class="panel-title">
+        <div class="title-main d-flex justify-content-between">
+            <h2>期审核比较</h2>
+            <div>
+                <button href="#cate-set" class="btn btn-sm btn-light" data-toggle="modal" data-target="#select-qi"><i class="fa fa-clone"></i> 选择比较期</button>
+            </div>
+        </div>
+    </div>
+    <div class="content-wrap">
+        <div class="c-header p-0"></div>
+        <div class="c-body">
+            <div class="sjs-height-1" id="bills-spread">
+            </div>
+            <div class="bcontent-wrap">
+                <div class="bc-bar mb-1">
+                    <ul class="nav nav-tabs">
+                        <li class="nav-item">
+                            <a class="nav-link active" data-toggle="tab" href="#xmujie" role="tab">部位明细</a>
+                        </li>
+                    </ul>
+                </div>
+                <div class="tab-content">
+                    <div class="tab-pane active" id="xmujie">
+                        <div class="sp-wrap" id="bills-spread2">
+                        </div>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </div>
+</div>

+ 22 - 0
app/view/measure/compare_modal.ejs

@@ -0,0 +1,22 @@
+<!--选择比较期-->
+<div class="modal fade" id="select-qi" 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">
+                <table class="table table-sm">
+                    <tr><th>期</th><th width="90">选择</th></tr>
+                    <tr><td>1期</td><td><input type="checkbox"></td></tr>
+                    <tr><td>2期</td><td><input type="checkbox"></td></tr>
+                    <tr><td>3期</td><td><input type="checkbox"></td></tr>
+                </table>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-secondary" data-dismiss="modal">关闭</button>
+                <button type="button" class="btn btn-primary" >确认</button>
+            </div>
+        </div>
+    </div>
+</div>

+ 44 - 0
app/view/measure/gather.ejs

@@ -0,0 +1,44 @@
+<% include ../tender/tender_sub_menu.ejs %>
+<div class="panel-content">
+    <div class="panel-title">
+        <div class="title-main d-flex justify-content-between">
+            <h2>清单汇总</h2>
+            <div>
+            </div>
+        </div>
+    </div>
+    <div class="content-wrap">
+        <div class="c-header p-0"></div>
+        <div class="c-body">
+            <div class="sjs-height-1" id="bills-spread">
+            </div>
+            <div class="bcontent-wrap">
+                <div class="bc-bar mb-1">
+                    <ul class="nav nav-tabs">
+                        <li class="nav-item">
+                            <a class="nav-link active" data-toggle="tab" href="#xmujie" role="tab">所属项目节</a>
+                        </li>
+                        <li class="nav-item">
+                            <a class="nav-link " data-toggle="tab" href="#biangeng" role="tab">相关变更令</a>
+                        </li>
+                    </ul>
+                </div>
+                <div class="tab-content">
+                    <div class="tab-pane active" id="xmujie">
+                        <div class="sp-wrap" id="bills-spread2">
+                        </div>
+                    </div>
+                    <div class="tab-pane" id="biangeng">
+                        <div class="sp-wrap">
+                            <table class="table table-sm table-bordered">
+                                <tr><th>变更令</th><th>名称</th><th>数量</th><th>本期变更数量</th><th>批复文号</th></tr>
+                                <tr><td>BGL01</td><td></td><td></td><td>8</td><td></td></tr>
+                                <tr><td>BGL02</td><td></td><td></td><td>22</td><td></td></tr>
+                            </table>
+                        </div>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </div>
+</div>

+ 60 - 0
app/view/measure/stage.ejs

@@ -0,0 +1,60 @@
+<% include ../tender/tender_sub_menu.ejs %>
+<div class="panel-content">
+    <div class="panel-title">
+        <div class="title-main d-flex justify-content-between">
+            <h2>
+                期列表
+            </h2>
+            <div>
+                <% if (ctx.session.sessionUser.accountId === ctx.tender.user_id && ctx.tender.ledger_status === auditConst.status.checked &&
+                        (stages.length === 0 || stages[stages.length- 1].status === auditConst.status.checked)) { %>
+                <a href="#add-qi" data-toggle="modal" data-target="#add-qi" class="btn btn-primary btn-sm pull-right">开始新一期</a>
+                <% } %>
+            </div>
+        </div>
+    </div>
+    <div class="content-wrap">
+        <div class="c-body">
+            <table class="table table-bordered">
+                <thead>
+                <tr>
+                    <th>期数</th>
+                    <th class="text-center">计量月份</th>
+                    <th class="text-center">本期合同计量</th>
+                    <th class="text-center">本期数量变更计量</th>
+                    <th class="text-center">本期完成计量</th>
+                    <th class="text-center">截止上期完成计量</th>
+                    <th class="text-center">截止本期完成计量</th>
+                    <th class="text-center">审批状态</th>
+                    <th class="text-center">审批进度</th>
+                </tr>
+                </thead>
+                <tbody>
+                <% for (const s of stages) { %>
+                <tr>
+                    <td>
+                        <a href="<%- '/tender/' + ctx.tender.id + '/measure/stage/' + s.order %>">第 <%- s.order %> 期</a>
+                        <% if (s.status === auditConst.status.uncheck && s.user_id === ctx.session.sessionUser.accountId) { %>
+                        <a href="#edit" data-toggle="modal" data-target="#edit"><i class="fa fa-pencil-square-o "></i></a>
+                        <% } %>
+                    </td>
+                    <td class="text-center"></td>
+                    <td class="text-right"></td>
+                    <td class="text-right"></td>
+                    <td class="text-right"></td>
+                    <td class="text-right"></td>
+                    <td class="text-right"></td>
+                    <td class="text-center"><a href="" class="btn <%- auditConst.statusButtonClass[s.status] %> btn-sm"><%- auditConst.auditStatusString[s.status] %></a></td>
+                    <td class="<%- auditConst.statusClass[s.status] %>">
+                        <% if (s.curAuditor) { %>
+                        <a href="#sp-list" data-toggle="modal" data-target="#sp-list" s-order="<%- s.order %>"><%- s.curAuditor.name %>-<%s.curAuditor.role %></a>
+                        <% } %>
+                        <%- auditConst.auditStatusString[s.status] %>
+                    </td>
+                </tr>
+                <% } %>
+                </tbody>
+            </table>
+        </div>
+    </div>
+</div>

+ 162 - 0
app/view/measure/stage_modal.ejs

@@ -0,0 +1,162 @@
+<% if (ctx.session.sessionUser.accountId === ctx.tender.user_id && ctx.tender.ledger_status === auditConst.status.checked &&
+        (stages.length === 0 || stages[stages.length- 1].status === auditConst.status.checked)) { %>
+<!--弹出添加期-->
+<div class="modal fade" id="add-qi" data-backdrop="static">
+    <div class="modal-dialog" role="document">
+        <form class="modal-content" action="<%- preUrl + '/measure/stage/add' %>" method="post">
+            <div class="modal-header">
+                <h5 class="modal-title">添加新一期</h5>
+            </div>
+            <div class="modal-body">
+                <div class="form-group">
+                    <label>期</label>
+                    <input class="form-control" value="第 <%- stages.length + 1 %> 期" type="text" readonly="">
+                </div>
+                <div class="form-group">
+                    <label>计量年月</label>
+                    <input class="datepicker-here form-control" placeholder="点击选择年月" data-view="months" data-min-view="months" data-date-format="yyyy-MM" data-language="zh" type="text" name="date">
+                </div>
+                <div class="form-group">
+                    <label>开始-截止日期</label>
+                    <input class="datepicker-here form-control" placeholder="点击选择时间" data-range="true" data-multiple-dates-separator=" ~ " data-language="zh" type="text" name="period">
+                </div>
+            </div>
+            <div class="modal-footer">
+                <input type="hidden" name="_csrf" value="<%= ctx.csrf %>" />
+                <button type="button" class="btn btn-secondary" data-dismiss="modal">关闭</button>
+                <button type="submit" class="btn btn-primary">确定添加</button>
+            </div>
+        </form>
+    </div>
+</div>
+<% } %>
+<!--审批流程/结果-->
+<div class="modal fade" id="sp-list" data-backdrop="static">
+    <div class="modal-dialog modal-lg" role="document">
+        <div class="modal-content">
+            <div class="modal-header">
+                <h5 class="modal-title">审批流程</h5>
+            </div>
+            <div class="modal-body">
+                <div class="row">
+                    <div class="col-4">
+                        <div class="card mt-3">
+                            <ul class="list-group list-group-flush">
+                                <li class="list-group-item"><i class="fa fa fa-play-circle fa-rotate-90"></i> 布尔  <small class="text-muted">施工</small></li>
+                                <li class="list-group-item"><i class="fa fa-chevron-circle-down"></i> 张三  <small class="text-muted">监理</small></li>
+                                <li class="list-group-item"><i class="fa fa-chevron-circle-down"></i> 王五 <small class="text-muted">监理</small></li>
+                                <li class="list-group-item"><i class="fa fa fa-stop-circle"></i> 李四 <small class="text-muted">监理</small></li>
+                            </ul>
+                        </div>
+                    </div>
+                    <div class="col-8 modal-height-500" style="overflow: auto">
+                        <div class="card mt-3">
+                            <ul class="list-group list-group-flush">
+                                <li class="list-group-item">
+                                    <span class="text-success pull-right">上报</span>
+                                    <h5 class="card-title"><i class="fa fa-play-circle fa-rotate-90 text-success"></i> 布尔 <small class="text-muted">施工</small></h5>
+                                    <p class="card-text">2017-11-25</p>
+                                </li>
+                                <li class="list-group-item">
+                                    <span class="text-success pull-right">审批通过</span>
+                                    <h5 class="card-title"><i class="fa fa-chevron-circle-down text-success"></i> 张三 <small class="text-muted">监理</small></h5>
+                                    <p class="card-text">审批意见。2017-11-25</p>
+                                </li>
+                                <li class="list-group-item">
+                                    <span class="text-success pull-right">审批通过</span>
+                                    <h5 class="card-title"><i class="fa fa-chevron-circle-down text-success"></i> 王五 <small class="text-muted">监理</small></h5>
+                                    <p class="card-text">审批通过。2017-11-26</p>
+                                </li>
+                                <li class="list-group-item">
+                                    <span class="text-warning pull-right">审批退回 布尔</span>
+                                    <h5 class="card-title"><i class="fa fa-stop-circle text-warning"></i> 李四 <small class="text-muted">监理</small></h5>
+                                    <p class="card-text">审批退回,审批意见文本。2017-11-27</p>
+                                </li>
+                            </ul>
+                        </div>
+                        <!--退回原报重新上报-->
+                        <div class="card mt-3">
+                            <ul class="list-group list-group-flush">
+                                <li class="list-group-item">
+                                    <span class="text-success pull-right">重新上报</span>
+                                    <h5 class="card-title"><i class="fa fa-play-circle fa-rotate-90 text-success"></i> 布尔 <small class="text-muted">施工</small></h5>
+                                    <p class="card-text">2017-12-01</p>
+                                </li>
+                                <li class="list-group-item">
+                                    <span class="text-success pull-right">审批通过</span>
+                                    <h5 class="card-title"><i class="fa fa-chevron-circle-down text-success"></i> 张三 <small class="text-muted">监理</small></h5>
+                                    <p class="card-text">审批通过 2017-12-02</p>
+                                </li>
+                                <li class="list-group-item">
+                                    <span class="text-warning pull-right">审批退回 张三</span>
+                                    <h5 class="card-title"><i class="fa fa-chevron-circle-down text-warning"></i> 王五 <small class="text-muted">监理</small></h5>
+                                    <p class="card-text">审批退回 2017-12-02</p>
+                                </li>
+                                <!--王五退回上一审批人 张三,张三重新审批-->
+                                <li class="list-group-item">
+                                    <span class="pull-right">审批中</span>
+                                    <h5 class="card-title"><i class="fa fa-chevron-circle-down"></i> 张三 <small class="text-muted">监理</small></h5>
+                                    <p class="card-text"></p>
+                                </li>
+                                <li class="list-group-item">
+                                    <h5 class="card-title"><i class="fa fa-stop-circle"></i> 李四 <small class="text-muted">监理</small></h5>
+                                </li>
+                            </ul>
+                        </div>
+                    </div>
+                </div>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-secondary" data-dismiss="modal">关闭</button>
+            </div>
+        </div>
+    </div>
+</div>
+<% if (stages.length > 0 && stages[0].status === auditConst.status.uncheck && stages[0].user_id === ctx.session.sessionUser.accountId) { %>
+<!--设置-->
+<div class="modal fade" id="edit" data-backdrop="static">
+    <div class="modal-dialog" role="document">
+        <div class="modal-content">
+            <div class="modal-header">
+                <h5 class="modal-title">期编辑</h5>
+            </div>
+            <div class="modal-body">
+                <div class="form-group">
+                    <label>期</label>
+                    <input class="form-control" value="第 <%- stages[0].order %> 期" type="text" readonly="" name="name">
+                </div>
+                <div class="form-group">
+                    <label>计量年月</label>
+                    <input class="datepicker-here form-control" name="edit-date" placeholder="点击选择年月" data-view="months" data-min-view="months" data-date-format="yyyy-MM" data-language="zh" type="text" value="<%- stages[0].s_time %>">
+                </div>
+                <div class="form-group">
+                    <label>开始-截止日期</label>
+                    <input class="datepicker-here form-control" name="edit-period" placeholder="点击选择时间" data-range="true" data-multiple-dates-separator=" ~ " data-language="zh" type="text" value="<%- stages[0].period %>">
+                </div>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-secondary" data-dismiss="modal">关闭</button>
+                <button type="button" class="btn btn-primary" id="edit-ok" s-order="<%- stages[0].order%>">确定修改</button>
+            </div>
+        </div>
+    </div>
+</div>
+<% } %>
+<script src="/public/js/datepicker/datepicker.min.js"></script>
+<script src="/public/js/datepicker/datepicker.zh.js"></script>
+<script>
+    $('#edit-ok').click(function () {
+        const data = {
+            order: parseInt($(this).attr('s-order')),
+            date: $('input[name=edit-date]').val(),
+            period: $('input[name=edit-period]').val(),
+        };
+        if (data.date === '' || data.period === '') {
+            return;
+        }
+        const tenderId = window.location.pathname.split('/')[2];
+        postData('/tender/' + tenderId + '/measure/stage/save', data, function () {
+            $('#edit').modal('hide');
+        });
+    });
+</script>

+ 200 - 0
app/view/stage/change.ejs

@@ -0,0 +1,200 @@
+<% include ../tender/tender_sub_menu.ejs %>
+<div class="panel-content">
+    <div class="panel-title">
+        <div class="title-main d-flex justify-content-between">
+            <div>
+                <div class="d-inline-block ml-3">
+                    <div class="form-check-inline">
+                        <label class="form-check-label">
+                            <input class="form-check-input" type="checkbox">
+                            本期已用变更
+                        </label>
+                    </div>
+                </div>
+            </div>
+            <div>
+                <!--按钮-->
+            </div>
+        </div>
+    </div>
+    <div class="content-wrap">
+        <div class="c-header p-0">
+        </div>
+        <div class="row w-100 sub-content">
+            <!--左栏-->
+            <div class="c-body col-6">
+                <!--上部分-->
+                <div class="sjs-height-1">
+                    <table class="table table-bordered table-sm">
+                        <tr><th>变更令号</th><th>变更名称</th><th>金额</th><th>批复文号</th><th>已执行(%)</th></tr>
+                        <tr><td>LZTJ-1标项目部发变更(2015)001号 </td><td>XX变更1</td><td>140343</td><td>纵路投[2019]126号</td><td></td></tr>
+                        <tr><td>LZTJ-1标项目部发变更(2015)002号 </td><td>XX变更2</td><td></td><td>纵路投[2019]126号</td><td></td></tr>
+                        <tr><td>LZTJ-1标项目部发变更(2015)003号 </td><td>XX变更3</td><td></td><td>纵路投[2019]126号</td><td></td></tr>
+                    </table>
+                </div>
+                <!--下部分-->
+                <div class="bcontent-wrap">
+                    <div class="bc-bar mb-1">
+                        <ul class="nav nav-tabs">
+                            <li class="nav-item">
+                                <a class="nav-link active" data-toggle="tab" href="#qingdan" role="tab">变更清单</a>
+                            </li>
+                            <li class="nav-item">
+                                <a class="nav-link" data-toggle="tab" href="#detail" role="tab">变更信息</a>
+                            </li>
+                            <li class="nav-item">
+                                <a class="nav-link" data-toggle="tab" href="#fujian" role="tab">附件</a>
+                            </li>
+                        </ul>
+                    </div>
+                    <div class="tab-content">
+                        <div class="tab-pane active" id="qingdan">
+                            <div class="sp-wrap" style="overflow: auto">
+                                <table class="table table-sm table-bordered">
+                                    <tr><th>本期使用</th><th>清单编号</th><th>名称</th><th>单位</th><th>单价</th><th>数量</th><th>金额</th><th>已变更</th><th>未变更</th></tr>
+                                    <tr><td></td><td>203-1-a</td><td>挖土方</td><td>m3</td><td>7.53</td><td>6467.48</td><td>48700</td><td>1000</td></tr>
+                                    <tr><td><i class="fa fa-check text-success"></i></td><td>403-1-a</td><td>I级钢筋(包括基础、承台、支撑梁等)</td><td></td><td></td><td></td><td></td><td></td><td>1000</td></tr>
+                                    <tr><td></td><td>403-2</td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
+                                </table>
+                            </div>
+                        </div>
+                        <div class="tab-pane" id="detail">
+                            <div class="sp-wrap" style="overflow: auto">
+                                <div class="form-group">
+                                    <label>变更类型</label>
+                                    <div class="checkbox">
+                                        <label class="checkbox-inline">
+                                            <input id="inlineCheckbox1" value="option1" checked="" disabled="" type="checkbox">A.位置
+                                        </label>
+                                        <!-- <label class="checkbox-inline">
+                                          <input id="inlineCheckbox2" value="option2" type="checkbox">B.数量
+                                        </label>
+                                        <label class="checkbox-inline">
+                                          <input id="inlineCheckbox3" value="option3" type="checkbox">C.结构
+                                        </label>
+                                        <label class="checkbox-inline">
+                                          <input id="inlineCheckbox3" value="option3" checked="" type="checkbox">D.新增
+                                        </label>
+                                        <label class="checkbox-inline">
+                                          <input id="inlineCheckbox3" value="option3" type="checkbox">E.取消
+                                        </label>
+                                        <label class="checkbox-inline">
+                                          <input id="inlineCheckbox3" value="option3" type="checkbox">F.纠错
+                                        </label> -->
+                                    </div>
+                                </div>
+                                <div class="form-group">
+                                    <label>变更类别 </label>
+                                    <select class="form-control form-control-sm" disabled=""><option>A类变更</option><option>B类变更</option><option>C类变更</option><option>D类变更</option><option>E类变更</option></select>
+                                </div>
+                                <div class="form-group">
+                                    <label>变更性质 </label>
+                                    <select class="form-control form-control-sm" disabled=""><option>一般设计变更</option><option>较大设计变更</option><option>重大设计变更</option></select>
+                                </div>
+                                <div class="form-group">
+                                    <label>桩号</label>
+                                    <input class="form-control form-control-sm" value="K0+532" readonly="" type="text">
+                                </div>
+                                <div class="form-group">
+                                    <label>原设计图名称</label>
+                                    <input class="form-control form-control-sm" placeholder="" readonly="" type="text">
+                                </div>
+                                <div class="form-group">
+                                    <label>原图号</label>
+                                    <input class="form-control form-control-sm" placeholder="" readonly="" type="text">
+                                </div>
+                                <div class="form-group">
+                                    <label>变更设计图名称</label>
+                                    <input class="form-control form-control-sm" placeholder="" readonly="" type="text">
+                                </div>
+                                <div class="form-group">
+                                    <label>变更图号</label>
+                                    <input class="form-control form-control-sm" placeholder="" readonly="" type="text">
+                                </div>
+                                <div class="form-group">
+                                    <label>工程变更理由及内容</label>
+                                    <textarea class="form-control form-control-sm" rows="6" readonly="">由于K0+532涵洞基底土质天然含水率为25%、收费站出口右侧加宽段K0+120-K0+190段基底土质天然含水率为28.8%。含水率较大形成过湿土,地基松软,无法满足承载力要求。经处项目办、设计、监理、施工单位四方勘察,采用抛石挤淤、砂砾换填以达到地基承载力。
+                          </textarea>
+                                </div>
+                                <div class="form-group">
+                                    <label>工程变更合同依据</label>
+                                    <textarea class="form-control form-control-sm" rows="6" readonly=""></textarea>
+                                </div>
+                                <div class="form-group">
+                                    <label>变更提出单位</label>
+                                    <!-- <a href="#editcompany" style="float:right;" class="" data-toggle="modal">编辑</a> -->
+                                    <select class="form-control form-control-sm" disabled=""><option>AA公司</option><option>BB公司</option></select>
+                                </div>
+                                <div class="form-group">
+                                    <label>费用承担方</label>
+                                    <div class="radio">
+                                        <label class="radio-inline">
+                                            <input id="inlineCheckbox1" value="option1" checked="" disabled="" type="radio"> 业主
+                                        </label>
+                                        <!-- <label class="radio-inline">
+                                          <input id="inlineCheckbox2" value="option2" type="radio"> 承包人
+                                        </label> -->
+                                    </div>
+                                </div>
+                                <div class="form-group">
+                                    <label>备注</label>
+                                    <textarea class="form-control form-control-sm" rows="3" readonly=""></textarea>
+                                </div>
+                            </div>
+                        </div>
+                        <div class="tab-pane" id="fujian">
+                            <div class="sp-wrap" style="overflow: auto">
+                                <table class="table table-bordered">
+                                    <tbody><tr><th>名称</th><th>上传者</th></tr>
+                                    <tr><td><a href="">合同段工程变更现场办公会议纪要的通知.rar</a></td><th>张三</th></tr>
+                                    </tbody>
+                                </table>
+                            </div>
+                        </div>
+                    </div>
+                </div>
+            </div>
+            <!--右栏-->
+            <div class="c-body col">
+                <div class="sjs-bar">
+                    <ul class="nav nav-tabs">
+                        <li class="nav-item">
+                            <a class="nav-link active" data-toggle="tab" href="#qingdan" role="tab">本期使用</a>
+                        </li>
+                    </ul>
+                </div>
+                <!--上部-->
+                <div class="tab-content">
+                    <div class="tab-pane active">
+                        <div class="sjs-height-3" style="overflow: auto">
+                            <table class="table table-sm table-bordered">
+                                <tr><th>清单编号</th><th>名称</th><th>单位</th><th>单价</th><th>本期已变更</th></tr>
+                                <tr><td>403-1-a</td><td>I级钢筋(包括基础、承台、支撑梁等)</td><td>kg</td><td>5.18</td><td>400</td></tr>
+                            </table>
+                        </div>
+                        <div class="sjs-bottom">
+                            <ul class="nav nav-tabs">
+                                <li class="nav-item">
+                                    <a class="nav-link active" data-toggle="tab" href="#tz" role="tab" aria-selected="false">相关台帐</a>
+                                </li>
+                            </ul>
+                            <div class="tab-content">
+                                <div class="tab-pane active" id="tz">
+                                    <div class="sjs-bottom-2">
+                                        <!--分项清单-->
+                                        <table class="table table-bordered table-sm">
+                                            <tr><th>项目节编号</th><th>名称</th><th>部位明细</th><th>0号台帐数量</th><th>本期变更数量</th></tr>
+                                            <tr><td>1-4-5-1-1-1-1</td><td>桥台桩基础</td><td>0#桥台1#桩</td><td>126</td><td>100</td></tr>
+                                            <tr><td>1-4-5-1-1-1-1</td><td>桥台桩基础</td><td>0#桥台2#桩</td><td>126</td><td>100</td></tr>
+                                            <tr><td>1-4-5-1-1-1-1</td><td>桥台桩基础</td><td>8#桥台1#桩</td><td>126</td><td>100</td></tr>
+                                            <tr><td>1-4-5-1-1-1-1</td><td>桥台桩基础</td><td>8#桥台2#桩</td><td>126</td><td>100</td></tr>
+                                        </table>
+                                    </div>
+                                </div>
+                            </div>
+                        </div>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </div>

+ 39 - 0
app/view/stage/compare.ejs

@@ -0,0 +1,39 @@
+<% include ../tender/tender_sub_menu.ejs %>
+<div class="panel-content">
+    <div class="panel-title">
+        <div class="title-main d-flex justify-content-between">
+            <h2>审核比较</h2>
+            <div>
+                <a class="btn btn-sm btn-light">
+                    <div class="custom-control custom-checkbox">
+                        <input type="checkbox" class="custom-control-input" id="customCheckDisabled" checked="">
+                        <label class="custom-control-label" for="customCheckDisabled">显示本期计量</label>
+                    </div>
+                </a>
+                <button href="#cate-set" class="btn btn-sm btn-light" data-toggle="modal" data-target="#select-qi"><i class="fa fa-users"></i> 选择比较人</button>
+            </div>
+        </div>
+    </div>
+    <div class="content-wrap">
+        <div class="c-header p-0"></div>
+        <div class="c-body">
+            <div class="sjs-height-1" id="bills-spread">
+            </div>
+            <div class="bcontent-wrap">
+                <div class="bc-bar mb-1">
+                    <ul class="nav nav-tabs">
+                        <li class="nav-item">
+                            <a class="nav-link active" data-toggle="tab" href="#xmujie" role="tab">部位明细</a>
+                        </li>
+                    </ul>
+                </div>
+                <div class="tab-content">
+                    <div class="tab-pane active" id="xmujie">
+                        <div class="sp-wrap" id="bills-spread2">
+                        </div>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </div>
+</div>

+ 21 - 0
app/view/stage/compare_modal.ejs

@@ -0,0 +1,21 @@
+<!--选择比较期-->
+<div class="modal fade" id="select-qi" 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">
+                <table class="table table-sm">
+                    <tr><th>审批人</th><th width="90">选择</th></tr>
+                    <tr><td>王五</td><td><input type="checkbox"></td></tr>
+                    <tr><td>李四</td><td><input type="checkbox"></td></tr>
+                </table>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-secondary" data-dismiss="modal">关闭</button>
+                <button type="button" class="btn btn-primary" >确认</button>
+            </div>
+        </div>
+    </div>
+</div>

+ 157 - 0
app/view/stage/detail.ejs

@@ -0,0 +1,157 @@
+<% include ../tender/tender_sub_menu.ejs %>
+<div class="panel-content">
+    <div class="panel-title">
+        <div class="title-main d-flex justify-content-between">
+            <div>
+                <div class="d-inline-block ml-3">
+                    <div class="input-group input-group-sm">
+                        <div class="input-group-prepend">
+                            <span class="input-group-text">前缀</span>
+                        </div>
+                        <input type="text" class="form-control m-0">
+                    </div>
+                </div>
+                <div class="d-inline-block ml-3">
+                    <div class="form-group">
+                        <div class="form-check form-check-inline">
+                            <input class="form-check-input" name="inlineRadioOptions" id="inlineRadio1" value="option1" type="radio">
+                            <label class="form-check-label" for="inlineRadio1">总量控制</label>
+                        </div>
+                        <div class="form-check form-check-inline">
+                            <input class="form-check-input" name="inlineRadioOptions" id="inlineRadio2" value="option2" type="radio">
+                            <label class="form-check-label" for="inlineRadio2">0号台帐</label>
+                        </div>
+                        <button class="btn btn-sm btn-outline-primary">生成数据</button>
+                    </div>
+                </div>
+                <div class="d-inline-block ml-3">
+                    <a href="#choose" data-toggle="modal" data-target="#choose" class="btn btn-primary btn-sm">计量汇总</a>
+                </div>
+            </div>
+            <div>
+                <!--上报-->
+                <a href="#sub-sp" data-toggle="modal" data-target="#sub-sp" class="btn btn-primary btn-sm pull-right">上报审批</a>
+                <a href="#sub-sp2" data-toggle="modal" data-target="#sub-sp2" class="btn btn-primary btn-sm pull-right">重新上报</a>
+                <!--状态显示-->
+                <!-- <a href="#sp-list" data-toggle="modal" data-target="#sp-list" class="btn btn-outline-success btn-sm pull-right">审批完成</a>
+                <a href="#sp-list" data-toggle="modal" data-target="#sp-list" class="btn btn-outline-secondary btn-sm pull-right">审批中</a>
+                <a href="#sp-list" data-toggle="modal" data-target="#sp-list" class="btn btn-outline-warning btn-sm pull-right text-muted">审批退回</a> -->
+                <!--审批操作-->
+                <!-- <a href="#sp-done" data-toggle="modal" data-target="#sp-done" class="btn btn-success btn-sm pull-right">审批通过</a>
+                <a href="#sp-back" data-toggle="modal" data-target="#sp-back" class="btn btn-warning btn-sm pull-right">审批退回</a> -->
+            </div>
+        </div>
+    </div>
+    <div class="content-wrap">
+        <div class="c-header p-0">
+        </div>
+        <div class="row w-100 sub-content">
+            <!--左栏-->
+            <div class="c-body col-8">
+                <!--上部分-->
+                <div class="sjs-height-1">
+                    <table class="table table-sm table-bordered">
+                        <tr><th>编号</th><th>中间计量表号</th><th>交工证书/凭证号</th><th>分部分项工程</th><th>本期计量数量/金额</th></tr>
+                        <tr><td>403-1-a</td><td>1-1</td><td></td><td>k0+236大桥</td><td>100</td></tr>
+                    </table>
+                </div>
+                <!--下部分-->
+                <div class="bcontent-wrap">
+                    <div class="bc-bar mb-1">
+                        <ul class="nav nav-tabs">
+                            <li class="nav-item">
+                                <a class="nav-link active" data-toggle="tab" href="#zhongjian" role="tab">中间计量数据</a>
+                            </li>
+                            <li class="nav-item">
+                                <a class="nav-link" data-toggle="tab" href="#caotu" role="tab">计算草图</a>
+                            </li>
+                        </ul>
+                    </div>
+                    <div class="tab-content">
+                        <div class="tab-pane active" id="zhongjian">
+                            <div class="sp-wrap" style="overflow: auto">
+                                <div class="d-flex justify-content-end mt-1 mr-1">
+                                    <a href="#" class="btn btn-sm btn-outline-primary">编辑</a>
+                                    <a href="#" class="btn btn-sm btn-outline-success mr-1">保存</a>
+                                    <a href="#" class="btn btn-sm btn-outline-secondary">取消</a>
+                                </div>
+                                <div class="form-group">
+                                    <label>变更令号:</label>
+                                    <input class="form-control form-control-sm" type="text" readonly="">
+                                </div>
+                                <div class="form-group">
+                                    <label>部位:</label>
+                                    <input class="form-control form-control-sm" type="text" readonly="" value="左3#中横梁">
+                                </div>
+                                <div class="form-group">
+                                    <label>起始桩号:</label>
+                                    <input class="form-control form-control-sm" type="text" readonly="" value="K170+170.0">
+                                </div>
+                                <div class="form-group">
+                                    <label>终止桩号:</label>
+                                    <input class="form-control form-control-sm" type="text" readonly="" value="K170+170.0">
+                                </div>
+                                <div class="form-group">
+                                    <label>计量单元:</label>
+                                    <input class="form-control form-control-sm" type="text" readonly="" value="光圆钢筋(HPB235、HPB300)">
+                                </div>
+                                <div class="form-group">
+                                    <label>图号:</label>
+                                    <input class="form-control form-control-sm" type="text" readonly="" value="XL-1-13(通用图1)">
+                                </div>
+                                <div class="form-group">
+                                    <label>计算式说明:</label>
+                                    <textarea class="form-control" readonly=""></textarea>
+                                </div>
+                            </div>
+                        </div>
+                        <div class="tab-pane" id="caotu">
+                            <div class="sp-wrap" style="overflow: auto">
+                                <div class="d-flex justify-content-between my-3">
+                                    <label>计算草图:</label>
+                                    <a href="#edit-img" data-toggle="modal" data-target="#edit-img">添加草图</a>
+                                </div>
+                                <p><img src="img/sketch/2.png" class="d-100"></p>
+                            </div>
+                        </div>
+                    </div>
+                </div>
+            </div>
+            <!--右栏-->
+            <div class="c-body col">
+                <div class="sjs-bar">
+                    <ul class="nav nav-tabs">
+                        <li class="nav-item">
+                            <a class="nav-link active" data-toggle="tab" href="#qingdan" role="tab">项目节</a>
+                        </li>
+                    </ul>
+                </div>
+                <div class="sjs-height-3">
+                    <table class="table table-bordered">
+                        <tr><th>编号</th><th>名称</th></tr>
+                        <tr><td>1-4-1-5-1</td><td>桥台桩基础</td></tr>
+                        <tr><td>1-4-1-5-2</td><td>桥台桩基础</td></tr>
+                    </table>
+                </div>
+                <div class="sjs-bottom">
+                    <ul class="nav nav-tabs">
+                        <li class="nav-item">
+                            <a class="nav-link active">部位明细</a>
+                        </li>
+                    </ul>
+                    <div class="tab-content">
+                        <div class="tab-pane active">
+                            <div class="sjs-bottom-2">
+                                <table class="table table-sm table-bordered">
+                                    <tr><th>部位</th><th>台帐数量</th><th>本期计量数量</th></tr>
+                                    <tr><td>0#桥台1#桩</td><td>126</td><td>40</td></tr>
+                                    <tr><td>0#桥台2#桩</td><td>126</td><td>40</td></tr>
+                                </table>
+                            </div>
+                        </div>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </div>
+</div>

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

@@ -0,0 +1,58 @@
+<!--添加草图-->
+<div class="modal fade" id="edit-img" data-backdrop="static">
+    <div class="modal-dialog modal-lgx" role="document">
+        <div class="modal-content">
+            <div class="modal-header">
+                <h5 class="modal-title">添加草图</h5>
+            </div>
+            <div class="modal-body">
+                <p><a href="" class="btn btn-outline-primary btn-sm">上传图片</a></p>
+                <div class="img-view">
+                    <div class="img-item">
+                        <div class="img-bar">
+                            <a href="" class="text-danger" title="删除"><i class="fa fa-remove"></i></a>
+                        </div>
+                        <img src="img/sketch/1.png" id="draggable">
+                    </div>
+                    <div class="img-item" style="left:300px;">
+                        <div class="img-bar">
+                            <a href="" class="text-danger" title="删除"><i class="fa fa-remove"></i></a>
+                        </div>
+                        <img src="img/sketch/2.png" id="draggable">
+                    </div>
+                </div>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-secondary" data-dismiss="modal">关闭</button>
+                <button type="button" class="btn btn-primary" >确认</button>
+            </div>
+        </div>
+    </div>
+</div>
+<!--计量汇总-->
+<div class="modal fade" id="choose" data-backdrop="static">
+    <div class="modal-dialog modal-lg" role="document">
+        <div class="modal-content">
+            <div class="modal-header">
+                <h5 class="modal-title">计量汇总</h5>
+            </div>
+            <div class="modal-body">
+                <div class="modal-height-500">
+                    <table class="table table-sm table-bordered">
+                        <tr><th width="70">计量汇总</th><th>项目节编号</th><th>清单编号</th><th>名称</th><th>单位</th></tr>
+                        <tr><td><input type="checkbox"></td><td>1</td><td></td><td>第一部分 建筑安装工程费</td><td></td></tr>
+                        <tr><td><input type="checkbox"></td><td>1-2</td><td></td><td>路基工程</td><td></td></tr>
+                        <tr><td><input type="checkbox"></td><td>1-2-2</td><td></td><td>挖方</td><td></td></tr>
+                        <tr><td><input type="checkbox"></td><td>1-2-2-1</td><td></td><td>挖土方</td><td></td></tr>
+                        <tr><td><input type="checkbox"></td><td>1-2-2-1-1</td><td></td><td>挖路基土方</td><td></td></tr>
+                        <tr><td><input type="checkbox"></td><td></td><td>203-1-a</td><td>挖土方</td><td></td></tr>
+                    </table>
+                </div>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-secondary" data-dismiss="modal">关闭</button>
+                <button type="button" class="btn btn-primary" >确认</button>
+            </div>
+        </div>
+    </div>
+</div>

+ 280 - 125
app/view/stage/index.ejs

@@ -1,136 +1,291 @@
+<% include ../tender/tender_sub_menu.ejs %>
 <div class="panel-content">
-    <div class="panel-title fluid">
-        <div class="title-main  d-flex justify-content-between">
+    <div class="panel-title">
+        <div class="title-main d-flex justify-content-between">
             <div>
+                <div class="input-group input-group-sm ml-2 mt-2">
+                    <div class="input-group-prepend">
+                        <span class="input-group-text" id="basic-addon1">表达式</span>
+                    </div>
+                    <input type="text" class="form-control m-0">
+                </div>
             </div>
             <div>
-                <a href="#add-qi" data-toggle="modal" data-target="#add-qi" class="btn btn-primary btn-sm pull-right">添加期</a>
+                <!--上报-->
+                <a href="#sub-sp" data-toggle="modal" data-target="#sub-sp" class="btn btn-primary btn-sm pull-right">上报审批</a>
+                <a href="#sub-sp2" data-toggle="modal" data-target="#sub-sp2" class="btn btn-primary btn-sm pull-right">重新上报</a>
+                <!--状态显示-->
+                <a href="#sp-list" data-toggle="modal" data-target="#sp-list" class="btn btn-outline-success btn-sm pull-right">审批完成</a>
+                <a href="#sp-list" data-toggle="modal" data-target="#sp-list" class="btn btn-outline-secondary btn-sm pull-right">审批中</a>
+                <a href="#sp-list" data-toggle="modal" data-target="#sp-list" class="btn btn-outline-warning btn-sm pull-right text-muted">审批退回</a>
+                <!--审批操作-->
+                <a href="#sp-done" data-toggle="modal" data-target="#sp-done" class="btn btn-success btn-sm pull-right">审批通过</a>
+                <a href="#sp-back" data-toggle="modal" data-target="#sp-back" class="btn btn-warning btn-sm pull-right">审批退回</a>
+                <!--sjs 里调用变更令-->
+                <a href="#use-bg" data-toggle="modal" data-target="#use-bg" class="bt btn-sm pull-right">调用变更令</a>
             </div>
         </div>
     </div>
-    <div class="content-wrap">
-        <div class="c-body">
-            <!--期汇总图表-->
-            <div id="chartContainer4" style="height: 300px; width: 100%;" class="mb-4">
+    <div class="content-wrap pr-46">
+        <div class="c-header p-0">
+        </div>
+        <div class="row w-100 sub-content">
+            <div class="c-body col-8">
+                <!--上部分-->
+                <div class="sjs-height-1" id="stage-ledger">
+                </div>
+                <!--下部分-->
+                <div class="bcontent-wrap">
+                    <div class="bc-bar mb-1">
+                        <ul class="nav nav-tabs">
+                            <li class="nav-item">
+                                <a class="nav-link active" href="#">部位明细</a>
+                            </li>
+                            <!--工程量清单模式操作-->
+                            <% if (ctx.tender.data.measure_type === measureType.gcl) { %>
+                            <li class="nav-item">
+                                <div class="mt-1 ml-3">
+                                    <a href="" class="btn btn-sm" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="插入"><i class="fa fa-sign-in" aria-hidden="true"></i></a>
+                                    <a href="" class="btn btn-sm" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="复制"><i class="fa fa-files-o" aria-hidden="true"></i></a>
+                                    <a href="" class="btn btn-sm" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="剪切"><i class="fa fa-scissors" aria-hidden="true"></i></a>
+                                    <a href="" class="btn btn-sm" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="粘贴"><i class="fa fa-clipboard" aria-hidden="true"></i></a>
+                                    <a href="" class="btn btn-sm" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="删除"><i class="fa fa-remove" aria-hidden="true"></i></a>
+                                    <a href="" class="btn btn-sm" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="下移"><i class="fa fa-arrow-down" aria-hidden="true"></i></a>
+                                    <a href="" class="btn btn-sm" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="上移"><i class="fa fa-arrow-up" aria-hidden="true"></i></a>
+                                </div>
+                            </li>
+                            <% } %>
+                        </ul>
+                    </div>
+                    <div class="sp-wrap" id="stage-pos">
+                    </div>
+                </div>
+            </div>
+            <div class="c-body col">
+                <div class="tab-content">
+                    <!--中间审批记录-->
+                    <div id="spjilu" class="tab-pane active">
+                        <div class="side-bar-1">
+                            <table class="table table-bordered m-0">
+                                <tr><th>清单编号</th><th>清单名称</th><th>上报数量</th></tr>
+                                <tr><td>201-1</td><td>清理现场</td><td>10</td></tr>
+                            </table>
+                        </div>
+                        <div class="sjs-sh-1">
+                            <table class="table table-bordered">
+                                <tr><th>审批人</th><th>审批数量</th><th>审批意见</th></tr>
+                                <tr><td>张三</td><td>15</td><td>审批意见文本</td></tr>
+                                <tr><td>王五</td><td>20</td><td>审批意见文本</td></tr>
+                            </table>
+                        </div>
+                    </div>
+                    <!--附件-->
+                    <div id="fujian" class="tab-pane">
+                        <div class="sjs-bar">
+                            <ul class="nav nav-tabs">
+                                <li class="nav-item">
+                                    <a class="nav-link active" data-toggle="tab" href="#dqjiedian" role="tab">当前节点</a>
+                                </li>
+                                <li class="nav-item">
+                                    <a class="nav-link" data-toggle="tab" href="#syfujian" role="tab">所有附件</a>
+                                </li>
+                                <li class="nav-item ml-auto">
+                                    <!--所有附件 翻页-->
+                                    <a href="#"><i class="fa fa-chevron-left"></i></a> 1/10 <a href="#"><i class="fa fa-chevron-right"></i></a>
+                                    <a href="#upload" data-toggle="modal" data-target="#upload"  class="btn btn-sm btn-outline-primary ml-3">上传</a>
+                                </li>
+                            </ul>
+                        </div>
+                        <div class="tab-content">
+                            <div class="tab-pane active" id="dqjiedian">
+                                <div class="sjs-height-3">
+                                    <table class="table table-sm table-bordered table-hover">
+                                        <tbody><tr><th>文件名</th><th width="80">上传</th></tr>
+                                        <tr><td><a href="">asdasd.jpg</a></td><td>张三</td></tr>
+                                        <tr><td><a href="">sd22d.jpg</a></td><td>张三</td></tr>
+                                        </tbody></table>
+                                </div>
+                                <div class="sjs-bottom">
+                                    <div class="tab-content">
+                                        <div class="tab-pane active">
+                                            <div class="sjs-bottom-2">
+                                                <div class="d-flex justify-content-end mb-1">
+                                                    <!--默认 有删除权限-->
+                                                    <a href="#" class="btn btn-sm text-danger">删除</a>
+                                                    <!--默认 有编辑权限-->
+                                                    <a href="#" class="btn btn-sm btn-outline-primary">编辑</a>
+                                                    <!--编辑模式-->
+                                                    <a href="#" class="btn btn-sm btn-outline-success mr-1">保存</a>
+                                                    <a href="#" class="btn btn-sm btn-outline-secondary">取消</a>
+                                                </div>
+                                                <!--显示信息-->
+                                                <table class="table table-sm table-bordered">
+                                                    <tbody>
+                                                    <tr><th>文件名</th><td colspan="3">asdasd.jpg</td></tr>
+                                                    <tr><th>所在节点</th><td colspan="3">1 第一部分 建筑安装工程非</td></tr>
+                                                    <tr><td colspan="4"><a href="">下载附件</a></td></tr>
+                                                    <tr><th>上传者</th><td>张三</td><th>上传时间</th><td>2018-10-20</td></tr>
+                                                    <tr><th>备注</th><td colspan="3"></td></tr>
+                                                    </tbody>
+                                                </table>
+                                                <!--编辑模式-->
+                                                <div class="form-group">
+                                                    <div class="input-group input-group-sm">
+                                                        <div class="input-group-prepend">
+                                                            <span class="input-group-text">文件名</span>
+                                                        </div>
+                                                        <input type="text" class="form-control" value="asdasd">
+                                                        <div class="input-group-append">
+                                                            <span class="input-group-text">.jpg</span>
+                                                        </div>
+                                                    </div>
+                                                </div>
+                                                <div class="form-group">
+                                                    <div class="input-group input-group-sm">
+                                                        <div class="input-group-prepend">
+                                                            <span class="input-group-text">所在节点</span>
+                                                        </div>
+                                                        <input type="text" class="form-control" value="1 第一部分 建筑安装工程非" readonly="">
+                                                    </div>
+                                                </div>
+                                                <div class="form-group">
+                                                    <div class="input-group input-group-sm">
+                                                        <div class="input-group-prepend">
+                                                            <span class="input-group-text">上传时间</span>
+                                                        </div>
+                                                        <input type="text" class="form-control" value="2018-10-20" readonly="">
+                                                    </div>
+                                                </div>
+                                                <div class="form-group">
+                                                    <div class="input-group input-group-sm">
+                                                        <div class="input-group-prepend">
+                                                            <span class="input-group-text">备注</span>
+                                                        </div>
+                                                        <input type="text" class="form-control" value="">
+                                                    </div>
+                                                </div>
+                                                <div class="form-group">
+                                                    <label>替换文件</label>
+                                                    <div class="custom-file">
+                                                        <input type="file" class="custom-file-input" id="customFile">
+                                                        <label class="custom-file-label" for="customFile">Choose file</label>
+                                                    </div>
+                                                </div>
+                                            </div>
+                                        </div>
+                                    </div>
+                                </div>
+                            </div>
+                            <div class="tab-pane" id="syfujian">
+                                <div class="sjs-height-3">
+                                    <table class="table table-sm table-bordered table-hover">
+                                        <tbody><tr><th>文件名</th><th width="80">上传</th></tr>
+                                        <tr><td><a href="">asdasd.jpg</a></td><td>张三</td></tr>
+                                        <tr><td><a href="">sd22d.jpg</a></td><td>张三</td></tr>
+                                        </tbody></table>
+                                </div>
+                                <div class="sjs-bottom">
+                                    <div class="tab-content">
+                                        <div class="tab-pane active">
+                                            <div class="sjs-bottom-2">
+                                                <div class="d-flex justify-content-end mb-1">
+                                                    <!--默认 有删除权限-->
+                                                    <a href="#" class="btn btn-sm text-danger">删除</a>
+                                                    <!--默认 有编辑权限-->
+                                                    <a href="#" class="btn btn-sm btn-outline-primary">编辑</a>
+                                                    <!--编辑模式-->
+                                                    <a href="#" class="btn btn-sm btn-outline-success mr-1">保存</a>
+                                                    <a href="#" class="btn btn-sm btn-outline-secondary">取消</a>
+                                                </div>
+                                                <!--显示信息-->
+                                                <table class="table table-sm table-bordered">
+                                                    <tbody>
+                                                    <tr><th>文件名</th><td colspan="3">asdasd.jpg</td></tr>
+                                                    <tr><th>所在节点</th><td colspan="3">1 第一部分 建筑安装工程非</td></tr>
+                                                    <tr><td colspan="4"><a href="">下载附件</a></td></tr>
+                                                    <tr><th>上传者</th><td>张三</td><th>上传时间</th><td>2018-10-20</td></tr>
+                                                    <tr><th>备注</th><td colspan="3"></td></tr>
+                                                    </tbody>
+                                                </table>
+                                                <!--编辑模式-->
+                                                <div class="form-group">
+                                                    <div class="input-group input-group-sm">
+                                                        <div class="input-group-prepend">
+                                                            <span class="input-group-text">文件名</span>
+                                                        </div>
+                                                        <input type="text" class="form-control" value="asdasd">
+                                                        <div class="input-group-append">
+                                                            <span class="input-group-text">.jpg</span>
+                                                        </div>
+                                                    </div>
+                                                </div>
+                                                <div class="form-group">
+                                                    <div class="input-group input-group-sm">
+                                                        <div class="input-group-prepend">
+                                                            <span class="input-group-text">所在节点</span>
+                                                        </div>
+                                                        <input type="text" class="form-control" value="1 第一部分 建筑安装工程非" readonly="">
+                                                    </div>
+                                                </div>
+                                                <div class="form-group">
+                                                    <div class="input-group input-group-sm">
+                                                        <div class="input-group-prepend">
+                                                            <span class="input-group-text">上传时间</span>
+                                                        </div>
+                                                        <input type="text" class="form-control" value="2018-10-20" readonly="">
+                                                    </div>
+                                                </div>
+                                                <div class="form-group">
+                                                    <div class="input-group input-group-sm">
+                                                        <div class="input-group-prepend">
+                                                            <span class="input-group-text">备注</span>
+                                                        </div>
+                                                        <input type="text" class="form-control" value="">
+                                                    </div>
+                                                </div>
+                                                <div class="form-group">
+                                                    <label>替换文件</label>
+                                                    <div class="custom-file">
+                                                        <input type="file" class="custom-file-input" id="customFile">
+                                                        <label class="custom-file-label" for="customFile">Choose file</label>
+                                                    </div>
+                                                </div>
+                                            </div>
+                                        </div>
+                                    </div>
+                                </div>
+                            </div>
+                        </div>
+                    </div>
+                </div>
             </div>
-            <table class="table table-bordered">
-                <thead>
-                <tr>
-                    <th>期数</th>
-                    <th class="text-center">计量月份</th>
-                    <th class="text-center">本期合同计量</th>
-                    <th class="text-center">本期数量变更计量</th>
-                    <th class="text-center">截止上期累计完成</th>
-                    <th class="text-center">本期完成计量</th>
-                    <th class="text-center">累计完成计量</th>
-                    <th class="text-center">状态</th>
-                </tr>
-                </thead>
-                <tbody>
-                    <% for (const s of stages) { %>
-                    <tr>
-                        <td><a href="/stage/<%- s.order %>/measure">第 <%- s.order %> 期</a></td>
-                        <td class="text-center"><%- moment(s.s_time).format('YYYYMM') %></td>
-                        <td class="text-right"></td>
-                        <td class="text-right"></td>
-                        <td class="text-right"></td>
-                        <td class="text-right"></td>
-                        <td class="text-right"></td>
-                        <td class="text-centre"><%- auditConst.statusString[s.status] %></td>
-                    </tr>
-                    <% } %>
-                </tbody>
-            </table>
+        </div>
+        <div class="side-menu">
+            <!--右侧菜单-->
+            <ul class="nav flex-column right-nav">
+                <li class="nav-item">
+                    <a class="nav-link active" data-toggle="tab" href="#spjilu" role="tab">审批记录</a>
+                </li>
+                <li class="nav-item">
+                    <a class="nav-link" data-toggle="tab" href="#fujian" role="tab">附件</a>
+                </li>
+            </ul>
         </div>
     </div>
 </div>
-<script src=/public/js/echarts/echarts.min.js></script>
-<script type="text/javascript">
-    //4 标段期数计量进度//
-    var myChart = echarts.init(document.getElementById('chartContainer4'));
-    var option = {
-        color: ['#e9af68','#57b7b6','#e4575a','#959eac','#6699FF',
-            '#d38b70','#8fb7cf','#cd5c5c','#ffa500','#40e0d0',
-            '#1e90ff','#ff6347','#7b68ee','#00fa9a','#ffd700',
-            '#5c616b','#ff6666','#3cb371','#b8860b','#30e0e0'],
-        tooltip : {
-            trigger: 'axis'
-        },
-        calculable : true,
-        legend: {
-            data:['本期合同计量','本期数量变更计量','截至上期累计完成','本期完成计量','完成度']
-        },
-        dataZoom: [
-            {show: true,start: 0, end: 100}
-        ],
-        xAxis : [
-            {
-                type : 'category',
-                splitLine : {show : true},
-                data : ['第一期','第二期','第三期','第四期','第五期','第六期','第七期']
-            }
-        ],
-        yAxis : [
-            {
-                type : 'value',
-                name : '金额',
-                position:'left',
-                axisLabel : {
-                    formatter: '{value} 元'
-                },
-                splitArea : {show : true}
-            },
-            {
-                type : 'value',
-                name:'完成度',
-                axisLabel : {
-                    formatter: '{value} %'
-                },
-                position: 'right',
-                splitArea : {show : true}
-            }
-        ],
-        series : [
-            {
-                name:'本期合同计量',
-                type:'bar',
-                tooltip : {trigger: 'item',formatter: "{b}  <br/>{a}:{c}元"},
-                stack: '合同',
-                data:[320, 332, 301, 334, 390, 330, 320]
-            },
-            {
-                name:'本期数量变更计量',
-                type:'bar',
-                tooltip : {trigger: 'item',formatter: "{b}  <br/>{a}:{c}元"},
-                stack: '合同',
-                data:[320, -20, 301, 334, 390, 330, 320]
-            },
-            {
-                name:'截至上期累计完成',
-                type:'bar',
-                tooltip : {trigger: 'item',formatter: "{b}  <br/>{a}:{c}元"},
-                stack: '完成',
-                data:[120, 132, 101, 134, 90, 230, 210]
-            },
-            {
-                name:'本期完成计量',
-                type:'bar',
-                tooltip : {trigger: 'item',formatter: "{b}  <br/>{a}:{c}元"},
-                stack: '完成',
-                data:[220, 182, 191, 234, 290, 330, 310]
-            },
-            {
-                name:'完成度',
-                type:'line',
-                tooltip : {trigger: 'axis',formatter: "{b}占总标段<br/>{a}:{c} %"},
-                yAxisIndex: 1,
-                data:[10, 15, 20, 13, 11, 9, 5]
-            },
-        ]
-    };
-
-    // 为echarts对象加载数据
-    myChart.setOption(option);
-    //4 标段期数计量进度//
-
-</script>
+<script src="/public/js/spreadjs/sheets/gc.spread.sheets.all.10.0.1.min.js"></script>
+<script>
+    GC.Spread.Sheets.LicenseKey = "559432293813965#A0y3iTOzEDOzkjMyMDN9UTNiojIklkI1pjIEJCLi4TPB9mM5AFNTd4cvZ7SaJUVy3CWKtWYXx4VVhjMpp7dYNGdx2ia9sEVlZGOTh7NRlTUwkWR9wEV4gmbjBDZ4ElR8N7cGdHVvEWVBtCOwIGW0ZmeYVWVr3mI0IyUiwCMzETN8kzNzYTM0IicfJye&Qf35VfiEzRwEkI0IyQiwiIwEjL6ByUKBCZhVmcwNlI0IiTis7W0ICZyBlIsIyNyMzM5ADI5ADNwcTMwIjI0ICdyNkIsIibj9SbvNmL4N7bjRnch56ciojIz5GRiwiI8+Y9sWY9QmZ0Jyp96uL9v6L0wap9biY9qiq95q197Wr9g+89iojIh94Wiqi";
+</script>
+<script src="/public/js/spreadjs_rela/spreadjs_zh<%= min %>.js"></script>
+<script src="/public/js/path_tree<%= min %>.js"></script>
+<script>
+    const ledgerSpreadSetting = JSON.parse('<%- JSON.stringify(ledgerSpread) %>');
+    const posSpreadSetting = JSON.parse('<%- JSON.stringify(posSpread) %>');
+    const tender = JSON.parse('<%- JSON.stringify(tender) %>');
+    const measureType = JSON.parse('<%- JSON.stringify(measureType) %>');
+    const stage = JSON.parse('<%- JSON.stringify(ctx.stage) %>');
+    const ledgerData = JSON.parse('<%- JSON.stringify(ledgerData) %>');
+    const curStageData = JSON.parse('<%- JSON.stringify(curStageData) %>');
+</script>
+<script type="text/javascript" src="/public/js/stage.js"></script>

+ 459 - 31
app/view/stage/modal.ejs

@@ -1,47 +1,475 @@
-<!--弹出添加标段-->
-<div class="modal fade" id="add-qi" data-backdrop="static">
+<!--弹出调用变更令-->
+<div class="modal fade" id="use-bg" data-backdrop="static">
+    <div class="modal-dialog modal-lgx" role="document">
+        <div class="modal-content">
+            <div class="modal-header">
+                <h5 class="modal-title">使用变更令</h5>
+            </div>
+            <div class="modal-body">
+                <div class="row">
+                    <div class="col-7">
+                        <ul class="nav nav-tabs">
+                            <li class="nav-item">
+                                <div class="custom-control custom-checkbox my-2">
+                                    <input class="custom-control-input" id="customCheckDisabled" checked="" type="checkbox">
+                                    <label class="custom-control-label" for="customCheckDisabled">显示可变更数量为0项 </label>
+                                </div>
+                            </li>
+                        </ul>
+                        <div class="modal-height-500">
+                            <table class="table table-bordered">
+                                <tr><th>已用</th><th>变更令号</th><th>名称</th><th>总数量</th><th>可变更数量</th><th>本期计量</th></tr>
+                                <tr><td><i class="fa fa-check text-success"></i></td><td>CW01-BG-1</td><td>增加清淤换填数量</td><td>100</td><td>50</td><td>20</td></tr>
+                                <tr><td><i class="fa fa-check text-success"></i></td><td>CW01-BG-2</td><td>增加挖淤及回填数量</td><td>150</td><td>120</td><td>100</td></tr>
+                                <tr><td></td><td>CW01-BG-3</td><td>增加清淤换填数量</td><td>150</td><td>100</td><td></td></tr>
+                            </table>
+                        </div>
+                    </div>
+                    <div class="col-5">
+                        <ul class="nav nav-tabs">
+                            <li class="nav-item">
+                                <a class="nav-link active" data-toggle="tab" href="#bgsm" role="tab">工程变更理由及内容</a>
+                            </li>
+                            <li class="nav-item">
+                                <a class="nav-link" data-toggle="tab" href="#detail" role="tab">更多变更信息</a>
+                            </li>
+                            <li class="nav-item">
+                                <a class="nav-link" data-toggle="tab" href="#fujian" role="tab">附件</a>
+                            </li>
+                        </ul>
+                        <div class="modal-height-500" style="overflow: auto">
+                            <div class="tab-content">
+                                <div id="bgsm" class="tab-pane active">
+                                    <div class="form-group">
+                                        <label></label>
+                                        <textarea class="form-control form-control-sm" rows="6" readonly>由于K0+532涵洞基底土质天然含水率为25%、收费站出口右侧加宽段K0+120-K0+190段基底土质天然含水率为28.8%。含水率较大形成过湿土,地基松软,无法满足承载力要求。经处项目办、设计、监理、施工单位四方勘察,采用抛石挤淤、砂砾换填以达到地基承载力。
+                                  </textarea>
+                                    </div>
+                                </div>
+                                <div id="detail" class="tab-pane">
+                                    <div class="form-group mt-3">
+                                        <label>批复编号</label>
+                                        <input class="form-control form-control-sm" value="LZTJ-1标项目部发变更(2015)001号" type="text" readonly>
+                                    </div>
+                                    <div class="form-group">
+                                        <label>工程名称</label>
+                                        <input class="form-control form-control-sm" value="收费站出口右侧加宽段及K0+532基底换填" type="text" readonly>
+                                    </div>
+                                    <div class="form-group">
+                                        <label>桩号</label>
+                                        <input class="form-control form-control-sm" value="K0+532" type="text" readonly>
+                                    </div>
+                                    <div class="form-group">
+                                        <label>原设计图名称</label>
+                                        <input class="form-control form-control-sm" placeholder="" type="text" readonly>
+                                    </div>
+                                    <div class="form-group">
+                                        <label>原图号</label>
+                                        <input class="form-control form-control-sm" placeholder="" type="text" readonly>
+                                    </div>
+                                    <div class="form-group">
+                                        <label>变更设计图名称</label>
+                                        <input class="form-control form-control-sm" placeholder="" type="text" readonly>
+                                    </div>
+                                    <div class="form-group">
+                                        <label>变更图号</label>
+                                        <input class="form-control form-control-sm" placeholder="" type="text" readonly>
+                                    </div>
+                                    <div class="form-group">
+                                        <label>工程变更理由及内容</label>
+                                        <textarea class="form-control form-control-sm" rows="6" readonly>由于K0+532涵洞基底土质天然含水率为25%、收费站出口右侧加宽段K0+120-K0+190段基底土质天然含水率为28.8%。含水率较大形成过湿土,地基松软,无法满足承载力要求。经处项目办、设计、监理、施工单位四方勘察,采用抛石挤淤、砂砾换填以达到地基承载力。
+                                    </textarea>
+                                    </div>
+                                    <div class="form-group">
+                                        <label>工程变更合同依据</label>
+                                        <textarea class="form-control form-control-sm" rows="6" readonly></textarea>
+                                    </div>
+                                    <div class="form-group">
+                                        <label>变更类型</label>
+                                        <div class="checkbox">
+                                            <label class="checkbox-inline">
+                                                <input id="inlineCheckbox1" value="option1" checked="" type="checkbox" disabled>A.位置
+                                            </label>
+                                            <!-- <label class="checkbox-inline">
+                                              <input id="inlineCheckbox2" value="option2" type="checkbox">B.数量
+                                            </label>
+                                            <label class="checkbox-inline">
+                                              <input id="inlineCheckbox3" value="option3" type="checkbox">C.结构
+                                            </label>
+                                            <label class="checkbox-inline">
+                                              <input id="inlineCheckbox3" value="option3" checked="" type="checkbox">D.新增
+                                            </label>
+                                            <label class="checkbox-inline">
+                                              <input id="inlineCheckbox3" value="option3" type="checkbox">E.取消
+                                            </label>
+                                            <label class="checkbox-inline">
+                                              <input id="inlineCheckbox3" value="option3" type="checkbox">F.纠错
+                                            </label> -->
+                                        </div>
+                                    </div>
+                                    <div class="form-group">
+                                        <label>变更类别 </label>
+                                        <select class="form-control form-control-sm" disabled><option>A类变更</option><option>B类变更</option><option>C类变更</option><option>D类变更</option><option>E类变更</option></select>
+                                    </div>
+                                    <div class="form-group">
+                                        <label>变更性质 </label>
+                                        <select class="form-control form-control-sm" disabled><option>一般设计变更</option><option>较大设计变更</option><option>重大设计变更</option></select>
+                                    </div>
+                                    <div class="form-group">
+                                        <label>变更提出单位</label>
+                                        <!-- <a href="#editcompany" style="float:right;" class="" data-toggle="modal">编辑</a> -->
+                                        <select class="form-control form-control-sm" disabled><option>AA公司</option><option>BB公司</option></select>
+                                    </div>
+                                    <div class="form-group">
+                                        <label>费用承担方</label>
+                                        <div class="radio">
+                                            <label class="radio-inline">
+                                                <input id="inlineCheckbox1" value="option1" checked="" type="radio" disabled> 业主
+                                            </label>
+                                            <!-- <label class="radio-inline">
+                                              <input id="inlineCheckbox2" value="option2" type="radio"> 承包人
+                                            </label> -->
+                                        </div>
+                                    </div>
+                                    <div class="form-group">
+                                        <label>备注</label>
+                                        <textarea class="form-control form-control-sm" rows="3" readonly></textarea>
+                                    </div>
+                                </div>
+                                <div id="fujian" class="tab-pane">
+                                    <table class="table table-bordered">
+                                        <tr><th>名称</th><th>上传者</th></tr>
+                                        <tr><td><a href="">合同段工程变更现场办公会议纪要的通知.rar</a></td><th>张三</th></tr>
+                                    </table>
+                                </div>
+                            </div>
+                        </div>
+                    </div>
+                </div>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-secondary" data-dismiss="modal">关闭</button>
+                <button type="button" class="btn btn-primary">添加</button>
+            </div>
+        </div>
+    </div>
+</div>
+<!--上报审批-->
+<div class="modal fade" id="sub-sp" data-backdrop="static">
     <div class="modal-dialog" role="document">
         <div class="modal-content">
             <div class="modal-header">
-                <h5 class="modal-title">添加新一期</h5>
+                <h5 class="modal-title">上报审批</h5>
             </div>
             <div class="modal-body">
                 <div class="form-group">
-                    <label>期</label>
-                    <input class="form-control" value="第 4 期" type="text" readonly="" name="stageName">
+                    <label>搜索审批人</label>
+                    <div class="input-group">
+                        <input class="form-control" placeholder="请输入姓名进行检索" type="text">
+                        <div class="input-group-append">
+                            <button class="btn btn-outline-secondary" type="button"><i class="fa fa-search"></i></button>
+                        </div>
+                    </div>
                 </div>
-                <div class="form-group">
-                    <label>计量年月</label>
-                    <input class="datepicker-here form-control" placeholder="点击选择年月" data-view="months" data-min-view="months" data-date-format="yyyy-MM" data-language="zh" type="text" name="stageTime">
+                <div class="card border-primary">
+                    <div class="card-body">
+                        <h5 class="card-title">
+                            <a href="#" class="btn btn-primary btn-sm pull-right">添加</a>张三
+                        </h5>
+                        <h6 class="card-subtitle mb-2 text-muted">监理</h6>
+                        <p class="card-text">XXXXX公司</p>
+                    </div>
                 </div>
-                <div class="form-group">
-                    <label>开始-截止日期</label>
-                    <input class="datepicker-here form-control" placeholder="点击选择时间" data-range="true" data-multiple-dates-separator=" ~ " data-language="zh" type="text" name="rangeTime">
+                <div class="card mt-3">
+                    <div class="card-header">
+                        审批流程
+                    </div>
+                    <ul class="list-group list-group-flush">
+                        <li class="list-group-item"><i class="fa fa fa-play-circle fa-rotate-90"></i> 布尔  <small class="text-muted">施工</small></li>
+                        <li class="list-group-item"><a href="" class="text-danger pull-right">移除</a><i class="fa fa-chevron-circle-down"></i> 张三  <small class="text-muted">监理</small></li>
+                        <li class="list-group-item"><a href="" class="text-danger pull-right">移除</a><i class="fa fa-chevron-circle-down"></i> 王五 <small class="text-muted">监理</small></li>
+                        <li class="list-group-item"><a href="" class="text-danger pull-right">移除</a><i class="fa fa fa-stop-circle"></i> 李四 <small class="text-muted">监理</small></li>
+                    </ul>
+                </div>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-secondary" data-dismiss="modal">关闭</button>
+                <button type="button" class="btn btn-primary">确认上报</button>
+            </div>
+        </div>
+    </div>
+</div>
+<!--重新上报-->
+<div class="modal fade" id="sub-sp2" 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="card mt-3">
+                    <ul class="list-group list-group-flush">
+                        <li class="list-group-item">
+                            <span class="text-success pull-right">上报</span>
+                            <h5 class="card-title"><i class="fa fa-play-circle fa-rotate-90 text-success"></i> 布尔 <small class="text-muted">施工</small></h5>
+                            <p class="card-text">2017-11-25</p>
+                        </li>
+                        <li class="list-group-item">
+                            <span class="text-success pull-right">审批通过</span>
+                            <h5 class="card-title"><i class="fa fa-chevron-circle-down text-success"></i> 张三 <small class="text-muted">监理</small></h5>
+                            <p class="card-text">审批意见。2017-11-25</p>
+                        </li>
+                        <li class="list-group-item">
+                            <span class="text-success pull-right">审批通过</span>
+                            <h5 class="card-title"><i class="fa fa-chevron-circle-down text-success"></i> 王五 <small class="text-muted">监理</small></h5>
+                            <p class="card-text">审批通过。2017-11-26</p>
+                        </li>
+                        <li class="list-group-item">
+                            <span class="text-warning pull-right">审批退回</span>
+                            <h5 class="card-title"><i class="fa fa-stop-circle text-warning"></i> 李四 <small class="text-muted">监理</small></h5>
+                            <p class="card-text">审批退回,审批意见文本。2017-11-27</p>
+                        </li>
+                    </ul>
+                </div>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-secondary" data-dismiss="modal">关闭</button>
+                <button type="button" class="btn btn-primary">确认上报</button>
+            </div>
+        </div>
+    </div>
+</div>
+<!--审批流程/结果-->
+<div class="modal fade" id="sp-list" data-backdrop="static">
+    <div class="modal-dialog modal-lg" role="document">
+        <div class="modal-content">
+            <div class="modal-header">
+                <h5 class="modal-title">审批流程</h5>
+            </div>
+            <div class="modal-body">
+                <div class="row">
+                    <div class="col-4">
+                        <div class="card mt-3">
+                            <ul class="list-group list-group-flush">
+                                <li class="list-group-item"><i class="fa fa fa-play-circle fa-rotate-90"></i> 布尔  <small class="text-muted">施工</small></li>
+                                <li class="list-group-item"><i class="fa fa-chevron-circle-down"></i> 张三  <small class="text-muted">监理</small></li>
+                                <li class="list-group-item"><i class="fa fa-chevron-circle-down"></i> 王五 <small class="text-muted">监理</small></li>
+                                <li class="list-group-item"><i class="fa fa fa-stop-circle"></i> 李四 <small class="text-muted">监理</small></li>
+                            </ul>
+                        </div>
+                    </div>
+                    <div class="col-8 modal-height-500" style="overflow: auto">
+                        <div class="card mt-3">
+                            <ul class="list-group list-group-flush">
+                                <li class="list-group-item">
+                                    <span class="text-success pull-right">上报</span>
+                                    <h5 class="card-title"><i class="fa fa-play-circle fa-rotate-90 text-success"></i> 布尔 <small class="text-muted">施工</small></h5>
+                                    <p class="card-text">2017-11-25</p>
+                                </li>
+                                <li class="list-group-item">
+                                    <span class="text-success pull-right">审批通过</span>
+                                    <h5 class="card-title"><i class="fa fa-chevron-circle-down text-success"></i> 张三 <small class="text-muted">监理</small></h5>
+                                    <p class="card-text">审批意见。2017-11-25</p>
+                                </li>
+                                <li class="list-group-item">
+                                    <span class="text-success pull-right">审批通过</span>
+                                    <h5 class="card-title"><i class="fa fa-chevron-circle-down text-success"></i> 王五 <small class="text-muted">监理</small></h5>
+                                    <p class="card-text">审批通过。2017-11-26</p>
+                                </li>
+                                <li class="list-group-item">
+                                    <span class="text-warning pull-right">审批退回 布尔</span>
+                                    <h5 class="card-title"><i class="fa fa-stop-circle text-warning"></i> 李四 <small class="text-muted">监理</small></h5>
+                                    <p class="card-text">审批退回,审批意见文本。2017-11-27</p>
+                                </li>
+                            </ul>
+                        </div>
+                        <!--退回原报重新上报-->
+                        <div class="card mt-3">
+                            <ul class="list-group list-group-flush">
+                                <li class="list-group-item">
+                                    <span class="text-success pull-right">重新上报</span>
+                                    <h5 class="card-title"><i class="fa fa-play-circle fa-rotate-90 text-success"></i> 布尔 <small class="text-muted">施工</small></h5>
+                                    <p class="card-text">2017-12-01</p>
+                                </li>
+                                <li class="list-group-item">
+                                    <span class="text-success pull-right">审批通过</span>
+                                    <h5 class="card-title"><i class="fa fa-chevron-circle-down text-success"></i> 张三 <small class="text-muted">监理</small></h5>
+                                    <p class="card-text">审批通过 2017-12-02</p>
+                                </li>
+                                <li class="list-group-item">
+                                    <span class="text-warning pull-right">审批退回 张三</span>
+                                    <h5 class="card-title"><i class="fa fa-chevron-circle-down text-warning"></i> 王五 <small class="text-muted">监理</small></h5>
+                                    <p class="card-text">审批退回 2017-12-02</p>
+                                </li>
+                                <!--王五退回上一审批人 张三,张三重新审批-->
+                                <li class="list-group-item">
+                                    <span class="pull-right">审批中</span>
+                                    <h5 class="card-title"><i class="fa fa-chevron-circle-down"></i> 张三 <small class="text-muted">监理</small></h5>
+                                    <p class="card-text"></p>
+                                </li>
+                                <li class="list-group-item">
+                                    <h5 class="card-title"><i class="fa fa-stop-circle"></i> 李四 <small class="text-muted">监理</small></h5>
+                                </li>
+                            </ul>
+                        </div>
+                    </div>
+                </div>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-secondary" data-dismiss="modal">关闭</button>
+            </div>
+        </div>
+    </div>
+</div>
+<!--审批通过-->
+<div class="modal fade" id="sp-done" 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="card mt-3">
+                    <ul class="list-group list-group-flush">
+                        <li class="list-group-item">
+                            <span class="text-success pull-right">上报</span>
+                            <h5 class="card-title"><i class="fa fa-play-circle fa-rotate-90 text-success"></i> 布尔 <small class="text-muted">施工</small></h5>
+                            <p class="card-text">2017-11-25</p>
+                        </li>
+                        <li class="list-group-item">
+                            <span class="text-success pull-right">审批通过</span>
+                            <h5 class="card-title"><i class="fa fa-chevron-circle-down text-success"></i> 张三 <small class="text-muted">监理</small></h5>
+                            <p class="card-text">审批意见。2018-01-01</p>
+                        </li>
+                        <li class="list-group-item">
+                            <span class="text-success pull-right">审批通过</span>
+                            <h5 class="card-title"><i class="fa fa-chevron-circle-down text-success"></i> 王五 <small class="text-muted">监理</small></h5>
+                            <p class="card-text">审批意见。2018-01-01</p>
+                        </li>
+                        <li class="list-group-item">
+                            <span class="text-warning pull-right">审批退回 王五</span>
+                            <h5 class="card-title"><i class="fa fa-stop-circle text-warning"></i> 李四 <small class="text-muted">监理</small></h5>
+                            <p class="card-text">审批意见。2018-01-01</p>
+                        </li>
+                        <li class="list-group-item">
+                            <span class="pull-right">审批中</span>
+                            <h5 class="card-title"><i class="fa fa-chevron-circle-down"></i> 王五 <small class="text-muted">监理</small></h5>
+                            <div class="form-group">
+                                <label>审批意见<b class="text-danger">*</b></label>
+                                <textarea class="form-control" ></textarea>
+                            </div>
+                        </li>
+                        <li class="list-group-item">
+                            <h5 class="card-title"><i class="fa fa-stop-circle"></i> 李四 <small class="text-muted">监理</small></h5>
+                        </li>
+                    </ul>
                 </div>
             </div>
             <div class="modal-footer">
                 <button type="button" class="btn btn-secondary" data-dismiss="modal">关闭</button>
-                <button type="button" class="btn btn-primary" id="addOk">确定添加</button>
+                <button type="button" class="btn btn-success" >确认通过</button>
             </div>
         </div>
     </div>
 </div>
-<script src="/public/js/moment/moment.min.js"></script>
-<script>
-    $(document).ready(() => {
-        $('input[type=month]').val(moment().format('YYYY-MM'));
-        $('#addOk').click(function () {
-            const rangeTime = $('input[name=rangeTime]');
-
-            const data = {
-                name: $('input[name=stageName]').val(),
-                time: $('input[name=stageTime]').val(),
-                begin_time: rangeTime.val().split(' ~ '),
-                be_time: $('input[name=rangeTime]').val(),
-            };
-            postData('/stage/add', data, function (rst) {
-                window.location.href = '/stage/' + rst.order + '/measure';
-            });
-        });
-    });
-</script>
+<!--审批退回-->
+<div class="modal fade" id="sp-back" 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="card mt-3">
+                    <ul class="list-group list-group-flush">
+                        <li class="list-group-item">
+                            <span class="text-success pull-right">上报</span>
+                            <h5 class="card-title"><i class="fa fa-play-circle fa-rotate-90 text-success"></i> 布尔 <small class="text-muted">施工</small></h5>
+                            <p class="card-text">2017-11-25</p>
+                        </li>
+                        <li class="list-group-item">
+                            <span class="text-success pull-right">审批通过</span>
+                            <h5 class="card-title"><i class="fa fa-chevron-circle-down text-success"></i> 张三 <small class="text-muted">监理</small></h5>
+                            <p class="card-text">审批意见。2018-01-01</p>
+                        </li>
+                        <li class="list-group-item">
+                            <span class="pull-right">审批中</span>
+                            <h5 class="card-title"><i class="fa fa-chevron-circle-down"></i> 王五 <small class="text-muted">监理</small></h5>
+                            <div class="form-group">
+                                <label>审批意见<b class="text-danger">*</b></label>
+                                <textarea class="form-control" ></textarea>
+                            </div>
+                            <div class="alert alert-warning"><div class="form-check form-check-inline">
+                                    <input class="form-check-input" type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1">
+                                    <label class="form-check-label" for="inlineRadio1">退回上报 布尔</label>
+                                </div>
+                                <div class="form-check form-check-inline">
+                                    <input class="form-check-input" type="radio" name="inlineRadioOptions" id="inlineRadio2" value="option2">
+                                    <label class="form-check-label" for="inlineRadio2">退回上一审批人 张三</label>
+                                </div></data-min-view>
+                        </li>
+                        <li class="list-group-item">
+                            <h5 class="card-title"><i class="fa fa-stop-circle"></i> 李四 <small class="text-muted">监理</small></h5>
+                        </li>
+                    </ul>
+                </div>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-secondary" data-dismiss="modal">关闭</button>
+                <button type="button" class="btn btn-warning" >确认退回</button>
+            </div>
+        </div>
+    </div>
+</div>
+<!--添加草图-->
+<div class="modal fade" id="edit-img" data-backdrop="static">
+    <div class="modal-dialog modal-lgx" role="document">
+        <div class="modal-content">
+            <div class="modal-header">
+                <h5 class="modal-title">添加草图</h5>
+            </div>
+            <div class="modal-body">
+                <p><a href="" class="btn btn-outline-primary btn-sm">上传图片</a></p>
+                <div class="img-view">
+                    <div class="img-item">
+                        <div class="img-bar">
+                            <a href="" class="text-danger" title="删除"><i class="fa fa-remove"></i></a>
+                        </div>
+                        <img src="img/sketch/1.png" id="draggable">
+                    </div>
+                    <div class="img-item" style="left:300px;">
+                        <div class="img-bar">
+                            <a href="" class="text-danger" title="删除"><i class="fa fa-remove"></i></a>
+                        </div>
+                        <img src="img/sketch/2.png" id="draggable">
+                    </div>
+                </div>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-secondary" data-dismiss="modal">关闭</button>
+                <button type="button" class="btn btn-primary" >确认</button>
+            </div>
+        </div>
+    </div>
+</div>
+<!--上传附件-->
+<div class="modal fade" id="upload" data-backdrop="static">
+    <div class="modal-dialog" role="document">
+        <div class="modal-content">
+            <div class="modal-header">
+                <h5 class="modal-title">上传附件</h5>
+            </div>
+            <div class="modal-body">
+                <div class="form-group">
+                    <label for="formGroupExampleInput">大小限制:10MB,支持<span data-toggle="tooltip" data-placement="bottom" title="" data-original-title="doc,docx,xls,xlsx,ppt,pptx,pdf">office等文档格式</span>、<span data-toggle="tooltip" data-placement="bottom" title="" data-original-title="jpg,png,bmp">图片格式</span>、<span data-toggle="tooltip" data-placement="bottom" title="" data-original-title="rar,zip">压缩包格式</span></label>
+                    <input type="file" class="form-control">
+                </div>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-secondary" data-dismiss="modal">关闭</button>
+                <button type="button" class="btn btn-primary" >确认</button>
+            </div>
+        </div>
+    </div>
+</div>

+ 211 - 0
app/view/stage/pay.ejs

@@ -0,0 +1,211 @@
+<% include ../tender/tender_sub_menu.ejs %>
+<div class="panel-content">
+    <div class="panel-title">
+        <div class="title-main d-flex justify-content-between">
+            <h2>合同支付</h2>
+            <div>
+                <a href="#sub-sp" data-toggle="modal" data-target="#sub-sp" class="btn btn-primary btn-sm pull-right">上报审批</a>
+                <a href="#sub-sp2" data-toggle="modal" data-target="#sub-sp2" class="btn btn-primary btn-sm pull-right">重新上报</a>
+                <a href="#sp-list" data-toggle="modal" data-target="#sp-list" class="btn btn-outline-secondary btn-sm pull-right">审批中</a>
+                <a href="#sp-list" data-toggle="modal" data-target="#sp-list" class="btn btn-outline-warning btn-sm pull-right text-muted">审批退回</a>
+                <!--审批-->
+                <a href="#sp-done" data-toggle="modal" data-target="#sp-done" class="btn btn-success btn-sm pull-right">审批通过</a>
+                <a href="#sp-back" data-toggle="modal" data-target="#sp-back" class="btn btn-warning btn-sm pull-right">审批退回</a>
+            </div>
+        </div>
+    </div>
+    <div class="content-wrap">
+        <div class="c-header p-0"></div>
+        <div class="c-body">
+            <!--上部-->
+            <div class="body-height-top">
+                <div class="row">
+                    <!--左栏-->
+                    <div class="col-8">
+                        <!--工具栏-->
+                        <div class="row">
+                            <div class="col-7">
+                                <div class="btn-group">
+                                    <a href="#" class="btn btn-sm" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="插入"><i class="fa fa-sign-in" aria-hidden="true"></i></a>
+                                    <a href="#" class="btn btn-sm" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="添加"><i class="fa fa-plus" aria-hidden="true"></i></a>
+                                    <a href="#" class="btn btn-sm" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="删除"><i class="fa fa-remove" aria-hidden="true"></i></a>
+                                    <a href="#" class="btn btn-sm" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="下移"><i class="fa fa-arrow-down" aria-hidden="true"></i></a>
+                                    <a href="#" class="btn btn-sm" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="上移"><i class="fa fa-arrow-up" aria-hidden="true"></i></a>
+                                    <a href="#" class="btn btn-sm" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="上移"><i class="fa fa-unlock" aria-hidden="true"></i> 解锁</a>
+                                </div>
+                            </div>
+                            <div class="col-5">
+                                <div class="input-group input-group-sm mb-1">
+                                    <div class="input-group-prepend">
+                                        <span class="input-group-text" id="basic-addon1">表达式</span>
+                                    </div>
+                                    <input type="text" class="form-control" >
+                                </div>
+                            </div>
+                        </div>
+                        <div style="height:330px;overflow: auto;">
+                            <table class="table table-bordered">
+                                <tr><th></th><th>名称</th><th>扣款</th><th>本期金额(表达式)</th><th>累计金额</th><th>起扣金额</th><th>付(扣)款限额</th><th>附件</th><th>操作</th></tr>
+                                <tr><td>1</td><td>本期应付</td><td><input type="checkbox"></td><td></td><td></td><td></td><td></td><td><a class="btn btn-sm" href="#file" data-toggle="modal" data-target="#file"><i class="fa fa-paperclip "></i> 0</a></td>
+                                    <td>
+                                    </td>
+                                </tr>
+                                <tr><td>2</td><td>本期实付</td><td><input type="checkbox"></td><td></td><td></td><td></td><td></td><td><a class="btn btn-sm" href="#file" data-toggle="modal" data-target="#file"><i class="fa fa-paperclip "></i> 3</a></td>
+                                    <td>
+                                    </td>
+                                </tr>
+                                <tr><td>3</td><td>本期完成计量</td><td><input type="checkbox"></td><td></td><td></td><td></td><td></td><td><a class="btn btn-sm" href="#file" data-toggle="modal" data-target="#file"><i class="fa fa-paperclip "></i> 0</a></td>
+                                    <td>
+                                    </td>
+                                </tr>
+                                <!--被停用了-->
+                                <tr class="table-secondary"><td>4</td><td>质量保证金</td><td><input type="checkbox"></td><td></td><td></td><td></td><td></td><td><a class="btn btn-sm" href="#file" data-toggle="modal" data-target="#file"><i class="fa fa-paperclip "></i> 0</a></td>
+                                    <td>
+                                        <a class="btn btn-sm btn-success" href="#">启用</a>
+                                    </td>
+                                </tr>
+                                <tr><td>5</td><td>扣回开工预付款</td><td><input type="checkbox"></td><td></td><td></td><td></td><td></td><td><a class="btn btn-sm" href="#file" data-toggle="modal" data-target="#file"><i class="fa fa-paperclip "></i> 0</a></td>
+                                    <td>
+                                        <div class="dropdown dropleft">
+                                            <button class="btn btn-sm btn-primary dropdown-toggle" type="button" id="dropdownMenuButton" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
+                                                操作
+                                            </button>
+                                            <div class="dropdown-menu" aria-labelledby="dropdownMenuButton">
+                                                <a class="dropdown-item" href="#op2" data-toggle="modal" data-target="#op2">设置计提期限</a>
+                                                <a class="dropdown-item" href="#op3">不参与本期应付计算</a>
+                                                <a class="dropdown-item" href="#op4">停用</a>
+                                            </div>
+                                        </div>
+                                    </td>
+                                </tr>
+                                <!--不参与本期应付计算-->
+                                <tr class="table-secondary"><td>6</td><td>扣回材料预付款</td><td><input type="checkbox"></td><td></td><td></td><td></td><td></td><td><a class="btn btn-sm" href="#file" data-toggle="modal" data-target="#file"><i class="fa fa-paperclip "></i> 1</a></td>
+                                    <td>
+                                        <div class="dropdown dropleft">
+                                            <button class="btn btn-sm btn-primary dropdown-toggle" type="button" id="dropdownMenuButton" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
+                                                操作
+                                            </button>
+                                            <div class="dropdown-menu" aria-labelledby="dropdownMenuButton">
+                                                <a class="dropdown-item" href="#op2" data-toggle="modal" data-target="#op2">设置计提期限</a>
+                                                <a class="dropdown-item" href="#op3">加入本期应付计算</a>
+                                                <a class="dropdown-item" href="#op4">停用</a>
+                                            </div>
+                                        </div>
+                                    </td>
+                                </tr>
+                                <!--新增-->
+                                <tr class="table-warning"><td>7</td><td>审核人新增数据</td><td><input type="checkbox"></td><td></td><td></td><td></td><td></td><td><a class="btn btn-sm" href="#file" data-toggle="modal" data-target="#file"><i class="fa fa-paperclip "></i> 1</a></td>
+                                    <td>
+                                        <button class="btn btn-sm btn-primary" type="button"  href="#op2" data-toggle="modal" data-target="#op2">
+                                            设置计提期限
+                                        </button>
+                                    </td>
+                                </tr>
+                            </table>
+                        </div>
+                    </div>
+                    <!--右栏-->
+                    <div class="col-4">
+                        <div style="height:365px;overflow: hidden;">
+                            <table class="table table-bordered">
+                                <tr><th></th><th>可选基数</th><th>计算代号</th></tr>
+                                <tr><td>1</td><td>签约合同价</td><td>htj</td></tr>
+                                <tr><td>2</td><td>签约合同价(不含暂列金)</td><td>htjszl</td></tr>
+                                <tr><td>3</td><td>签约开工预付款</td><td>kgyfk</td></tr>
+                                <tr><td>4</td><td>签约材料预付款</td><td>clyfk</td></tr>
+                                <tr><td>5</td><td>本期完成计量</td><td>bqwc</td></tr>
+                                <tr><td>6</td><td>100章本期完成计量</td><td>ybbqwc</td></tr>
+                            </table>
+                        </div>
+                    </div>
+                </div>
+            </div>
+            <!--下部-->
+            <div class="body-height-bottom">
+                <legend>章节明细 <a href="#zjset" data-toggle="modal" data-target="#zjset" class="btn btn-sm">章节设置</a></legend>
+                <div class="sjs-height-6">
+                    <table class="table table-bordered">
+                        <tr class="text-center"><th>章节名称</th><th>本期合同计量金额</th><th>本期变更计量金额</th><th>本期完成计量基金</th><th>累计完成计量金额</th></tr>
+                        <tr>
+                            <td>清单 第100章 总则</td>
+                            <td class="text-right">1000</td>
+                            <td class="text-right">1000</td>
+                            <td class="text-right">2000</td>
+                            <td class="text-right">2000</td>
+                        </tr>
+                        <tr>
+                            <td>清单 第200章 路基</td>
+                            <td class="text-right"></td>
+                            <td class="text-right"></td>
+                            <td class="text-right"></td>
+                            <td class="text-right"></td>
+                        </tr>
+                        <tr>
+                            <td>清单 第300章 路面</td>
+                            <td class="text-right"></td>
+                            <td class="text-right"></td>
+                            <td class="text-right"></td>
+                            <td class="text-right"></td>
+                        </tr>
+                        <tr>
+                            <td>清单 第400章 桥梁、涵洞</td>
+                            <td class="text-right"></td>
+                            <td class="text-right"></td>
+                            <td class="text-right"></td>
+                            <td class="text-right"></td>
+                        </tr>
+                        <tr>
+                            <td>清单 第500章 隧道</td>
+                            <td class="text-right"></td>
+                            <td class="text-right"></td>
+                            <td class="text-right"></td>
+                            <td class="text-right"></td>
+                        </tr>
+                        <tr>
+                            <td>清单 第600章 安全设施及预埋管线</td>
+                            <td class="text-right"></td>
+                            <td class="text-right"></td>
+                            <td class="text-right"></td>
+                            <td class="text-right"></td>
+                        </tr>
+                        <tr>
+                            <td>清单 第700章 绿化及环境保护</td>
+                            <td class="text-right"></td>
+                            <td class="text-right"></td>
+                            <td class="text-right"></td>
+                            <td class="text-right"></td>
+                        </tr>
+                        <tr>
+                            <td>未计入章节清单合计</td>
+                            <td class="text-right"></td>
+                            <td class="text-right"></td>
+                            <td class="text-right"></td>
+                            <td class="text-right"></td>
+                        </tr>
+                        <tr>
+                            <td>清单小计(A)</td>
+                            <td class="text-right">1000</td>
+                            <td class="text-right">1000</td>
+                            <td class="text-right">2000</td>
+                            <td class="text-right">2000</td>
+                        </tr>
+                        <tr>
+                            <td>非清单项费用(B)</td>
+                            <td class="text-right">500</td>
+                            <td class="text-right"></td>
+                            <td class="text-right">500</td>
+                            <td class="text-right">500</td>
+                        </tr>
+                        <tr>
+                            <td>合计(C=A+B)</td>
+                            <td class="text-right">1500</td>
+                            <td class="text-right">1000</td>
+                            <td class="text-right">2500</td>
+                            <td class="text-right">2500</td>
+                        </tr>
+                    </table>
+                </div>
+            </div>
+        </div>
+    </div>
+</div>

+ 275 - 0
app/view/stage/pay_modal.ejs

@@ -0,0 +1,275 @@
+<!--上报审批-->
+<div class="modal fade" id="sub-sp" data-backdrop="static">
+    <div class="modal-dialog" role="document">
+        <div class="modal-content">
+            <div class="modal-header">
+                <h5 class="modal-title">上报审批</h5>
+            </div>
+            <div class="modal-body">
+                <div class="form-group">
+                    <label>搜索审批人</label>
+                    <div class="input-group">
+                        <input class="form-control" placeholder="请输入姓名进行检索" type="text">
+                        <div class="input-group-append">
+                            <button class="btn btn-outline-secondary" type="button"><i class="fa fa-search"></i></button>
+                        </div>
+                    </div>
+                </div>
+                <div class="card border-primary">
+                    <div class="card-body">
+                        <h5 class="card-title">
+                            <a href="#" class="btn btn-primary btn-sm pull-right">添加</a>张三
+                        </h5>
+                        <h6 class="card-subtitle mb-2 text-muted">监理</h6>
+                        <p class="card-text">XXXXX公司</p>
+                    </div>
+                </div>
+                <div class="card mt-3">
+                    <div class="card-header">
+                        审批流程
+                    </div>
+                    <ul class="list-group list-group-flush">
+                        <li class="list-group-item"><a href="" class="text-danger pull-right">移除</a>1 张三  <small class="text-muted">监理</small></li>
+                        <li class="list-group-item"><a href="" class="text-danger pull-right">移除</a>2 王五 <small class="text-muted">监理</small></li>
+                        <li class="list-group-item"><a href="" class="text-danger pull-right">移除</a>3 李四 <small class="text-muted">监理</small></li>
+                    </ul>
+                </div>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-secondary" data-dismiss="modal">关闭</button>
+                <button type="button" class="btn btn-primary">确认上报</button>
+            </div>
+        </div>
+    </div>
+</div>
+<!--审批流程/结果-->
+<div class="modal fade" id="sp-list" 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="card mt-3">
+                    <ul class="list-group list-group-flush">
+                        <li class="list-group-item">
+                            <span class="text-success pull-right">审批通过</span>
+                            <h5 class="card-title">1 张三 <small class="text-muted">监理</small></h5>
+                            <p class="card-text">审批意见。2018-01-01</p>
+                        </li>
+                        <li class="list-group-item">
+                            <span class="text-warning pull-right">审批中</span>
+                            <h5 class="card-title">2 王五 <small class="text-muted">监理</small></h5>
+                            <p class="card-text"></p>
+                        </li>
+                        <li class="list-group-item">
+                            <h5 class="card-title">3 李四 <small class="text-muted">监理</small></h5>
+                        </li>
+                    </ul>
+                </div>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-secondary" data-dismiss="modal">关闭</button>
+            </div>
+        </div>
+    </div>
+</div>
+<!--审批通过-->
+<div class="modal fade" id="sp-done" 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="card mt-3">
+                    <ul class="list-group list-group-flush">
+                        <li class="list-group-item">
+                            <span class="text-success pull-right">审批通过</span>
+                            <h5 class="card-title">1 张三 <small class="text-muted">监理</small></h5>
+                            <p class="card-text">审批意见。2018-01-01</p>
+                        </li>
+                        <li class="list-group-item">
+                            <span class="text-warning pull-right">审批中</span>
+                            <h5 class="card-title">2 王五 <small class="text-muted">监理</small></h5>
+                            <div class="form-group">
+                                <label>审批意见<b class="text-danger">*</b></label>
+                                <textarea class="form-control" ></textarea>
+                            </div>
+                        </li>
+                        <li class="list-group-item">
+                            <h5 class="card-title">3 李四 <small class="text-muted">监理</small></h5>
+                        </li>
+                    </ul>
+                </div>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-secondary" data-dismiss="modal">关闭</button>
+                <button type="button" class="btn btn-success" >确认通过</button>
+            </div>
+        </div>
+    </div>
+</div>
+<!--审批退回-->
+<div class="modal fade" id="sp-back" 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="card mt-3">
+                    <ul class="list-group list-group-flush">
+                        <li class="list-group-item">
+                            <span class="text-success pull-right">审批通过</span>
+                            <h5 class="card-title">1 张三 <small class="text-muted">监理</small></h5>
+                            <p class="card-text">审批意见。2018-01-01</p>
+                        </li>
+                        <li class="list-group-item">
+                            <span class="text-warning pull-right">审批中</span>
+                            <h5 class="card-title">2 王五 <small class="text-muted">监理</small></h5>
+                            <div class="form-group">
+                                <label>审批意见<b class="text-danger">*</b></label>
+                                <textarea class="form-control" ></textarea>
+                            </div>
+                            <div class="alert alert-warning">审批退回,将直接退回给上报人。</data-min-view>
+                        </li>
+                        <li class="list-group-item">
+                            <h5 class="card-title">3 李四 <small class="text-muted">监理</small></h5>
+                        </li>
+                    </ul>
+                </div>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-secondary" data-dismiss="modal">关闭</button>
+                <button type="button" class="btn btn-warning" >确认退回</button>
+            </div>
+        </div>
+    </div>
+</div>
+<!--章节设置-->
+<div class="modal fade" id="zjset" 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">
+                <table class="table table-sm table-bordered">
+                    <tr><th width="200">章节</th><th>名称</th></tr>
+                    <tr><td>100</td><td><input type="text" class="form-control form-control-sm" value="总则"></td></tr>
+                    <tr><td>200</td><td><input type="text" class="form-control form-control-sm" value="路基"></td></tr>
+                    <tr><td>300</td><td><input type="text" class="form-control form-control-sm" value="路面"></td></tr>
+                    <tr><td>400</td><td><input type="text" class="form-control form-control-sm" value="桥梁、涵洞"></td></tr>
+                    <tr><td>500</td><td><input type="text" class="form-control form-control-sm" value="隧道"></td></tr>
+                    <tr><td>600</td><td><input type="text" class="form-control form-control-sm" value="安全设施及预埋管线"></td></tr>
+                    <tr><td>700</td><td><input type="text" class="form-control form-control-sm" value="绿化及环境保护"></td></tr>
+                    <tr><td>800</td><td><input type="text" class="form-control form-control-sm" value="公路沿线管理用房设施"></td></tr>
+                    <tr><td>900</td><td><input type="text" class="form-control form-control-sm" value="监控系统"></td></tr>
+                    <tr><td>1000</td><td><input type="text" class="form-control form-control-sm" value="收费系统"></td></tr>
+                    <tr><td>1100</td><td><input type="text" class="form-control form-control-sm" value="通信系统"></td></tr>
+                    <tr><td>1200</td><td><input type="text" class="form-control form-control-sm" value="消防系统"></td></tr>
+                    <tr><td>1300</td><td><input type="text" class="form-control form-control-sm" value="供配电及照明系统"></td></tr>
+                </table>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-secondary" data-dismiss="modal">关闭</button>
+                <button type="button" class="btn btn-primary">确定修改</button>
+            </div>
+        </div>
+    </div>
+</div>
+<!--设置计提期限-->
+<div class="modal fade" id="op2" 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">
+                <p>请设置付(扣)款项 <b>本期应付</b> 的计提期限</p>
+                <div class="form-group">
+                    <label for="formGroupExampleInput">限制模式为:</label>
+                    <div>
+                        <div class="form-check form-check-inline">
+                            <input class="form-check-input" type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1">
+                            <label class="form-check-label" for="inlineRadio1">无</label>
+                        </div>
+                        <div class="form-check form-check-inline">
+                            <input class="form-check-input" type="radio" name="inlineRadioOptions" id="inlineRadio2" value="option2">
+                            <label class="form-check-label" for="inlineRadio2">计量期数</label>
+                        </div>
+                        <div class="form-check form-check-inline">
+                            <input class="form-check-input" type="radio" name="inlineRadioOptions" id="inlineRadio3" value="option3">
+                            <label class="form-check-label" for="inlineRadio3">计量金额</label>
+                        </div>
+                    </div>
+                </div>
+                <!--计量期数模式-->
+                <div class="form-group">
+                    <label>限制期数</label>
+                    <input class="form-control" type="number">
+                </div>
+                <!--计量金额模式-->
+                <div class="form-group">
+                    <label for="formGroupExampleInput"></label>
+                    <div>
+                        <div class="form-check form-check-inline">
+                            <input class="form-check-input" type="radio" name="iROptions" id="ilRadio1" value="option1">
+                            <label class="form-check-label" for="ilRadio1">累计完成计量金额</label>
+                        </div>
+                        <div class="form-check form-check-inline">
+                            <input class="form-check-input" type="radio" name="iROptions" id="ilRadio2" value="option2">
+                            <label class="form-check-label" for="ilRadio2">累计合同计量金额</label>
+                        </div>
+                        <div class="form-check form-check-inline">
+                            <input class="form-check-input" type="radio" name="iROptions" id="ilRadio3" value="option3">
+                            <label class="form-check-label" for="ilRadio3">累计变更计量金额</label>
+                        </div>
+                    </div>
+                </div>
+                <div class="form-group">
+                    <label>限制金额</label>
+                    <input class="form-control" type="number">
+                </div>
+                <!--公用提示-->
+                <p>设置为:</p>
+                <p class="pl-3 text-danger">当 累计完成计量金额 >= 50000.00 时</p>
+                <p class="pl-3">当期金额直接计量至(扣款限额 - 截止上期金额)</p>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-secondary" data-dismiss="modal">关闭</button>
+                <button type="button" class="btn btn-primary" >确定</button>
+            </div>
+        </div>
+    </div>
+</div>
+<!--附件-->
+<div class="modal fade" id="file" data-backdrop="static">
+    <div class="modal-dialog" role="document">
+        <div class="modal-content">
+            <div class="modal-header">
+                <h5 class="modal-title">附件</h5>
+            </div>
+            <div class="modal-body">
+                <div class="form-group">
+                    <label for="formGroupExampleInput">大小限制:10MB,支持<span data-toggle="tooltip" data-placement="bottom" title="doc,docx,xls,xlsx,ppt,pptx,pdf">office等文档格式</span>、<span data-toggle="tooltip" data-placement="bottom" title="jpg,png,bmp">图片格式</span>、<span data-toggle="tooltip" data-placement="bottom" title="rar,zip">压缩包格式</span></label>
+                    <input type="file" class="form-control">
+                </div>
+                <div class="modal-height-500">
+                    <table class="table table-sm table-bordered">
+                        <thead>
+                        <tr><th>文件名</th><th>上传人</th><th>上传时间</th><th>操作</th></tr>
+                        </thead>
+                        <tr><td>XXXX.jpg</td><td>张三</td><td>2018-01-01</td><td><a href="#" target="_blank" title="下载"><i class="fa fa-download "></i></a> <a class="text-danger" href="#" target="_blank" title="删除"><i class="fa fa-remove "></i></a></td></tr>
+                        <tr><td>YYYY.jpg</td><td>李四</td><td>2018-01-01</td><td><a href="#" target="_blank" title="下载"><i class="fa fa-download "></i></a></td></tr>
+                    </table>
+                </div>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-secondary" data-dismiss="modal">关闭</button>
+                <button type="button" class="btn btn-primary" >确定</button>
+            </div>
+        </div>
+    </div>
+</div>

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

@@ -15,7 +15,7 @@
             <ul class="nav-list list-unstyled <% if (menu.display) { %>sub-list<% } %>">
                 <% for (const mc of menu.children) { %>
                 <li <% if (ctx.url === preUrl + mc.url) { %>class="active"<% } %>>
-                    <a href="<%- preUrl %><%- mc.url %>"><%- mc.icon %><span><%- mc.name %></span></a>
+                    <a href="<%- (mc.fixedUrl ? mc.url : preUrl + mc.url) %>"><%- mc.icon %><span <%- mc.class%>><%- mc.name %></span></a>
                 </li>
                 <% } %>
             </ul>

+ 2 - 1
config/config.qa.js

@@ -46,9 +46,10 @@ module.exports = appInfo => {
     // session配置
     config.session = {
         key: 'ZHC_SESS',
-        maxAge: 8 * 3600 * 1000, // 8小时
+        maxAge: 3600 * 1000, // 1小时
         httpOnly: true,
         encrypt: true,
+        renew: true, // session临近过期更新过期时间
     };
 
     // 是否压缩替换前端js

+ 95 - 0
config/menu.js

@@ -114,6 +114,100 @@ const tenderMenu = {
     },
 };
 
+const stageMenu = {
+    back: {
+        name: '返回',
+        display: false,
+        children: [
+            {
+                name: '返回',
+                icon: '<i class="fa fa-chevron-left "></i> ',
+                display: true,
+                url: '/measure/stage',
+                fixedUrl: true,
+            }
+        ]
+    },
+    ledger: {
+        name: '本期计量台帐',
+        display: false,
+        children: [
+            {
+                name: '本期计量台帐',
+                icon: '',
+                display: true,
+                url: '',
+                class: ' class="ml-3"',
+            }
+        ]
+    },
+    detail: {
+        name: '中间计量',
+        display: false,
+        children: [
+            {
+                name: '中间计量',
+                icon: '',
+                display: true,
+                url: '/detail',
+                class: ' class="ml-3"',
+            }
+        ]
+    },
+    pay: {
+        name: '合同支付',
+        display: false,
+        children: [
+            {
+                name: '合同支付',
+                icon: '',
+                display: true,
+                url: '/pay',
+                class: ' class="ml-3"',
+            }
+        ]
+    },
+    change: {
+        name: '变更令',
+        display: false,
+        children: [
+            {
+                name: '变更令',
+                icon: '',
+                display: true,
+                url: '/change',
+                class: ' class="ml-3"',
+            }
+        ]
+    },
+    compare: {
+        name: '审核比较',
+        display: false,
+        children: [
+            {
+                name: '审核比较',
+                icon: '',
+                display: true,
+                url: '/compare',
+                class: ' class="ml-3"',
+            }
+        ]
+    },
+    report: {
+        name: '报表',
+        display: false,
+        children: [
+            {
+                name: '报表',
+                icon: '<i class="fa fa-file-text-o"></i> ',
+                display: true,
+                url: '/report',
+                class: '',
+            }
+        ]
+    },
+};
+
 const sumMenu = {
     info: {
         name: '总包概况',
@@ -165,6 +259,7 @@ const settingMenu = {
 module.exports = {
     menu,
     tenderMenu,
+    stageMenu,
     sumMenu,
     settingMenu,
 };